diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json
index a2d2ea718d..96d3518ee7 100644
--- a/public/locales/en/translation.json
+++ b/public/locales/en/translation.json
@@ -267,11 +267,11 @@
"makeActive": "Active",
"noActionItems": "No Action Items",
"options": "Options",
- "preCompletionNotes": "Pre Completion Notes",
+ "preCompletionNotes": "Notes",
"actionItemActive": "Active",
"markCompletion": "Mark Completion",
"actionItemStatus": "Action Item Status",
- "postCompletionNotes": "Post Completion Notes",
+ "postCompletionNotes": "Completion Notes",
"selectActionItemCategory": "Select an action item category",
"selectAssignee": "Select an assignee",
"status": "Status",
@@ -944,8 +944,16 @@
"actionItemCategory": "Action Item Category",
"selectActionItemCategory": "Select an action item category",
"selectAssignee": "Select an assignee",
- "preCompletionNotes": "Pre Completion Notes",
- "postCompletionNotes": "Post Completion Notes",
+ "assignee": "Assignee",
+ "assigner": "Assigner",
+ "preCompletionNotes": "Notes",
+ "postCompletionNotes": "Completion Notes",
+ "assignmentDate": "Assignment Date",
+ "status": "Status",
+ "actionItemActive": "Active",
+ "actionItemStatus": "Action Item Status",
+ "actionItemCompleted": "Action Item Completed",
+ "markCompletion": "Mark Completion",
"actionItemDetails": "Action Item Details",
"dueDate": "Due Date",
"completionDate": "Completion Date",
diff --git a/public/locales/fr/translation.json b/public/locales/fr/translation.json
index d1683af4c8..1239b49158 100644
--- a/public/locales/fr/translation.json
+++ b/public/locales/fr/translation.json
@@ -951,8 +951,8 @@
"actionItemCategory": "Catégorie d'élément d'action",
"selectActionItemCategory": "Sélectionnez une catégorie d'élément d'action",
"selectAssignee": "Sélectionnez un responsable",
- "preCompletionNotes": "Notes préalables à l'achèvement",
- "postCompletionNotes": "Notes post-achèvement",
+ "preCompletionNotes": "Remarques",
+ "postCompletionNotes": "Notes d'achèvement",
"actionItemDetails": "Détails de l'action",
"dueDate": "Date d'échéance",
"completionDate": "Date d'achèvement",
@@ -963,6 +963,14 @@
"successfulCreation": "Élément d'action créé avec succès",
"successfulUpdation": "Élément d'action mis à jour avec succès",
"notes": "Remarques",
+ "assignee": "Cessionnaire",
+ "assigner": "Assigner",
+ "assignmentDate": "Date d'affectation",
+ "status": "Statut",
+ "actionItemActive": "Actif",
+ "actionItemStatus": "Statut de l'action",
+ "actionItemCompleted": "Élément d'action terminé",
+ "markCompletion": "Marquer l'achèvement",
"save": "Sauvegarder"
},
"checkIn": {
diff --git a/public/locales/hi/translation.json b/public/locales/hi/translation.json
index 36e1dbefca..b78b7f17b2 100644
--- a/public/locales/hi/translation.json
+++ b/public/locales/hi/translation.json
@@ -951,8 +951,8 @@
"actionItemCategory": "कार्य आइटम श्रेणी",
"selectActionItemCategory": "एक क्रिया आइटम श्रेणी का चयन करें",
"selectAssignee": "एक समनुदेशिती का चयन करें",
- "preCompletionNotes": "समापन पूर्व नोट्स",
- "postCompletionNotes": "समापन के बाद के नोट्स",
+ "preCompletionNotes": "टिप्पणियाँ",
+ "postCompletionNotes": "समापन नोट्स",
"actionItemDetails": "कार्रवाई मद विवरण",
"dueDate": "नियत तारीख",
"completionDate": "पूरा करने की तिथि",
@@ -963,6 +963,14 @@
"successfulCreation": "कार्रवाई आइटम सफलतापूर्वक बनाया गया",
"successfulUpdation": "कार्रवाई आइटम सफलतापूर्वक अपडेट किया गया",
"notes": "टिप्पणियाँ",
+ "assignee": "संपत्ति-भागी",
+ "assigner": "असाइनर",
+ "assignmentDate": "असाइनमेंट तिथि",
+ "status": "स्थिति",
+ "actionItemActive": "सक्रिय",
+ "actionItemStatus": "कार्रवाई आइटम स्थिति",
+ "actionItemCompleted": "कार्रवाई आइटम पूर्ण हुआ",
+ "markCompletion": "पूर्णता को चिह्नित करें",
"save": "बचाना"
},
"checkIn": {
diff --git a/public/locales/sp/translation.json b/public/locales/sp/translation.json
index 2016ed2e4c..854b60b19d 100644
--- a/public/locales/sp/translation.json
+++ b/public/locales/sp/translation.json
@@ -1191,8 +1191,8 @@
"actionItemCategory": "Categoría de elemento de acción",
"selectActionItemCategory": "Seleccione una categoría de elemento de acción",
"selectAssignee": "Seleccione un asignado",
- "preCompletionNotes": "Notas previas a la finalización",
- "postCompletionNotes": "Publicar notas de finalización",
+ "preCompletionNotes": "Notas",
+ "postCompletionNotes": "Notas finales",
"actionItemDetails": "Detalles del elemento de acción",
"dueDate": "Fecha de vencimiento",
"completionDate": "Fecha de finalización",
@@ -1205,6 +1205,14 @@
"successfulCreation": "Elemento de acción creado exitosamente",
"successfulUpdation": "Elemento de acción actualizado correctamente",
"notes": "Notas",
+ "assignee": "Cesionario",
+ "assigner": "Asignador",
+ "assignmentDate": "Fecha de asignación",
+ "status": "Estado",
+ "actionItemActive": "Activo",
+ "actionItemStatus": "Estado del elemento de acción",
+ "actionItemCompleted": "Elemento de acción completado",
+ "markCompletion": "Marcar finalización",
"save": "Guardar"
},
"checkIn": {
diff --git a/public/locales/zh/translation.json b/public/locales/zh/translation.json
index 3f44aeae9c..581701c9db 100644
--- a/public/locales/zh/translation.json
+++ b/public/locales/zh/translation.json
@@ -951,8 +951,8 @@
"actionItemCategory": "行动项目类别",
"selectActionItemCategory": "选择操作项类别",
"selectAssignee": "选择受托人",
- "preCompletionNotes": "预完成注释",
- "postCompletionNotes": "完成后注释",
+ "preCompletionNotes": "笔记",
+ "postCompletionNotes": "完成说明",
"actionItemDetails": "行动项目详情",
"dueDate": "到期日",
"completionDate": "完成日期",
@@ -963,6 +963,14 @@
"successfulCreation": "操作项创建成功",
"successfulUpdation": "操作项已成功更新",
"notes": "笔记",
+ "assignee": "受让人",
+ "assigner": "分配者",
+ "assignmentDate": "任务分配日期",
+ "status": "地位",
+ "actionItemActive": "积极的",
+ "actionItemStatus": "行动项目状态",
+ "actionItemCompleted": "行动项目已完成",
+ "markCompletion": "标记完成",
"save": "节省"
},
"checkIn": {
diff --git a/src/assets/css/app.css b/src/assets/css/app.css
index a96b3afabe..f58f1008ca 100644
--- a/src/assets/css/app.css
+++ b/src/assets/css/app.css
@@ -2401,8 +2401,8 @@ textarea.form-control-lg {
.form-check-input {
--bs-form-check-bg: #f2f2f2;
- width: 1em;
- height: 1em;
+ width: 1.3em;
+ height: 1.3em;
margin-top: 0.25em;
vertical-align: top;
background-color: var(--bs-form-check-bg);
diff --git a/src/components/ActionItems/ActionItemsContainer.test.tsx b/src/components/ActionItems/ActionItemsContainer.test.tsx
index 21f1148d6a..7cb9c9e9bb 100644
--- a/src/components/ActionItems/ActionItemsContainer.test.tsx
+++ b/src/components/ActionItems/ActionItemsContainer.test.tsx
@@ -86,6 +86,35 @@ describe('Testing Action Item Categories Component', () => {
screen.queryByText(translations.noActionItems),
).not.toBeInTheDocument();
});
+
+ expect(screen.getByText('#')).toBeInTheDocument();
+ expect(screen.getByText(translations.assignee)).toBeInTheDocument();
+ expect(
+ screen.getByText(translations.actionItemCategory),
+ ).toBeInTheDocument();
+ expect(
+ screen.getByText(translations.preCompletionNotes),
+ ).toBeInTheDocument();
+ expect(
+ screen.getByText(translations.postCompletionNotes),
+ ).toBeInTheDocument();
+
+ await wait();
+ expect(screen.getAllByText('Harve Lance')[0]).toBeInTheDocument();
+
+ const asigneeAnchorElement = screen.getAllByText('Harve Lance')[0];
+ expect(asigneeAnchorElement.tagName).toBe('A');
+ expect(asigneeAnchorElement).toHaveAttribute('href', '/member/event1');
+
+ expect(screen.getAllByText('ActionItemCategory 1')[0]).toBeInTheDocument();
+ const updateButtons = screen.getAllByTestId('editActionItemModalBtn');
+ const previewButtons = screen.getAllByTestId('previewActionItemModalBtn');
+ const updateStatusButtons = screen.getAllByTestId(
+ 'actionItemStatusChangeCheckbox',
+ );
+ expect(updateButtons[0]).toBeInTheDocument();
+ expect(previewButtons[0]).toBeInTheDocument();
+ expect(updateStatusButtons[0]).toBeInTheDocument();
});
test('component loads correctly with no action items', async () => {
@@ -715,4 +744,34 @@ describe('Testing Action Item Categories Component', () => {
);
});
});
+
+ test('Action Items loads with correct headers', async () => {
+ render(
+
+
+
+
+
+
+
+
+ ,
+ );
+
+ await wait();
+
+ const actionItemHeaders = screen.getByTestId('actionItemsHeader');
+ expect(actionItemHeaders).toBeInTheDocument();
+ expect(screen.getByText(translations.assignee)).toBeInTheDocument();
+ expect(
+ screen.getByText(translations.actionItemCategory),
+ ).toBeInTheDocument();
+ expect(
+ screen.getByText(translations.preCompletionNotes),
+ ).toBeInTheDocument();
+ expect(
+ screen.getByText(translations.postCompletionNotes),
+ ).toBeInTheDocument();
+ expect(screen.getByText(translations.options)).toBeInTheDocument();
+ });
});
diff --git a/src/components/ActionItems/ActionItemsContainer.tsx b/src/components/ActionItems/ActionItemsContainer.tsx
index 0cb799c212..42e57f3822 100644
--- a/src/components/ActionItems/ActionItemsContainer.tsx
+++ b/src/components/ActionItems/ActionItemsContainer.tsx
@@ -27,6 +27,7 @@ import styles from './ActionItemsContainer.module.css';
import ActionItemUpdateModal from '../../screens/OrganizationActionItems/ActionItemUpdateModal';
import ActionItemPreviewModal from '../../screens/OrganizationActionItems/ActionItemPreviewModal';
import ActionItemDeleteModal from '../../screens/OrganizationActionItems/ActionItemDeleteModal';
+import { Link } from 'react-router-dom';
function actionItemsContainer({
actionItemsConnection,
@@ -193,18 +194,28 @@ function actionItemsContainer({
>
+
+ {'#'}
+
{t('assignee')}
{t('preCompletionNotes')}
{t('postCompletionNotes')}
-
+
{t('options')}
@@ -237,29 +253,44 @@ function actionItemsContainer({
{actionItemsData?.map((actionItem, index) => (
+
+ {index + 1}
+
- {`${actionItem.assignee.firstName} ${actionItem.assignee.lastName}`}
+
+ {`${actionItem.assignee.firstName} ${actionItem.assignee.lastName}`}
+
{actionItem.actionItemCategory.name}
-
+
{actionItem.isCompleted ? (
@@ -311,7 +342,12 @@ function actionItemsContainer({
)}
-
+
{
+ const formData = {
+ assignee: 'Anna Bradley',
+ preCompletionNotes: 'pre completion notes edited',
+ dueDate: '02/14/2024',
+ completionDate: '02/21/2024',
+ };
test('Testing add new action item modal', async () => {
window.location.assign('/event/111/123');
render(
@@ -357,7 +519,9 @@ describe('Event Action Items Page', () => {
userEvent.click(screen.getByTestId('createEventActionItemBtn'));
await wait();
- expect(screen.getByText('Action Item Details')).toBeInTheDocument();
+ expect(
+ screen.getByText(translations.actionItemDetails),
+ ).toBeInTheDocument();
const categoryDropdown = screen.getByTestId('formSelectActionItemCategory');
userEvent.selectOptions(categoryDropdown, 'Default');
@@ -369,17 +533,22 @@ describe('Event Action Items Page', () => {
expect(assigneeDropdown).toHaveValue('658930fd2caa9d8d6908745c');
- fireEvent.change(screen.getByPlaceholderText('Notes'), {
- target: { value: 'task to be done with high priority' },
- });
- expect(screen.getByPlaceholderText('Notes')).toHaveValue(
- 'task to be done with high priority',
+ fireEvent.change(
+ screen.getByPlaceholderText(translations.preCompletionNotes),
+ {
+ target: { value: 'task to be done with high priority' },
+ },
);
+ expect(
+ screen.getByPlaceholderText(translations.preCompletionNotes),
+ ).toHaveValue('task to be done with high priority');
- fireEvent.change(screen.getByLabelText('Due Date'), {
+ fireEvent.change(screen.getByLabelText(translations.dueDate), {
target: { value: '04/05/2024' },
});
- expect(screen.getByLabelText('Due Date')).toHaveValue('04/05/2024');
+ expect(screen.getByLabelText(translations.dueDate)).toHaveValue(
+ '04/05/2024',
+ );
userEvent.click(screen.getByTestId('createActionItemFormSubmitBtn'));
@@ -406,22 +575,51 @@ describe('Event Action Items Page', () => {
await wait();
expect(screen.getByText('#')).toBeInTheDocument();
- expect(screen.getByText('Assignee')).toBeInTheDocument();
- expect(screen.getByText('Action Item Category')).toBeInTheDocument();
- expect(screen.getByText('Notes')).toBeInTheDocument();
- expect(screen.getByText('Completion Notes')).toBeInTheDocument();
+ expect(screen.getByText(translations.assignee)).toBeInTheDocument();
+ expect(
+ screen.getByText(translations.actionItemCategory),
+ ).toBeInTheDocument();
+ expect(
+ screen.getByText(translations.preCompletionNotes),
+ ).toBeInTheDocument();
+ expect(
+ screen.getByText(translations.postCompletionNotes),
+ ).toBeInTheDocument();
await wait();
+ const asigneeAnchorElement = screen.getByText('Burton Sanders');
+ expect(asigneeAnchorElement.tagName).toBe('A');
+ expect(asigneeAnchorElement).toHaveAttribute('href', '/member/123');
+
expect(screen.getByText('Burton Sanders')).toBeInTheDocument();
- expect(screen.getByText('Pre Completion Note')).toBeInTheDocument();
- const updateButtons = screen.getAllByText(/Manage Actions/i);
+ const updateButtons = screen.getAllByTestId('editActionItemModalBtn');
+ const previewButtons = screen.getAllByTestId('previewActionItemModalBtn');
+ const updateStatusButtons = screen.getAllByTestId(
+ 'actionItemStatusChangeCheckbox',
+ );
expect(updateButtons[0]).toBeInTheDocument();
+ expect(previewButtons[0]).toBeInTheDocument();
+ expect(updateStatusButtons[0]).toBeInTheDocument();
+
+ // Truncate notes and long completion notes txt
+ expect(
+ screen.getAllByTestId('actionItemPreCompletionNotesOverlay')[1],
+ ).toHaveTextContent('Long Pre Completion Notes...');
+ expect(
+ screen.getAllByTestId('actionItemPostCompletionNotesOverlay')[0],
+ ).toHaveTextContent('Long Post Completion Note...');
+ expect(
+ screen.getAllByTestId('actionItemPostCompletionNotesOverlay')[1],
+ ).toHaveTextContent('Post Completion Text');
+ expect(
+ screen.getAllByTestId('actionItemPreCompletionNotesOverlay')[2],
+ ).toHaveTextContent('Pre Completion Text');
});
- test('Testing update action item modal', async () => {
+ test('opens and closes the update and delete modals through the preview modal', async () => {
window.location.assign('/event/111/123');
render(
-
+
@@ -433,45 +631,235 @@ describe('Event Action Items Page', () => {
,
);
+
await wait();
- const updateButtons = screen.getAllByText(/Manage Actions/i);
- userEvent.click(updateButtons[0]);
- expect(screen.getByText('Action Item Details')).toBeInTheDocument();
+ expect(
+ screen.getAllByTestId('previewActionItemModalBtn')[0],
+ ).toBeInTheDocument();
+ userEvent.click(screen.getAllByTestId('previewActionItemModalBtn')[0]);
- const assigneeDropdown = screen.getByTestId('formUpdateAssignee');
- userEvent.selectOptions(assigneeDropdown, 'Teresa Bradley');
+ await waitFor(() => {
+ return expect(
+ screen.findByTestId('previewActionItemModalCloseBtn'),
+ ).resolves.toBeInTheDocument();
+ });
- expect(assigneeDropdown).toHaveValue('658930fd2caa9d8d6908745c');
- fireEvent.change(screen.getByPlaceholderText('Notes'), {
- target: { value: 'task to be done with high priority' },
+ await waitFor(() => {
+ expect(
+ screen.getByTestId('deleteActionItemPreviewModalBtn'),
+ ).toBeInTheDocument();
});
- expect(screen.getByPlaceholderText('Notes')).toHaveValue(
- 'task to be done with high priority',
+ userEvent.click(screen.getByTestId('deleteActionItemPreviewModalBtn'));
+
+ await waitFor(() => {
+ return expect(
+ screen.findByTestId('actionItemDeleteModalCloseBtn'),
+ ).resolves.toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('actionItemDeleteModalCloseBtn'));
+
+ await waitForElementToBeRemoved(() =>
+ screen.queryByTestId('actionItemDeleteModalCloseBtn'),
);
- fireEvent.change(screen.getByPlaceholderText('Post Completion Notes'), {
- target: { value: 'Done' },
+ expect(
+ screen.getByTestId('editActionItemPreviewModalBtn'),
+ ).toBeInTheDocument();
+ userEvent.click(screen.getByTestId('editActionItemPreviewModalBtn'));
+
+ await waitFor(() => {
+ return expect(
+ screen.findByTestId('updateActionItemModalCloseBtn'),
+ ).resolves.toBeInTheDocument();
});
- expect(screen.getByPlaceholderText('Post Completion Notes')).toHaveValue(
- 'Done',
+ userEvent.click(screen.getByTestId('updateActionItemModalCloseBtn'));
+
+ await waitForElementToBeRemoved(() =>
+ screen.queryByTestId('updateActionItemModalCloseBtn'),
);
+ });
+ test('opens and closes the action item status change modal correctly', async () => {
+ window.location.assign('/event/111/123');
+ render(
+
+
+
+
+
+ {}
+
+
+
+
+ ,
+ );
+ await wait();
- fireEvent.change(screen.getByLabelText('Due Date'), {
- target: { value: '04/05/2024' },
+ await waitFor(() => {
+ expect(
+ screen.getAllByTestId('actionItemStatusChangeCheckbox')[0],
+ ).toBeInTheDocument();
});
- expect(screen.getByLabelText('Due Date')).toHaveValue('04/05/2024');
+ userEvent.click(screen.getAllByTestId('actionItemStatusChangeCheckbox')[0]);
- fireEvent.change(screen.getByLabelText('Completion Date'), {
- target: { value: '04/05/2024' },
+ await waitFor(() => {
+ return expect(
+ screen.findByTestId('actionItemStatusChangeModalCloseBtn'),
+ ).resolves.toBeInTheDocument();
});
- expect(screen.getByLabelText('Completion Date')).toHaveValue('04/05/2024');
+ userEvent.click(screen.getByTestId('actionItemStatusChangeModalCloseBtn'));
- userEvent.click(screen.getByTestId('updateActionItemFormSubmitBtn'));
+ await waitForElementToBeRemoved(() =>
+ screen.queryByTestId('actionItemStatusChangeModalCloseBtn'),
+ );
+ });
+
+ test('updates an action item status through the action item status change modal', async () => {
+ window.location.assign('/event/111/123');
+ 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('Testing update action item modal', async () => {
+ window.location.assign('/event/111/123');
+ render(
+
+
+
+
+
+ {}
+
+
+
+
+ ,
+ );
await wait();
- expect(toast.success).toBeCalledWith(translations.successfulUpdation);
+ await waitFor(() => {
+ expect(
+ screen.getAllByTestId('editActionItemModalBtn')[0],
+ ).toBeInTheDocument();
+ });
+ userEvent.click(screen.getAllByTestId('editActionItemModalBtn')[0]);
+
+ await waitFor(() => {
+ expect(screen.getByTestId('formUpdateAssignee')).toBeInTheDocument();
+ });
+
+ userEvent.selectOptions(
+ screen.getByTestId('formUpdateAssignee'),
+ formData.assignee,
+ );
+
+ const preCompletionNotes = screen.getByPlaceholderText(
+ translations.preCompletionNotes,
+ );
+ fireEvent.change(preCompletionNotes, { target: { value: '' } });
+ userEvent.type(preCompletionNotes, formData.preCompletionNotes);
+
+ const dueDatePicker = screen.getByLabelText(translations.dueDate);
+ fireEvent.change(dueDatePicker, {
+ target: { value: formData.dueDate },
+ });
+
+ const completionDatePicker = screen.getByLabelText(
+ translations.completionDate,
+ );
+ fireEvent.change(completionDatePicker, {
+ target: { value: formData.completionDate },
+ });
+
+ await waitFor(() => {
+ expect(
+ screen.getByTestId('updateActionItemFormSubmitBtn'),
+ ).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('updateActionItemFormSubmitBtn'));
+
+ await waitFor(() => {
+ expect(toast.success).toBeCalledWith(translations.successfulUpdation);
+ });
});
test('Testing delete action item modal and delete the record', async () => {
window.location.assign('/event/111/123');
@@ -489,16 +877,20 @@ describe('Event Action Items Page', () => {
,
);
await wait();
- const updateButtons = screen.getAllByText(/Manage Actions/i);
- userEvent.click(updateButtons[0]);
-
- expect(screen.getByText('Action Item Details')).toBeInTheDocument();
+ expect(
+ screen.getAllByTestId('previewActionItemModalBtn')[0],
+ ).toBeInTheDocument();
+ userEvent.click(screen.getAllByTestId('previewActionItemModalBtn')[0]);
- expect(screen.getByTestId('deleteActionItemBtn')).toBeInTheDocument();
- userEvent.click(screen.getByTestId('deleteActionItemBtn'));
+ await waitFor(() => {
+ expect(
+ screen.getByTestId('deleteActionItemPreviewModalBtn'),
+ ).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('deleteActionItemPreviewModalBtn'));
await wait();
expect(
- screen.getByText('Do you want to remove this action item?'),
+ screen.getByText(translations.deleteActionItemMsg),
).toBeInTheDocument();
userEvent.click(screen.getByText('Yes'));
await wait();
@@ -522,20 +914,137 @@ describe('Event Action Items Page', () => {
,
);
await wait();
- const updateButtons = screen.getAllByText(/Manage Actions/i);
- userEvent.click(updateButtons[0]);
-
- expect(screen.getByText('Action Item Details')).toBeInTheDocument();
+ expect(
+ screen.getAllByTestId('previewActionItemModalBtn')[0],
+ ).toBeInTheDocument();
+ userEvent.click(screen.getAllByTestId('previewActionItemModalBtn')[0]);
- expect(screen.getByTestId('deleteActionItemBtn')).toBeInTheDocument();
- userEvent.click(screen.getByTestId('deleteActionItemBtn'));
+ await waitFor(() => {
+ expect(
+ screen.getByTestId('deleteActionItemPreviewModalBtn'),
+ ).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('deleteActionItemPreviewModalBtn'));
await wait();
expect(
- screen.getByText('Do you want to remove this action item?'),
+ screen.getByText(translations.deleteActionItemMsg),
).toBeInTheDocument();
userEvent.click(screen.getByText('No'));
await wait();
- expect(screen.getByText('Teresa Bradley')).toBeInTheDocument();
+ expect(screen.getByRole('dialog')).toBeInTheDocument();
+ expect(
+ screen.getByText(translations.actionItemDetails),
+ ).toBeInTheDocument();
+ });
+
+ test('toasts error on unsuccessful deletion', async () => {
+ window.location.assign('/event/111/123');
+ render(
+
+
+
+
+
+ {}
+
+
+
+
+ ,
+ );
+ await wait();
+
+ expect(
+ 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.getByTestId('deleteActionItemPreviewModalBtn'));
+
+ await waitFor(() => {
+ return expect(
+ screen.findByTestId('actionItemDeleteModalCloseBtn'),
+ ).resolves.toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('deleteActionItemBtn'));
+
+ await waitFor(() => {
+ expect(toast.error).toHaveBeenCalled();
+ });
+ });
+
+ test('toasts error on unsuccessful updation', async () => {
+ window.location.assign('/event/111/123');
+ render(
+
+
+
+
+
+ {}
+
+
+
+
+ ,
+ );
+ await wait();
+
+ await waitFor(() => {
+ expect(
+ screen.getAllByTestId('editActionItemModalBtn')[0],
+ ).toBeInTheDocument();
+ });
+ userEvent.click(screen.getAllByTestId('editActionItemModalBtn')[0]);
+
+ await waitFor(() => {
+ expect(screen.getByTestId('formUpdateAssignee')).toBeInTheDocument();
+ });
+
+ userEvent.selectOptions(
+ screen.getByTestId('formUpdateAssignee'),
+ formData.assignee,
+ );
+
+ const preCompletionNotes = screen.getByPlaceholderText(
+ translations.preCompletionNotes,
+ );
+ fireEvent.change(preCompletionNotes, { target: { value: '' } });
+ userEvent.type(preCompletionNotes, formData.preCompletionNotes);
+
+ const dueDatePicker = screen.getByLabelText(translations.dueDate);
+ fireEvent.change(dueDatePicker, {
+ target: { value: formData.dueDate },
+ });
+
+ const completionDatePicker = screen.getByLabelText(
+ translations.completionDate,
+ );
+ fireEvent.change(completionDatePicker, {
+ target: { value: formData.completionDate },
+ });
+
+ await waitFor(() => {
+ expect(
+ screen.getByTestId('updateActionItemFormSubmitBtn'),
+ ).toBeInTheDocument();
+ });
+ userEvent.click(screen.getByTestId('updateActionItemFormSubmitBtn'));
+
+ await waitFor(() => {
+ expect(toast.error).toHaveBeenCalled();
+ });
});
test('Raises an error when incorrect information is filled while creation', async () => {
@@ -557,19 +1066,26 @@ describe('Event Action Items Page', () => {
userEvent.click(screen.getByTestId('createEventActionItemBtn'));
await wait();
- expect(screen.getByText('Action Item Details')).toBeInTheDocument();
+ expect(
+ screen.getByText(translations.actionItemDetails),
+ ).toBeInTheDocument();
- fireEvent.change(screen.getByPlaceholderText('Notes'), {
- target: { value: 'task to be done with high priority' },
- });
- expect(screen.getByPlaceholderText('Notes')).toHaveValue(
- 'task to be done with high priority',
+ fireEvent.change(
+ screen.getByPlaceholderText(translations.preCompletionNotes),
+ {
+ target: { value: 'task to be done with high priority' },
+ },
);
+ expect(
+ screen.getByPlaceholderText(translations.preCompletionNotes),
+ ).toHaveValue('task to be done with high priority');
- fireEvent.change(screen.getByLabelText('Due Date'), {
+ fireEvent.change(screen.getByLabelText(translations.dueDate), {
target: { value: '04/05/2024' },
});
- expect(screen.getByLabelText('Due Date')).toHaveValue('04/05/2024');
+ expect(screen.getByLabelText(translations.dueDate)).toHaveValue(
+ '04/05/2024',
+ );
userEvent.click(screen.getByTestId('createActionItemFormSubmitBtn'));
await wait();
@@ -593,17 +1109,17 @@ describe('Event Action Items Page', () => {
,
);
await wait();
- const updateButtons = screen.getAllByText(/Manage Actions/i);
+ const updateButtons = screen.getAllByTestId('editActionItemModalBtn');
userEvent.click(updateButtons[0]);
- expect(screen.getByText('Action Item Details')).toBeInTheDocument();
+ expect(
+ screen.getByText(translations.actionItemDetails),
+ ).toBeInTheDocument();
userEvent.click(screen.getByTestId('updateActionItemFormSubmitBtn'));
await wait();
- expect(toast.success).toBeCalledWith(translations.successfulUpdation);
-
expect(toast.error).toBeCalled();
});
@@ -625,4 +1141,39 @@ describe('Event Action Items Page', () => {
await wait();
expect(screen.getByText('Nothing Found !!')).toBeInTheDocument();
});
+
+ test('Testing update action modal to have correct initial values', async () => {
+ window.location.assign('/event/111/123');
+ render(
+
+
+
+
+
+ {}
+
+
+
+
+ ,
+ );
+ await wait();
+ const updateButtons = screen.getAllByTestId('editActionItemModalBtn');
+ userEvent.click(updateButtons[0]);
+
+ expect(screen.getByText('Action Item Details')).toBeInTheDocument();
+ const assigneeDropdown = screen.getByTestId(
+ 'formUpdateAssignee',
+ ) as HTMLSelectElement;
+ expect(assigneeDropdown.value).toBe('658930fd2caa9d8d6908745c');
+ expect(assigneeDropdown).toHaveTextContent('Teresa Bradley');
+
+ expect(
+ screen.getByPlaceholderText(translations.preCompletionNotes),
+ ).toHaveValue('Pre Completion Notes');
+ const editActionItem = screen.getByRole('button', {
+ name: translations.editActionItem,
+ });
+ expect(editActionItem).toBeInTheDocument();
+ });
});
diff --git a/src/components/EventManagement/EventActionItems/EventActionItems.tsx b/src/components/EventManagement/EventActionItems/EventActionItems.tsx
index 717269b3ce..57950a3a24 100644
--- a/src/components/EventManagement/EventActionItems/EventActionItems.tsx
+++ b/src/components/EventManagement/EventActionItems/EventActionItems.tsx
@@ -5,11 +5,10 @@ import type { ChangeEvent } from 'react';
import React, { useEffect, useState } from 'react';
import { Button, Form } from 'react-bootstrap';
import { useTranslation } from 'react-i18next';
-import { Link } from 'react-router-dom';
import { toast } from 'react-toastify';
import styles from './EventActionItems.module.css';
import { DataGrid } from '@mui/x-data-grid';
-import type { GridColDef, GridCellParams } from '@mui/x-data-grid';
+import type { GridCellParams } from '@mui/x-data-grid';
import { Stack } from '@mui/material';
import Modal from 'react-bootstrap/Modal';
import {
@@ -19,6 +18,7 @@ import {
} from 'GraphQl/Mutations/ActionItemMutations';
import type {
InterfaceActionItemCategoryList,
+ InterfaceActionItemInfo,
InterfaceMembersList,
} from 'utils/interfaces';
import { DatePicker } from '@mui/x-date-pickers';
@@ -27,6 +27,9 @@ import {
MEMBERS_LIST,
} from 'GraphQl/Queries/Queries';
import { ACTION_ITEM_LIST_BY_EVENTS } from 'GraphQl/Queries/ActionItemQueries';
+import { useEventActionColumnConfig } from './useEventActionColumnConfig';
+import ActionItemPreviewModal from 'screens/OrganizationActionItems/ActionItemPreviewModal';
+import ActionItemDeleteModal from 'screens/OrganizationActionItems/ActionItemDeleteModal';
function eventActionItems(props: { eventId: string }): JSX.Element {
const { eventId } = props;
@@ -35,6 +38,11 @@ function eventActionItems(props: { eventId: string }): JSX.Element {
});
const { t: tCommon } = useTranslation('common');
+ const [actionItemPreviewModalIsOpen, setActionItemPreviewModalIsOpen] =
+ useState(false);
+ const [actionItemStatusModal, setActionItemStatusModal] = useState(false);
+ const [isActionItemCompleted, setIsActionItemCompleted] = useState(false);
+ const [assignmentDate, setAssignmentDate] = useState
(new Date());
const [actionItemCreateModalIsOpen, setActionItemCreateModalIsOpen] =
useState(false);
const [actionItemUpdateModalIsOpen, setActionItemUpdateModalIsOpen] =
@@ -74,6 +82,21 @@ function eventActionItems(props: { eventId: string }): JSX.Element {
const toggleDeleteModal = (): void => {
setActionItemDeleteModalIsOpen(!actionItemDeleteModalIsOpen);
};
+ const setActionItemState = (actionItem: InterfaceActionItemInfo): void => {
+ setFormState((prevState) => ({
+ ...prevState,
+ 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 {
data: actionItemCategoriesData,
}: {
@@ -164,6 +187,7 @@ function eventActionItems(props: { eventId: string }): JSX.Element {
});
actionItemsRefetch();
hideUpdateModal();
+ hideActionItemStatusModal();
toast.success(t('successfulUpdation'));
} catch (error: unknown) {
if (error instanceof Error) {
@@ -173,116 +197,60 @@ function eventActionItems(props: { eventId: string }): JSX.Element {
};
const [removeActionItem] = useMutation(DELETE_ACTION_ITEM_MUTATION);
const deleteActionItemHandler = async (): Promise => {
- await removeActionItem({
- variables: {
- actionItemId,
- },
- });
- actionItemsRefetch();
- toggleDeleteModal();
- hideUpdateModal();
- toast.success(t('successfulDeletion'));
+ try {
+ await removeActionItem({
+ variables: {
+ actionItemId,
+ },
+ });
+ actionItemsRefetch();
+ toggleDeleteModal();
+ hidePreviewModal();
+ toast.success(t('successfulDeletion'));
+ } catch (error: unknown) {
+ if (error instanceof Error) {
+ toast.error(error.message);
+ console.log(error.message);
+ }
+ }
};
- const columns: GridColDef[] = [
- {
- field: 'serialNo',
- headerName: '#',
- flex: 1,
- minWidth: 50,
- align: 'center',
- headerAlign: 'center',
- headerClassName: `${styles.tableHeader}`,
- sortable: false,
- renderCell: (params: GridCellParams) => {
- return params.row?.index;
- },
- },
- {
- field: 'assignee',
- headerName: 'Assignee',
- flex: 2,
- minWidth: 150,
- align: 'center',
- headerAlign: 'center',
- headerClassName: `${styles.tableHeader}`,
- sortable: false,
- renderCell: (params: GridCellParams) => {
- return (
-
- {params.row?.assignee.firstName +
- ' ' +
- params.row?.assignee.lastName}
-
- );
- },
- },
- {
- field: 'actionItemCategory',
- headerName: 'Action Item Category',
- flex: 2,
- minWidth: 100,
- align: 'center',
- headerAlign: 'center',
- headerClassName: `${styles.tableHeader}`,
- sortable: false,
- renderCell: (params: GridCellParams) => {
- return params.row.actionItemCategory.name;
- },
- },
- {
- field: 'notes',
- headerName: 'Notes',
- minWidth: 150,
- align: 'center',
- headerAlign: 'center',
- headerClassName: `${styles.tableHeader}`,
- flex: 2,
- sortable: false,
- renderCell: (params: GridCellParams) => {
- return params.row.preCompletionNotes;
- },
- },
- {
- field: 'completionNotes',
- headerName: 'Completion Notes',
- minWidth: 150,
- align: 'center',
- headerAlign: 'center',
- headerClassName: `${styles.tableHeader}`,
- flex: 2,
- sortable: false,
- renderCell: (params: GridCellParams) => {
- return params.row.postCompletionNotes;
- },
- },
- {
- field: 'options',
- headerName: 'Options',
- flex: 2,
- minWidth: 100,
- align: 'center',
- headerAlign: 'center',
- headerClassName: `${styles.tableHeader}`,
- sortable: false,
- renderCell: (params: GridCellParams) => {
- return (
-
- );
- },
- },
- ];
+
+ const handleActionItemStatusChange = (
+ actionItem: InterfaceActionItemInfo,
+ ): void => {
+ actionItem = { ...actionItem, isCompleted: !actionItem.isCompleted };
+ setIsActionItemCompleted(!actionItem.isCompleted);
+ setActionItemState(actionItem);
+ setActionItemStatusModal(true);
+ };
+
+ const showPreviewModal = (actionItem: InterfaceActionItemInfo): void => {
+ setActionItemState(actionItem);
+ setActionItemPreviewModalIsOpen(true);
+ };
+
+ const handleEditClick = (actionItem: InterfaceActionItemInfo): void => {
+ setActionItemId(actionItem._id);
+ setActionItemState(actionItem);
+ showUpdateModal();
+ };
+
+ const hidePreviewModal = (): void => {
+ setActionItemPreviewModalIsOpen(false);
+ };
+
+ const hideActionItemStatusModal = (): void => {
+ setActionItemStatusModal(false);
+ setActionItemUpdateModalIsOpen(false);
+ };
+
+ const { columns } = useEventActionColumnConfig({
+ eventId,
+ handleActionItemStatusChange,
+ showPreviewModal,
+ handleEditClick,
+ });
+
return (
<>
+
{/* create action item modal */}
Assignee
setFormState({ ...formState, assigneeId: e.target.value })
}
>
-
{membersData?.organizations[0].members.map((member, index) => {
@@ -447,24 +417,6 @@ function eventActionItems(props: { eventId: string }): JSX.Element {
});
}}
/>
-
- {
- setFormState({
- ...formState,
- postCompletionNotes: e.target.value,
- });
- }}
- className="mb-2"
- />
-
{
- if (date) {
- setCompletionDate(date?.toDate());
+ onChange={
+ /* istanbul ignore next */ (date: Dayjs | null): void => {
+ /* istanbul ignore next */
+ if (date) {
+ setCompletionDate(date?.toDate());
+ }
}
- }}
+ }
/>
-
-
+
-
-
- {/* delete modal */}
+
+ {/* preview modal */}
+
+
+ {/* Delete Modal */}
+
+
+ {/* action item status change modal */}
-
-
- {t('deleteActionItem')}
-
-
- {t('deleteActionItemMsg')}
-
-
+
+ {t('actionItemStatus')}
-
+
+
+
+ {isActionItemCompleted
+ ? t('preCompletionNotes')
+ : t('postCompletionNotes')}
+
+ {
+ if (isActionItemCompleted) {
+ setFormState({
+ ...formState,
+ preCompletionNotes: e.target.value,
+ });
+ } else {
+ setFormState({
+ ...formState,
+ postCompletionNotes: e.target.value,
+ });
+ }
+ }}
+ />
+
+
+
{actionItemsData && (
@@ -578,10 +574,14 @@ function eventActionItems(props: { eventId: string }): JSX.Element {
'& .MuiDataGrid-row.Mui-hovered': {
backgroundColor: 'transparent',
},
+ '& .MuiDataGrid-columnHeaderTitle': {
+ fontWeight: 700,
+ },
}}
getRowClassName={() => `${styles.rowBackground}`}
autoHeight
- rowHeight={70}
+ rowHeight={50}
+ columnHeaderHeight={40}
rows={actionItemsData?.actionItemsByEvent?.map(
(item: object, index: number) => ({
...item,
diff --git a/src/components/EventManagement/EventActionItems/useEventActionColumnConfig.tsx b/src/components/EventManagement/EventActionItems/useEventActionColumnConfig.tsx
new file mode 100644
index 0000000000..0db74323f6
--- /dev/null
+++ b/src/components/EventManagement/EventActionItems/useEventActionColumnConfig.tsx
@@ -0,0 +1,200 @@
+import React from 'react';
+import type { GridCellParams, GridColDef } from '@mui/x-data-grid';
+import { Link } from 'react-router-dom';
+import { Button, OverlayTrigger, Popover } from 'react-bootstrap';
+import styles from './EventActionItems.module.css';
+import type { InterfaceActionItemInfo } from 'utils/interfaces';
+import { useTranslation } from 'react-i18next';
+
+export type Props = {
+ eventId: string;
+ handleActionItemStatusChange: (actionItem: InterfaceActionItemInfo) => void;
+ showPreviewModal: (actionItem: InterfaceActionItemInfo) => void;
+ handleEditClick: (actionItem: InterfaceActionItemInfo) => void;
+};
+
+type ColumnConfig = {
+ columns: GridColDef[];
+};
+
+const popover = (
+ actionItemId: string,
+ actionItemNotes: string,
+): JSX.Element => {
+ return (
+
+ {actionItemNotes}
+
+ );
+};
+
+export const useEventActionColumnConfig = ({
+ eventId,
+ handleActionItemStatusChange,
+ showPreviewModal,
+ handleEditClick,
+}: Props): ColumnConfig => {
+ const { t } = useTranslation('translation', {
+ keyPrefix: 'eventActionItems',
+ });
+ const columns: GridColDef[] = [
+ {
+ field: 'serialNo',
+ headerName: '#',
+ flex: 1,
+ minWidth: 50,
+ align: 'center',
+ headerAlign: 'center',
+ headerClassName: `${styles.tableHeader}`,
+ sortable: false,
+ renderCell: (params: GridCellParams) => {
+ return params.row?.index;
+ },
+ },
+ {
+ field: 'assignee',
+ headerName: 'Assignee',
+ flex: 2,
+ minWidth: 150,
+ align: 'center',
+ headerAlign: 'center',
+ headerClassName: `${styles.tableHeader}`,
+ sortable: false,
+ renderCell: (params: GridCellParams) => {
+ return (
+
+ {params.row?.assignee.firstName +
+ ' ' +
+ params.row?.assignee.lastName}
+
+ );
+ },
+ },
+ {
+ field: 'actionItemCategory',
+ headerName: 'Action Item Category',
+ flex: 2,
+ minWidth: 100,
+ align: 'center',
+ headerAlign: 'center',
+ headerClassName: `${styles.tableHeader}`,
+ sortable: false,
+ renderCell: (params: GridCellParams) => {
+ return params.row.actionItemCategory.name;
+ },
+ },
+ {
+ field: 'notes',
+ headerName: 'Notes',
+ minWidth: 150,
+ align: 'center',
+ headerAlign: 'center',
+ headerClassName: `${styles.tableHeader}`,
+ flex: 2,
+ sortable: false,
+ renderCell: (params: GridCellParams) => {
+ const actionItem = params.row;
+ return (
+
+
+ {actionItem.preCompletionNotes.length > 25
+ ? `${actionItem.preCompletionNotes.substring(0, 25)}...`
+ : actionItem.preCompletionNotes}
+
+
+ );
+ },
+ },
+ {
+ field: 'completionNotes',
+ headerName: 'Completion Notes',
+ minWidth: 150,
+ align: 'center',
+ headerAlign: 'center',
+ headerClassName: `${styles.tableHeader}`,
+ flex: 2,
+ sortable: false,
+ renderCell: (params: GridCellParams) => {
+ const actionItem = params.row;
+ return actionItem.isCompleted ? (
+
+
+ {actionItem.postCompletionNotes?.length > 25
+ ? `${actionItem.postCompletionNotes.substring(0, 25)}...`
+ : actionItem.postCompletionNotes}
+
+
+ ) : (
+
+ {t('actionItemActive')}
+
+ );
+ },
+ },
+ {
+ field: 'options',
+ headerName: 'Options',
+ flex: 2,
+ minWidth: 100,
+ align: 'center',
+ headerAlign: 'center',
+ headerClassName: `${styles.tableHeader}`,
+ sortable: false,
+ renderCell: (params: GridCellParams) => {
+ return (
+
+ handleActionItemStatusChange(params.row)}
+ />
+
+
+
+ );
+ },
+ },
+ ];
+ return {
+ columns,
+ };
+};
diff --git a/src/screens/OrganizationActionItems/ActionItemUpdateModal.tsx b/src/screens/OrganizationActionItems/ActionItemUpdateModal.tsx
index 901101e58b..8b0510a4dc 100644
--- a/src/screens/OrganizationActionItems/ActionItemUpdateModal.tsx
+++ b/src/screens/OrganizationActionItems/ActionItemUpdateModal.tsx
@@ -75,11 +75,11 @@ const ActionItemUpdateModal: React.FC
= ({
- {membersData?.map((member, index) => {
+ {membersData?.map((member: InterfaceMemberInfo) => {
const currMemberName = `${member.firstName} ${member.lastName}`;
if (currMemberName !== formState.assignee) {
return (
-
);
@@ -107,36 +107,33 @@ const ActionItemUpdateModal: React.FC = ({
/>
-
- {
- /* istanbul ignore next */
- if (date) {
- setDueDate(date?.toDate());
- }
+ {
+ /* istanbul ignore next */
+ if (date) {
+ setDueDate(date?.toDate());
}
}
- />
-
-
- {
- /* istanbul ignore next */
- if (date) {
- setCompletionDate(date?.toDate());
- }
+ }
+ />
+
+ {
+ /* istanbul ignore next */
+ if (date) {
+ setCompletionDate(date?.toDate());
}
}
- />
-
+ }
+ />