From 2addcd455da8960d65a8d845bbacff28b195eb1c Mon Sep 17 00:00:00 2001 From: gautam-divyanshu Date: Mon, 3 Jun 2024 14:41:00 +0530 Subject: [PATCH 01/10] mutations and query added --- .../Mutations/AgendaCategoryMutations.ts | 45 +++++++++++++++++++ src/GraphQl/Queries/AgendaCategoryQueries.ts | 17 +++++++ 2 files changed, 62 insertions(+) create mode 100644 src/GraphQl/Mutations/AgendaCategoryMutations.ts create mode 100644 src/GraphQl/Queries/AgendaCategoryQueries.ts diff --git a/src/GraphQl/Mutations/AgendaCategoryMutations.ts b/src/GraphQl/Mutations/AgendaCategoryMutations.ts new file mode 100644 index 0000000000..c344eca7e2 --- /dev/null +++ b/src/GraphQl/Mutations/AgendaCategoryMutations.ts @@ -0,0 +1,45 @@ +import gql from 'graphql-tag'; + +/** + * GraphQL mutation to create an agenda category. + * + * @param input - Name, Description, OrganizationID of the AgendaCategory. + */ + +export const CREATE_AGENDA_ITEM_CATEGORY_MUTATION = gql` + mutation CreateAgendaCategory($input: CreateAgendaCategoryInput!) { + createAgendaCategory(input: $input) { + _id + } + } +`; + +/** + * GraphQL mutation to delete an agenda category. + * + * @param deleteAgendaCategoryId - The ID of the AgendaCategory to be deleted. + */ + +export const DELETE_AGENDA_ITEM_CATEGORY_MUTATION = gql` + mutation DeleteAgendaCategory($deleteAgendaCategoryId: ID!) { + deleteAgendaCategory(id: $deleteAgendaCategoryId) + } +`; + +/** + * GraphQL mutation to update an agenda category. + * + * @param updateAgendaCategoryId - The ID of the AgendaCategory to be updated. + * @param input - Updated Name, Description, OrganizationID of the AgendaCategory. + */ + +export const UPDATE_AGENDA_ITEM_CATEGORY_MUTATION = gql` + mutation UpdateAgendaCategory( + $updateAgendaCategoryId: ID! + $input: UpdateAgendaCategoryInput! + ) { + updateAgendaCategory(id: $updateAgendaCategoryId, input: $input) { + _id + } + } +`; diff --git a/src/GraphQl/Queries/AgendaCategoryQueries.ts b/src/GraphQl/Queries/AgendaCategoryQueries.ts new file mode 100644 index 0000000000..1cf7caaf64 --- /dev/null +++ b/src/GraphQl/Queries/AgendaCategoryQueries.ts @@ -0,0 +1,17 @@ +import gql from 'graphql-tag'; + +/** + * GraphQL query to retrieve agenda category by id. + * + * @param agendaCategoryId - The ID of the category which is being retrieved. + * @returns Agenda category associated with the id. + */ + +export const AGENDA_ITEM_CATEGORY_LIST = gql` + query AgendaCategory($agendaCategoryId: ID!) { + agendaCategory(id: $agendaCategoryId) { + _id + name + } + } +`; From 6864733144cd6f08d448019ef571370ae1774605 Mon Sep 17 00:00:00 2001 From: gautam-divyanshu Date: Mon, 3 Jun 2024 14:43:35 +0530 Subject: [PATCH 02/10] interface added --- src/utils/interfaces.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/utils/interfaces.ts b/src/utils/interfaces.ts index abaac2efcc..822262f9e1 100644 --- a/src/utils/interfaces.ts +++ b/src/utils/interfaces.ts @@ -456,3 +456,12 @@ export interface InterfaceQueryMembershipRequestsListItem { }[]; }[]; } + +export interface InterfaceAgendaItemCategoryInfo { + _id: string; + name: string; +} + +export interface InterfaceAgendaItemCategoryList { + agendaCategory: InterfaceAgendaItemCategoryInfo[]; +} From ab61dcd6e12ba3bdd8829f9762e3c189e3253faf Mon Sep 17 00:00:00 2001 From: gautam-divyanshu Date: Mon, 3 Jun 2024 15:17:47 +0530 Subject: [PATCH 03/10] svg & route added --- src/assets/svgs/agenda-category-icon.svg | 1 + src/components/IconComponent/IconComponent.tsx | 12 ++++++++++-- src/state/reducers/routesReducer.ts | 5 +++++ 3 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 src/assets/svgs/agenda-category-icon.svg diff --git a/src/assets/svgs/agenda-category-icon.svg b/src/assets/svgs/agenda-category-icon.svg new file mode 100644 index 0000000000..8e3d4562f3 --- /dev/null +++ b/src/assets/svgs/agenda-category-icon.svg @@ -0,0 +1 @@ +checklist \ No newline at end of file diff --git a/src/components/IconComponent/IconComponent.tsx b/src/components/IconComponent/IconComponent.tsx index d65982edef..17f9af8b32 100644 --- a/src/components/IconComponent/IconComponent.tsx +++ b/src/components/IconComponent/IconComponent.tsx @@ -1,5 +1,6 @@ import { QuestionMarkOutlined } from '@mui/icons-material'; import { ReactComponent as ActionItemIcon } from 'assets/svgs/actionItem.svg'; +import { ReactComponent as AgendaCategoryIcon } from 'assets/svgs/agenda-category-icon.svg'; import { ReactComponent as BlockUserIcon } from 'assets/svgs/blockUser.svg'; import { ReactComponent as CheckInRegistrantsIcon } from 'assets/svgs/checkInRegistrants.svg'; import { ReactComponent as DashboardIcon } from 'assets/svgs/dashboard.svg'; @@ -14,8 +15,8 @@ import { ReactComponent as PostsIcon } from 'assets/svgs/posts.svg'; import { ReactComponent as SettingsIcon } from 'assets/svgs/settings.svg'; import { ReactComponent as VenueIcon } from 'assets/svgs/venues.svg'; import { ReactComponent as RequestsIcon } from 'assets/svgs/requests.svg'; -import { ReactComponent as HomeIcon } from 'assets/svgs/home.svg'; -import { ReactComponent as DonateIcon } from 'assets/svgs/donate.svg'; +// import { ReactComponent as HomeIcon } from 'assets/svgs/home.svg'; +// import { ReactComponent as DonateIcon } from 'assets/svgs/donate.svg'; import React from 'react'; @@ -54,6 +55,13 @@ const iconComponent = (props: InterfaceIconComponent): JSX.Element => { data-testid="Icon-Component-ActionItemIcon" /> ); + case 'Agenda Items Category': + return ( + + ); case 'Posts': return ; case 'Block/Unblock': diff --git a/src/state/reducers/routesReducer.ts b/src/state/reducers/routesReducer.ts index 34cfdbbcca..ef25fe154d 100644 --- a/src/state/reducers/routesReducer.ts +++ b/src/state/reducers/routesReducer.ts @@ -76,6 +76,11 @@ const components: ComponentType[] = [ comp_id: 'orgactionitems', component: 'OrganizationActionItems', }, + { + name: 'Agenda Items Category', + comp_id: 'orgagendaitemscategory', + component: 'OrganizationAgendaCategory', + }, { name: 'Posts', comp_id: 'orgpost', component: 'OrgPost' }, { name: 'Block/Unblock', comp_id: 'blockuser', component: 'BlockUser' }, { name: 'Advertisement', comp_id: 'orgads', component: 'Advertisements' }, From 68034b3d25ffe0eb9b86126f5e358cfac278ed83 Mon Sep 17 00:00:00 2001 From: gautam-divyanshu Date: Wed, 5 Jun 2024 13:41:52 +0530 Subject: [PATCH 04/10] completed with translation.json, components, graphql, reducers, App.tsx --- public/locales/en/translation.json | 17 ++ src/App.tsx | 5 + src/GraphQl/Mutations/mutations.ts | 6 + src/GraphQl/Queries/AgendaCategoryQueries.ts | 10 +- src/GraphQl/Queries/Queries.ts | 1 + .../AgendaCategoryContainer.module.css | 20 ++ .../AgendaCategoryContainer.tsx | 281 ++++++++++++++++++ .../OrganizationScreen/OrganizationScreen.tsx | 1 + .../AgendaCategoryCreateModal.tsx | 86 ++++++ .../AgendaCategoryDeleteModal.tsx | 63 ++++ .../AgendaCategoryPreviewModal.tsx | 91 ++++++ .../AgendaCategoryUpdateModal.tsx | 82 +++++ .../OrganizationAgendaCategory.module.css | 171 +++++++++++ .../OrganizationAgendaCategory.tsx | 158 ++++++++++ src/state/reducers/routesReducer.ts | 2 +- src/utils/interfaces.ts | 8 +- 16 files changed, 998 insertions(+), 4 deletions(-) create mode 100644 src/components/AgendaCategory/AgendaCategoryContainer.module.css create mode 100644 src/components/AgendaCategory/AgendaCategoryContainer.tsx create mode 100644 src/screens/OrganizationAgendaCategory/AgendaCategoryCreateModal.tsx create mode 100644 src/screens/OrganizationAgendaCategory/AgendaCategoryDeleteModal.tsx create mode 100644 src/screens/OrganizationAgendaCategory/AgendaCategoryPreviewModal.tsx create mode 100644 src/screens/OrganizationAgendaCategory/AgendaCategoryUpdateModal.tsx create mode 100644 src/screens/OrganizationAgendaCategory/OrganizationAgendaCategory.module.css create mode 100644 src/screens/OrganizationAgendaCategory/OrganizationAgendaCategory.tsx diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index aa473e5886..31cfe64d3a 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -278,6 +278,23 @@ "successfulDeletion": "Action Item deleted successfully", "title": "Action Items" }, + "organizationAgendaCategory": { + "agendaCategoryDetails": "Agenda Category Details", + "updateAgendaCategory": "Update Agenda Category", + "title": "Agenda Categories", + "name": "Category", + "description": "Description", + "createdBy": "Created By", + "options": "Options", + "createAgendaCategory": "Create Agenda Category", + "noAgendaCategories": "No Agenda Categories", + "update": "Update", + "agendaCategoryCreated": "Agenda Category created successfully", + "agendaCategoryUpdated": "Agenda Category updated successfully", + "agendaCategoryDeleted": "Agenda Category deleted successfully", + "deleteAgendaCategory": "Delete Agenda Category", + "deleteAgendaCategoryMsg": "Do you want to remove this agenda category?" + }, "eventListCard": { "deleteEvent": "Delete Event", "deleteEventMsg": "Do you want to remove this event?", diff --git a/src/App.tsx b/src/App.tsx index 1d8787e7eb..d2e5d8306e 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -14,6 +14,7 @@ import OrgList from 'screens/OrgList/OrgList'; import OrgPost from 'screens/OrgPost/OrgPost'; import OrgSettings from 'screens/OrgSettings/OrgSettings'; import OrganizationActionItems from 'screens/OrganizationActionItems/OrganizationActionItems'; +import OrganizationAgendaCategory from 'screens/OrganizationAgendaCategory/OrganizationAgendaCategory'; import OrganizationDashboard from 'screens/OrganizationDashboard/OrganizationDashboard'; import OrganizationEvents from 'screens/OrganizationEvents/OrganizationEvents'; import OrganizaitionFundCampiagn from 'screens/OrganizationFundCampaign/OrganizationFundCampagins'; @@ -135,6 +136,10 @@ function app(): JSX.Element { path="/orgactionitems/:orgId" element={} /> + } + /> } /> void; +}): JSX.Element { + const { t } = useTranslation('translation', { + keyPrefix: 'organizationAgendaCategory', + }); + const { t: tCommon } = useTranslation('common'); + const [ + agendaCategoryPreviewModalIsOpen, + setAgendaCategoryPreviewModalIsOpen, + ] = useState(false); + const [agendaCategoryUpdateModalIsOpen, setAgendaCategoryUpdateModalIsOpen] = + useState(false); + const [agendaCategoryDeleteModalIsOpen, setAgendaCategoryDeleteModalIsOpen] = + useState(false); + + const [agendaCategoryId, setAgendaCategoryId] = useState(''); + // const [creationDate, setcreationDate] = useState(new Date()); + + const [formState, setFormState] = useState({ + name: '', + description: '', + createdBy: '', + }); + + const showPreviewModal = ( + agendaItemCategory: InterfaceAgendaItemCategoryInfo, + ): void => { + setAgendaCategoryState(agendaItemCategory); + setAgendaCategoryPreviewModalIsOpen(true); + }; + + const hidePreviewModal = (): void => { + setAgendaCategoryPreviewModalIsOpen(false); + }; + + const showUpdateModal = (): void => { + setAgendaCategoryUpdateModalIsOpen(!agendaCategoryUpdateModalIsOpen); + }; + + const hideUpdateModal = (): void => { + setAgendaCategoryUpdateModalIsOpen(!agendaCategoryUpdateModalIsOpen); + }; + + const toggleDeleteModal = (): void => { + setAgendaCategoryDeleteModalIsOpen(!agendaCategoryDeleteModalIsOpen); + }; + + const [updateAgendaCategory] = useMutation( + UPDATE_AGENDA_ITEM_CATEGORY_MUTATION, + ); + + const updateAgendaCategoryHandler = async ( + event: ChangeEvent, + ): Promise => { + event.preventDefault(); + try { + await updateAgendaCategory({ + variables: { + updateAgendaCategoryId: agendaCategoryId, + input: { + name: formState.name, + description: formState.description, + }, + }, + }); + toast.success('Agenda Category Updated Successfully'); + agendaCategoryRefetch(); + hideUpdateModal(); + } catch (error: unknown) { + if (error instanceof Error) { + toast.error(`Agenda Category Update Failed ${error.message}`); + } + } + }; + + const [deleteAgendaCategory] = useMutation( + DELETE_AGENDA_ITEM_CATEGORY_MUTATION, + ); + + const deleteAgendaCategoryHandler = async (): Promise => { + try { + await deleteAgendaCategory({ + variables: { + deleteAgendaCategoryId: agendaCategoryId, + }, + }); + agendaCategoryRefetch(); + toggleDeleteModal(); + toast.success('Agenda Category Deleted Successfully'); + } catch (error: unknown) { + if (error instanceof Error) { + toast.error(`Agenda Category Delete Failed, ${error.message}`); + } + } + }; + + const handleEditClick = ( + agendaItemCategory: InterfaceAgendaItemCategoryInfo, + ): void => { + setAgendaCategoryState(agendaItemCategory); + showUpdateModal(); + }; + + const setAgendaCategoryState = ( + agendaItemCategory: InterfaceAgendaItemCategoryInfo, + ): void => { + setFormState({ + ...formState, + name: `${agendaItemCategory.name} `, + description: `${agendaItemCategory.description}`, + createdBy: `${agendaItemCategory.createdBy.firstName} ${agendaItemCategory.createdBy.lastName}`, + }); + setAgendaCategoryId(agendaItemCategory._id); + }; + + return ( + <> +
+
+ + +
{t('name')}
+ + + {t('description')} + + +
{t('createdBy')}
+ + +
{t('options')}
+ +
+
+
+ {agendaCategoryData?.map((agendaCategory, index) => ( +
+ + + {`${agendaCategory.name}`} + + + {agendaCategory.description} + + + {`${agendaCategory.createdBy.firstName} ${agendaCategory.createdBy.lastName}`} + + + +
+ + +
+ +
+ + {index !== agendaCategoryData.length - 1 && ( +
+ )} +
+ ))} + {agendaCategoryData?.length === 0 && ( +
+ {t('noAgendaCategories')} +
+ )} +
+
+ + {/* Preview model */} + + {/* Update model */} + + {/* Delete model */} + + + ); +} + +export default agendaCategoryContainer; diff --git a/src/components/OrganizationScreen/OrganizationScreen.tsx b/src/components/OrganizationScreen/OrganizationScreen.tsx index 59b6f289c9..a8f9d16b9c 100644 --- a/src/components/OrganizationScreen/OrganizationScreen.tsx +++ b/src/components/OrganizationScreen/OrganizationScreen.tsx @@ -112,6 +112,7 @@ const map: InterfaceMapType = { member: 'memberDetail', orgevents: 'organizationEvents', orgactionitems: 'organizationActionItems', + orgagendacategory: 'organizationAgendaCategory', orgcontribution: 'orgContribution', orgpost: 'orgPost', orgfunds: 'funds', diff --git a/src/screens/OrganizationAgendaCategory/AgendaCategoryCreateModal.tsx b/src/screens/OrganizationAgendaCategory/AgendaCategoryCreateModal.tsx new file mode 100644 index 0000000000..0e2f469071 --- /dev/null +++ b/src/screens/OrganizationAgendaCategory/AgendaCategoryCreateModal.tsx @@ -0,0 +1,86 @@ +import React from 'react'; +import { Modal, Form, Button } from 'react-bootstrap'; +import type { ChangeEvent } from 'react'; +import styles from './OrganizationAgendaCategory.module.css'; + +// import type { InterfaceAgendaItemCategoryInfo } from 'utils/interfaces'; + +interface InterfaceFormStateType { + name: string; + description: string; + createdBy: string; +} + +interface InterfaceAgendaCategoryCreateModalProps { + agendaCategoryCreateModalIsOpen: boolean; + hideCreateModal: () => void; + formState: InterfaceFormStateType; + setFormState: (state: React.SetStateAction) => void; + createAgendaCategoryHandler: ( + e: ChangeEvent, + ) => Promise; + t: (key: string) => string; +} + +const AgendaCategoryCreateModal: React.FC< + InterfaceAgendaCategoryCreateModalProps +> = ({ + agendaCategoryCreateModalIsOpen, + hideCreateModal, + formState, + setFormState, + createAgendaCategoryHandler, + t, +}) => { + return ( + + +

{t('agendaCategoryDetails')}

+ +
+ +
+ + {t('name')} + + setFormState({ ...formState, name: e.target.value }) + } + /> + + + {t('description')} + + setFormState({ ...formState, description: e.target.value }) + } + /> + + +
+
+
+ ); +}; + +export default AgendaCategoryCreateModal; diff --git a/src/screens/OrganizationAgendaCategory/AgendaCategoryDeleteModal.tsx b/src/screens/OrganizationAgendaCategory/AgendaCategoryDeleteModal.tsx new file mode 100644 index 0000000000..bc705f43d5 --- /dev/null +++ b/src/screens/OrganizationAgendaCategory/AgendaCategoryDeleteModal.tsx @@ -0,0 +1,63 @@ +import React from 'react'; +import { Modal, Button } from 'react-bootstrap'; +import styles from './OrganizationAgendaCategory.module.css'; + +interface InterfaceAgendaCategoryDeleteModalProps { + agendaCategoryDeleteModalIsOpen: boolean; + toggleDeleteModal: () => void; + deleteAgendaCategoryHandler: () => Promise; + t: (key: string) => string; + tCommon: (key: string) => string; +} + +const AgendaCategoryDeleteModal: React.FC< + InterfaceAgendaCategoryDeleteModalProps +> = ({ + agendaCategoryDeleteModalIsOpen, + toggleDeleteModal, + deleteAgendaCategoryHandler, + t, + tCommon, +}) => { + return ( + + + + {t('deleteAgendaCategory')} + + + +

{t('deleteAgendaCategoryMsg')}

+
+ + + + +
+ ); +}; + +export default AgendaCategoryDeleteModal; diff --git a/src/screens/OrganizationAgendaCategory/AgendaCategoryPreviewModal.tsx b/src/screens/OrganizationAgendaCategory/AgendaCategoryPreviewModal.tsx new file mode 100644 index 0000000000..c9dc17e56f --- /dev/null +++ b/src/screens/OrganizationAgendaCategory/AgendaCategoryPreviewModal.tsx @@ -0,0 +1,91 @@ +import React from 'react'; +import { Modal, Form, Button } from 'react-bootstrap'; + +import styles from './OrganizationAgendaCategory.module.css'; + +interface InterfaceFormStateType { + name: string; + description: string; + createdBy: string; +} + +interface InterfaceAgendaCategoryPreviewModalProps { + agendaCategoryPreviewModalIsOpen: boolean; + hidePreviewModal: () => void; + showUpdateModal: () => void; + toggleDeleteModal: () => void; + formState: InterfaceFormStateType; + + t: (key: string) => string; +} + +const AgendaCategoryPreviewModal: React.FC< + InterfaceAgendaCategoryPreviewModalProps +> = ({ + agendaCategoryPreviewModalIsOpen, + hidePreviewModal, + showUpdateModal, + toggleDeleteModal, + formState, + t, +}) => { + return ( + + +

{t('agendaCategoryDetails')}

+ +
+ +
+
+

+ {t('name')} + {formState.name} +

+

+ {t('description')} + {formState.description} +

+

+ {t('createdBy')} + {formState.createdBy} +

+
+
+ + +
+
+
+
+ ); +}; + +export default AgendaCategoryPreviewModal; diff --git a/src/screens/OrganizationAgendaCategory/AgendaCategoryUpdateModal.tsx b/src/screens/OrganizationAgendaCategory/AgendaCategoryUpdateModal.tsx new file mode 100644 index 0000000000..05eb46f3e6 --- /dev/null +++ b/src/screens/OrganizationAgendaCategory/AgendaCategoryUpdateModal.tsx @@ -0,0 +1,82 @@ +import React from 'react'; +import { Modal, Form, Button } from 'react-bootstrap'; +import type { ChangeEvent } from 'react'; + +import styles from './OrganizationAgendaCategory.module.css'; + +interface InterfaceFormStateType { + name: string; + description: string; + createdBy: string; +} + +interface InterfaceAgendaCategoryUpdateModalProps { + agendaCategoryUpdateModalIsOpen: boolean; + hideUpdateModal: () => void; + formState: InterfaceFormStateType; + setFormState: (state: React.SetStateAction) => void; + updateAgendaCategoryHandler: ( + e: ChangeEvent, + ) => Promise; + t: (key: string) => string; +} + +const AgendaCategoryUpdateModal: React.FC< + InterfaceAgendaCategoryUpdateModalProps +> = ({ + agendaCategoryUpdateModalIsOpen, + hideUpdateModal, + formState, + setFormState, + updateAgendaCategoryHandler, + t, +}) => { + return ( + + +

{t('updateAgendaCategory')}

+ +
+ +
+ + {t('name')} + + setFormState({ ...formState, name: e.target.value }) + } + /> + + + {t('description')} + + setFormState({ ...formState, description: e.target.value }) + } + /> + + +
+
+
+ ); +}; + +export default AgendaCategoryUpdateModal; diff --git a/src/screens/OrganizationAgendaCategory/OrganizationAgendaCategory.module.css b/src/screens/OrganizationAgendaCategory/OrganizationAgendaCategory.module.css new file mode 100644 index 0000000000..13e187f8b5 --- /dev/null +++ b/src/screens/OrganizationAgendaCategory/OrganizationAgendaCategory.module.css @@ -0,0 +1,171 @@ +.agendaCategoryContainer { + height: 90vh; +} + +.agendaCategoryModal { + max-width: 80vw; + margin-top: 2vh; + margin-left: 13vw; +} + +.btnsContainer { + display: flex; + gap: 10px; +} + +.btnsContainer .btnsBlock { + display: flex; + gap: 10px; +} + +.btnsContainer button { + display: flex; + align-items: center; +} + +.container { + min-height: 100vh; +} + +.datediv { + display: flex; + flex-direction: row; +} + +.datebox { + width: 90%; + border-radius: 7px; + outline: none; + box-shadow: none; + padding-top: 2px; + padding-bottom: 2px; + padding-right: 5px; + padding-left: 5px; + margin-right: 5px; + margin-left: 5px; +} + +.dropdown { + display: block; +} + +.dropdownToggle { + margin-bottom: 0; + display: flex; +} + +.dropdownModalToggle { + width: 50%; +} + +.errorIcon { + transform: scale(1.5); + color: var(--bs-danger); + margin-bottom: 1rem; +} + +.greenregbtn { + margin: 1rem 0 0; + margin-top: 15px; + 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; + width: 100%; +} + +h2 { + margin-top: 0.5rem; +} + +hr { + border: none; + height: 1px; + background-color: var(--bs-gray-500); + margin: 1rem; +} + +.iconContainer { + display: flex; + justify-content: flex-end; +} +.icon { + margin: 1px; +} + +.message { + margin-top: 25%; + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; +} + +.organizationAgendaCategoryContainer h2 { + margin: 0.6rem 0; +} + +.preview { + display: flex; + flex-direction: row; + font-weight: 900; + font-size: 16px; + color: rgb(80, 80, 80); +} + +.removeFilterIcon { + cursor: pointer; +} + +.searchForm { + display: inline; +} + +.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%; +} + +.view { + margin-left: 2%; + font-weight: 600; + font-size: 16px; + color: var(--bs-gray-600); +} + +@media (max-width: 767px) { + .btnsContainer { + margin-bottom: 0; + display: flex; + flex-direction: column; + } + + .btnsContainer .btnsBlock .dropdownToggle { + flex-grow: 1; + } + + .btnsContainer button { + width: 100%; + } + + .createAgendaCategoryButton { + position: absolute; + top: 1rem; + right: 2rem; + } +} diff --git a/src/screens/OrganizationAgendaCategory/OrganizationAgendaCategory.tsx b/src/screens/OrganizationAgendaCategory/OrganizationAgendaCategory.tsx new file mode 100644 index 0000000000..fa58062b33 --- /dev/null +++ b/src/screens/OrganizationAgendaCategory/OrganizationAgendaCategory.tsx @@ -0,0 +1,158 @@ +import React, { useState } from 'react'; +import type { ChangeEvent } from 'react'; +import { useTranslation } from 'react-i18next'; +import { Button } from 'react-bootstrap'; +import { useParams } from 'react-router-dom'; + +import { WarningAmberRounded } from '@mui/icons-material'; +import { toast } from 'react-toastify'; + +import { useMutation, useQuery } from '@apollo/client'; +import { AGENDA_ITEM_CATEGORY_LIST } from 'GraphQl/Queries/Queries'; +import { CREATE_AGENDA_ITEM_CATEGORY_MUTATION } from 'GraphQl/Mutations/mutations'; + +import type { InterfaceAgendaItemCategoryList } from 'utils/interfaces'; +import AgendaCategoryContainer from 'components/AgendaCategory/AgendaCategoryContainer'; +import AgendaCategoryCreateModal from './AgendaCategoryCreateModal'; +import styles from './OrganizationAgendaCategory.module.css'; +import Loader from 'components/Loader/Loader'; + +function organizationAgendaCategory(): JSX.Element { + const { t } = useTranslation('translation', { + keyPrefix: 'organizationAgendaCategory', + }); + + const { orgId: currentUrl } = useParams(); + + const [agendaCategoryCreateModalIsOpen, setAgendaCategoryCreateModalIsOpen] = + useState(false); + + const [formState, setFormState] = useState({ + name: '', + description: '', + createdBy: '', + }); + + const { + data: agendaCategoryData, + loading: agendaCategoryLoading, + error: agendaCategoryError, + refetch: refetchAgendaCategory, + }: { + data: InterfaceAgendaItemCategoryList | undefined; + loading: boolean; + error?: unknown | undefined; + refetch: () => void; + } = useQuery(AGENDA_ITEM_CATEGORY_LIST, { + variables: { organizationId: currentUrl }, + notifyOnNetworkStatusChange: true, + }); + + const [createAgendaCategory] = useMutation( + CREATE_AGENDA_ITEM_CATEGORY_MUTATION, + ); + + const createAgendaCategoryHandler = async ( + e: ChangeEvent, + ): Promise => { + e.preventDefault(); + try { + await createAgendaCategory({ + variables: { + input: { + organizationId: currentUrl, + name: formState.name, + description: formState.description, + }, + }, + }); + toast.success(t('agendaCategoryCreated')); + setFormState({ name: '', description: '', createdBy: '' }); + refetchAgendaCategory(); + hideCreateModal(); + } catch (error: unknown) { + if (error instanceof Error) { + toast.error(error.message); + console.error(error.message); + } + } + }; + + const showCreateModal = (): void => { + setAgendaCategoryCreateModalIsOpen(!agendaCategoryCreateModalIsOpen); + }; + + const hideCreateModal = (): void => { + setAgendaCategoryCreateModalIsOpen(!agendaCategoryCreateModalIsOpen); + }; + + if (agendaCategoryLoading) return ; + + if (agendaCategoryError) { + return ( +
+
+ +
+ Error occured while loading{' '} + {agendaCategoryError && 'Agenda Categories'} + Data +
+ {agendaCategoryError && (agendaCategoryError as Error).message} +
+
+
+ ); + } + + return ( +
+
+
+
+
+ {/* setSearchValue(e.target.value)} + value={searchValue} + data-testid="searchAgendaCategories" + /> */} +
+ + +
+
+ +
+ + +
+ +
+ ); +} + +export default organizationAgendaCategory; diff --git a/src/state/reducers/routesReducer.ts b/src/state/reducers/routesReducer.ts index ef25fe154d..bcda9f02d8 100644 --- a/src/state/reducers/routesReducer.ts +++ b/src/state/reducers/routesReducer.ts @@ -78,7 +78,7 @@ const components: ComponentType[] = [ }, { name: 'Agenda Items Category', - comp_id: 'orgagendaitemscategory', + comp_id: 'orgagendacategory', component: 'OrganizationAgendaCategory', }, { name: 'Posts', comp_id: 'orgpost', component: 'OrgPost' }, diff --git a/src/utils/interfaces.ts b/src/utils/interfaces.ts index 822262f9e1..1d65b63189 100644 --- a/src/utils/interfaces.ts +++ b/src/utils/interfaces.ts @@ -460,8 +460,14 @@ export interface InterfaceQueryMembershipRequestsListItem { export interface InterfaceAgendaItemCategoryInfo { _id: string; name: string; + description: string; + createdBy: { + _id: string; + firstName: string; + lastName: string; + }; } export interface InterfaceAgendaItemCategoryList { - agendaCategory: InterfaceAgendaItemCategoryInfo[]; + agendaItemCategoriesByOrganization: InterfaceAgendaItemCategoryInfo[]; } From f8461c7ddf3ffadb6f930eef2c13427ef884fb95 Mon Sep 17 00:00:00 2001 From: gautam-divyanshu Date: Thu, 6 Jun 2024 00:41:41 +0530 Subject: [PATCH 05/10] test cases done mostly --- .../AgendaCategoryContainer.test.tsx | 372 ++++++++++++++++++ .../AgendaCategoryContainer.tsx | 5 +- .../AgendaCategoryContainerMocks.ts | 104 +++++ .../AgendaCategoryContainerProps.ts | 34 ++ .../AgendaCategoryCreateModal.tsx | 11 +- .../AgendaCategoryDeleteModal.tsx | 2 +- .../AgendaCategoryUpdateModal.test.tsx | 151 +++++++ .../AgendaCategoryUpdateModal.tsx | 6 +- .../OrganizationAgendaCategory.test.tsx | 192 +++++++++ .../OrganizationAgendaCategory.tsx | 4 +- .../OrganizationAgendaCategoryErrorMocks.ts | 27 ++ .../OrganizationAgendaCategoryMocks.ts | 47 +++ 12 files changed, 946 insertions(+), 9 deletions(-) create mode 100644 src/components/AgendaCategory/AgendaCategoryContainer.test.tsx create mode 100644 src/components/AgendaCategory/AgendaCategoryContainerMocks.ts create mode 100644 src/components/AgendaCategory/AgendaCategoryContainerProps.ts create mode 100644 src/screens/OrganizationAgendaCategory/AgendaCategoryUpdateModal.test.tsx create mode 100644 src/screens/OrganizationAgendaCategory/OrganizationAgendaCategory.test.tsx create mode 100644 src/screens/OrganizationAgendaCategory/OrganizationAgendaCategoryErrorMocks.ts create mode 100644 src/screens/OrganizationAgendaCategory/OrganizationAgendaCategoryMocks.ts diff --git a/src/components/AgendaCategory/AgendaCategoryContainer.test.tsx b/src/components/AgendaCategory/AgendaCategoryContainer.test.tsx new file mode 100644 index 0000000000..8a8a9c70ef --- /dev/null +++ b/src/components/AgendaCategory/AgendaCategoryContainer.test.tsx @@ -0,0 +1,372 @@ +import React from 'react'; +import { + render, + screen, + waitFor, + act, + waitForElementToBeRemoved, + fireEvent, +} from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import 'jest-localstorage-mock'; +import { MockedProvider } from '@apollo/client/testing'; +import 'jest-location-mock'; +import { I18nextProvider } from 'react-i18next'; +import { Provider } from 'react-redux'; +import { BrowserRouter } from 'react-router-dom'; +import i18nForTest from 'utils/i18nForTest'; +import { toast } from 'react-toastify'; +import { LocalizationProvider } from '@mui/x-date-pickers'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; + +import { store } from 'state/store'; +import { StaticMockLink } from 'utils/StaticMockLink'; + +import AgendaCategoryContainer from './AgendaCategoryContainer'; +import { props, props2 } from './AgendaCategoryContainerProps'; +import { MOCKS, MOCKS_ERROR_MUTATIONS } from './AgendaCategoryContainerMocks'; + +const link = new StaticMockLink(MOCKS, true); +const link2 = new StaticMockLink(MOCKS_ERROR_MUTATIONS, true); + +jest.mock('react-toastify', () => ({ + toast: { + success: jest.fn(), + error: jest.fn(), + }, +})); + +async function wait(ms = 100): Promise { + await act(async () => { + return new Promise((resolve) => setTimeout(resolve, ms)); + }); +} + +const translations = JSON.parse( + JSON.stringify( + i18nForTest.getDataByLanguage('en')?.translation.organizationAgendaCategory, + ), +); + +describe('Testing Agenda Category Component', () => { + const formData = { + name: 'Test', + description: 'Test', + }; + + test('component loads correctly with categories', async () => { + render( + + + + + + + + + , + ); + await wait(); + + await waitFor(() => { + expect( + screen.queryByText(translations.noAgendaCategories), + ).not.toBeInTheDocument(); + }); + }); + + test('component loads correctly with no agenda Categories', async () => { + render( + + + + + + + + + , + ); + + await wait(); + + await waitFor(() => { + expect( + screen.queryByText(translations.noAgendaCategories), + ).toBeInTheDocument(); + }); + }); + + test('opens and closes the update modal correctly', async () => { + render( + + + + + + + + + , + ); + + await wait(); + + await waitFor(() => { + expect( + screen.getAllByTestId('editAgendCategoryModalBtn')[0], + ).toBeInTheDocument(); + }); + userEvent.click(screen.getAllByTestId('editAgendCategoryModalBtn')[0]); + + await waitFor(() => { + return expect( + screen.findByTestId('updateAgendaCategoryModalCloseBtn'), + ).resolves.toBeInTheDocument(); + }); + userEvent.click(screen.getByTestId('updateAgendaCategoryModalCloseBtn')); + + await waitForElementToBeRemoved(() => + screen.queryByTestId('updateAgendaCategoryModalCloseBtn'), + ); + }); + + test('opens and closes the preview modal correctly', async () => { + render( + + + + + + + + + , + ); + + await wait(); + + await waitFor(() => { + expect( + screen.getAllByTestId('previewAgendaCategoryModalBtn')[0], + ).toBeInTheDocument(); + }); + userEvent.click(screen.getAllByTestId('previewAgendaCategoryModalBtn')[0]); + + await waitFor(() => { + return expect( + screen.findByTestId('previewAgendaCategoryModalCloseBtn'), + ).resolves.toBeInTheDocument(); + }); + userEvent.click(screen.getByTestId('previewAgendaCategoryModalCloseBtn')); + + await waitForElementToBeRemoved(() => + screen.queryByTestId('previewAgendaCategoryModalCloseBtn'), + ); + }); + + test('opens and closes the update and delete modals through the preview modal', async () => { + render( + + + + + + + + + , + ); + + await wait(); + + await waitFor(() => { + expect( + screen.getAllByTestId('previewAgendaCategoryModalBtn')[0], + ).toBeInTheDocument(); + }); + userEvent.click(screen.getAllByTestId('previewAgendaCategoryModalBtn')[0]); + + await waitFor(() => { + return expect( + screen.findByTestId('previewAgendaCategoryModalCloseBtn'), + ).resolves.toBeInTheDocument(); + }); + + await waitFor(() => { + expect( + screen.getByTestId('deleteAgendaCategoryModalBtn'), + ).toBeInTheDocument(); + }); + userEvent.click(screen.getByTestId('deleteAgendaCategoryModalBtn')); + + await waitFor(() => { + return expect( + screen.findByTestId('deleteAgendaCategoryCloseBtn'), + ).resolves.toBeInTheDocument(); + }); + userEvent.click(screen.getByTestId('deleteAgendaCategoryCloseBtn')); + + await waitForElementToBeRemoved(() => + screen.queryByTestId('deleteAgendaCategoryCloseBtn'), + ); + + await waitFor(() => { + expect( + screen.getByTestId('editAgendaCategoryPreviewModalBtn'), + ).toBeInTheDocument(); + }); + userEvent.click(screen.getByTestId('editAgendaCategoryPreviewModalBtn')); + + await waitFor(() => { + return expect( + screen.findByTestId('updateAgendaCategoryModalCloseBtn'), + ).resolves.toBeInTheDocument(); + }); + userEvent.click(screen.getByTestId('updateAgendaCategoryModalCloseBtn')); + + await waitForElementToBeRemoved(() => + screen.queryByTestId('updateAgendaCategoryModalCloseBtn'), + ); + }); + + test('toasts error on unsuccessful updation', async () => { + render( + + + + + + + + + + + , + ); + + await wait(); + + await waitFor(() => { + expect( + screen.getAllByTestId('editAgendCategoryModalBtn')[0], + ).toBeInTheDocument(); + }); + userEvent.click(screen.getAllByTestId('editAgendCategoryModalBtn')[0]); + + const nameInput = screen.getByLabelText(translations.name); + const descriptionInput = screen.getByLabelText(translations.description); + fireEvent.change(nameInput, { target: { value: '' } }); + fireEvent.change(descriptionInput, { + target: { value: '' }, + }); + userEvent.type(nameInput, formData.name); + userEvent.type(descriptionInput, formData.description); + + await waitFor(() => { + expect(screen.getByTestId('editAgendaCategoryBtn')).toBeInTheDocument(); + }); + userEvent.click(screen.getByTestId('editAgendaCategoryBtn')); + + await waitFor(() => { + expect(toast.error).toHaveBeenCalled(); + }); + }); + + test('deletes the agenda category and toasts success', async () => { + render( + + + + + + + + + + + , + ); + + await wait(); + + await waitFor(() => { + expect( + screen.getAllByTestId('previewAgendaCategoryModalBtn')[0], + ).toBeInTheDocument(); + }); + userEvent.click(screen.getAllByTestId('previewAgendaCategoryModalBtn')[0]); + + await waitFor(() => { + return expect( + screen.findByTestId('previewAgendaCategoryModalCloseBtn'), + ).resolves.toBeInTheDocument(); + }); + + await waitFor(() => { + expect( + screen.getByTestId('deleteAgendaCategoryModalBtn'), + ).toBeInTheDocument(); + }); + userEvent.click(screen.getByTestId('deleteAgendaCategoryModalBtn')); + + await waitFor(() => { + return expect( + screen.findByTestId('deleteAgendaCategoryCloseBtn'), + ).resolves.toBeInTheDocument(); + }); + + userEvent.click(screen.getByTestId('deleteAgendaCategoryBtn')); + + await waitFor(() => { + expect(toast.success).toBeCalledWith(translations.agendaCategoryDeleted); + }); + }); + + test('toasts error on unsuccessful deletion', async () => { + render( + + + + + + + + + , + ); + + await wait(); + + await waitFor(() => { + expect( + screen.getAllByTestId('previewAgendaCategoryModalBtn')[0], + ).toBeInTheDocument(); + }); + userEvent.click(screen.getAllByTestId('previewAgendaCategoryModalBtn')[0]); + + await waitFor(() => { + return expect( + screen.findByTestId('previewAgendaCategoryModalCloseBtn'), + ).resolves.toBeInTheDocument(); + }); + + await waitFor(() => { + expect( + screen.getByTestId('deleteAgendaCategoryModalBtn'), + ).toBeInTheDocument(); + }); + userEvent.click(screen.getByTestId('deleteAgendaCategoryModalBtn')); + + await waitFor(() => { + return expect( + screen.findByTestId('deleteAgendaCategoryCloseBtn'), + ).resolves.toBeInTheDocument(); + }); + userEvent.click(screen.getByTestId('deleteAgendaCategoryBtn')); + + await waitFor(() => { + expect(toast.error).toHaveBeenCalled(); + }); + }); +}); diff --git a/src/components/AgendaCategory/AgendaCategoryContainer.tsx b/src/components/AgendaCategory/AgendaCategoryContainer.tsx index ae86fc9e5b..1f6b936974 100644 --- a/src/components/AgendaCategory/AgendaCategoryContainer.tsx +++ b/src/components/AgendaCategory/AgendaCategoryContainer.tsx @@ -89,9 +89,10 @@ function agendaCategoryContainer({ }, }, }); - toast.success('Agenda Category Updated Successfully'); + agendaCategoryRefetch(); hideUpdateModal(); + toast.success(t('agendaCategoryUpdated')); } catch (error: unknown) { if (error instanceof Error) { toast.error(`Agenda Category Update Failed ${error.message}`); @@ -112,7 +113,7 @@ function agendaCategoryContainer({ }); agendaCategoryRefetch(); toggleDeleteModal(); - toast.success('Agenda Category Deleted Successfully'); + toast.success(t('agendaCategoryDeleted')); } catch (error: unknown) { if (error instanceof Error) { toast.error(`Agenda Category Delete Failed, ${error.message}`); diff --git a/src/components/AgendaCategory/AgendaCategoryContainerMocks.ts b/src/components/AgendaCategory/AgendaCategoryContainerMocks.ts new file mode 100644 index 0000000000..5440fff82b --- /dev/null +++ b/src/components/AgendaCategory/AgendaCategoryContainerMocks.ts @@ -0,0 +1,104 @@ +import { + UPDATE_AGENDA_ITEM_CATEGORY_MUTATION, + DELETE_AGENDA_ITEM_CATEGORY_MUTATION, +} from 'GraphQl/Mutations/AgendaCategoryMutations'; + +export const MOCKS = [ + { + request: { + query: UPDATE_AGENDA_ITEM_CATEGORY_MUTATION, + variables: { + updateAgendaCategoryId: 'agendaCategory1', + input: { + name: 'AgendaCategory 1 Edited', + description: 'AgendaCategory 1 Description Edited', + }, + }, + }, + result: { + data: { + updateAgendaCategory: { + _id: 'agendaCategory1', + }, + }, + }, + }, + { + request: { + query: UPDATE_AGENDA_ITEM_CATEGORY_MUTATION, + variables: { + updateAgendaCategoryId: 'agendaCategory1', + input: { + name: 'AgendaCategory 1', + description: 'AgendaCategory 1 Description', + }, + }, + }, + result: { + data: { + updateAgendaCategory: { + _id: 'agendaCategory1', + }, + }, + }, + }, + { + request: { + query: UPDATE_AGENDA_ITEM_CATEGORY_MUTATION, + variables: { + updateAgendaCategoryId: 'agendaCategory2', + input: { + name: 'AgendaCategory 2', + description: 'AgendaCategory 2 Description', + }, + }, + }, + result: { + data: { + updateAgendaCategory: { + _id: 'agendaCategory2', + }, + }, + }, + }, + { + request: { + query: DELETE_AGENDA_ITEM_CATEGORY_MUTATION, + variables: { + deleteAgendaCategoryId: 'agendaCategory1', + }, + }, + result: { + data: { + deleteAgendaCategory: { + _id: 'agendaCategory1', + }, + }, + }, + }, +]; + +export const MOCKS_ERROR_MUTATIONS = [ + { + request: { + query: UPDATE_AGENDA_ITEM_CATEGORY_MUTATION, + variables: { + updateAgendaCategoryId: 'agendaCategory1', + input: { + name: 'AgendaCategory 1 Edited', + description: 'AgendaCategory 1 Description Edited', + }, + }, + }, + error: new Error('Mock Graphql Error'), + }, + { + request: { + query: DELETE_AGENDA_ITEM_CATEGORY_MUTATION, + variables: { + deleteAgendaCategoryId: 'agendaCategory1', + }, + }, + error: new Error('Mock Graphql Error'), + }, +]; diff --git a/src/components/AgendaCategory/AgendaCategoryContainerProps.ts b/src/components/AgendaCategory/AgendaCategoryContainerProps.ts new file mode 100644 index 0000000000..5181eec153 --- /dev/null +++ b/src/components/AgendaCategory/AgendaCategoryContainerProps.ts @@ -0,0 +1,34 @@ +type AgendaCategoryConnectionType = 'Organization'; + +export const props = { + agendaCategoryConnection: 'Organization' as AgendaCategoryConnectionType, + agendaCategoryData: [ + { + _id: 'agendaCategory1', + name: 'AgendaCategory 1', + description: 'AgendaCategory 1 Description', + createdBy: { + _id: 'user0', + firstName: 'Wilt', + lastName: 'Shepherd', + }, + }, + { + _id: 'agendaCategory2', + name: 'AgendaCategory 2', + description: 'AgendaCategory 2 Description', + createdBy: { + _id: 'user0', + firstName: 'Wilt', + lastName: 'Shepherd', + }, + }, + ], + agendaCategoryRefetch: jest.fn(), +}; + +export const props2 = { + agendaCategoryConnection: 'Organization' as AgendaCategoryConnectionType, + agendaCategoryData: [], + agendaCategoryRefetch: jest.fn(), +}; diff --git a/src/screens/OrganizationAgendaCategory/AgendaCategoryCreateModal.tsx b/src/screens/OrganizationAgendaCategory/AgendaCategoryCreateModal.tsx index 0e2f469071..88132d72a1 100644 --- a/src/screens/OrganizationAgendaCategory/AgendaCategoryCreateModal.tsx +++ b/src/screens/OrganizationAgendaCategory/AgendaCategoryCreateModal.tsx @@ -54,7 +54,7 @@ const AgendaCategoryCreateModal: React.FC< {t('name')} @@ -66,7 +66,7 @@ const AgendaCategoryCreateModal: React.FC< {t('description')} @@ -74,7 +74,12 @@ const AgendaCategoryCreateModal: React.FC< } /> - diff --git a/src/screens/OrganizationAgendaCategory/AgendaCategoryDeleteModal.tsx b/src/screens/OrganizationAgendaCategory/AgendaCategoryDeleteModal.tsx index bc705f43d5..f2edb8fc62 100644 --- a/src/screens/OrganizationAgendaCategory/AgendaCategoryDeleteModal.tsx +++ b/src/screens/OrganizationAgendaCategory/AgendaCategoryDeleteModal.tsx @@ -43,7 +43,7 @@ const AgendaCategoryDeleteModal: React.FC< className="btn btn-danger" data-dismiss="modal" onClick={toggleDeleteModal} - data-testid="cancelDeleteAgendaCategoryBtn" + data-testid="deleteAgendaCategoryCloseBtn" > {tCommon('no')} diff --git a/src/screens/OrganizationAgendaCategory/AgendaCategoryUpdateModal.test.tsx b/src/screens/OrganizationAgendaCategory/AgendaCategoryUpdateModal.test.tsx new file mode 100644 index 0000000000..b22794fa3d --- /dev/null +++ b/src/screens/OrganizationAgendaCategory/AgendaCategoryUpdateModal.test.tsx @@ -0,0 +1,151 @@ +import React from 'react'; +import { render, screen, fireEvent } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { MockedProvider } from '@apollo/react-testing'; +import { I18nextProvider } from 'react-i18next'; +import { Provider } from 'react-redux'; +import { BrowserRouter } from 'react-router-dom'; +import { store } from 'state/store'; +import i18nForTest from 'utils/i18nForTest'; + +import { LocalizationProvider } from '@mui/x-date-pickers'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; + +import AgendaCategoryUpdateModal from './/AgendaCategoryUpdateModal'; + +const mockFormState = { + name: 'Test Name', + description: 'Test Description', + createdBy: 'Test User', +}; +const mockHideUpdateModal = jest.fn(); +const mockSetFormState = jest.fn(); +const mockUpdateAgendaCategoryHandler = jest.fn(); +const mockT = (key: string): string => key; + +describe('AgendaCategoryUpdateModal', () => { + test('renders modal correctly', () => { + render( + + + + + + + + + + + , + ); + + expect(screen.getByText('updateAgendaCategory')).toBeInTheDocument(); + expect(screen.getByTestId('editAgendaCategoryBtn')).toBeInTheDocument(); + expect( + screen.getByTestId('updateAgendaCategoryModalCloseBtn'), + ).toBeInTheDocument(); + }); + + test('calls hideUpdateModal when close button is clicked', () => { + render( + + + + + + + + + + + , + ); + + userEvent.click(screen.getByTestId('updateAgendaCategoryModalCloseBtn')); + expect(mockHideUpdateModal).toHaveBeenCalledTimes(1); + }); + + test('tests the condition for formState.preCompletionNotes', () => { + const mockFormState = { + name: 'Test Name', + description: 'Test Description', + createdBy: 'Test User', + }; + render( + + + + + + + + + + + , + ); + const nameInput = screen.getByLabelText('name'); + fireEvent.change(nameInput, { + target: { value: 'New name' }, + }); + const descriptionInput = screen.getByLabelText('description'); + fireEvent.change(descriptionInput, { + target: { value: 'New description' }, + }); + expect(mockSetFormState).toHaveBeenCalledWith({ + ...mockFormState, + name: 'New name', + }); + expect(mockSetFormState).toHaveBeenCalledWith({ + ...mockFormState, + description: 'New description', + }); + }); + + test('calls updateAgendaCategoryHandler when form is submitted', () => { + render( + + + + + + + + + + + , + ); + + fireEvent.submit(screen.getByTestId('editAgendaCategoryBtn')); + expect(mockUpdateAgendaCategoryHandler).toHaveBeenCalledTimes(1); + }); +}); diff --git a/src/screens/OrganizationAgendaCategory/AgendaCategoryUpdateModal.tsx b/src/screens/OrganizationAgendaCategory/AgendaCategoryUpdateModal.tsx index 05eb46f3e6..f648c7367d 100644 --- a/src/screens/OrganizationAgendaCategory/AgendaCategoryUpdateModal.tsx +++ b/src/screens/OrganizationAgendaCategory/AgendaCategoryUpdateModal.tsx @@ -70,7 +70,11 @@ const AgendaCategoryUpdateModal: React.FC< } /> - diff --git a/src/screens/OrganizationAgendaCategory/OrganizationAgendaCategory.test.tsx b/src/screens/OrganizationAgendaCategory/OrganizationAgendaCategory.test.tsx new file mode 100644 index 0000000000..11120f2cc0 --- /dev/null +++ b/src/screens/OrganizationAgendaCategory/OrganizationAgendaCategory.test.tsx @@ -0,0 +1,192 @@ +import React from 'react'; +import { + render, + screen, + fireEvent, + waitFor, + act, + waitForElementToBeRemoved, +} from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import 'jest-localstorage-mock'; +import { MockedProvider } from '@apollo/client/testing'; +import 'jest-location-mock'; +import { I18nextProvider } from 'react-i18next'; +import { Provider } from 'react-redux'; +import { BrowserRouter } from 'react-router-dom'; +import i18n from 'utils/i18nForTest'; +import { toast } from 'react-toastify'; +import { LocalizationProvider } from '@mui/x-date-pickers'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; + +import { store } from 'state/store'; +import { StaticMockLink } from 'utils/StaticMockLink'; + +import OrganizationAgendaCategory from './OrganizationAgendaCategory'; +import { + MOCKS_ERROR_AGENDA_ITEM_CATEGORY_LIST_QUERY, + MOCKS_ERROR_MUTATION, +} from './OrganizationAgendaCategoryErrorMocks'; +import { MOCKS } from './/OrganizationAgendaCategoryMocks'; + +jest.mock('react-toastify', () => ({ + toast: { + success: jest.fn(), + error: jest.fn(), + }, +})); + +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useParams: () => ({ orgId: '123' }), +})); + +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_AGENDA_ITEM_CATEGORY_LIST_QUERY, + true, +); +const link3 = new StaticMockLink(MOCKS_ERROR_MUTATION, true); + +const translations = { + ...JSON.parse( + JSON.stringify( + i18n.getDataByLanguage('en')?.translation.organizationAgendaCategory ?? + {}, + ), + ), + ...JSON.parse(JSON.stringify(i18n.getDataByLanguage('en')?.common ?? {})), + ...JSON.parse(JSON.stringify(i18n.getDataByLanguage('en')?.errors ?? {})), +}; + +describe('Testing Agenda Categories Component', () => { + const formData = { + name: 'Test Name', + description: 'Test Description', + }; + + test('Component loads correctly', async () => { + const { getByText } = render( + + + + + {} + + + + , + ); + + await wait(); + + await waitFor(() => { + expect(getByText(translations.createAgendaCategory)).toBeInTheDocument(); + }); + }); + + test('render error component on unsuccessful agenda category list query', async () => { + const { queryByText } = render( + + + + + {} + + + + , + ); + + await wait(); + + await waitFor(() => { + expect( + queryByText(translations.createAgendaCategory), + ).not.toBeInTheDocument(); + }); + }); + + test('opens and closes the create agenda category modal', async () => { + render( + + + + + + {} + + + + + , + ); + + await wait(); + + await waitFor(() => { + expect(screen.getByTestId('createAgendaCategoryBtn')).toBeInTheDocument(); + }); + userEvent.click(screen.getByTestId('createAgendaCategoryBtn')); + + await waitFor(() => { + return expect( + screen.findByTestId('createAgendaCategoryModalCloseBtn'), + ).resolves.toBeInTheDocument(); + }); + userEvent.click(screen.getByTestId('createAgendaCategoryModalCloseBtn')); + + await waitForElementToBeRemoved(() => + screen.queryByTestId('createAgendaCategoryModalCloseBtn'), + ); + }); + + // test('creates new agenda category with correct orgId', async () => { + // render( + // + // + // + // + // + // + // + // + // + // + // , + // ); + + // await wait(); + + // await waitFor(() => { + // expect(screen.getByTestId('createAgendaCategoryBtn')).toBeInTheDocument(); + // }); + // userEvent.click(screen.getByTestId('createAgendaCategoryBtn')); + + // await waitFor(() => { + // expect( + // screen.getByTestId('createAgendaCategoryModalCloseBtn'), + // ).toBeInTheDocument(); + // }); + + // userEvent.type(screen.getByPlaceholderText('Category'), formData.name); + // userEvent.type( + // screen.getByPlaceholderText('Description'), + // formData.description, + // ); + + // userEvent.click(screen.getByTestId('createAgendaCategoryFormSubmitBtn')); + + // await waitFor(() => { + // expect(toast.success).toBeCalledWith(translations.agendaCategoryCreated); + // }); + // }); +}); diff --git a/src/screens/OrganizationAgendaCategory/OrganizationAgendaCategory.tsx b/src/screens/OrganizationAgendaCategory/OrganizationAgendaCategory.tsx index fa58062b33..09d12664a5 100644 --- a/src/screens/OrganizationAgendaCategory/OrganizationAgendaCategory.tsx +++ b/src/screens/OrganizationAgendaCategory/OrganizationAgendaCategory.tsx @@ -124,8 +124,8 @@ function organizationAgendaCategory(): JSX.Element { diff --git a/src/screens/OrganizationAgendaCategory/AgendaCategoryUpdateModal.tsx b/src/screens/OrganizationAgendaCategory/AgendaCategoryUpdateModal.tsx index c6a906830a..868205c339 100644 --- a/src/screens/OrganizationAgendaCategory/AgendaCategoryUpdateModal.tsx +++ b/src/screens/OrganizationAgendaCategory/AgendaCategoryUpdateModal.tsx @@ -43,7 +43,7 @@ const AgendaCategoryUpdateModal: React.FC< onClick={hideUpdateModal} data-testid="updateAgendaCategoryModalCloseBtn" > - +