diff --git a/public/locales/en.json b/public/locales/en.json index c90d1bf197..b55a27f63c 100644 --- a/public/locales/en.json +++ b/public/locales/en.json @@ -271,7 +271,10 @@ }, "organizationActionItems": { "actionItemCategory": "Action Item Category", + "actionItemActive": "Action Item Active", + "actionItemCompleted": "Action Item Completed", "actionItemDetails": "Action Item Details", + "actionItemStatus": "Action Item Status", "assignee": "Assignee", "assigner": "Assigner", "assignmentDate": "Assignment Date", @@ -287,13 +290,16 @@ "dueDate": "Due Date", "earliest": "Earliest", "editActionItem": "Edit Action Item", + "eventActionItems": "Event Action Items", "isCompleted": "Completed", "latest": "Latest", + "makeActive": "Make Active", + "markCompletion": "Mark Completion", "no": "No", "noActionItems": "No Action Items", "options": "Options", - "preCompletionNotes": "Pre Completion Notes", - "postCompletionNotes": "Post Completion Notes", + "preCompletionNotes": "Notes", + "postCompletionNotes": "Completion Notes", "selectActionItemCategory": "Select an action item category", "selectAssignee": "Select an assignee", "status": "Status", diff --git a/public/locales/fr.json b/public/locales/fr.json index 7842eb6a67..0fc275c755 100644 --- a/public/locales/fr.json +++ b/public/locales/fr.json @@ -266,7 +266,10 @@ }, "organizationActionItems": { "actionItemCategory": "Catégorie de l'élément d'action", + "actionItemActive": "Mesure de suivi active", + "actionItemCompleted": "Mesure de suivi terminée", "actionItemDetails": "Détails de l'élément d'action", + "actionItemStatus": "État de l’action", "assignee": "Attributaire", "assigner": "Assignateur", "assignmentDate": "Date d'attribution", @@ -282,8 +285,11 @@ "dueDate": "Date d'échéance", "earliest": "Le plus tôt", "editActionItem": "Modifier l'élément d'action", + "eventActionItems": "Mesures à prendre pour l’événement", "isCompleted": "Terminé", "latest": "Le plus récent", + "makeActive": "Rendre actif", + "markCompletion": "Marquer l'achèvement", "no": "Non", "noActionItems": "Pas d'éléments d'action", "options": "Options", diff --git a/public/locales/hi.json b/public/locales/hi.json index d5f0c6c143..f116148147 100644 --- a/public/locales/hi.json +++ b/public/locales/hi.json @@ -266,7 +266,10 @@ }, "organizationActionItems": { "actionItemCategory": "कार्य आइटम श्रेणी", + "actionItemActive": "क्रिया आइटम सक्रिय", + "actionItemCompleted": "क्रिया आइटम पूर्ण हुआ", "actionItemDetails": "कार्य आइटम विवरण", + "actionItemStatus": "कार्रवाई आइटम स्थिति", "assignee": "सौंपने वाला", "assigner": "निर्धारक", "assignmentDate": "सौंपने की तारीख", @@ -282,8 +285,11 @@ "dueDate": "नियत तारीख", "earliest": "सबसे पहले", "editActionItem": "कार्य आइटम संपादित करें", + "eventActionItems": "इवेंट एक्शन आइटम", "isCompleted": "पूरा हुआ", "latest": "नवीनतम", + "makeActive": "सक्रिय करें", + "markCompletion": "मार्क पूर्णता", "no": "नहीं", "noActionItems": "कोई कार्य आइटम नहीं", "options": "विकल्प", diff --git a/public/locales/sp.json b/public/locales/sp.json index 470c4c12e7..051d3f73f8 100644 --- a/public/locales/sp.json +++ b/public/locales/sp.json @@ -266,7 +266,10 @@ }, "organizationActionItems": { "actionItemCategory": "Categoría del ítem de acción", + "actionItemActive": "Elemento de acción activo", + "actionItemCompleted": "Elemento de acción completado", "actionItemDetails": "Detalles del ítem de acción", + "actionItemStatus": "Estado del elemento de acción", "assignee": "Asignado", "assigner": "Asignador", "assignmentDate": "Fecha de asignación", @@ -282,8 +285,11 @@ "dueDate": "Fecha de vencimiento", "earliest": "Lo más temprano", "editActionItem": "Editar ítem de acción", + "eventActionItems": "Elementos de acción del evento", "isCompleted": "Completado", "latest": "Lo más reciente", + "makeActive": "Activar", + "markCompletion": "Marcar finalización", "no": "No", "noActionItems": "No hay ítems de acción", "options": "Opciones", diff --git a/public/locales/zh.json b/public/locales/zh.json index b2044851bd..f43b2dc2c8 100644 --- a/public/locales/zh.json +++ b/public/locales/zh.json @@ -295,7 +295,10 @@ }, "organizationActionItems": { "actionItemCategory": "行动项目类别", + "actionItemActive": "活动措施项", + "actionItemCompleted": "已完成操作项", "actionItemDetails": "行动项目详情", + "actionItemStatus": "措施项状态", "assignee": "受让人", "assigner": "分配者", "assignmentDate": "分配日期", @@ -311,8 +314,11 @@ "dueDate": "到期日", "earliest": "最早", "editActionItem": "编辑行动项目", + "eventActionItems": "事件操作项", "isCompleted": "已完成", "latest": "最新", + "makeActive": "激活", + "markCompletion": "标记完成", "no": "否", "noActionItems": "无行动项目", "options": "选项", diff --git a/src/components/ActionItems/ActionItemsContainer.module.css b/src/components/ActionItems/ActionItemsContainer.module.css new file mode 100644 index 0000000000..b55328c563 --- /dev/null +++ b/src/components/ActionItems/ActionItemsContainer.module.css @@ -0,0 +1,25 @@ +.actionItemStatusBadge { + width: 5.5rem; + margin-left: 1.1rem; +} + +.createModal { + margin-top: 20vh; + margin-left: 13vw; + max-width: 80vw; +} + +.titlemodal { + color: var(--bs-gray-600); + font-weight: 600; + font-size: 20px; + margin-bottom: 20px; + padding-bottom: 5px; + border-bottom: 3px solid var(--bs-primary); + width: 65%; +} + +.actionItemsOptionsButton { + width: 24px; + height: 24px; +} diff --git a/src/components/ActionItemsContainer/ActionItemsContainer.test.tsx b/src/components/ActionItems/ActionItemsContainer.test.tsx similarity index 68% rename from src/components/ActionItemsContainer/ActionItemsContainer.test.tsx rename to src/components/ActionItems/ActionItemsContainer.test.tsx index 107e027588..dda9e07035 100644 --- a/src/components/ActionItemsContainer/ActionItemsContainer.test.tsx +++ b/src/components/ActionItems/ActionItemsContainer.test.tsx @@ -62,7 +62,6 @@ describe('Testing Action Item Categories Component', () => { const formData = { assignee: 'Scott Norris', preCompletionNotes: 'pre completion notes edited', - postCompletionNotes: 'post completion notes', dueDate: '02/14/2024', completionDate: '02/21/2024', }; @@ -147,7 +146,7 @@ describe('Testing Action Item Categories Component', () => { ); }); - test('opens and closes the delete modal correctly', async () => { + test('opens and closes the action item status change modal correctly', async () => { render( @@ -166,20 +165,20 @@ describe('Testing Action Item Categories Component', () => { await waitFor(() => { expect( - screen.getAllByTestId('deleteActionItemModalBtn')[0], + screen.getAllByTestId('actionItemStatusChangeCheckbox')[0], ).toBeInTheDocument(); }); - userEvent.click(screen.getAllByTestId('deleteActionItemModalBtn')[0]); + userEvent.click(screen.getAllByTestId('actionItemStatusChangeCheckbox')[0]); await waitFor(() => { return expect( - screen.findByTestId('actionItemDeleteModalCloseBtn'), + screen.findByTestId('actionItemStatusChangeModalCloseBtn'), ).resolves.toBeInTheDocument(); }); - userEvent.click(screen.getByTestId('actionItemDeleteModalCloseBtn')); + userEvent.click(screen.getByTestId('actionItemStatusChangeModalCloseBtn')); await waitForElementToBeRemoved(() => - screen.queryByTestId('actionItemDeleteModalCloseBtn'), + screen.queryByTestId('actionItemStatusChangeModalCloseBtn'), ); }); @@ -325,11 +324,11 @@ describe('Testing Action Item Categories Component', () => { fireEvent.change(preCompletionNotes, { target: { value: '' } }); userEvent.type(preCompletionNotes, formData.preCompletionNotes); - const postCompletionNotes = screen.getByPlaceholderText( - translations.postCompletionNotes, - ); - fireEvent.change(postCompletionNotes, { target: { value: '' } }); - userEvent.type(postCompletionNotes, formData.postCompletionNotes); + // const postCompletionNotes = screen.getByPlaceholderText( + // translations.postCompletionNotes, + // ); + // fireEvent.change(postCompletionNotes, { target: { value: '' } }); + // userEvent.type(postCompletionNotes, formData.postCompletionNotes); const dueDatePicker = screen.getByLabelText(translations.dueDate); fireEvent.change(dueDatePicker, { @@ -343,10 +342,10 @@ describe('Testing Action Item Categories Component', () => { target: { value: formData.completionDate }, }); - await waitFor(() => { - expect(screen.getByTestId('alldayCheck')).toBeInTheDocument(); - }); - userEvent.click(screen.getByTestId('alldayCheck')); + // await waitFor(() => { + // expect(screen.getByTestId('alldayCheck')).toBeInTheDocument(); + // }); + // userEvent.click(screen.getByTestId('alldayCheck')); await waitFor(() => { expect(screen.getByTestId('editActionItemBtn')).toBeInTheDocument(); @@ -397,11 +396,11 @@ describe('Testing Action Item Categories Component', () => { fireEvent.change(preCompletionNotes, { target: { value: '' } }); userEvent.type(preCompletionNotes, formData.preCompletionNotes); - const postCompletionNotes = screen.getByPlaceholderText( - translations.postCompletionNotes, - ); - fireEvent.change(postCompletionNotes, { target: { value: '' } }); - userEvent.type(postCompletionNotes, formData.postCompletionNotes); + // const postCompletionNotes = screen.getByPlaceholderText( + // translations.postCompletionNotes, + // ); + // fireEvent.change(postCompletionNotes, { target: { value: '' } }); + // userEvent.type(postCompletionNotes, formData.postCompletionNotes); const dueDatePicker = screen.getByLabelText(translations.dueDate); fireEvent.change(dueDatePicker, { @@ -415,10 +414,10 @@ describe('Testing Action Item Categories Component', () => { target: { value: formData.completionDate }, }); - await waitFor(() => { - expect(screen.getByTestId('alldayCheck')).toBeInTheDocument(); - }); - userEvent.click(screen.getByTestId('alldayCheck')); + // await waitFor(() => { + // expect(screen.getByTestId('alldayCheck')).toBeInTheDocument(); + // }); + // userEvent.click(screen.getByTestId('alldayCheck')); await waitFor(() => { expect(screen.getByTestId('editActionItemBtn')).toBeInTheDocument(); @@ -430,6 +429,90 @@ describe('Testing Action Item Categories Component', () => { }); }); + test('updates an action item status through the action item status change modal', async () => { + render( + + + + + + + + + + + , + ); + + await wait(); + + await waitFor(() => { + expect( + screen.getAllByTestId('actionItemStatusChangeCheckbox')[0], + ).toBeInTheDocument(); + }); + userEvent.click(screen.getAllByTestId('actionItemStatusChangeCheckbox')[0]); + + await waitFor(() => { + expect( + screen.getByTestId('actionItemsStatusChangeNotes'), + ).toBeInTheDocument(); + }); + + const postCompletionNotes = screen.getByTestId( + 'actionItemsStatusChangeNotes', + ); + fireEvent.change(postCompletionNotes, { target: { value: '' } }); + userEvent.type( + postCompletionNotes, + 'this action item has been completed successfully', + ); + + await waitFor(() => { + expect( + screen.getByTestId('actionItemStatusChangeSubmitBtn'), + ).toBeInTheDocument(); + }); + userEvent.click(screen.getByTestId('actionItemStatusChangeSubmitBtn')); + + await waitFor(() => { + expect(toast.success).toBeCalledWith(translations.successfulUpdation); + }); + + await waitFor(() => { + expect( + screen.getAllByTestId('actionItemStatusChangeCheckbox')[1], + ).toBeInTheDocument(); + }); + userEvent.click(screen.getAllByTestId('actionItemStatusChangeCheckbox')[1]); + + await waitFor(() => { + expect( + screen.getByTestId('actionItemsStatusChangeNotes'), + ).toBeInTheDocument(); + }); + + const preCompletionNotes = screen.getByTestId( + 'actionItemsStatusChangeNotes', + ); + fireEvent.change(preCompletionNotes, { target: { value: '' } }); + userEvent.type( + preCompletionNotes, + 'this action item has been made active again', + ); + + await waitFor(() => { + expect( + screen.getByTestId('actionItemStatusChangeSubmitBtn'), + ).toBeInTheDocument(); + }); + userEvent.click(screen.getByTestId('actionItemStatusChangeSubmitBtn')); + + await waitFor(() => { + expect(toast.success).toBeCalledWith(translations.successfulUpdation); + }); + }); + test('deletes the action item and toasts success', async () => { render( @@ -449,16 +532,30 @@ describe('Testing Action Item Categories Component', () => { await waitFor(() => { expect( - screen.getAllByTestId('deleteActionItemModalBtn')[0], + screen.getAllByTestId('previewActionItemModalBtn')[0], + ).toBeInTheDocument(); + }); + userEvent.click(screen.getAllByTestId('previewActionItemModalBtn')[0]); + + await waitFor(() => { + return expect( + screen.findByTestId('previewActionItemModalCloseBtn'), + ).resolves.toBeInTheDocument(); + }); + + await waitFor(() => { + expect( + screen.getByTestId('deleteActionItemPreviewModalBtn'), ).toBeInTheDocument(); }); - userEvent.click(screen.getAllByTestId('deleteActionItemModalBtn')[0]); + userEvent.click(screen.getByTestId('deleteActionItemPreviewModalBtn')); await waitFor(() => { return expect( screen.findByTestId('actionItemDeleteModalCloseBtn'), ).resolves.toBeInTheDocument(); }); + userEvent.click(screen.getByTestId('deleteActionItemBtn')); await waitFor(() => { @@ -485,10 +582,23 @@ describe('Testing Action Item Categories Component', () => { await waitFor(() => { expect( - screen.getAllByTestId('deleteActionItemModalBtn')[0], + screen.getAllByTestId('previewActionItemModalBtn')[0], + ).toBeInTheDocument(); + }); + userEvent.click(screen.getAllByTestId('previewActionItemModalBtn')[0]); + + await waitFor(() => { + return expect( + screen.findByTestId('previewActionItemModalCloseBtn'), + ).resolves.toBeInTheDocument(); + }); + + await waitFor(() => { + expect( + screen.getByTestId('deleteActionItemPreviewModalBtn'), ).toBeInTheDocument(); }); - userEvent.click(screen.getAllByTestId('deleteActionItemModalBtn')[0]); + userEvent.click(screen.getByTestId('deleteActionItemPreviewModalBtn')); await waitFor(() => { return expect( @@ -501,4 +611,60 @@ describe('Testing Action Item Categories Component', () => { expect(toast.error).toHaveBeenCalled(); }); }); + + test('shows the overlay text on action item notes', async () => { + const { getAllByTestId } = render( + + + + + + + + + + + , + ); + + await wait(); + + await waitFor(() => { + expect( + screen.getAllByTestId('actionItemPreCompletionNotesOverlay')[0], + ).toBeInTheDocument(); + }); + + await waitFor(() => { + fireEvent.mouseEnter( + getAllByTestId('actionItemPreCompletionNotesOverlay')[0], + ); + }); + + await waitFor(() => { + expect(screen.getByTestId('popover-actionItem1')).toBeInTheDocument(); + }); + + await waitFor(() => { + fireEvent.mouseLeave( + getAllByTestId('actionItemPreCompletionNotesOverlay')[0], + ); + }); + + await waitFor(() => { + fireEvent.mouseEnter( + getAllByTestId('actionItemPostCompletionNotesOverlay')[0], + ); + }); + + await waitFor(() => { + expect(screen.getByTestId('popover-actionItem2')).toBeInTheDocument(); + }); + + await waitFor(() => { + fireEvent.mouseLeave( + getAllByTestId('actionItemPostCompletionNotesOverlay')[0], + ); + }); + }); }); diff --git a/src/components/ActionItems/ActionItemsContainer.tsx b/src/components/ActionItems/ActionItemsContainer.tsx new file mode 100644 index 0000000000..80a5d9651e --- /dev/null +++ b/src/components/ActionItems/ActionItemsContainer.tsx @@ -0,0 +1,451 @@ +import React, { useState } from 'react'; +import dayjs from 'dayjs'; +import type { ChangeEvent } from 'react'; +import { + Button, + Col, + Form, + Modal, + OverlayTrigger, + Popover, + Row, +} from 'react-bootstrap'; +import { useTranslation } from 'react-i18next'; +import { toast } from 'react-toastify'; + +import { + DELETE_ACTION_ITEM_MUTATION, + UPDATE_ACTION_ITEM_MUTATION, +} from 'GraphQl/Mutations/mutations'; +import { useMutation } from '@apollo/client'; + +import type { + InterfaceActionItemInfo, + InterfaceMemberInfo, +} from 'utils/interfaces'; +import styles from './ActionItemsContainer.module.css'; +import ActionItemUpdateModal from '../../screens/OrganizationActionItems/ActionItemUpdateModal'; +import ActionItemPreviewModal from '../../screens/OrganizationActionItems/ActionItemPreviewModal'; +import ActionItemDeleteModal from '../../screens/OrganizationActionItems/ActionItemDeleteModal'; + +function actionItemsContainer({ + actionItemsConnection, + actionItemsData, + membersData, + actionItemsRefetch, +}: { + actionItemsConnection: 'Organization' | 'Event'; + actionItemsData: InterfaceActionItemInfo[] | undefined; + membersData: InterfaceMemberInfo[] | undefined; + actionItemsRefetch: any; +}): JSX.Element { + const { t } = useTranslation('translation', { + keyPrefix: 'organizationActionItems', + }); + + const [actionItemPreviewModalIsOpen, setActionItemPreviewModalIsOpen] = + useState(false); + const [actionItemUpdateModalIsOpen, setActionItemUpdateModalIsOpen] = + useState(false); + const [actionItemDeleteModalIsOpen, setActionItemDeleteModalIsOpen] = + useState(false); + const [actionItemStatusModal, setActionItemStatusModal] = useState(false); + const [isActionItemCompleted, setIsActionItemCompleted] = useState(false); + + const [assignmentDate, setAssignmentDate] = useState(new Date()); + const [dueDate, setDueDate] = useState(new Date()); + const [completionDate, setCompletionDate] = useState(new Date()); + const [actionItemId, setActionItemId] = useState(''); + const [actionItemNotes, setActionItemNotes] = useState(''); + + const [formState, setFormState] = useState({ + assignee: '', + assigner: '', + assigneeId: '', + preCompletionNotes: '', + postCompletionNotes: '', + isCompleted: false, + }); + + const showPreviewModal = (actionItem: InterfaceActionItemInfo): void => { + setActionItemState(actionItem); + setActionItemPreviewModalIsOpen(true); + }; + + const showUpdateModal = (): void => { + setActionItemUpdateModalIsOpen(!actionItemUpdateModalIsOpen); + }; + + const hidePreviewModal = (): void => { + setActionItemPreviewModalIsOpen(false); + }; + + const hideUpdateModal = (): void => { + setActionItemId(''); + setActionItemUpdateModalIsOpen(!actionItemUpdateModalIsOpen); + }; + + const toggleDeleteModal = (): void => { + setActionItemDeleteModalIsOpen(!actionItemDeleteModalIsOpen); + }; + + const [updateActionItem] = useMutation(UPDATE_ACTION_ITEM_MUTATION); + + const updateActionItemHandler = async ( + e: ChangeEvent, + ): Promise => { + e.preventDefault(); + try { + await updateActionItem({ + variables: { + actionItemId, + assigneeId: formState.assigneeId, + preCompletionNotes: formState.preCompletionNotes, + postCompletionNotes: formState.postCompletionNotes, + dueDate: dayjs(dueDate).format('YYYY-MM-DD'), + completionDate: dayjs(completionDate).format('YYYY-MM-DD'), + isCompleted: formState.isCompleted, + }, + }); + + actionItemsRefetch(); + hideUpdateModal(); + toast.success(t('successfulUpdation')); + } catch (error: any) { + toast.error(error.message); + console.log(error); + } + }; + + const [removeActionItem] = useMutation(DELETE_ACTION_ITEM_MUTATION); + const deleteActionItemHandler = async (): Promise => { + try { + await removeActionItem({ + variables: { + actionItemId, + }, + }); + + actionItemsRefetch(); + toggleDeleteModal(); + toast.success(t('successfulDeletion')); + } catch (error: any) { + toast.error(error.message); + console.log(error); + } + }; + + const handleEditClick = (actionItem: InterfaceActionItemInfo): void => { + setActionItemState(actionItem); + showUpdateModal(); + }; + + const handleActionItemStatusChange = ( + actionItem: InterfaceActionItemInfo, + ): void => { + actionItem = { ...actionItem, isCompleted: !actionItem.isCompleted }; + setIsActionItemCompleted(!actionItem.isCompleted); + setActionItemState(actionItem); + setActionItemStatusModal(true); + }; + + const hideActionItemStatusModal = (): void => { + setActionItemStatusModal(false); + }; + + const setActionItemState = (actionItem: InterfaceActionItemInfo): void => { + setFormState({ + ...formState, + assignee: `${actionItem.assignee.firstName} ${actionItem.assignee.lastName}`, + assigner: `${actionItem.assigner.firstName} ${actionItem.assigner.lastName}`, + assigneeId: actionItem.assignee._id, + preCompletionNotes: actionItem.preCompletionNotes, + postCompletionNotes: actionItem.postCompletionNotes, + isCompleted: actionItem.isCompleted, + }); + setActionItemId(actionItem._id); + setDueDate(actionItem.dueDate); + setAssignmentDate(actionItem.assignmentDate); + setCompletionDate(actionItem.completionDate); + }; + + const popover = ( + + {actionItemNotes} + + ); + + return ( + <> +
+
+ + +
{t('assignee')}
+ + + {t('actionItemCategory')} + + +
{t('preCompletionNotes')}
+ + +
{t('postCompletionNotes')}
+ + +
{t('options')}
+ +
+
+ +
+ {actionItemsData?.map((actionItem, index) => ( +
+ + + {`${actionItem.assignee.firstName} ${actionItem.assignee.lastName}`} + + + {actionItem.actionItemCategory.name} + + +
+ + { + setActionItemId(actionItem._id); + setActionItemNotes(actionItem.preCompletionNotes); + }} + > + {actionItem.preCompletionNotes.length > 25 + ? `${actionItem.preCompletionNotes.substring(0, 25)}...` + : actionItem.preCompletionNotes} + + +
+ + +
+ {actionItem.isCompleted ? ( + + { + setActionItemId(actionItem._id); + setActionItemNotes(actionItem.postCompletionNotes); + }} + > + {actionItem.postCompletionNotes?.length > 25 + ? `${actionItem.postCompletionNotes.substring(0, 25)}...` + : actionItem.postCompletionNotes} + + + ) : ( + + {t('actionItemActive')} + + )} +
+ + +
+ handleActionItemStatusChange(actionItem)} + /> + + +
+ +
+ + {index !== actionItemsData.length - 1 &&
} +
+ ))} + + {actionItemsData?.length === 0 && ( +
+ {t('noActionItems')} +
+ )} +
+
+ + {/* action item status change modal */} + + +

{t('actionItemStatus')}

+ +
+ +
+ + {isActionItemCompleted + ? t('preCompletionNotes') + : t('postCompletionNotes')} + + { + if (isActionItemCompleted) { + setFormState({ + ...formState, + preCompletionNotes: e.target.value, + }); + } else { + setFormState({ + ...formState, + postCompletionNotes: e.target.value, + }); + } + }} + /> + + +
+
+ + {/* preview modal */} + + + {/* Update Modal */} + + + {/* Delete Modal */} + + + ); +} + +export default actionItemsContainer; diff --git a/src/components/ActionItemsContainer/ActionItemsContainerMocks.ts b/src/components/ActionItems/ActionItemsContainerMocks.ts similarity index 55% rename from src/components/ActionItemsContainer/ActionItemsContainerMocks.ts rename to src/components/ActionItems/ActionItemsContainerMocks.ts index 92b7a2fdba..a2dc20c5eb 100644 --- a/src/components/ActionItemsContainer/ActionItemsContainerMocks.ts +++ b/src/components/ActionItems/ActionItemsContainerMocks.ts @@ -11,9 +11,30 @@ export const MOCKS = [ actionItemId: 'actionItem1', assigneeId: 'user2', preCompletionNotes: 'pre completion notes edited', - postCompletionNotes: 'post completion notes', + postCompletionNotes: 'Post Completion Notes', dueDate: '2024-02-14', completionDate: '2024-02-21', + isCompleted: false, + }, + }, + result: { + data: { + updateActionItem: { + _id: 'actionItem1', + }, + }, + }, + }, + { + request: { + query: UPDATE_ACTION_ITEM_MUTATION, + variables: { + actionItemId: 'actionItem1', + assigneeId: 'user1', + preCompletionNotes: 'Pre Completion Notes', + postCompletionNotes: 'this action item has been completed successfully', + dueDate: '2024-02-21', + completionDate: '2024-02-21', isCompleted: true, }, }, @@ -25,6 +46,27 @@ export const MOCKS = [ }, }, }, + { + request: { + query: UPDATE_ACTION_ITEM_MUTATION, + variables: { + actionItemId: 'actionItem2', + assigneeId: 'user1', + preCompletionNotes: 'this action item has been made active again', + postCompletionNotes: 'Post Completion Notes', + dueDate: '2024-02-21', + completionDate: '2024-02-21', + isCompleted: false, + }, + }, + result: { + data: { + updateActionItem: { + _id: 'actionItem1', + }, + }, + }, + }, { request: { query: DELETE_ACTION_ITEM_MUTATION, @@ -50,10 +92,10 @@ export const MOCKS_ERROR_MUTATIONS = [ actionItemId: 'actionItem1', assigneeId: 'user2', preCompletionNotes: 'pre completion notes edited', - postCompletionNotes: 'post completion notes', + postCompletionNotes: '', dueDate: '2024-02-14', completionDate: '2024-02-21', - isCompleted: true, + isCompleted: false, }, }, error: new Error('Mock Graphql Error'), diff --git a/src/components/ActionItemsContainer/ActionItemsContainerProps.ts b/src/components/ActionItems/ActionItemsContainerProps.ts similarity index 65% rename from src/components/ActionItemsContainer/ActionItemsContainerProps.ts rename to src/components/ActionItems/ActionItemsContainerProps.ts index d91b18e7c8..19ba6f6369 100644 --- a/src/components/ActionItemsContainer/ActionItemsContainerProps.ts +++ b/src/components/ActionItems/ActionItemsContainerProps.ts @@ -1,4 +1,7 @@ +type ActionItemsConnectionType = 'Organization' | 'Event'; + export const props = { + actionItemsConnection: 'Organization' as ActionItemsConnectionType, actionItemsData: [ { _id: 'actionItem1', @@ -15,7 +18,7 @@ export const props = { postCompletionNotes: 'Post Completion Notes', assignmentDate: new Date('2024-02-14'), dueDate: new Date('2024-02-21'), - completionDate: new Date('2024-20-21'), + completionDate: new Date('2024-02-21'), isCompleted: false, assigner: { _id: 'user0', @@ -47,7 +50,39 @@ export const props = { postCompletionNotes: 'Post Completion Notes', assignmentDate: new Date('2024-02-14'), dueDate: new Date('2024-02-21'), - completionDate: new Date('2024-20-21'), + completionDate: new Date('2024-02-21'), + isCompleted: true, + assigner: { + _id: 'user0', + firstName: 'Wilt', + lastName: 'Shepherd', + }, + event: { + _id: 'event1', + title: 'event 1', + }, + creator: { + _id: 'user0', + firstName: 'Wilt', + lastName: 'Shepherd', + }, + }, + { + _id: 'actionItem3', + assignee: { + _id: 'user1', + firstName: 'Harve', + lastName: 'Lance', + }, + actionItemCategory: { + _id: 'actionItemCategory1', + name: 'ActionItemCategory 1', + }, + preCompletionNotes: 'Pre Completion Notes more than 25 characters', + postCompletionNotes: 'Post Completion Notes more than 25 characters', + assignmentDate: new Date('2024-02-14'), + dueDate: new Date('2024-02-21'), + completionDate: new Date('2024-02-21'), isCompleted: true, assigner: { _id: 'user0', @@ -89,6 +124,7 @@ export const props = { }; export const props2 = { + actionItemsConnection: 'Organization' as ActionItemsConnectionType, actionItemsData: [], membersData: [], actionItemsRefetch: jest.fn(), diff --git a/src/components/ActionItems/ActionItemsModal.test.tsx b/src/components/ActionItems/ActionItemsModal.test.tsx new file mode 100644 index 0000000000..3e4b3b0dfa --- /dev/null +++ b/src/components/ActionItems/ActionItemsModal.test.tsx @@ -0,0 +1,294 @@ +import React from 'react'; +import { + act, + fireEvent, + render, + screen, + waitFor, +} from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { MockedProvider } from '@apollo/react-testing'; +import { ActionItemsModal } from './ActionItemsModal'; +import { BrowserRouter } from 'react-router-dom'; +import { Provider } from 'react-redux'; +import { store } from 'state/store'; +import { I18nextProvider } from 'react-i18next'; +import i18nForTest from 'utils/i18nForTest'; +import { LocalizationProvider } from '@mui/x-date-pickers'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import { toast } from 'react-toastify'; + +import { + MOCKS_ERROR_ACTION_ITEM_CATEGORY_LIST_QUERY, + MOCKS_ERROR_ACTION_ITEM_LIST_QUERY, + MOCKS_ERROR_MEMBERS_LIST_QUERY, + MOCKS_ERROR_MUTATIONS, +} from '../../screens/OrganizationActionItems/OrganizationActionItemsErrorMocks'; +import { MOCKS } from '../../screens/OrganizationActionItems/OrganizationActionItemMocks'; +import { StaticMockLink } from 'utils/StaticMockLink'; + +jest.mock('react-toastify', () => ({ + toast: { + success: jest.fn(), + error: jest.fn(), + }, +})); + +jest.mock('@mui/x-date-pickers/DateTimePicker', () => { + return { + DateTimePicker: jest.requireActual( + '@mui/x-date-pickers/DesktopDateTimePicker', + ).DesktopDateTimePicker, + }; +}); + +async function wait(ms = 100): Promise { + await act(() => { + return new Promise((resolve) => { + setTimeout(resolve, ms); + }); + }); +} + +const link = new StaticMockLink(MOCKS, true); +const link2 = new StaticMockLink( + MOCKS_ERROR_ACTION_ITEM_CATEGORY_LIST_QUERY, + true, +); +const link3 = new StaticMockLink(MOCKS_ERROR_MEMBERS_LIST_QUERY, true); +const link4 = new StaticMockLink(MOCKS_ERROR_ACTION_ITEM_LIST_QUERY, true); +const link5 = new StaticMockLink(MOCKS_ERROR_MUTATIONS, true); + +const translations = JSON.parse( + JSON.stringify( + i18nForTest.getDataByLanguage('en')?.translation.organizationActionItems, + ), +); + +describe('Testing Check In Attendees Modal', () => { + const formData = { + actionItemCategory: 'ActionItemCategory 1', + assignee: 'Harve Lance', + preCompletionNotes: 'pre completion notes', + dueDate: '02/14/2024', + }; + + const props = { + show: true, + eventId: 'event1', + orgId: '123', + handleClose: jest.fn(), + }; + + test('The modal should be rendered properly', async () => { + render( + + + + + + + + + + + , + ); + + await waitFor(() => + expect(screen.queryByText('Event Action Items')).toBeInTheDocument(), + ); + + await waitFor(() => { + return expect( + screen.findByTestId('createEventActionItemBtn'), + ).resolves.toBeInTheDocument(); + }); + }); + + test('render error component on unsuccessful action item category list query', async () => { + const { queryByText } = render( + + + + + + + + + , + ); + + await wait(); + + await waitFor(() => { + expect(queryByText('createEventActionItemBtn')).not.toBeInTheDocument(); + }); + }); + + test('render error component on unsuccessful member list query', async () => { + const { queryByText } = render( + + + + + + + + + , + ); + + await wait(); + + await waitFor(() => { + expect(queryByText('createEventActionItemBtn')).not.toBeInTheDocument(); + }); + }); + + test('render error component on unsuccessful action items list query', async () => { + const { queryByText } = render( + + + + + + + + + , + ); + + await wait(); + + await waitFor(() => { + expect(queryByText('createEventActionItemBtn')).not.toBeInTheDocument(); + }); + }); + + test('creates new action item associated with the event', async () => { + render( + + + + + + + + + + + , + ); + + await wait(); + + await waitFor(() => { + expect( + screen.getByTestId('createEventActionItemBtn'), + ).toBeInTheDocument(); + }); + userEvent.click(screen.getByTestId('createEventActionItemBtn')); + + await waitFor(() => { + return expect( + screen.findByTestId('createActionItemModalCloseBtn'), + ).resolves.toBeInTheDocument(); + }); + + await waitFor(() => { + expect( + screen.getByTestId('formSelectActionItemCategory'), + ).toBeInTheDocument(); + }); + + userEvent.selectOptions( + screen.getByTestId('formSelectActionItemCategory'), + formData.actionItemCategory, + ); + + userEvent.selectOptions( + screen.getByTestId('formSelectAssignee'), + formData.assignee, + ); + + userEvent.type( + screen.getByPlaceholderText(translations.preCompletionNotes), + formData.preCompletionNotes, + ); + + const dueDatePicker = screen.getByLabelText(translations.dueDate); + fireEvent.change(dueDatePicker, { + target: { value: formData.dueDate }, + }); + + userEvent.click(screen.getByTestId('createActionItemFormSubmitBtn')); + + await waitFor(() => { + expect(toast.success).toBeCalledWith(translations.successfulCreation); + }); + }); + + test('toasts error on unsuccessful creation', async () => { + render( + + + + + + + + + + + , + ); + + await wait(); + + await waitFor(() => { + expect( + screen.getByTestId('createEventActionItemBtn'), + ).toBeInTheDocument(); + }); + userEvent.click(screen.getByTestId('createEventActionItemBtn')); + + await waitFor(() => { + return expect( + screen.findByTestId('createActionItemModalCloseBtn'), + ).resolves.toBeInTheDocument(); + }); + + await waitFor(() => { + expect( + screen.getByTestId('formSelectActionItemCategory'), + ).toBeInTheDocument(); + }); + + userEvent.selectOptions( + screen.getByTestId('formSelectActionItemCategory'), + formData.actionItemCategory, + ); + + userEvent.selectOptions( + screen.getByTestId('formSelectAssignee'), + formData.assignee, + ); + + userEvent.type( + screen.getByPlaceholderText(translations.preCompletionNotes), + formData.preCompletionNotes, + ); + + const dueDatePicker = screen.getByLabelText(translations.dueDate); + fireEvent.change(dueDatePicker, { + target: { value: formData.dueDate }, + }); + + userEvent.click(screen.getByTestId('createActionItemFormSubmitBtn')); + + await waitFor(() => { + expect(toast.error).toHaveBeenCalled(); + }); + }); +}); diff --git a/src/components/ActionItems/ActionItemsModal.tsx b/src/components/ActionItems/ActionItemsModal.tsx new file mode 100644 index 0000000000..8b8cafba4a --- /dev/null +++ b/src/components/ActionItems/ActionItemsModal.tsx @@ -0,0 +1,42 @@ +import React from 'react'; +import { Modal } from 'react-bootstrap'; +import styles from 'components/ActionItems/ActionItemsWrapper.module.css'; +import { ActionItemsModalBody } from './ActionItemsModalBody'; +import { useTranslation } from 'react-i18next'; + +export interface InterfaceModalProp { + show: boolean; + eventId: string; + orgId: string; + handleClose: () => void; +} + +export const ActionItemsModal = (props: InterfaceModalProp): JSX.Element => { + const { t } = useTranslation('translation', { + keyPrefix: 'organizationActionItems', + }); + + return ( + <> + + + + {t('eventActionItems')} + + + + + + + + ); +}; diff --git a/src/components/ActionItems/ActionItemsModalBody.tsx b/src/components/ActionItems/ActionItemsModalBody.tsx new file mode 100644 index 0000000000..e7c505a9a4 --- /dev/null +++ b/src/components/ActionItems/ActionItemsModalBody.tsx @@ -0,0 +1,215 @@ +import React, { useState } from 'react'; +import type { ChangeEvent } from 'react'; +import { Button } from 'react-bootstrap'; +import { useMutation, useQuery } from '@apollo/client'; +import { + ACTION_ITEM_CATEGORY_LIST, + ACTION_ITEM_LIST, + MEMBERS_LIST, +} from 'GraphQl/Queries/Queries'; +import styles from 'components/ActionItems/ActionItemsWrapper.module.css'; +import type { + InterfaceActionItemCategoryList, + InterfaceActionItemList, + InterfaceMembersList, +} from 'utils/interfaces'; + +import ActionItemsContainer from 'components/ActionItems/ActionItemsContainer'; +import Loader from 'components/Loader/Loader'; +import { WarningAmberRounded } from '@mui/icons-material'; +import { CREATE_ACTION_ITEM_MUTATION } from 'GraphQl/Mutations/ActionItemMutations'; +import dayjs from 'dayjs'; +import { toast } from 'react-toastify'; +import ActionItemCreateModal from 'screens/OrganizationActionItems/ActionItemCreateModal'; +import { useTranslation } from 'react-i18next'; + +export const ActionItemsModalBody = ({ + organizationId, + eventId, +}: { + organizationId: string; + eventId: string; +}): JSX.Element => { + const { t } = useTranslation('translation', { + keyPrefix: 'organizationActionItems', + }); + + const [dueDate, setDueDate] = useState(new Date()); + const [actionItemCreateModalIsOpen, setActionItemCreateModalIsOpen] = + useState(false); + + const [formState, setFormState] = useState({ + actionItemCategoryId: '', + assigneeId: '', + preCompletionNotes: '', + }); + + const { + data: actionItemCategoriesData, + loading: actionItemCategoriesLoading, + error: actionItemCategoriesError, + }: { + data: InterfaceActionItemCategoryList | undefined; + loading: boolean; + error?: Error | undefined; + } = useQuery(ACTION_ITEM_CATEGORY_LIST, { + variables: { + organizationId, + }, + notifyOnNetworkStatusChange: true, + }); + + const { + data: membersData, + loading: membersLoading, + error: membersError, + }: { + data: InterfaceMembersList | undefined; + loading: boolean; + error?: Error | undefined; + } = useQuery(MEMBERS_LIST, { + variables: { id: organizationId }, + }); + + const { + data: actionItemsData, + loading: actionItemsLoading, + error: actionItemsError, + refetch: actionItemsRefetch, + }: { + data: InterfaceActionItemList | undefined; + loading: boolean; + error?: Error | undefined; + refetch: any; + } = useQuery(ACTION_ITEM_LIST, { + variables: { + organizationId, + eventId, + orderBy: 'createdAt_DESC', + }, + notifyOnNetworkStatusChange: true, + }); + + const [createActionItem] = useMutation(CREATE_ACTION_ITEM_MUTATION); + + const createActionItemHandler = async ( + e: ChangeEvent, + ): Promise => { + e.preventDefault(); + try { + await createActionItem({ + variables: { + assigneeId: formState.assigneeId, + actionItemCategoryId: formState.actionItemCategoryId, + eventId, + preCompletionNotes: formState.preCompletionNotes, + dueDate: dayjs(dueDate).format('YYYY-MM-DD'), + }, + }); + + setFormState({ + assigneeId: '', + actionItemCategoryId: '', + preCompletionNotes: '', + }); + + setDueDate(new Date()); + + actionItemsRefetch(); + hideCreateModal(); + toast.success(t('successfulCreation')); + } catch (error: any) { + toast.error(error.message); + console.log(error); + } + }; + + const showCreateModal = (): void => { + setActionItemCreateModalIsOpen(!actionItemCreateModalIsOpen); + }; + + const hideCreateModal = (): void => { + setActionItemCreateModalIsOpen(!actionItemCreateModalIsOpen); + }; + + if (actionItemCategoriesLoading || membersLoading || actionItemsLoading) { + return ; + } + + if (actionItemCategoriesError || membersError || actionItemsError) { + return ( +
+ +
+ Error occured while loading{' '} + {actionItemCategoriesError + ? 'Action Item Categories' + : membersError + ? 'Members List' + : 'Action Items List'}{' '} + Data +
+ {actionItemCategoriesError + ? actionItemCategoriesError.message + : membersError + ? membersError.message + : actionItemsError?.message} +
+
+ ); + } + + const actionItemCategories = + actionItemCategoriesData?.actionItemCategoriesByOrganization.filter( + (category) => !category.isDisabled, + ); + + const completedActionItemsCount = + actionItemsData?.actionItemsByOrganization.reduce( + (acc, item) => (item.isCompleted === true ? acc + 1 : acc), + 0, + ); + + return ( + <> +
+ + Status: + {actionItemsData?.actionItemsByOrganization.length} action items + assigned, {completedActionItemsCount} completed + + + +
+ + + + {/* Create Modal */} + + + ); +}; diff --git a/src/components/ActionItems/ActionItemsWrapper.module.css b/src/components/ActionItems/ActionItemsWrapper.module.css new file mode 100644 index 0000000000..125db8a125 --- /dev/null +++ b/src/components/ActionItems/ActionItemsWrapper.module.css @@ -0,0 +1,53 @@ +.actionItemsModal { + margin: auto; + max-width: 85%; +} + +button .iconWrapper { + width: 32px; + padding-right: 4px; + margin-right: 4px; +} + +button .iconWrapperSm { + width: 32px; + display: flex; + justify-content: center; + align-items: center; +} + +.errorIcon { + transform: scale(1.5); + color: var(--bs-danger); + margin-bottom: 1rem; +} + +.greenregbtn { + margin: 1rem 0 0; + margin-top: 0; + margin-right: 4px; + border: 1px solid var(--bs-gray-300); + box-shadow: 0 2px 2px var(--bs-gray-300); + padding: 10px 10px; + border-radius: 5px; + background-color: var(--bs-primary); + width: 100%; + font-size: 16px; + color: var(--bs-white); + outline: none; + font-weight: 600; + cursor: pointer; + transition: + transform 0.2s, + box-shadow 0.2s; + max-width: 100px; +} + +.message { + margin-top: 15%; + margin-bottom: 15%; + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; +} diff --git a/src/components/ActionItems/ActionItemsWrapper.test.tsx b/src/components/ActionItems/ActionItemsWrapper.test.tsx new file mode 100644 index 0000000000..7b27e7ccd3 --- /dev/null +++ b/src/components/ActionItems/ActionItemsWrapper.test.tsx @@ -0,0 +1,73 @@ +import React from 'react'; +import { act, render, screen, waitFor } from '@testing-library/react'; +import { MockedProvider } from '@apollo/react-testing'; +import { ActionItemsWrapper } from './ActionItemsWrapper'; +import { BrowserRouter } from 'react-router-dom'; +import { Provider } from 'react-redux'; +import { store } from 'state/store'; +import { I18nextProvider } from 'react-i18next'; +import i18nForTest from 'utils/i18nForTest'; +import { ToastContainer } from 'react-toastify'; +import { LocalizationProvider } from '@mui/x-date-pickers'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import { StaticMockLink } from 'utils/StaticMockLink'; +import userEvent from '@testing-library/user-event'; +import { MOCKS } from '../../screens/OrganizationActionItems/OrganizationActionItemMocks'; + +async function wait(ms = 100): Promise { + await act(() => { + return new Promise((resolve) => { + setTimeout(resolve, ms); + }); + }); +} + +const link = new StaticMockLink(MOCKS, true); + +describe('Testing Action Items Wrapper', () => { + const props = { + eventId: 'event1', + orgId: '123', + }; + + test('The button to open and close the modal should work properly', async () => { + render( + + + + + + + + + + + + , + ); + + await wait(); + + await waitFor(() => { + expect( + screen.getByLabelText('eventDashboardActionItems'), + ).toBeInTheDocument(); + }); + + userEvent.click(screen.getByLabelText('eventDashboardActionItems')); + + await waitFor(() => + expect(screen.queryByTestId('modal-title')).toBeInTheDocument(), + ); + + await waitFor(() => { + expect(screen.getByLabelText('Close')).toBeInTheDocument(); + }); + + userEvent.click(screen.getByLabelText('Close')); + + await waitFor(() => + expect(screen.queryByTestId('modal-title')).not.toBeInTheDocument(), + ); + }); +}); diff --git a/src/components/ActionItems/ActionItemsWrapper.tsx b/src/components/ActionItems/ActionItemsWrapper.tsx new file mode 100644 index 0000000000..b44e0782b2 --- /dev/null +++ b/src/components/ActionItems/ActionItemsWrapper.tsx @@ -0,0 +1,45 @@ +import React, { useState } from 'react'; +import { ActionItemsModal } from './ActionItemsModal'; +import { Button } from 'react-bootstrap'; +import IconComponent from 'components/IconComponent/IconComponent'; +import styles from './ActionItemsWrapper.module.css'; +import { useTranslation } from 'react-i18next'; + +type PropType = { + orgId: string; + eventId: string; +}; + +export const ActionItemsWrapper = (props: PropType): JSX.Element => { + const { t } = useTranslation('translation', { + keyPrefix: 'organizationActionItems', + }); + + const [showModal, setShowModal] = useState(false); + + return ( + <> + + {showModal && ( + setShowModal(false)} + orgId={props.orgId} + eventId={props.eventId} + /> + )} + + ); +}; diff --git a/src/components/ActionItemsContainer/ActionItemsContainer.module.css b/src/components/ActionItemsContainer/ActionItemsContainer.module.css deleted file mode 100644 index a15262e08e..0000000000 --- a/src/components/ActionItemsContainer/ActionItemsContainer.module.css +++ /dev/null @@ -1,4 +0,0 @@ -.actionItemStatusBadge { - width: 5.5rem; - margin-left: 1.1rem; -} diff --git a/src/components/ActionItemsContainer/ActionItemsContainer.tsx b/src/components/ActionItemsContainer/ActionItemsContainer.tsx deleted file mode 100644 index a710d621d1..0000000000 --- a/src/components/ActionItemsContainer/ActionItemsContainer.tsx +++ /dev/null @@ -1,300 +0,0 @@ -import React, { useState } from 'react'; -import dayjs from 'dayjs'; -import type { ChangeEvent } from 'react'; -import { Button, Col, Row } from 'react-bootstrap'; -import { useTranslation } from 'react-i18next'; -import { toast } from 'react-toastify'; - -import { - DELETE_ACTION_ITEM_MUTATION, - UPDATE_ACTION_ITEM_MUTATION, -} from 'GraphQl/Mutations/mutations'; -import { useMutation } from '@apollo/client'; - -import type { - InterfaceActionItemInfo, - InterfaceMemberInfo, -} from 'utils/interfaces'; -import styles from './ActionItemsContainer.module.css'; -import ActionItemUpdateModal from '../../screens/OrganizationActionItems/ActionItemUpdateModal'; -import ActionItemPreviewModal from '../../screens/OrganizationActionItems/ActionItemPreviewModal'; -import ActionItemDeleteModal from '../../screens/OrganizationActionItems/ActionItemDeleteModal'; - -function actionItemsContainer({ - actionItemsData, - membersData, - actionItemsRefetch, -}: { - actionItemsData: InterfaceActionItemInfo[] | undefined; - membersData: InterfaceMemberInfo[] | undefined; - actionItemsRefetch: any; -}): JSX.Element { - const { t } = useTranslation('translation', { - keyPrefix: 'organizationActionItems', - }); - - const [actionItemPreviewModalIsOpen, setActionItemPreviewModalIsOpen] = - useState(false); - const [actionItemUpdateModalIsOpen, setActionItemUpdateModalIsOpen] = - useState(false); - const [actionItemDeleteModalIsOpen, setActionItemDeleteModalIsOpen] = - useState(false); - - const [assignmentDate, setAssignmentDate] = useState(new Date()); - const [dueDate, setDueDate] = useState(new Date()); - const [completionDate, setCompletionDate] = useState(new Date()); - const [actionItemId, setActionItemId] = useState(''); - - const [formState, setFormState] = useState({ - assignee: '', - assigner: '', - assigneeId: '', - preCompletionNotes: '', - postCompletionNotes: '', - isCompleted: false, - }); - - const showPreviewModal = (actionItem: InterfaceActionItemInfo): void => { - setFormState({ - ...formState, - assignee: `${actionItem.assignee.firstName} ${actionItem.assignee.lastName}`, - assigner: `${actionItem.assigner.firstName} ${actionItem.assigner.lastName}`, - assigneeId: actionItem.assignee._id, - preCompletionNotes: actionItem.preCompletionNotes, - postCompletionNotes: actionItem.postCompletionNotes, - isCompleted: actionItem.isCompleted, - }); - setActionItemId(actionItem._id); - setAssignmentDate(actionItem.assignmentDate); - setDueDate(actionItem.dueDate); - setCompletionDate(actionItem.completionDate); - setActionItemPreviewModalIsOpen(true); - }; - - const showUpdateModal = (): void => { - setActionItemUpdateModalIsOpen(!actionItemUpdateModalIsOpen); - }; - - const hidePreviewModal = (): void => { - setActionItemPreviewModalIsOpen(false); - }; - - const hideUpdateModal = (): void => { - setActionItemId(''); - setActionItemUpdateModalIsOpen(!actionItemUpdateModalIsOpen); - }; - - const toggleDeleteModal = (): void => { - setActionItemDeleteModalIsOpen(!actionItemDeleteModalIsOpen); - }; - - const [updateActionItem] = useMutation(UPDATE_ACTION_ITEM_MUTATION); - - const updateActionItemHandler = async ( - e: ChangeEvent, - ): Promise => { - e.preventDefault(); - try { - await updateActionItem({ - variables: { - actionItemId, - assigneeId: formState.assigneeId, - preCompletionNotes: formState.preCompletionNotes, - postCompletionNotes: formState.postCompletionNotes, - dueDate: dayjs(dueDate).format('YYYY-MM-DD'), - completionDate: dayjs(completionDate).format('YYYY-MM-DD'), - isCompleted: formState.isCompleted, - }, - }); - - actionItemsRefetch(); - hideUpdateModal(); - toast.success(t('successfulUpdation')); - } catch (error: any) { - toast.error(error.message); - console.log(error); - } - }; - - const [removeActionItem] = useMutation(DELETE_ACTION_ITEM_MUTATION); - const deleteActionItemHandler = async (): Promise => { - try { - await removeActionItem({ - variables: { - actionItemId, - }, - }); - - actionItemsRefetch(); - toggleDeleteModal(); - toast.success(t('successfulDeletion')); - } catch (error: any) { - toast.error(error.message); - console.log(error); - } - }; - - const handleEditClick = (actionItem: InterfaceActionItemInfo): void => { - setFormState({ - ...formState, - assignee: `${actionItem.assignee.firstName} ${actionItem.assignee.lastName}`, - assigneeId: actionItem.assignee._id, - preCompletionNotes: actionItem.preCompletionNotes, - postCompletionNotes: actionItem.postCompletionNotes, - isCompleted: actionItem.isCompleted, - }); - setActionItemId(actionItem._id); - setDueDate(actionItem.dueDate); - setCompletionDate(actionItem.completionDate); - showUpdateModal(); - }; - - return ( - <> -
-
- - -
{t('assignee')}
- - - {t('actionItemCategory')} - - -
{t('status')}
- - -
{t('options')}
- -
-
- -
- {actionItemsData?.map((actionItem, index) => ( -
- - - {`${actionItem.assignee.firstName} ${actionItem.assignee.lastName}`} - - - {actionItem.actionItemCategory.name} - - -
- {actionItem.isCompleted ? t('completed') : t('active')} -
- - - - - - -
- - {index !== actionItemsData.length - 1 &&
} -
- ))} - - {actionItemsData?.length === 0 && ( -
- {t('noActionItems')} -
- )} -
-
- - {/* preview modal */} - - - {/* Update Modal */} - - - {/* Delete Modal */} - - - ); -} - -export default actionItemsContainer; diff --git a/src/components/LeftDrawerEvent/LeftDrawerEvent.tsx b/src/components/LeftDrawerEvent/LeftDrawerEvent.tsx index 2ff3aae733..a294ba61dc 100644 --- a/src/components/LeftDrawerEvent/LeftDrawerEvent.tsx +++ b/src/components/LeftDrawerEvent/LeftDrawerEvent.tsx @@ -8,6 +8,7 @@ import styles from './LeftDrawerEvent.module.css'; import IconComponent from 'components/IconComponent/IconComponent'; import { EventRegistrantsWrapper } from 'components/EventRegistrantsModal/EventRegistrantsWrapper'; import { CheckInWrapper } from 'components/CheckIn/CheckInWrapper'; +import { ActionItemsWrapper } from 'components/ActionItems/ActionItemsWrapper'; import { EventStatsWrapper } from 'components/EventStats/EventStatsWrapper'; import { REVOKE_REFRESH_TOKEN } from 'GraphQl/Mutations/mutations'; import { useMutation } from '@apollo/client'; @@ -101,6 +102,11 @@ const leftDrawerEvent = ({ eventId={event._id} key={`${event?._id || 'loading'}CheckIn`} /> + { defaultOptions={defaultOptions} > - - + + + +
, diff --git a/src/screens/OrganizationActionItems/ActionItemUpdateModal.tsx b/src/screens/OrganizationActionItems/ActionItemUpdateModal.tsx index aaf7263a06..51c57e818f 100644 --- a/src/screens/OrganizationActionItems/ActionItemUpdateModal.tsx +++ b/src/screens/OrganizationActionItems/ActionItemUpdateModal.tsx @@ -106,24 +106,6 @@ const ActionItemUpdateModal: React.FC = ({ className="mb-2" /> - - { - setFormState({ - ...formState, - postCompletionNotes: e.target.value, - }); - }} - className="mb-2" - /> -
= ({
-
-
- - - setFormState({ - ...formState, - isCompleted: !formState.isCompleted, - }) - } - /> -
-
-