From 15316a50f20d7288a1884985afb2add922c9d59e Mon Sep 17 00:00:00 2001 From: Glen Date: Thu, 20 Jun 2024 01:05:35 +0530 Subject: [PATCH 01/17] refac/redesign Funds --- public/locales/en/translation.json | 21 +- public/locales/fr/translation.json | 25 +- public/locales/hi/translation.json | 39 +- public/locales/sp/translation.json | 40 +- public/locales/zh/translation.json | 33 +- .../FundCampaignPledge/FundCampaignPledge.tsx | 40 +- .../OrganizationFunds/FundCreateModal.tsx | 118 ---- .../OrganizationFunds/FundDeleteModal.tsx | 81 +++ src/screens/OrganizationFunds/FundModal.tsx | 259 ++++++++ .../OrganizationFunds/FundUpdateModal.tsx | 155 ----- .../OrganizationFunds.module.css | 41 +- .../OrganizationFunds/OrganizationFunds.tsx | 605 +++++++++--------- src/utils/interfaces.ts | 19 +- 13 files changed, 749 insertions(+), 727 deletions(-) delete mode 100644 src/screens/OrganizationFunds/FundCreateModal.tsx create mode 100644 src/screens/OrganizationFunds/FundDeleteModal.tsx create mode 100644 src/screens/OrganizationFunds/FundModal.tsx delete mode 100644 src/screens/OrganizationFunds/FundUpdateModal.tsx diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index d94d050e53..37ea261f97 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -326,33 +326,24 @@ "title": "Funds", "createFund": "Create Fund", "fundName": "Fund Name", - "fundId": "Fund ID", - "fundOptions": "Opitons", - "noFunds": "No Funds Found", - "fundDetails": "Fund Details", + "fundId": "Fund (Reference) ID", "taxDeductible": "Tax Deductible", - "enterfundName": "Enter Fund Name", - "enterfundId": "Enter Fund ID", - "default": "Default Fund", + "default": "Default", "archived": "Archived", - "nonArchive": "Non-Archived", "fundCreate": "Create Fund", "fundUpdate": "Update Fund", "fundDelete": "Delete Fund", - "manageFund": "Manage Fund", - "searchFullName": "Search By Name", + "searchByName": "Search By Name", "noFundsFound": "No Funds Found", "createdBy": "Created By", "createdOn": "Created On", "status": "Status", - "archiveFund": "Archive Fund", - "archiveFundMsg": "On Archiving this fund will remove it from the fund listing.Thisaction can be undone", "fundCreated": "Fund created successfully", "fundUpdated": "Fund updated successfully", "fundDeleted": "Fund deleted successfully", - "fundArchived": "Fund archived successfully", - "fundUnarchived": "Fund unarchived successfully", - "deleteFundMsg": "Do you want to remove this fund?" + "deleteFundMsg": "Are you sure you want to delete this fund?", + "createdLatest": "Created Latest", + "createdEarliest": "Created Earliest" }, "fundCampaign": { "title": "Fundraising Campaigns", diff --git a/public/locales/fr/translation.json b/public/locales/fr/translation.json index 1d41e2c54a..23b4d6c225 100644 --- a/public/locales/fr/translation.json +++ b/public/locales/fr/translation.json @@ -331,33 +331,24 @@ "title": "Fonds", "createFund": "Créer un fonds", "fundName": "Nom du fonds", - "fundId": "ID du fonds", - "fundOptions": "Options", - "noFunds": "Aucun fonds trouvé", - "fundDetails": "Détails du fonds", - "taxDeductible": "Déductible d'impôts", - "enterfundName": "Entrez le nom du fonds", - "enterfundId": "Entrez l'ID du fonds", - "default": "Fonds de défaut", + "fundId": "ID de référence du fonds", + "taxDeductible": "Déductible d'impôt", + "default": "Par défaut", "archived": "Archivé", - "nonArchive": "Non archivé", "fundCreate": "Créer un fonds", "fundUpdate": "Mettre à jour le fonds", "fundDelete": "Supprimer le fonds", - "manageFund": "Gérer le fonds", - "searchFullName": "Rechercher par nom", + "searchByName": "Rechercher par nom", "noFundsFound": "Aucun fonds trouvé", "createdBy": "Créé par", - "createdOn": "Créé sur", + "createdOn": "Créé le", "status": "Statut", - "archiveFund": "Fonds d'archives", - "archiveFundMsg": "Lors de l'archivage, ce fonds le supprimera de la liste des fonds. Cette action peut être annulée.", "fundCreated": "Fonds créé avec succès", "fundUpdated": "Fonds mis à jour avec succès", "fundDeleted": "Fonds supprimé avec succès", - "fundArchived": "Fonds archivé avec succès", - "fundUnarchived": "Fonds désarchivé avec succès", - "deleteFundMsg": "Voulez-vous supprimer ce fonds ?" + "deleteFundMsg": "Êtes-vous sûr de vouloir supprimer ce fonds ?", + "createdLatest": "Créé le plus récemment", + "createdEarliest": "Créé le plus tôt" }, "fundCampaign": { "title": "Campagnes de collecte de fonds", diff --git a/public/locales/hi/translation.json b/public/locales/hi/translation.json index b01838a702..6e88dc3c76 100644 --- a/public/locales/hi/translation.json +++ b/public/locales/hi/translation.json @@ -329,35 +329,26 @@ }, "funds": { "title": "फंड", - "createFund": "फंड बनाएं", + "createFund": "फंड बनाएँ", "fundName": "फंड का नाम", - "fundId": "फंड आईडी", - "fundOptions": "Opitons", - "noFunds": "कोई फंड नहीं मिला", - "fundDetails": "फंड विवरण", - "taxDeductible": "कर छूट", - "enterfundName": "फंड का नाम दर्ज करें", - "enterfundId": "फंड आईडी दर्ज करें", - "default": "डिफ़ॉल्ट फंड", - "archived": "संग्रहीत", - "nonArchive": "गैर-अभिलेखित", - "fundCreate": "फंड बनाएं", - "fundUpdate": "अद्यतन निधि", - "fundDelete": "फ़ंड हटाएँ", - "manageFund": "फंड का प्रबंधन करें", - "searchFullName": "नाम से खोजें", + "fundId": "फंड (संदर्भ) आईडी", + "taxDeductible": "कर ड्यूटी कटाई", + "default": "डिफ़ॉल्ट", + "archived": "आर्काइव", + "fundCreate": "फंड बनाएँ", + "fundUpdate": "फंड अपडेट करें", + "fundDelete": "फंड को हटाएँ", + "searchByName": "नाम से खोजें", "noFundsFound": "कोई फंड नहीं मिला", - "createdBy": "के द्वारा बनाई गई", - "createdOn": "पर बनाया", + "createdBy": "द्वारा बनाया गया", + "createdOn": "पर बनाया गया", "status": "स्थिति", - "archiveFund": "पुरालेख कोष", - "archiveFundMsg": "इस फंड को संग्रहित करने पर इसे फंड सूची से हटा दिया जाएगा। इस कार्रवाई को पूर्ववत किया जा सकता है", "fundCreated": "फंड सफलतापूर्वक बनाया गया", "fundUpdated": "फंड सफलतापूर्वक अपडेट किया गया", - "fundDeleted": "फंड सफलतापूर्वक हटा दिया गया", - "fundArchived": "निधि सफलतापूर्वक संग्रहित की गई", - "fundUnarchived": "फंड सफलतापूर्वक संग्रहित किया गया", - "deleteFundMsg": "क्या आप इस फंड को हटाना चाहते हैं?" + "fundDeleted": "फंड सफलतापूर्वक हटाया गया", + "deleteFundMsg": "क्या आप वाकई इस फंड को हटाना चाहते हैं?", + "createdLatest": "सबसे पहले बनाया", + "createdEarliest": "सबसे जल्दी बनाया" }, "fundCampaign": { "title": "धन उगाही अभियान", diff --git a/public/locales/sp/translation.json b/public/locales/sp/translation.json index 96345b1567..47d950d6cd 100644 --- a/public/locales/sp/translation.json +++ b/public/locales/sp/translation.json @@ -456,37 +456,25 @@ "funds": { "title": "Fondos", "createFund": "Crear fondo", - "fundName": "Nombre del Fondo", - "fundId": "ID del Fondo", - "fundOptions": "Opciones", - "noFunds": "No se encontraron fondos", - "fundDetails": "Detalles del Fondo", + "fundName": "Nombre del fondo", + "fundId": "ID de referencia del fondo", "taxDeductible": "Deducible de impuestos", - "enterfundName": "Ingrese el nombre del fondo", - "enterfundId": "Ingrese el ID del fondo", - "default": "Fondo predeterminado", + "default": "Predeterminado", "archived": "Archivado", - "nonArchive": "No archivado", - "fundCreate": "Crear Fondo", - "fundUpdate": "Actualizar Fondo", - "fundDelete": "Eliminar Fondo", - "no": "No", - "yes": "Sí", - "manageFund": "Administrar fondo", - "searchFullName": "Buscar por nombre", - "filter": "Filtrar", + "fundCreate": "Crear fondo", + "fundUpdate": "Actualizar fondo", + "fundDelete": "Eliminar fondo", + "searchByName": "Buscar por nombre", "noFundsFound": "No se encontraron fondos", "createdBy": "Creado por", - "createdOn": "Creado el", + "createdOn": "Creado en", "status": "Estado", - "archiveFund": "Archivar Fondo", - "archiveFundMsg": "¿Desea archivar este fondo?", - "fundCreated": "Fondo creado exitosamente", - "fundUpdated": "Fondo actualizado exitosamente", - "fundDeleted": "Fondo eliminado exitosamente", - "fundArchived": "Fondo archivado exitosamente", - "fundUnarchived": "Fondo no archivado exitosamente", - "deleteFundMsg": "¿Desea eliminar este fondo?" + "fundCreated": "Fondo creado correctamente", + "fundUpdated": "Fondo actualizado correctamente", + "fundDeleted": "Fondo eliminado correctamente", + "deleteFundMsg": "¿Está seguro de que desea eliminar este fondo?", + "createdLatest": "Creado más reciente", + "createdEarliest": "Creado más temprano" }, "fundCampaign": { "title": "Campañas de recaudación de fondos", diff --git a/public/locales/zh/translation.json b/public/locales/zh/translation.json index e8ea831a91..30f2c375f2 100644 --- a/public/locales/zh/translation.json +++ b/public/locales/zh/translation.json @@ -328,36 +328,27 @@ "occurences": "事件" }, "funds": { - "title": "资金", + "title": "基金", "createFund": "创建基金", "fundName": "基金名称", - "fundId": "基金编号", - "fundOptions": "奥皮通斯", - "noFunds": "未找到资金", - "fundDetails": "基金详情", - "taxDeductible": "免税额", - "enterfundName": "输入基金名称", - "enterfundId": "输入基金ID", - "default": "违约基金", + "fundId": "基金(参考)ID", + "taxDeductible": "税前扣除", + "default": "默认", "archived": "已归档", - "nonArchive": "非存档", "fundCreate": "创建基金", "fundUpdate": "更新基金", "fundDelete": "删除基金", - "manageFund": "管理基金", - "searchFullName": "按名称搜索", - "noFundsFound": "未找到资金", - "createdBy": "由...制作", + "searchByName": "按名称搜索", + "noFundsFound": "未找到基金", + "createdBy": "由...创建", "createdOn": "创建于", - "status": "地位", - "archiveFund": "档案基金", - "archiveFundMsg": "存档后,该基金将从基金列表中删除。此操作可以撤消", + "status": "状态", "fundCreated": "基金创建成功", "fundUpdated": "基金更新成功", - "fundDeleted": "资金删除成功", - "fundArchived": "资金归档成功", - "fundUnarchived": "基金已成功解档", - "deleteFundMsg": "您想删除该基金吗?" + "fundDeleted": "基金删除成功", + "deleteFundMsg": "您确定要删除此基金吗?", + "createdLatest": "最近创建", + "createdEarliest": "最早创建" }, "fundCampaign": { "title": "筹款活动", diff --git a/src/screens/FundCampaignPledge/FundCampaignPledge.tsx b/src/screens/FundCampaignPledge/FundCampaignPledge.tsx index c8d1c95d5b..71d976de52 100644 --- a/src/screens/FundCampaignPledge/FundCampaignPledge.tsx +++ b/src/screens/FundCampaignPledge/FundCampaignPledge.tsx @@ -26,6 +26,27 @@ enum Modal { DELETE = 'delete', } +const dataGridStyle = { + '&.MuiDataGrid-root .MuiDataGrid-cell:focus-within': { + outline: 'none !important', + }, + '&.MuiDataGrid-root .MuiDataGrid-columnHeader:focus-within': { + outline: 'none', + }, + '& .MuiDataGrid-row:hover': { + backgroundColor: 'transparent', + }, + '& .MuiDataGrid-row.Mui-hovered': { + backgroundColor: 'transparent', + }, + '& .MuiDataGrid-root': { + borderRadius: '0.5rem', + }, + '& .MuiDataGrid-main': { + borderRadius: '0.5rem', + }, +}; + const fundCampaignPledge = (): JSX.Element => { const { t } = useTranslation('translation', { keyPrefix: 'pledges', @@ -245,7 +266,7 @@ const fundCampaignPledge = (): JSX.Element => { - - -
- - {t('fundName')} - - setFormState({ - ...formState, - fundName: e.target.value, - }) - } - /> - - - {t('fundId')} - - setFormState({ - ...formState, - fundRef: e.target.value, - }) - } - /> - -
- -
- - setTaxDeductible(!taxDeductible)} - /> -
-
- -
- - setIsDefault(!isDefault)} - /> -
-
-
- -
-
- - - ); -}; -export default FundCreateModal; diff --git a/src/screens/OrganizationFunds/FundDeleteModal.tsx b/src/screens/OrganizationFunds/FundDeleteModal.tsx new file mode 100644 index 0000000000..b5ce303d66 --- /dev/null +++ b/src/screens/OrganizationFunds/FundDeleteModal.tsx @@ -0,0 +1,81 @@ +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import type { InterfaceFundInfo } from 'utils/interfaces'; +import styles from './OrganizationFunds.module.css'; +import { Button, Modal } from 'react-bootstrap'; +import { REMOVE_FUND_MUTATION } from 'GraphQl/Mutations/FundMutation'; +import { useMutation } from '@apollo/client'; +import { toast } from 'react-toastify'; + +export interface InterfaceDeleteFundModal { + isOpen: boolean; + hide: () => void; + fund: InterfaceFundInfo | null; + refetchFunds: () => void; +} + +const FundDeleteModal: React.FC = ({ + isOpen, + hide, + fund, + refetchFunds, +}) => { + const { t } = useTranslation('translation', { + keyPrefix: 'funds', + }); + const { t: tCommon } = useTranslation('common'); + + const [deleteFund] = useMutation(REMOVE_FUND_MUTATION); + + const deleteFundHandler = async (): Promise => { + try { + await deleteFund({ + variables: { + id: fund?._id, + }, + }); + refetchFunds(); + hide(); + toast.success(t('fundDeleted')); + } catch (error: unknown) { + if (error instanceof Error) { + toast.error(error.message); + } + } + }; + + return ( + <> + + +

{t('fundDelete')}

+ +
+ +

{t('deleteFundMsg')}

+
+ + + + +
+ + ); +}; + +export default FundDeleteModal; diff --git a/src/screens/OrganizationFunds/FundModal.tsx b/src/screens/OrganizationFunds/FundModal.tsx new file mode 100644 index 0000000000..3cfbecb05a --- /dev/null +++ b/src/screens/OrganizationFunds/FundModal.tsx @@ -0,0 +1,259 @@ +import React, { useEffect, useState } from 'react'; +import type { ChangeEvent } from 'react'; +import { Button, Form, Modal } from 'react-bootstrap'; +import type { InterfaceCreateFund, InterfaceFundInfo } from 'utils/interfaces'; +import styles from './OrganizationFunds.module.css'; +import { useTranslation } from 'react-i18next'; +import { useMutation } from '@apollo/client'; +import { + CREATE_FUND_MUTATION, + UPDATE_FUND_MUTATION, +} from 'GraphQl/Mutations/FundMutation'; +import { toast } from 'react-toastify'; +import { FormControl, TextField } from '@mui/material'; + +interface InterfaceFundModal { + isOpen: boolean; + hide: () => void; + refetchFunds: () => void; + fund: InterfaceFundInfo | null; + orgId: string; + mode: 'create' | 'edit'; +} + +const FundModal: React.FC = ({ + isOpen, + hide, + refetchFunds, + fund, + orgId, + mode, +}) => { + const { t } = useTranslation('translation', { + keyPrefix: 'funds', + }); + + const [formState, setFormState] = useState({ + fundName: fund?.name ?? '', + fundRef: fund?.refrenceNumber ?? '', + isDefault: fund?.isDefault ?? false, + taxDeductible: fund?.taxDeductible ?? false, + isArchived: fund?.isArchived ?? false, + }); + + useEffect(() => { + setFormState({ + fundName: fund?.name ?? '', + fundRef: fund?.refrenceNumber ?? '', + isDefault: fund?.isDefault ?? false, + taxDeductible: fund?.taxDeductible ?? false, + isArchived: fund?.isArchived ?? false, + }); + }, [fund]); + + const [createFund] = useMutation(CREATE_FUND_MUTATION); + const [updateFund] = useMutation(UPDATE_FUND_MUTATION); + + const createFundHandler = async ( + e: ChangeEvent, + ): Promise => { + e.preventDefault(); + const { fundName, fundRef, isDefault, taxDeductible, isArchived } = + formState; + try { + await createFund({ + variables: { + name: fundName, + refrenceNumber: fundRef, + organizationId: orgId, + taxDeductible, + isArchived, + isDefault, + }, + }); + + setFormState({ + fundName: '', + fundRef: '', + isDefault: false, + taxDeductible: false, + isArchived: false, + }); + toast.success(t('fundCreated')); + refetchFunds(); + hide(); + } catch (error: unknown) { + if (error instanceof Error) { + toast.error(error.message); + } + } + }; + + const updateFundHandler = async ( + e: ChangeEvent, + ): Promise => { + e.preventDefault(); + const { fundName, fundRef, taxDeductible, isArchived, isDefault } = + formState; + try { + const updatedFields: { [key: string]: string | boolean } = {}; + if (fundName != fund?.name) { + updatedFields.name = fundName; + } + if (fundRef != fund?.refrenceNumber) { + updatedFields.refrenceNumber = fundRef; + } + if (taxDeductible != fund?.taxDeductible) { + updatedFields.taxDeductible = taxDeductible; + } + if (isArchived != fund?.isArchived) { + updatedFields.isArchived = isArchived; + } + if (isDefault != fund?.isDefault) { + updatedFields.isDefault = isDefault; + } + + await updateFund({ + variables: { + id: fund?._id, + ...updatedFields, + }, + }); + setFormState({ + fundName: '', + fundRef: '', + isDefault: false, + taxDeductible: false, + isArchived: false, + }); + refetchFunds(); + hide(); + toast.success(t('fundUpdated')); + } catch (error: unknown) { + if (error instanceof Error) { + toast.error(error.message); + } + } + }; + + return ( + <> + + +

+ {t(mode === 'create' ? 'fundCreate' : 'fundUpdate')} +

+ +
+ +
+ + + + setFormState({ + ...formState, + fundName: e.target.value, + }) + } + /> + + + + + + setFormState({ + ...formState, + fundRef: e.target.value, + }) + } + /> + + + +
+ + + + setFormState({ + ...formState, + taxDeductible: !formState.taxDeductible, + }) + } + /> + + + + + setFormState({ + ...formState, + isDefault: !formState.isDefault, + }) + } + /> + + {mode === 'edit' && ( + + + + setFormState({ + ...formState, + isArchived: !formState.isArchived, + }) + } + /> + + )} +
+ +
+
+
+ + ); +}; +export default FundModal; diff --git a/src/screens/OrganizationFunds/FundUpdateModal.tsx b/src/screens/OrganizationFunds/FundUpdateModal.tsx deleted file mode 100644 index 2a351a1c8c..0000000000 --- a/src/screens/OrganizationFunds/FundUpdateModal.tsx +++ /dev/null @@ -1,155 +0,0 @@ -import type { ChangeEvent } from 'react'; -import React from 'react'; -import { Button, Form, Modal } from 'react-bootstrap'; -import type { InterfaceCreateFund } from 'utils/interfaces'; -import styles from './OrganizationFunds.module.css'; - -interface InterfaceFundUpdateModal { - fundUpdateModalIsOpen: boolean; - hideUpdateModal: () => void; - formState: InterfaceCreateFund; - setFormState: (state: React.SetStateAction) => void; - updateFundHandler: (e: ChangeEvent) => Promise; - taxDeductible: boolean; - setTaxDeductible: (state: React.SetStateAction) => void; - isArchived: boolean; - deleteFundHandler: () => Promise; - setIsArchived: (state: React.SetStateAction) => void; - isDefault: boolean; - setIsDefault: (state: React.SetStateAction) => void; - t: (key: string) => string; -} - -const FundUpdateModal: React.FC = ({ - fundUpdateModalIsOpen, - hideUpdateModal, - formState, - setFormState, - updateFundHandler, - taxDeductible, - setTaxDeductible, - isArchived, - deleteFundHandler, - setIsArchived, - isDefault, - setIsDefault, - t, -}) => { - return ( - <> - - -

{t('manageFund')}

- -
- -
- - {t('fundName')} - - setFormState({ - ...formState, - fundName: e.target.value, - }) - } - /> - - - {t('fundId')} - - setFormState({ - ...formState, - fundRef: e.target.value, - }) - } - /> - - -
-
- -
- - setTaxDeductible(!taxDeductible)} - /> -
-
- -
- - setIsDefault(!isDefault)} - /> -
-
-
-
- -
- - setIsArchived(!isArchived)} - /> -
-
-
-
-
- - -
-
-
-
- - ); -}; -export default FundUpdateModal; diff --git a/src/screens/OrganizationFunds/OrganizationFunds.module.css b/src/screens/OrganizationFunds/OrganizationFunds.module.css index 07e16ad0a8..b3739f3e13 100644 --- a/src/screens/OrganizationFunds/OrganizationFunds.module.css +++ b/src/screens/OrganizationFunds/OrganizationFunds.module.css @@ -34,11 +34,6 @@ font-weight: 600; cursor: pointer; } -.fundModal { - max-width: 80vw; - margin-top: 2vh; - margin-left: 13vw; -} .modalHeader { border: none; @@ -49,12 +44,33 @@ color: var(--bs-emphasis-color); } +.fundModal { + max-width: 80vw; + margin-top: 2vh; + margin-left: 13vw; +} + .titlemodal { + color: #707070; font-weight: 600; - font-size: 25px; - margin-top: 1rem; + font-size: 32px; width: 65%; + margin-bottom: 0px; +} + +.noOutline input { + outline: none; +} + +.modalCloseBtn { + width: 40px; + height: 40px; + padding: 1rem; + display: flex; + justify-content: center; + align-items: center; } + .greenregbtn { margin: 1rem 0 0; margin-top: 15px; @@ -130,6 +146,17 @@ margin-bottom: 20px; } +.rowBackground { + background-color: var(--bs-white); + max-height: 120px; +} + +.tableHeader { + background-color: var(--bs-primary); + color: var(--bs-white); + font-size: 1rem; +} + @media (max-width: 1020px) { .btnsContainer { flex-direction: column; diff --git a/src/screens/OrganizationFunds/OrganizationFunds.tsx b/src/screens/OrganizationFunds/OrganizationFunds.tsx index b0846c8c0a..b78430f887 100644 --- a/src/screens/OrganizationFunds/OrganizationFunds.tsx +++ b/src/screens/OrganizationFunds/OrganizationFunds.tsx @@ -1,55 +1,48 @@ -/*eslint-disable*/ -import { useMutation, useQuery } from '@apollo/client'; -import { Search, WarningAmberRounded } from '@mui/icons-material'; -import { - CREATE_FUND_MUTATION, - REMOVE_FUND_MUTATION, - UPDATE_FUND_MUTATION, -} from 'GraphQl/Mutations/FundMutation'; +import { useQuery } from '@apollo/client'; +import { Search, Sort, WarningAmberRounded } from '@mui/icons-material'; import Loader from 'components/Loader/Loader'; -import { useState, type ChangeEvent } from 'react'; -import { Button, Col, Dropdown, Form, Row } from 'react-bootstrap'; +import React, { useCallback, useMemo, useState } from 'react'; +import { Button, Dropdown, Form, Row } from 'react-bootstrap'; import { useTranslation } from 'react-i18next'; -import { useNavigate, useParams } from 'react-router-dom'; -import { toast } from 'react-toastify'; -import type { - InterfaceCreateFund, - InterfaceFundInfo, - InterfaceQueryOrganizationFunds, -} from 'utils/interfaces'; -import FundCreateModal from './FundCreateModal'; -import FundUpdateModal from './FundUpdateModal'; -import FilterAltOutlinedIcon from '@mui/icons-material/FilterAltOutlined'; +import { Navigate, useNavigate, useParams } from 'react-router-dom'; +import type { InterfaceFundInfo } from 'utils/interfaces'; +import FundModal from './FundModal'; import styles from './OrganizationFunds.module.css'; -import { - Paper, - Table, - TableBody, - TableCell, - TableContainer, - TableHead, - TableRow, - styled, - tableCellClasses, -} from '@mui/material'; +import { Stack } from '@mui/material'; import dayjs from 'dayjs'; import { FUND_LIST } from 'GraphQl/Queries/fundQueries'; +import { + DataGrid, + type GridCellParams, + type GridColDef, +} from '@mui/x-data-grid'; +import FundDeleteModal from './FundDeleteModal'; -const StyledTableCell = styled(TableCell)(({ theme }) => ({ - [`&.${tableCellClasses.head}`]: { - backgroundColor: ['#31bb6b', '!important'], - color: theme.palette.common.white, +const dataGridStyle = { + '&.MuiDataGrid-root .MuiDataGrid-cell:focus-within': { + outline: 'none !important', }, - [`&.${tableCellClasses.body}`]: { - fontSize: 14, + '&.MuiDataGrid-root .MuiDataGrid-columnHeader:focus-within': { + outline: 'none', }, -})); - -const StyledTableRow = styled(TableRow)(() => ({ - '&:last-child td, &:last-child th': { - border: 0, + '& .MuiDataGrid-row:hover': { + backgroundColor: 'transparent', + }, + '& .MuiDataGrid-row.Mui-hovered': { + backgroundColor: 'transparent', + }, + '& .MuiDataGrid-root': { + borderRadius: '0.5rem', + }, + '& .MuiDataGrid-main': { + borderRadius: '0.5rem', }, -})); +}; + +enum Modal { + SAME = 'same', + DELETE = 'delete', +} const organizationFunds = (): JSX.Element => { const { t } = useTranslation('translation', { @@ -57,21 +50,40 @@ const organizationFunds = (): JSX.Element => { }); const { t: tCommon } = useTranslation('common'); - const { orgId: currentUrl } = useParams(); + const { orgId } = useParams(); const navigate = useNavigate(); - const [fundCreateModalIsOpen, setFundCreateModalIsOpen] = - useState(false); - const [fundUpdateModalIsOpen, setFundUpdateModalIsOpen] = - useState(false); - const [taxDeductible, setTaxDeductible] = useState(true); - const [isArchived, setIsArchived] = useState(false); - const [isDefault, setIsDefault] = useState(false); + if (!orgId) { + return ; + } + const [fund, setFund] = useState(null); - const [formState, setFormState] = useState({ - fundName: '', - fundRef: '', + const [searchTerm, setSearchTerm] = useState(''); + const [sortBy, setSortBy] = useState(null); + + const [modalState, setModalState] = useState<{ [key in Modal]: boolean }>({ + [Modal.SAME]: false, + [Modal.DELETE]: false, }); + const [fundModalMode, setFundModalMode] = useState<'edit' | 'create'>( + 'create', + ); + const openModal = (modal: Modal): void => { + setModalState((prevState) => ({ ...prevState, [modal]: true })); + }; + + const closeModal = (modal: Modal): void => { + setModalState((prevState) => ({ ...prevState, [modal]: false })); + }; + + const handleOpenModal = useCallback( + (fund: InterfaceFundInfo | null, mode: 'edit' | 'create'): void => { + setFund(fund); + setFundModalMode(mode); + openModal(Modal.SAME); + }, + [openModal], + ); const { data: fundData, @@ -80,144 +92,40 @@ const organizationFunds = (): JSX.Element => { refetch: refetchFunds, }: { data?: { - fundsByOrganization: InterfaceQueryOrganizationFunds[]; + fundsByOrganization: InterfaceFundInfo[]; }; loading: boolean; error?: Error | undefined; - refetch: any; + refetch: () => void; } = useQuery(FUND_LIST, { variables: { - organizationId: currentUrl, + organizationId: orgId, }, }); - const [fullName, setFullName] = useState(''); - const handleSearch = (): void => { - refetchFunds({ organizationId: currentUrl, filter: fullName }); - }; - - const [createFund] = useMutation(CREATE_FUND_MUTATION); - const [updateFund] = useMutation(UPDATE_FUND_MUTATION); - const [deleteFund] = useMutation(REMOVE_FUND_MUTATION); - - const showCreateModal = (): void => { - setFundCreateModalIsOpen(!fundCreateModalIsOpen); - }; - const hideCreateModal = (): void => { - setFundCreateModalIsOpen(!fundCreateModalIsOpen); - }; - const showUpdateModal = (): void => { - setFundUpdateModalIsOpen(!fundUpdateModalIsOpen); - }; - const hideUpdateModal = (): void => { - setFundUpdateModalIsOpen(!fundUpdateModalIsOpen); - }; - const toggleDeleteModal = (): void => { - setFundUpdateModalIsOpen(!fundUpdateModalIsOpen); - }; - const handleEditClick = (fund: InterfaceFundInfo): void => { - setFormState({ - fundName: fund.name, - fundRef: fund.refrenceNumber, - }); - setTaxDeductible(fund.taxDeductible); - setIsArchived(fund.isArchived); - setIsDefault(fund.isDefault); - setFund(fund); - showUpdateModal(); - }; - const createFundHandler = async ( - e: ChangeEvent, - ): Promise => { - e.preventDefault(); - try { - await createFund({ - variables: { - name: formState.fundName, - refrenceNumber: formState.fundRef, - organizationId: currentUrl, - taxDeductible: taxDeductible, - isArchived: isArchived, - isDefault: isDefault, - }, - }); - - setFormState({ - fundName: '', - fundRef: '', - }); - toast.success(t('fundCreated')); - refetchFunds(); - hideCreateModal(); - } catch (error: unknown) { - if (error instanceof Error) { - toast.error(error.message); - console.log(error.message); - } - } - }; - const updateFundHandler = async ( - e: ChangeEvent, - ): Promise => { - e.preventDefault(); - try { - const updatedFields: { [key: string]: any } = {}; - if (formState.fundName != fund?.name) { - updatedFields.name = formState.fundName; - } - if (formState.fundRef != fund?.refrenceNumber) { - updatedFields.refrenceNumber = formState.fundRef; - } - if (taxDeductible != fund?.taxDeductible) { - updatedFields.taxDeductible = taxDeductible; - } - if (isArchived != fund?.isArchived) { - updatedFields.isArchived = isArchived; - } - if (isDefault != fund?.isDefault) { - updatedFields.isDefault = isDefault; - } + const handleDeleteClick = useCallback( + (fund: InterfaceFundInfo): void => { + setFund(fund); + openModal(Modal.DELETE); + }, + [openModal], + ); - await updateFund({ - variables: { - id: fund?._id, - ...updatedFields, - }, - }); - setFormState({ - fundName: '', - fundRef: '', - }); - refetchFunds(); - hideUpdateModal(); - toast.success(t('fundUpdated')); - } catch (error: unknown) { - if (error instanceof Error) { - toast.error(error.message); - console.log(error.message); - } - } - }; - const deleteFundHandler = async (): Promise => { - try { - await deleteFund({ - variables: { - id: fund?._id, - }, - }); - refetchFunds(); - toggleDeleteModal(); - toast.success(t('fundDeleted')); - } catch (error: unknown) { - if (error instanceof Error) { - toast.error(error.message); - console.log(error.message); - } - } - }; + const funds = useMemo(() => { + return ( + fundData?.fundsByOrganization.filter((fund) => { + const search = searchTerm.toLowerCase(); + const fullName = `${fund.creator.firstName} ${fund.creator.lastName}`; + return ( + fullName.toLowerCase().includes(search) || + fund.name.toLowerCase().includes(search) + ); + }) ?? [] + ); + }, [fundData, searchTerm]); - const handleClick = (fundId: String) => { - navigate(`/orgfundcampaign/${currentUrl}/${fundId}`); + const handleClick = (fundId: string): void => { + navigate(`/orgfundcampaign/${orgId}/${fundId}`); }; if (fundLoading) { @@ -238,8 +146,149 @@ const organizationFunds = (): JSX.Element => { ); } + const columns: GridColDef[] = [ + { + field: 'id', + headerName: 'Sr. No.', + flex: 1, + minWidth: 100, + align: 'center', + headerAlign: 'center', + headerClassName: `${styles.tableHeader}`, + sortable: false, + renderCell: (params: GridCellParams) => { + return
{params.row.id}
; + }, + }, + { + field: 'fundName', + headerName: 'Fund Name', + flex: 2, + minWidth: 150, + align: 'center', + headerAlign: 'center', + headerClassName: `${styles.tableHeader}`, + sortable: false, + renderCell: (params: GridCellParams) => { + return ( +
handleClick(params.row.fund._id as string)} + > + {params.row.fund.name} +
+ ); + }, + }, + { + field: 'createdBy', + headerName: 'Created By', + flex: 2, + minWidth: 150, + align: 'center', + headerAlign: 'center', + headerClassName: `${styles.tableHeader}`, + sortable: false, + renderCell: (params: GridCellParams) => { + return ( + params.row.fund.creator.firstName + + ' ' + + params.row.fund.creator.lastName + ); + }, + }, + { + field: 'createdOn', + headerName: 'Created On', + minWidth: 150, + align: 'center', + headerAlign: 'center', + headerClassName: `${styles.tableHeader}`, + flex: 2, + sortable: false, + renderCell: (params: GridCellParams) => { + return dayjs(params.row.fund.createdAt).format('DD/MM/YYYY'); + }, + }, + { + field: 'status', + headerName: 'Status', + flex: 2, + minWidth: 100, + align: 'center', + headerAlign: 'center', + headerClassName: `${styles.tableHeader}`, + sortable: false, + renderCell: (params: GridCellParams) => { + return params.row.fund.isArchived ?

Archived

:

Active

; + }, + }, + { + field: 'action', + headerName: 'Action', + flex: 2, + minWidth: 100, + align: 'center', + headerAlign: 'center', + headerClassName: `${styles.tableHeader}`, + sortable: false, + renderCell: (params: GridCellParams) => { + return ( + <> + + + + ); + }, + }, + { + field: 'assocCampaigns', + headerName: 'Associated Campaigns', + flex: 2, + minWidth: 100, + align: 'center', + headerAlign: 'center', + headerClassName: `${styles.tableHeader}`, + sortable: false, + renderCell: (params: GridCellParams) => { + return ( + + ); + }, + }, + ]; + return ( - <> +
@@ -250,15 +299,12 @@ const organizationFunds = (): JSX.Element => { autoComplete="off" required className={styles.inputField} - value={fullName} - onChange={(e: ChangeEvent) => { - setFullName(e.target.value); - }} + value={searchTerm} + onChange={(e) => setSearchTerm(e.target.value)} data-testid="searchFullName" />
-
-
- {fundData?.fundsByOrganization && - fundData.fundsByOrganization.length > 0 ? ( -
- - - - - # - - {t('fundName')} - - - {t('createdBy')} - - - {t('createdOn')} - - - {t('status')} - - - {t('manageFund')} - - - - - {fundData.fundsByOrganization.map( - (fund: any, index: number) => ( - - - {index + 1} - - handleClick(fund._id)} - > - - {fund.name} - - - - {fund.creator.firstName} {fund.creator.lastName} - - - {dayjs(fund.createdAt).format('DD/MM/YYYY')} - - - - - - - - - ), - )} - -
-
-
- ) : ( -
-
{t('noFundsFound')}
-
- )} -
- {/* + row.fund._id} + components={{ + NoRowsOverlay: () => ( + + {t('noFundsFound')} + + ), + }} + sx={dataGridStyle} + getRowClassName={() => `${styles.rowBackground}`} + autoHeight + rowHeight={65} + rows={funds.map((fund, index) => ({ + id: index + 1, + fund, + }))} + columns={columns} + isRowSelectable={() => false} + /> - {/* -
- + closeModal(Modal.SAME)} + refetchFunds={refetchFunds} + fund={fund} + orgId={orgId} + mode={fundModalMode} + /> + + closeModal(Modal.DELETE)} + fund={fund} + refetchFunds={refetchFunds} + /> +
); }; diff --git a/src/utils/interfaces.ts b/src/utils/interfaces.ts index 3945003f40..c99aab5aa7 100644 --- a/src/utils/interfaces.ts +++ b/src/utils/interfaces.ts @@ -226,19 +226,7 @@ export interface InterfaceQueryOrganizationAdvertisementListItem { totalCount: number; }; } -export interface InterfaceQueryOrganizationFunds { - fundsByOrganization: { - _id: string; - name: string; - refrenceNumber: string; - taxDeductible: boolean; - isArchived: boolean; - isDefault: boolean; - createdAt: string; - organizationId: string; - creator: { _id: string; firstName: string; lastName: string }; - }[]; -} + export interface InterfaceQueryOrganizationFundCampaigns { campaigns: { _id: string; @@ -270,6 +258,8 @@ export interface InterfaceFundInfo { isArchived: boolean; isDefault: boolean; createdAt: string; + organizationId: string; + creator: { _id: string; firstName: string; lastName: string }; } export interface InterfaceCampaignInfo { _id: string; @@ -383,6 +373,9 @@ export interface InterfaceAddress { export interface InterfaceCreateFund { fundName: string; fundRef: string; + isDefault: boolean; + isArchived: boolean; + taxDeductible: boolean; } export interface InterfacePostCard { From abdcbb4ee827ad9f2a6189ee93aa73b57136f8f0 Mon Sep 17 00:00:00 2001 From: Glen Date: Sun, 23 Jun 2024 13:56:07 +0530 Subject: [PATCH 02/17] refac/redesign campaigns --- public/locales/en/common.json | 3 +- public/locales/en/translation.json | 16 +- public/locales/fr/common.json | 3 +- public/locales/fr/translation.json | 4 +- public/locales/hi/common.json | 3 +- public/locales/hi/translation.json | 4 +- public/locales/sp/common.json | 3 +- public/locales/sp/translation.json | 8 +- public/locales/zh/common.json | 3 +- public/locales/zh/translation.json | 4 +- .../CampaignCreateModal.tsx | 158 ----- .../CampaignDeleteModal.tsx | 54 +- .../CampaignModal.tsx | 290 ++++++++ .../CampaignUpdateModal.tsx | 170 ----- .../OrganizationFundCampagins.tsx | 637 +++++++++--------- .../OrganizationFundCampaign.module.css | 29 +- .../OrganizationFunds/OrganizationFunds.tsx | 14 +- src/utils/interfaces.ts | 7 - 18 files changed, 685 insertions(+), 725 deletions(-) delete mode 100644 src/screens/OrganizationFundCampaign/CampaignCreateModal.tsx create mode 100644 src/screens/OrganizationFundCampaign/CampaignModal.tsx delete mode 100644 src/screens/OrganizationFundCampaign/CampaignUpdateModal.tsx diff --git a/public/locales/en/common.json b/public/locales/en/common.json index 3539eb037e..c815629eac 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -76,5 +76,6 @@ "noFiltersApplied": "No filters applied", "manage": "Manage", "searchResultsFor": "Search results for {{text}}", - "none": "None" + "none": "None", + "sort": "Sort" } diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index 37ea261f97..caf7dd52e9 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -333,7 +333,6 @@ "fundCreate": "Create Fund", "fundUpdate": "Update Fund", "fundDelete": "Delete Fund", - "searchByName": "Search By Name", "noFundsFound": "No Funds Found", "createdBy": "Created By", "createdOn": "Created On", @@ -343,7 +342,8 @@ "fundDeleted": "Fund deleted successfully", "deleteFundMsg": "Are you sure you want to delete this fund?", "createdLatest": "Created Latest", - "createdEarliest": "Created Earliest" + "createdEarliest": "Created Earliest", + "viewCampaigns": "View Campaigns" }, "fundCampaign": { "title": "Fundraising Campaigns", @@ -356,13 +356,14 @@ "deletedCampaign": "Campaign deleted successfully", "deleteCampaignMsg": "Are you sure you want to delete this campaign?", "noCampaigns": "No Campaigns Found", - "createCampaign": "Create Fund Campaign", - "updateCampaign": "Update Fund Campaign", - "manageCampaign": "Manage Fund Campaign", - "deleteCampaign": "Delete Fund Campaign", + "createCampaign": "Create Campaign", + "updateCampaign": "Update Campaign", + "deleteCampaign": "Delete Campaign", "currency": "Currency", "selectCurrency": "Select Currency", - "searchFullName": "Search By Name" + "searchFullName": "Search By Name", + "viewPledges": "View Pledges", + "noCampaignsFound": "No Campaigns Found" }, "pledges": { "title": "Fund Campaign Pledges", @@ -382,7 +383,6 @@ "editPledge": "Edit Pledge", "deletePledgeMsg": "Are you sure you want to delete this pledge?", "noPledges": "No Pledges Found", - "sort": "Sort", "searchVolunteer": "Search By Volunteer", "highestAmount": "Highest Amount", "lowestAmount": "Lowest Amount", diff --git a/public/locales/fr/common.json b/public/locales/fr/common.json index 46f4ac85cd..9f85270274 100644 --- a/public/locales/fr/common.json +++ b/public/locales/fr/common.json @@ -76,5 +76,6 @@ "noFiltersApplied": "Aucun filtre appliqué", "manage": "Gérer", "searchResultsFor": "Résultats de recherche pour {{text}}", - "none": "Aucun" + "none": "Aucun", + "sort": "Trier" } diff --git a/public/locales/fr/translation.json b/public/locales/fr/translation.json index 23b4d6c225..db3c4aa7e7 100644 --- a/public/locales/fr/translation.json +++ b/public/locales/fr/translation.json @@ -348,7 +348,8 @@ "fundDeleted": "Fonds supprimé avec succès", "deleteFundMsg": "Êtes-vous sûr de vouloir supprimer ce fonds ?", "createdLatest": "Créé le plus récemment", - "createdEarliest": "Créé le plus tôt" + "createdEarliest": "Créé le plus tôt", + "viewCampaigns": "Voir les campagnes" }, "fundCampaign": { "title": "Campagnes de collecte de fonds", @@ -387,7 +388,6 @@ "editPledge": "Modifier l'engagement", "deletePledgeMsg": "Etes-vous sûr de vouloir supprimer cet engagement ?", "noPledges": "Aucun engagement trouvé", - "sort": "Trier", "searchVolunteer": "Rechercher par bénévole", "highestAmount": "Montant le plus élevé", "lowestAmount": "Montant le plus bas", diff --git a/public/locales/hi/common.json b/public/locales/hi/common.json index cb30270abb..148dde89b0 100644 --- a/public/locales/hi/common.json +++ b/public/locales/hi/common.json @@ -76,5 +76,6 @@ "noFiltersApplied": "कोई फ़िल्टर लागू नहीं हैं", "manage": "प्रबंधित करें", "searchResultsFor": "{{text}} के लिए खोज परिणाम", - "none": "कोई नहीं" + "none": "कोई नहीं", + "sort": "क्रम से लगाना" } diff --git a/public/locales/hi/translation.json b/public/locales/hi/translation.json index 6e88dc3c76..18b2901560 100644 --- a/public/locales/hi/translation.json +++ b/public/locales/hi/translation.json @@ -348,7 +348,8 @@ "fundDeleted": "फंड सफलतापूर्वक हटाया गया", "deleteFundMsg": "क्या आप वाकई इस फंड को हटाना चाहते हैं?", "createdLatest": "सबसे पहले बनाया", - "createdEarliest": "सबसे जल्दी बनाया" + "createdEarliest": "सबसे जल्दी बनाया", + "viewCampaigns": "कैम्पेंस देखें" }, "fundCampaign": { "title": "धन उगाही अभियान", @@ -387,7 +388,6 @@ "editPledge": "प्रतिज्ञा संपादित करें", "deletePledgeMsg": "क्या आप वाकई इस प्रतिज्ञा को हटाना चाहते हैं?", "noPledges": "कोई प्रतिज्ञा नहीं मिली", - "sort": "क्रमबद्ध करें", "searchVolunteer": "स्वयंसेवक द्वारा खोजें", "highestAmount": "सबसे अधिक राशि", "lowestAmount": "सबसे कम राशि", diff --git a/public/locales/sp/common.json b/public/locales/sp/common.json index d9d1663f7d..ab79672e31 100644 --- a/public/locales/sp/common.json +++ b/public/locales/sp/common.json @@ -76,5 +76,6 @@ "noFiltersApplied": "No se aplicaron filtros", "manage": "Administrar", "searchResultsFor": "Resultados de búsqueda para {{text}}", - "none": "Ninguno" + "none": "Ninguno", + "sort": "Ordenar" } diff --git a/public/locales/sp/translation.json b/public/locales/sp/translation.json index 47d950d6cd..46f0c184e0 100644 --- a/public/locales/sp/translation.json +++ b/public/locales/sp/translation.json @@ -474,7 +474,8 @@ "fundDeleted": "Fondo eliminado correctamente", "deleteFundMsg": "¿Está seguro de que desea eliminar este fondo?", "createdLatest": "Creado más reciente", - "createdEarliest": "Creado más temprano" + "createdEarliest": "Creado más temprano", + "viewCampaigns": "Ver campañas" }, "fundCampaign": { "title": "Campañas de recaudación de fondos", @@ -493,8 +494,6 @@ "updateCampaign": "Actualizar campaña de recaudación de fondos", "manageCampaign": "Administrar campaña de recaudación de fondos", "deleteCampaign": "Eliminar campaña de recaudación de fondos", - "no": "No", - "yes": "Sí", "currency": "Moneda", "selectCurrency": "Seleccionar moneda", "filter": "Filtrar", @@ -519,10 +518,7 @@ "amount": "Monto", "editPledge": "Editar Compromiso", "deletePledgeMsg": "¿Estás seguro de que quieres eliminar este compromiso?", - "no": "No", - "yes": "Sí", "noPledges": "No se encontraron compromisos", - "sort": "Ordenar", "searchVolunteer": "Buscar por voluntario", "highestAmount": "Cantidad más alta", "lowestAmount": "Cantidad más baja", diff --git a/public/locales/zh/common.json b/public/locales/zh/common.json index 3e3da4db19..cc97d66113 100644 --- a/public/locales/zh/common.json +++ b/public/locales/zh/common.json @@ -76,5 +76,6 @@ "noFiltersApplied": "未应用筛选器", "manage": "管理", "searchResultsFor": "搜索结果:{{text}}", - "none": "没有" + "none": "没有", + "sort": "种类" } diff --git a/public/locales/zh/translation.json b/public/locales/zh/translation.json index 30f2c375f2..2c97256d67 100644 --- a/public/locales/zh/translation.json +++ b/public/locales/zh/translation.json @@ -348,7 +348,8 @@ "fundDeleted": "基金删除成功", "deleteFundMsg": "您确定要删除此基金吗?", "createdLatest": "最近创建", - "createdEarliest": "最早创建" + "createdEarliest": "最早创建", + "viewCampaigns": "查看活动" }, "fundCampaign": { "title": "筹款活动", @@ -387,7 +388,6 @@ "editPledge": "编辑承诺", "deletePledgeMsg": "您确定要删除此承诺吗?", "noPledges": "未找到承诺", - "sort": "排序", "searchVolunteer": "按志愿者搜索", "highestAmount": "最高金额", "lowestAmount": "最低金额", diff --git a/src/screens/OrganizationFundCampaign/CampaignCreateModal.tsx b/src/screens/OrganizationFundCampaign/CampaignCreateModal.tsx deleted file mode 100644 index a466e9326e..0000000000 --- a/src/screens/OrganizationFundCampaign/CampaignCreateModal.tsx +++ /dev/null @@ -1,158 +0,0 @@ -import { DatePicker } from '@mui/x-date-pickers'; -import type { Dayjs } from 'dayjs'; -import dayjs from 'dayjs'; -import type { ChangeEvent } from 'react'; -import React from 'react'; -import { Button, Col, Form, Modal } from 'react-bootstrap'; -import { currencyOptions } from 'utils/currency'; -import type { InterfaceCreateCampaign } from 'utils/interfaces'; -import styles from './OrganizationFundCampaign.module.css'; - -interface InterfaceCampaignCreateModal { - campaignCreateModalIsOpen: boolean; - hideCreateCampaignModal: () => void; - formState: InterfaceCreateCampaign; - setFormState: (state: React.SetStateAction) => void; - createCampaignHandler: (e: ChangeEvent) => Promise; - t: (key: string) => string; -} - -const CampaignCreateModal: React.FC = ({ - campaignCreateModalIsOpen, - hideCreateCampaignModal, - formState, - setFormState, - createCampaignHandler, - t, -}) => { - return ( - <> - - -

{t('createCampaign')}

- -
- -
- - {t('campaignName')} - - setFormState({ - ...formState, - campaignName: e.target.value, - }) - } - /> - - -
- { - if (date) { - setFormState({ - ...formState, - campaignStartDate: date.toDate(), - campaignEndDate: - formState.campaignEndDate && - (formState.campaignEndDate < date?.toDate() - ? date.toDate() - : formState.campaignEndDate), - }); - } - }} - minDate={dayjs(new Date())} - /> -
-
- { - if (date) { - setFormState({ - ...formState, - campaignEndDate: date.toDate(), - }); - } - }} - minDate={dayjs(formState.campaignStartDate)} - /> -
-
- - - {t('currency')} - - { - setFormState({ - ...formState, - campaignCurrency: e.target.value, - }); - }} - > - - {currencyOptions.map((currency) => ( - - ))} - - - - - {t('fundingGoal')} - { - if (parseInt(e.target.value) > 0) { - setFormState({ - ...formState, - campaignGoal: parseInt(e.target.value), - }); - } - }} - /> - - - -
-
-
- - ); -}; -export default CampaignCreateModal; diff --git a/src/screens/OrganizationFundCampaign/CampaignDeleteModal.tsx b/src/screens/OrganizationFundCampaign/CampaignDeleteModal.tsx index 58351e1d1d..c605d503f8 100644 --- a/src/screens/OrganizationFundCampaign/CampaignDeleteModal.tsx +++ b/src/screens/OrganizationFundCampaign/CampaignDeleteModal.tsx @@ -1,29 +1,53 @@ import React from 'react'; import { Button, Modal } from 'react-bootstrap'; import styles from './OrganizationFundCampaign.module.css'; +import { useMutation } from '@apollo/client'; +import { DELETE_CAMPAIGN_MUTATION } from 'GraphQl/Mutations/CampaignMutation'; +import type { InterfaceCampaignInfo } from 'utils/interfaces'; +import { toast } from 'react-toastify'; +import { useTranslation } from 'react-i18next'; interface InterfaceDeleteCampaignModal { - campaignDeleteModalIsOpen: boolean; - hideDeleteCampaignModal: () => void; - deleteCampaignHandler: () => Promise; - t: (key: string) => string; - tCommon: (key: string) => string; + isOpen: boolean; + hide: () => void; + campaign: InterfaceCampaignInfo | null; + refetchCampaign: () => void; } const CampaignDeleteModal: React.FC = ({ - campaignDeleteModalIsOpen, - hideDeleteCampaignModal, - deleteCampaignHandler, - t, - tCommon, + isOpen, + hide, + campaign, + refetchCampaign, }) => { + const { t } = useTranslation('translation', { + keyPrefix: 'fundCampaign', + }); + const { t: tCommon } = useTranslation('common'); + + const [deleteCampaign] = useMutation(DELETE_CAMPAIGN_MUTATION); + + const deleteCampaignHandler = async (): Promise => { + try { + await deleteCampaign({ + variables: { + id: campaign?._id, + }, + }); + toast.success(t('deletedCampaign')); + refetchCampaign(); + hide(); + } catch (error: unknown) { + if (error instanceof Error) toast.error(error.message); + } + }; return ( <> - +

{t('deleteCampaign')}

- diff --git a/src/screens/OrganizationFundCampaign/CampaignModal.tsx b/src/screens/OrganizationFundCampaign/CampaignModal.tsx new file mode 100644 index 0000000000..3dcad7f2ee --- /dev/null +++ b/src/screens/OrganizationFundCampaign/CampaignModal.tsx @@ -0,0 +1,290 @@ +import { DatePicker } from '@mui/x-date-pickers'; +import type { Dayjs } from 'dayjs'; +import dayjs from 'dayjs'; +import type { ChangeEvent } from 'react'; +import React, { useEffect, useState } from 'react'; +import { Button, Col, Form, Modal } from 'react-bootstrap'; +import { currencyOptions, currencySymbols } from 'utils/currency'; +import styles from './OrganizationFundCampaign.module.css'; +import { useTranslation } from 'react-i18next'; +import { useMutation } from '@apollo/client'; +import { + CREATE_CAMPAIGN_MUTATION, + UPDATE_CAMPAIGN_MUTATION, +} from 'GraphQl/Mutations/CampaignMutation'; +import { toast } from 'react-toastify'; +import { + FormControl, + InputLabel, + MenuItem, + Select, + TextField, +} from '@mui/material'; +import type { InterfaceCampaignInfo } from 'utils/interfaces'; + +interface InterfaceCampaignModal { + isOpen: boolean; + hide: () => void; + fundId: string; + campaign: InterfaceCampaignInfo | null; + refetchCampaign: () => void; + mode: 'create' | 'edit'; +} + +const CampaignModal: React.FC = ({ + isOpen, + hide, + fundId, + refetchCampaign, + mode, + campaign, +}) => { + const { t } = useTranslation('translation', { + keyPrefix: 'fundCampaign', + }); + const { t: tCommon } = useTranslation('common'); + + const [formState, setFormState] = useState({ + campaignName: campaign?.name ?? '', + campaignCurrency: campaign?.currency ?? 'USD', + campaignGoal: campaign?.fundingGoal ?? 0, + campaignStartDate: campaign?.startDate ?? new Date(), + campaignEndDate: campaign?.endDate ?? new Date(), + }); + + useEffect(() => { + setFormState({ + campaignCurrency: campaign?.currency ?? 'USD', + campaignEndDate: campaign?.endDate ?? new Date(), + campaignGoal: campaign?.fundingGoal ?? 0, + campaignName: campaign?.name ?? '', + campaignStartDate: campaign?.startDate ?? new Date(), + }); + }, [campaign]); + + const { + campaignName, + campaignCurrency, + campaignEndDate, + campaignGoal, + campaignStartDate, + } = formState; + + const [createCampaign] = useMutation(CREATE_CAMPAIGN_MUTATION); + const [updateCampaign] = useMutation(UPDATE_CAMPAIGN_MUTATION); + + const createCampaignHandler = async ( + e: ChangeEvent, + ): Promise => { + e.preventDefault(); + try { + await createCampaign({ + variables: { + name: formState.campaignName, + currency: formState.campaignCurrency, + fundingGoal: formState.campaignGoal, + startDate: dayjs(formState.campaignStartDate).format('YYYY-MM-DD'), + endDate: dayjs(formState.campaignEndDate).format('YYYY-MM-DD'), + fundId, + }, + }); + toast.success(t('createdCampaign')); + setFormState({ + campaignName: '', + campaignCurrency: 'USD', + campaignGoal: 0, + campaignStartDate: new Date(), + campaignEndDate: new Date(), + }); + refetchCampaign(); + hide(); + } catch (error: unknown) { + if (error instanceof Error) toast.error(error.message); + } + }; + + const updateCampaignHandler = async ( + e: ChangeEvent, + ): Promise => { + e.preventDefault(); + try { + const updatedFields: { [key: string]: string | number | undefined } = {}; + if (campaign?.name !== campaignName) { + updatedFields.name = campaignName; + } + if (campaign?.currency !== campaignCurrency) { + updatedFields.currency = campaignCurrency; + } + if (campaign?.fundingGoal !== campaignGoal) { + updatedFields.fundingGoal = campaignGoal; + } + if (campaign?.startDate !== campaignStartDate) { + updatedFields.startDate = dayjs(campaignStartDate).format('YYYY-MM-DD'); + } + if (campaign?.endDate !== formState.campaignEndDate) { + updatedFields.endDate = dayjs(formState.campaignEndDate).format( + 'YYYY-MM-DD', + ); + } + await updateCampaign({ + variables: { + id: campaign?._id, + ...updatedFields, + }, + }); + setFormState({ + campaignName: '', + campaignCurrency: 'USD', + campaignGoal: 0, + campaignStartDate: new Date(), + campaignEndDate: new Date(), + }); + refetchCampaign(); + hide(); + toast.success(t('updatedCampaign')); + } catch (error: unknown) { + if (error instanceof Error) toast.error(error.message); + } + }; + + return ( + <> + + +

+ {t(mode === 'edit' ? 'updateCampaign' : 'createCampaign')} +

+ +
+ +
+ + + + setFormState({ + ...formState, + campaignName: e.target.value, + }) + } + /> + + + + + {/* Date Calendar Component to select start date of campaign*/} + { + if (date) { + setFormState({ + ...formState, + campaignStartDate: date.toDate(), + campaignEndDate: + campaignEndDate && + (campaignEndDate < date?.toDate() + ? date.toDate() + : campaignEndDate), + }); + } + }} + minDate={dayjs(new Date())} + /> + {/* Date Calendar Component to select end Date of campaign */} + { + if (date) { + setFormState({ + ...formState, + campaignEndDate: date.toDate(), + }); + } + }} + minDate={dayjs(campaignStartDate)} + /> + + + + {/* Dropdown to select the currency for funding goal of the campaign*/} + + + {t('currency')} + + + + {/* Input field to enter funding goal for the campaign */} + + { + if (parseInt(e.target.value) > 0) { + setFormState({ + ...formState, + campaignGoal: parseInt(e.target.value), + }); + } + }} + /> + + + {/* Button to create the campaign */} + +
+
+
+ + ); +}; +export default CampaignModal; diff --git a/src/screens/OrganizationFundCampaign/CampaignUpdateModal.tsx b/src/screens/OrganizationFundCampaign/CampaignUpdateModal.tsx deleted file mode 100644 index 561c7c9252..0000000000 --- a/src/screens/OrganizationFundCampaign/CampaignUpdateModal.tsx +++ /dev/null @@ -1,170 +0,0 @@ -import { DatePicker } from '@mui/x-date-pickers'; -import type { Dayjs } from 'dayjs'; -import dayjs from 'dayjs'; -import type { ChangeEvent } from 'react'; -import React from 'react'; -import { Button, Col, Form, Modal } from 'react-bootstrap'; -import { currencyOptions } from 'utils/currency'; -import type { InterfaceCreateCampaign } from 'utils/interfaces'; -import styles from './OrganizationFundCampaign.module.css'; - -interface InterfaceCampaignUpdateModal { - campaignUpdateModalIsOpen: boolean; - hideUpdateCampaignModal: () => void; - formState: InterfaceCreateCampaign; - setFormState: (state: React.SetStateAction) => void; - updateCampaignHandler: (e: ChangeEvent) => Promise; - t: (key: string) => string; - showDeleteCampaignModal: () => void; -} - -const CampaignUpdateModal: React.FC = ({ - campaignUpdateModalIsOpen, - hideUpdateCampaignModal, - formState, - setFormState, - updateCampaignHandler, - t, - showDeleteCampaignModal, -}) => { - return ( - <> - - -

{t('manageCampaign')}

- -
- -
- - {t('campaignName')} - - setFormState({ - ...formState, - campaignName: e.target.value, - }) - } - /> - - -
- { - if (date) { - setFormState({ - ...formState, - campaignStartDate: date.toDate(), - campaignEndDate: - formState.campaignEndDate && - (formState.campaignEndDate < date?.toDate() - ? date.toDate() - : formState.campaignEndDate), - }); - } - }} - minDate={dayjs(new Date())} - /> -
-
- { - if (date) { - setFormState({ - ...formState, - campaignEndDate: date.toDate(), - }); - } - }} - minDate={dayjs(formState.campaignStartDate)} - /> -
-
- - - {t('currency')} - - { - setFormState({ - ...formState, - campaignCurrency: e.target.value, - }); - }} - > - - {currencyOptions.map((currency) => ( - - ))} - - - - - {t('fundingGoal')} - { - if (parseInt(e.target.value) > 0) { - setFormState({ - ...formState, - campaignGoal: parseInt(e.target.value), - }); - } - }} - /> - - -
- - -
-
-
-
- - ); -}; -export default CampaignUpdateModal; diff --git a/src/screens/OrganizationFundCampaign/OrganizationFundCampagins.tsx b/src/screens/OrganizationFundCampaign/OrganizationFundCampagins.tsx index 24542d7e19..956ba6d049 100644 --- a/src/screens/OrganizationFundCampaign/OrganizationFundCampagins.tsx +++ b/src/screens/OrganizationFundCampaign/OrganizationFundCampagins.tsx @@ -1,57 +1,49 @@ /*eslint-disable*/ -import { useMutation, useQuery } from '@apollo/client'; -import { Search, WarningAmberRounded } from '@mui/icons-material'; -import { - CREATE_CAMPAIGN_MUTATION, - DELETE_CAMPAIGN_MUTATION, - UPDATE_CAMPAIGN_MUTATION, -} from 'GraphQl/Mutations/CampaignMutation'; +import { useQuery } from '@apollo/client'; +import { Search, Sort, WarningAmberRounded } from '@mui/icons-material'; import { FUND_CAMPAIGN } from 'GraphQl/Queries/fundQueries'; import Loader from 'components/Loader/Loader'; import dayjs from 'dayjs'; -import { useState, type ChangeEvent } from 'react'; -import { Button, Col, Row, Dropdown, Form } from 'react-bootstrap'; +import { useCallback, useMemo, useState } from 'react'; +import { Button, Dropdown, Form } from 'react-bootstrap'; import { useTranslation } from 'react-i18next'; -import { useNavigate, useParams } from 'react-router-dom'; -import { toast } from 'react-toastify'; -import { currencySymbols } from 'utils/currency'; -import FilterAltOutlinedIcon from '@mui/icons-material/FilterAltOutlined'; +import { Navigate, useNavigate, useParams } from 'react-router-dom'; import type { InterfaceCampaignInfo, - InterfaceCreateCampaign, InterfaceQueryOrganizationFundCampaigns, } from 'utils/interfaces'; -import CampaignCreateModal from './CampaignCreateModal'; +import CampaignModal from './CampaignModal'; import CampaignDeleteModal from './CampaignDeleteModal'; -import CampaignUpdateModal from './CampaignUpdateModal'; import styles from './OrganizationFundCampaign.module.css'; -import { - Paper, - Table, - TableBody, - TableCell, - TableContainer, - TableHead, - TableRow, - styled, - tableCellClasses, -} from '@mui/material'; +import { DataGrid, GridCellParams, GridColDef } from '@mui/x-data-grid'; +import { currencySymbols } from 'utils/currency'; +import { Stack } from '@mui/material'; -const StyledTableCell = styled(TableCell)(({ theme }) => ({ - [`&.${tableCellClasses.head}`]: { - backgroundColor: ['#31bb6b', '!important'], - color: theme.palette.common.white, +const dataGridStyle = { + '&.MuiDataGrid-root .MuiDataGrid-cell:focus-within': { + outline: 'none !important', }, - [`&.${tableCellClasses.body}`]: { - fontSize: 14, + '&.MuiDataGrid-root .MuiDataGrid-columnHeader:focus-within': { + outline: 'none', }, -})); - -const StyledTableRow = styled(TableRow)(() => ({ - '&:last-child td, &:last-child th': { - border: 0, + '& .MuiDataGrid-row:hover': { + backgroundColor: 'transparent', + }, + '& .MuiDataGrid-row.Mui-hovered': { + backgroundColor: 'transparent', + }, + '& .MuiDataGrid-root': { + borderRadius: '0.5rem', }, -})); + '& .MuiDataGrid-main': { + borderRadius: '0.5rem', + }, +}; + +enum Modal { + SAME = 'same', + DELETE = 'delete', +} const orgFundCampaign = (): JSX.Element => { const { t } = useTranslation('translation', { @@ -60,197 +52,80 @@ const orgFundCampaign = (): JSX.Element => { const { t: tCommon } = useTranslation('common'); const navigate = useNavigate(); - const { fundId: currentUrl, orgId: orgId } = useParams(); - const [campaignCreateModalIsOpen, setcampaignCreateModalIsOpen] = - useState(false); - const [campaignUpdateModalIsOpen, setcampaignUpdateModalIsOpen] = - useState(false); - const [campaignDeleteModalIsOpen, setcampaignDeleteModalIsOpen] = - useState(false); + const { fundId, orgId } = useParams(); + + if (!fundId || !orgId) { + return ; + } const [campaign, setCampaign] = useState(null); - const [formState, setFormState] = useState({ - campaignName: '', - campaignCurrency: 'USD', - campaignGoal: 0, - campaignStartDate: new Date(), - campaignEndDate: new Date(), + const [searchTerm, setSearchTerm] = useState(''); + const [sortBy, setSortBy] = useState(null); + + const [modalState, setModalState] = useState<{ [key in Modal]: boolean }>({ + [Modal.SAME]: false, + [Modal.DELETE]: false, }); + const [campaignModalMode, setCampaignModalMode] = useState<'edit' | 'create'>( + 'create', + ); + const openModal = (modal: Modal): void => + setModalState((prevState) => ({ ...prevState, [modal]: true })); + + const closeModal = (modal: Modal): void => + setModalState((prevState) => ({ ...prevState, [modal]: false })); + + const handleOpenModal = useCallback( + (campaign: InterfaceCampaignInfo | null, mode: 'edit' | 'create'): void => { + setCampaign(campaign); + setCampaignModalMode(mode); + openModal(Modal.SAME); + }, + [openModal], + ); + + const handleDeleteClick = useCallback( + (campaign: InterfaceCampaignInfo): void => { + setCampaign(campaign); + openModal(Modal.DELETE); + }, + [openModal], + ); + const { - data: fundCampaignData, - loading: fundCampaignLoading, - error: fundCampaignError, - refetch: refetchFundCampaign, + data: campaignData, + loading: campaignLoading, + error: campaignError, + refetch: refetchCampaign, }: { data?: { getFundById: InterfaceQueryOrganizationFundCampaigns; }; loading: boolean; error?: Error | undefined; - refetch: any; + refetch: () => void; } = useQuery(FUND_CAMPAIGN, { variables: { - id: currentUrl, + id: fundId, }, }); - const [createCampaign] = useMutation(CREATE_CAMPAIGN_MUTATION); - const [updateCampaign] = useMutation(UPDATE_CAMPAIGN_MUTATION); - const [deleteCampaign] = useMutation(DELETE_CAMPAIGN_MUTATION); - - const showCreateCampaignModal = (): void => { - setcampaignCreateModalIsOpen(!campaignCreateModalIsOpen); - }; - const hideCreateCampaignModal = (): void => { - setcampaignCreateModalIsOpen(!campaignCreateModalIsOpen); - }; - const showUpdateCampaignModal = (): void => { - setcampaignUpdateModalIsOpen(!campaignUpdateModalIsOpen); - }; - const hideUpdateCampaignModal = (): void => { - setcampaignUpdateModalIsOpen(!campaignUpdateModalIsOpen); - setFormState({ - campaignName: '', - campaignCurrency: 'USD', - campaignGoal: 0, - campaignStartDate: new Date(), - campaignEndDate: new Date(), - }); - }; - const showDeleteCampaignModal = (): void => { - setcampaignDeleteModalIsOpen(!campaignDeleteModalIsOpen); - }; - const hideDeleteCampaignModal = (): void => { - setcampaignDeleteModalIsOpen(!campaignDeleteModalIsOpen); - }; - - const handleEditClick = (campaign: InterfaceCampaignInfo): void => { - setFormState({ - campaignName: campaign.name, - campaignCurrency: campaign.currency, - campaignGoal: campaign.fundingGoal, - campaignStartDate: new Date(campaign.startDate), - campaignEndDate: new Date(campaign.endDate), - }); - setCampaign(campaign); - showUpdateCampaignModal(); - }; - - const createCampaignHandler = async ( - e: ChangeEvent, - ): Promise => { - e.preventDefault(); - try { - await createCampaign({ - variables: { - name: formState.campaignName, - currency: formState.campaignCurrency, - fundingGoal: formState.campaignGoal, - startDate: dayjs(formState.campaignStartDate).format('YYYY-MM-DD'), - endDate: dayjs(formState.campaignEndDate).format('YYYY-MM-DD'), - fundId: currentUrl, - }, - }); - toast.success(t('createdCampaign')); - setFormState({ - campaignName: '', - campaignCurrency: 'USD', - campaignGoal: 0, - campaignStartDate: new Date(), - campaignEndDate: new Date(), - }); - refetchFundCampaign(); - hideCreateCampaignModal(); - } catch (error: unknown) { - if (error instanceof Error) { - toast.error(error.message); - console.log(error.message); - } - } - }; - - const updateCampaignHandler = async ( - e: ChangeEvent, - ): Promise => { - e.preventDefault(); - try { - const updatedFields: { [key: string]: any } = {}; - if (campaign?.name !== formState.campaignName) { - updatedFields.name = formState.campaignName; - } - if (campaign?.currency !== formState.campaignCurrency) { - updatedFields.currency = formState.campaignCurrency; - } - if (campaign?.fundingGoal !== formState.campaignGoal) { - updatedFields.fundingGoal = formState.campaignGoal; - } - if (campaign?.startDate !== formState.campaignStartDate) { - updatedFields.startDate = dayjs(formState.campaignStartDate).format( - 'YYYY-MM-DD', - ); - } - if (campaign?.endDate !== formState.campaignEndDate) { - updatedFields.endDate = dayjs(formState.campaignEndDate).format( - 'YYYY-MM-DD', - ); - } - await updateCampaign({ - variables: { - id: campaign?._id, - ...updatedFields, - }, - }); - setFormState({ - campaignName: '', - campaignCurrency: 'USD', - campaignGoal: 0, - campaignStartDate: new Date(), - campaignEndDate: new Date(), - }); - refetchFundCampaign(); - hideUpdateCampaignModal(); - toast.success(t('updatedCampaign')); - } catch (error: unknown) { - if (error instanceof Error) { - toast.error(error.message); - console.log(error.message); - } - } - }; - - const deleteCampaignHandler = async (): Promise => { - try { - await deleteCampaign({ - variables: { - id: campaign?._id, - }, - }); - toast.success(t('deletedCampaign')); - refetchFundCampaign(); - hideDeleteCampaignModal(); - } catch (error: unknown) { - if (error instanceof Error) { - toast.error(error.message); - console.log(error.message); - } - } - }; - const handleClick = (campaignId: String) => { navigate(`/fundCampaignPledge/${orgId}/${campaignId}`); }; - const [searchQuery, setSearchQuery] = useState(''); - const [searchText, setSearchText] = useState(''); - const filteredCampaigns = fundCampaignData?.getFundById.campaigns.filter( - (campaign) => - campaign.name.toLowerCase().includes(searchQuery.toLowerCase()), - ); + const campaigns = useMemo(() => { + return ( + campaignData?.getFundById.campaigns.filter((campaign) => + campaign.name.toLowerCase().includes(searchTerm.toLowerCase()), + ) ?? [] + ); + }, [campaignData, searchTerm]); - if (fundCampaignLoading) { + if (campaignLoading) { return ; } - if (fundCampaignError) { + if (campaignError) { return (
@@ -258,32 +133,181 @@ const orgFundCampaign = (): JSX.Element => {
Error occured while loading Campaigns
- {fundCampaignError.message} + {campaignError.message}
); } + const columns: GridColDef[] = [ + { + field: 'id', + headerName: 'Sr. No.', + flex: 1, + minWidth: 100, + align: 'center', + headerAlign: 'center', + headerClassName: `${styles.tableHeader}`, + sortable: false, + renderCell: (params: GridCellParams) => { + return
{params.row.id}
; + }, + }, + { + field: 'campaignName', + headerName: 'Campaign Name', + flex: 2, + minWidth: 150, + align: 'center', + headerAlign: 'center', + headerClassName: `${styles.tableHeader}`, + sortable: false, + renderCell: (params: GridCellParams) => { + return ( +
handleClick(params.row.campaign._id as string)} + > + {params.row.campaign.name} +
+ ); + }, + }, + { + field: 'startDate', + headerName: 'Start Date', + flex: 1, + minWidth: 150, + align: 'center', + headerAlign: 'center', + headerClassName: `${styles.tableHeader}`, + sortable: false, + renderCell: (params: GridCellParams) => { + return dayjs(params.row.campaign.startDate).format('DD/MM/YYYY'); + }, + }, + { + field: 'endDate', + headerName: 'End Date', + minWidth: 150, + align: 'center', + headerAlign: 'center', + headerClassName: `${styles.tableHeader}`, + flex: 1, + sortable: false, + renderCell: (params: GridCellParams) => { + return dayjs(params.row.campaign.endDate).format('DD/MM/YYYY'); + }, + }, + { + field: 'fundingGoal', + headerName: 'Funding Goal', + flex: 1, + minWidth: 100, + align: 'center', + headerAlign: 'center', + headerClassName: `${styles.tableHeader}`, + sortable: false, + renderCell: (params: GridCellParams) => { + return ( +
+ { + currencySymbols[ + params.row.campaign.currency as keyof typeof currencySymbols + ] + } + {params.row.campaign.fundingGoal} +
+ ); + }, + }, + { + field: 'action', + headerName: 'Action', + flex: 1, + minWidth: 100, + align: 'center', + headerAlign: 'center', + headerClassName: `${styles.tableHeader}`, + sortable: false, + renderCell: (params: GridCellParams) => { + return ( + <> + + + + ); + }, + }, + { + field: 'assocPledge', + headerName: 'Associated Pledges', + flex: 2, + minWidth: 100, + align: 'center', + headerAlign: 'center', + headerClassName: `${styles.tableHeader}`, + sortable: false, + renderCell: (params: GridCellParams) => { + return ( + + ); + }, + }, + ]; + return (
{ - setSearchText(e.target.value); - }} + value={searchTerm} + onChange={(e) => setSearchTerm(e.target.value)} data-testid="searchFullName" />
-
- {filteredCampaigns && filteredCampaigns.length > 0 ? ( -
- - - - - # - - {t('campaignName')} - - - {tCommon('startDate')} - - - {tCommon('endDate')} - - - {t('fundingGoal')} - - - {t('campaignOptions')} - - - - - {filteredCampaigns.map((campaign, index) => ( - - - {index + 1} - - handleClick(campaign._id)} - > - - {campaign.name} - - - - {dayjs(campaign.startDate).format('DD/MM/YYYY')} - - - {dayjs(campaign.endDate).format('DD/MM/YYYY')} - - - - {`${currencySymbols[campaign.currency as keyof typeof currencySymbols]}${campaign.fundingGoal}`} - - - - - - - ))} - -
-
-
- ) : ( -
-
{t('noCampaigns')}
-
- )} -
- - {/* Create Campaign Modal */} - row.campaign._id} + components={{ + NoRowsOverlay: () => ( + + {t('noCampaignsFound')} + + ), + }} + sx={dataGridStyle} + getRowClassName={() => `${styles.rowBackground}`} + autoHeight + rowHeight={65} + rows={campaigns.map((campaign, index) => ({ + id: index + 1, + campaign, + }))} + columns={columns} + isRowSelectable={() => false} /> - {/* Update Campaign Modal */} - closeModal(Modal.SAME)} + refetchCampaign={refetchCampaign} + fundId={fundId} + campaign={campaign} + mode={campaignModalMode} /> - {/* Delete Campaign Modal */} closeModal(Modal.DELETE)} + campaign={campaign} + refetchCampaign={refetchCampaign} />
); diff --git a/src/screens/OrganizationFundCampaign/OrganizationFundCampaign.module.css b/src/screens/OrganizationFundCampaign/OrganizationFundCampaign.module.css index 3ed22aa094..e211cdd1ac 100644 --- a/src/screens/OrganizationFundCampaign/OrganizationFundCampaign.module.css +++ b/src/screens/OrganizationFundCampaign/OrganizationFundCampaign.module.css @@ -11,6 +11,10 @@ margin: auto; box-shadow: 5px 5px 4px 0px rgba(49, 187, 107, 0.12); } +.rowBackground { + background-color: var(--bs-white); + max-height: 120px; +} .container { min-height: 100vh; } @@ -20,13 +24,22 @@ margin-left: 13vw; } .titlemodal { - color: var(--bs-gray-600); + color: #707070; font-weight: 600; - font-size: 20px; - margin-bottom: 20px; - padding-bottom: 5px; - border-bottom: 3px solid var(--bs-primary); + font-size: 32px; width: 65%; + margin-bottom: 0px; +} +.noOutline input { + outline: none; +} +.modalCloseBtn { + width: 40px; + height: 40px; + padding: 1rem; + display: flex; + justify-content: center; + align-items: center; } .greenregbtn { margin: 1rem 0 0; @@ -134,6 +147,12 @@ margin-bottom: 20px; } +.tableHeader { + background-color: var(--bs-primary); + color: var(--bs-white); + font-size: 1rem; +} + @media (max-width: 1020px) { .btnsContainer { flex-direction: column; diff --git a/src/screens/OrganizationFunds/OrganizationFunds.tsx b/src/screens/OrganizationFunds/OrganizationFunds.tsx index b78430f887..ba2107e7f3 100644 --- a/src/screens/OrganizationFunds/OrganizationFunds.tsx +++ b/src/screens/OrganizationFunds/OrganizationFunds.tsx @@ -68,13 +68,11 @@ const organizationFunds = (): JSX.Element => { const [fundModalMode, setFundModalMode] = useState<'edit' | 'create'>( 'create', ); - const openModal = (modal: Modal): void => { + const openModal = (modal: Modal): void => setModalState((prevState) => ({ ...prevState, [modal]: true })); - }; - const closeModal = (modal: Modal): void => { + const closeModal = (modal: Modal): void => setModalState((prevState) => ({ ...prevState, [modal]: false })); - }; const handleOpenModal = useCallback( (fund: InterfaceFundInfo | null, mode: 'edit' | 'create'): void => { @@ -221,7 +219,7 @@ const organizationFunds = (): JSX.Element => { headerClassName: `${styles.tableHeader}`, sortable: false, renderCell: (params: GridCellParams) => { - return params.row.fund.isArchived ?

Archived

:

Active

; + return params.row.fund.isArchived ? 'Archived' : 'Active'; }, }, { @@ -280,7 +278,7 @@ const organizationFunds = (): JSX.Element => { onClick={() => handleClick(params.row.fund._id as string)} > - View + {t('viewCampaigns')} ); }, @@ -295,13 +293,13 @@ const organizationFunds = (): JSX.Element => {
setSearchTerm(e.target.value)} - data-testid="searchFullName" + data-testid="searchByName" />
} + /> + } + /> + } + /> + - + - , - ); - await wait(); - await waitFor(() => { - expect(getByText(translations.addCampaign)).toBeInTheDocument(); - }); + + , + ); +}; + +describe('FundCampaigns Screen', () => { + beforeEach(() => { + jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useParams: () => ({ orgId: 'orgId', fundId: 'fundId' }), + })); }); - it('renders the campaign screen with error', async () => { - const { queryByText } = render( - - - - - {} - - - - , - ); - await wait(); - await waitFor(() => { - expect(queryByText(translations.addCampaign)).not.toBeInTheDocument(); - }); + + afterEach(() => { + cleanup(); }); - it('renders the Error Component', async () => { - render( - - - - - {} - - - - , - ); - await wait(); - await waitFor(() => { - expect(screen.getByTestId('errorMsg')).toBeInTheDocument(); - }); + + afterAll(() => { + jest.clearAllMocks(); }); - it("opens and closes the 'Create Campaign' modal", async () => { - render( - - - - - - {} - - - - - , - ); - await wait(); - await waitFor(() => - expect(screen.getByTestId('addCampaignBtn')).toBeInTheDocument(), - ); - userEvent.click(screen.getByTestId('addCampaignBtn')); + + it('should render the Campaign Pledge screen', async () => { + renderFundCampaign(link1); await waitFor(() => { - return expect( - screen.findByTestId('createCampaignCloseBtn'), - ).resolves.toBeInTheDocument(); + expect(screen.getByTestId('searchFullName')).toBeInTheDocument(); }); - userEvent.click(screen.getByTestId('createCampaignCloseBtn')); - await waitForElementToBeRemoved(() => - screen.queryByTestId('createCampaignCloseBtn'), - ); + + expect(screen.getByText('Campaign 1')).toBeInTheDocument(); + expect(screen.getByText('Campaign 2')).toBeInTheDocument(); }); - it('creates a new Campaign', async () => { + + it('should redirect to fallback URL if URL params are undefined', async () => { render( - - + + - {} + + } + /> + } + /> + - - + + , ); - await wait(); - await waitFor(() => - expect(screen.getByTestId('addCampaignBtn')).toBeInTheDocument(), - ); - userEvent.click(screen.getByTestId('addCampaignBtn')); await waitFor(() => { - return expect( - screen.findByTestId('createCampaignCloseBtn'), - ).resolves.toBeInTheDocument(); - }); - userEvent.type( - screen.getByPlaceholderText('Enter Campaign Name'), - formData.campaignName, - ); - userEvent.type( - screen.getByPlaceholderText('Enter Funding Goal'), - formData.campaignGoal.toString(), - ); - const currency = screen.getByTestId('currencySelect'); - fireEvent.change(currency, { - target: { value: formData.campaignCurrency }, - }); - const startDate = screen.getByLabelText('Start Date'); - const endDate = screen.getByLabelText('End Date'); - fireEvent.change(startDate, { - target: { value: formData.campaignStartDate }, - }); - fireEvent.change(endDate, { - target: { value: formData.campaignEndDate }, + expect(screen.getByTestId('paramsError')).toBeInTheDocument(); }); + }); - userEvent.click(screen.getByTestId('createCampaignBtn')); + it('open and close Create Campaign modal', async () => { + renderFundCampaign(link1); + + const addCampaignBtn = await screen.findByTestId('addCampaignBtn'); + expect(addCampaignBtn).toBeInTheDocument(); + userEvent.click(addCampaignBtn); - await waitFor(() => { - expect(toast.success).toHaveBeenCalledWith(translations.createdCampaign); - }); - }); - it('toast an error on unsuccessful campaign creation', async () => { - render( - - - - - - {} - - - - - , - ); - await wait(); await waitFor(() => - expect(screen.getByTestId('addCampaignBtn')).toBeInTheDocument(), + expect(screen.getAllByText(translations.createCampaign)).toHaveLength(2), ); - userEvent.click(screen.getByTestId('addCampaignBtn')); - await waitFor(() => { - return expect( - screen.findByTestId('createCampaignCloseBtn'), - ).resolves.toBeInTheDocument(); - }); - userEvent.type( - screen.getByPlaceholderText('Enter Campaign Name'), - formData.campaignName, - ); - userEvent.type( - screen.getByPlaceholderText('Enter Funding Goal'), - formData.campaignGoal.toString(), + userEvent.click(screen.getByTestId('campaignCloseBtn')); + await waitFor(() => + expect(screen.queryByTestId('campaignCloseBtn')).toBeNull(), ); - const endDateDatePicker = screen.getByLabelText('End Date'); - const startDateDatePicker = screen.getByLabelText('Start Date'); - - fireEvent.change(endDateDatePicker, { - target: { value: formData.campaignEndDate }, - }); - fireEvent.change(startDateDatePicker, { - target: { value: formData.campaignStartDate }, - }); + }); - userEvent.click(screen.getByTestId('createCampaignBtn')); + it('open and close update campaign modal', async () => { + renderFundCampaign(link1); await waitFor(() => { - expect(toast.error).toHaveBeenCalled(); + expect(screen.getByTestId('searchFullName')).toBeInTheDocument(); }); - }); - it('opens and closes the Edit Campaign modal', async () => { - render( - - - - - - {} - - - - - , - ); - await wait(); + + const editCampaignBtn = await screen.findAllByTestId('editCampaignBtn'); + await waitFor(() => expect(editCampaignBtn[0]).toBeInTheDocument()); + userEvent.click(editCampaignBtn[0]); + await waitFor(() => - expect(screen.getAllByTestId('editCampaignBtn')[0]).toBeInTheDocument(), + expect( + screen.getAllByText(translations.updateCampaign)[0], + ).toBeInTheDocument(), ); - userEvent.click(screen.getAllByTestId('editCampaignBtn')[0]); - await waitFor(() => { - return expect( - screen.findByTestId('editCampaignCloseBtn'), - ).resolves.toBeInTheDocument(); - }); - userEvent.click(screen.getByTestId('editCampaignCloseBtn')); - await waitForElementToBeRemoved(() => - screen.queryByTestId('editCampaignCloseBtn'), + userEvent.click(screen.getByTestId('campaignCloseBtn')); + await waitFor(() => + expect(screen.queryByTestId('campaignCloseBtn')).toBeNull(), ); }); - it("updates the Campaign's details", async () => { - render( - - - - - - {} - - - - - , + it('open and closes delete campaign modal', async () => { + renderFundCampaign(link1); + + const deleteCampaignBtn = await screen.findAllByTestId('deleteCampaignBtn'); + await waitFor(() => expect(deleteCampaignBtn[0]).toBeInTheDocument()); + userEvent.click(deleteCampaignBtn[0]); + + await waitFor(() => + expect(screen.getByText(translations.deleteCampaign)).toBeInTheDocument(), ); - await wait(); + userEvent.click(screen.getByTestId('deleteCampaignCloseBtn')); await waitFor(() => - expect(screen.getAllByTestId('editCampaignBtn')[0]).toBeInTheDocument(), + expect(screen.queryByTestId('deleteCampaignCloseBtn')).toBeNull(), ); - userEvent.click(screen.getAllByTestId('editCampaignBtn')[0]); - await waitFor(() => { - return expect( - screen.findByTestId('editCampaignCloseBtn'), - ).resolves.toBeInTheDocument(); - }); - const campaignName = screen.getByPlaceholderText('Enter Campaign Name'); - fireEvent.change(campaignName, { - target: { value: 'Campaign 4' }, - }); - const fundingGoal = screen.getByPlaceholderText('Enter Funding Goal'); - fireEvent.change(fundingGoal, { - target: { value: 1000 }, - }); - const currency = screen.getByTestId('currencySelect'); - fireEvent.change(currency, { - target: { value: 'INR' }, - }); - const endDateDatePicker = screen.getByLabelText('End Date'); - const startDateDatePicker = screen.getByLabelText('Start Date'); - - const endDate = - formData.campaignEndDate < formData.campaignStartDate - ? formData.campaignStartDate - : formData.campaignEndDate; - fireEvent.change(endDateDatePicker, { - target: { value: endDate }, - }); + }); - fireEvent.change(startDateDatePicker, { - target: { value: formData.campaignStartDate }, + it('Search the Campaigns list by Name', async () => { + renderFundCampaign(link1); + const searchField = await screen.findByTestId('searchFullName'); + fireEvent.change(searchField, { + target: { value: '2' }, }); - userEvent.click(screen.getByTestId('editCampaignSubmitBtn')); - await waitFor(() => { - expect(toast.success).toHaveBeenCalledWith(translations.updatedCampaign); + expect(screen.getByText('Campaign 2')).toBeInTheDocument(); + expect(screen.queryByText('Campaign 1')).toBeNull(); }); }); - it("updates the Campaign's details when date is null", async () => { - render( - - - - - - {} - - - - - , - ); - await wait(); - await waitFor(() => - expect(screen.getAllByTestId('editCampaignBtn')[0]).toBeInTheDocument(), - ); - userEvent.click(screen.getAllByTestId('editCampaignBtn')[0]); + it('should render the Campaign screen with error', async () => { + renderFundCampaign(link2); await waitFor(() => { - return expect( - screen.findByTestId('editCampaignCloseBtn'), - ).resolves.toBeInTheDocument(); - }); - const endDateDatePicker = screen.getByLabelText('End Date'); - const startDateDatePicker = screen.getByLabelText('Start Date'); - - fireEvent.change(endDateDatePicker, { - target: { value: null }, - }); - - fireEvent.change(startDateDatePicker, { - target: { value: null }, + expect(screen.getByTestId('errorMsg')).toBeInTheDocument(); }); - - expect(startDateDatePicker.getAttribute('value')).toBe(''); - expect(endDateDatePicker.getAttribute('value')).toBe(''); }); - it("updates the Campaign's details when endDate is less than date", async () => { - const formData = { - campaignName: 'Campaign 1', - campaignCurrency: 'USD', - campaignGoal: 100, - campaignStartDate: '03/10/2024', - campaignEndDate: '03/10/2023', - }; - render( - - - - - - {} - - - - - , - ); - await wait(); + it('renders the empty campaign component', async () => { + renderFundCampaign(link3); await waitFor(() => - expect(screen.getAllByTestId('editCampaignBtn')[0]).toBeInTheDocument(), + expect( + screen.getByText(translations.noCampaignsFound), + ).toBeInTheDocument(), ); - userEvent.click(screen.getAllByTestId('editCampaignBtn')[0]); - await waitFor(() => { - return expect( - screen.findByTestId('editCampaignCloseBtn'), - ).resolves.toBeInTheDocument(); - }); + }); - const endDateDatePicker = screen.getByLabelText('End Date'); - const startDateDatePicker = screen.getByLabelText('Start Date'); + it('Sort the Campaigns list by Latest end Date', async () => { + renderFundCampaign(link1); - fireEvent.change(endDateDatePicker, { - target: { value: formData.campaignEndDate }, - }); + const sortBtn = await screen.findByTestId('filter'); + expect(sortBtn).toBeInTheDocument(); - fireEvent.change(startDateDatePicker, { - target: { value: formData.campaignStartDate }, - }); - }); + fireEvent.click(sortBtn); + fireEvent.click(screen.getByTestId('endDate_DESC')); - it("doesn't update when fund field has value less than or equal to 0", async () => { - render( - - - - - - {} - - - - - , - ); - await wait(); - await waitFor(() => - expect(screen.getAllByTestId('editCampaignBtn')[0]).toBeInTheDocument(), - ); - userEvent.click(screen.getAllByTestId('editCampaignBtn')[0]); await waitFor(() => { - return expect( - screen.findByTestId('editCampaignCloseBtn'), - ).resolves.toBeInTheDocument(); + expect(screen.getByText('Campaign 1')).toBeInTheDocument(); + expect(screen.queryByText('Campaign 2')).toBeInTheDocument(); }); - const fundingGoal = screen.getByPlaceholderText( - 'Enter Funding Goal', - ) as HTMLInputElement; - const initialValue = fundingGoal.value; //Vakue before updating - - fireEvent.change(fundingGoal, { - target: { value: 0 }, + await waitFor(() => { + expect(screen.getAllByTestId('endDateCell')[0]).toHaveTextContent( + '01/01/2024', + ); }); - - expect(fundingGoal.value).toBe(initialValue); //Retains previous value }); - it('toast an error on unsuccessful campaign update', async () => { - render( - - - - - - {} - - - - - , - ); - await wait(); - await waitFor(() => - expect(screen.getAllByTestId('editCampaignBtn')[0]).toBeInTheDocument(), - ); - userEvent.click(screen.getAllByTestId('editCampaignBtn')[0]); - await waitFor(() => { - return expect( - screen.findByTestId('editCampaignCloseBtn'), - ).resolves.toBeInTheDocument(); - }); - userEvent.type( - screen.getByPlaceholderText('Enter Campaign Name'), - formData.campaignName, - ); + it('Sort the Campaigns list by Earliest end Date', async () => { + renderFundCampaign(link1); + + const sortBtn = await screen.findByTestId('filter'); + expect(sortBtn).toBeInTheDocument(); - userEvent.click(screen.getByTestId('editCampaignSubmitBtn')); + fireEvent.click(sortBtn); + fireEvent.click(screen.getByTestId('endDate_ASC')); await waitFor(() => { - expect(toast.error).toHaveBeenCalled(); + expect(screen.getByText('Campaign 1')).toBeInTheDocument(); + expect(screen.queryByText('Campaign 2')).toBeInTheDocument(); }); - }); - it("opens and closes the 'Delete Campaign' modal", async () => { - render( - - - - - - {} - - - - - , - ); - await wait(); - await waitFor(() => - expect(screen.getAllByTestId('editCampaignBtn')[0]).toBeInTheDocument(), - ); - userEvent.click(screen.getAllByTestId('editCampaignBtn')[0]); - await wait(); - await waitFor(() => - expect(screen.getAllByTestId('deleteCampaignBtn')[0]).toBeInTheDocument(), - ); - userEvent.click(screen.getAllByTestId('deleteCampaignBtn')[0]); + await waitFor(() => { - return expect( - screen.findByTestId('deleteCampaignCloseBtn'), - ).resolves.toBeInTheDocument(); + expect(screen.getAllByTestId('endDateCell')[0]).toHaveTextContent( + '01/01/2021', + ); }); - userEvent.click(screen.getByTestId('deleteCampaignCloseBtn')); - await waitForElementToBeRemoved(() => - screen.queryByTestId('deleteCampaignCloseBtn'), - ); }); - it('deletes a Campaign', async () => { - render( - - - - - - {} - - - - - , - ); - await wait(); - await waitFor(() => - expect(screen.getAllByTestId('editCampaignBtn')[0]).toBeInTheDocument(), - ); - userEvent.click(screen.getAllByTestId('editCampaignBtn')[0]); - await wait(); - await waitFor(() => - expect(screen.getAllByTestId('deleteCampaignBtn')[0]).toBeInTheDocument(), - ); - userEvent.click(screen.getAllByTestId('deleteCampaignBtn')[0]); + + it('Sort the Campaigns list by lowest goal', async () => { + renderFundCampaign(link1); + + const sortBtn = await screen.findByTestId('filter'); + expect(sortBtn).toBeInTheDocument(); + + fireEvent.click(sortBtn); + fireEvent.click(screen.getByTestId('fundingGoal_ASC')); + await waitFor(() => { - return expect( - screen.findByTestId('deleteCampaignCloseBtn'), - ).resolves.toBeInTheDocument(); + expect(screen.getByText('Campaign 1')).toBeInTheDocument(); + expect(screen.queryByText('Campaign 2')).toBeInTheDocument(); }); - userEvent.click(screen.getByTestId('deleteyesbtn')); + await waitFor(() => { - expect(toast.success).toHaveBeenCalledWith(translations.deletedCampaign); + expect(screen.getAllByTestId('goalCell')[0]).toHaveTextContent('100'); }); }); - it('toast an error on unsuccessful campaign deletion', async () => { - render( - - - - - - {} - - - - - , - ); - await wait(); - await waitFor(() => - expect(screen.getAllByTestId('editCampaignBtn')[0]).toBeInTheDocument(), - ); - userEvent.click(screen.getAllByTestId('editCampaignBtn')[0]); - await wait(); - await waitFor(() => - expect(screen.getAllByTestId('deleteCampaignBtn')[0]).toBeInTheDocument(), - ); - userEvent.click(screen.getAllByTestId('deleteCampaignBtn')[0]); + + it('Sort the Campaigns list by highest goal', async () => { + renderFundCampaign(link1); + + const sortBtn = await screen.findByTestId('filter'); + expect(sortBtn).toBeInTheDocument(); + + fireEvent.click(sortBtn); + fireEvent.click(screen.getByTestId('fundingGoal_DESC')); + await waitFor(() => { - return expect( - screen.findByTestId('deleteCampaignCloseBtn'), - ).resolves.toBeInTheDocument(); + expect(screen.getByText('Campaign 1')).toBeInTheDocument(); + expect(screen.queryByText('Campaign 2')).toBeInTheDocument(); }); - userEvent.click(screen.getByTestId('deleteyesbtn')); + await waitFor(() => { - expect(toast.error).toHaveBeenCalled(); + expect(screen.getAllByTestId('goalCell')[0]).toHaveTextContent('200'); }); }); - it('renders the Empty Campaigns Component', async () => { - render( - - - - - - {} - - - - - , - ); - await wait(); + + it('Click on Campaign Name', async () => { + renderFundCampaign(link1); + + const campaignName = await screen.findAllByTestId('campaignName'); + expect(campaignName[0]).toBeInTheDocument(); + fireEvent.click(campaignName[0]); + await waitFor(() => { - expect(screen.getByText(translations.noCampaigns)).toBeInTheDocument(); + expect(screen.getByTestId('pledgeScreen')).toBeInTheDocument(); }); }); - it("redirects to 'FundCampaignPledge' screen", async () => { - render( - - - - - - {} - - - - - , - ); - await wait(); - await waitFor(() => - expect(screen.getAllByTestId('campaignName')[0]).toBeInTheDocument(), - ); - userEvent.click(screen.getAllByTestId('campaignName')[0]); + + it('Click on View Pledge', async () => { + renderFundCampaign(link1); + + const viewBtn = await screen.findAllByTestId('viewBtn'); + expect(viewBtn[0]).toBeInTheDocument(); + fireEvent.click(viewBtn[0]); + await waitFor(() => { - expect(mockNavigate).toHaveBeenCalledWith( - '/fundCampaignPledge/undefined/1', - ); + expect(screen.getByTestId('pledgeScreen')).toBeInTheDocument(); }); }); - it('search funds by name', async () => { - render( - - - - - {} - - - - , - ); - await wait(); - userEvent.type(screen.getByTestId('searchFullName'), 'Funndds'); - await wait(); - userEvent.click(screen.getByTestId('searchBtn')); + it('should render the Fund screen on fund breadcrumb click', async () => { + renderFundCampaign(link1); + + const fundBreadcrumb = await screen.findByTestId('fundsLink'); + expect(fundBreadcrumb).toBeInTheDocument(); + fireEvent.click(fundBreadcrumb); + + await waitFor(() => { + expect(screen.getByTestId('fundScreen')).toBeInTheDocument(); + }); }); }); diff --git a/src/screens/OrganizationFundCampaign/OrganizationFundCampaignMocks.tsx b/src/screens/OrganizationFundCampaign/OrganizationFundCampaignMocks.ts similarity index 59% rename from src/screens/OrganizationFundCampaign/OrganizationFundCampaignMocks.tsx rename to src/screens/OrganizationFundCampaign/OrganizationFundCampaignMocks.ts index 09fbb9f4b2..64ac21340f 100644 --- a/src/screens/OrganizationFundCampaign/OrganizationFundCampaignMocks.tsx +++ b/src/screens/OrganizationFundCampaign/OrganizationFundCampaignMocks.ts @@ -10,7 +10,9 @@ export const MOCKS = [ request: { query: FUND_CAMPAIGN, variables: { - id: undefined, + id: 'fundId', + orderBy: null, + where: { name_contains: '' }, }, }, result: { @@ -18,7 +20,7 @@ export const MOCKS = [ getFundById: { campaigns: [ { - _id: '1', + _id: 'campaignId1', name: 'Campaign 1', fundingGoal: 100, startDate: '2024-01-01', @@ -40,92 +42,111 @@ export const MOCKS = [ }, { request: { - query: CREATE_CAMPAIGN_MUTATION, + query: FUND_CAMPAIGN, variables: { - fundId: undefined, - name: 'Campaign 1', - fundingGoal: 100, - startDate: '2024-03-10', - endDate: '2024-03-10', - currency: 'USD', + id: 'fundId', + orderBy: null, + where: { name_contains: '2' }, }, }, result: { data: { - createFundraisingCampaign: { - _id: '3', + getFundById: { + campaigns: [ + { + _id: '2', + name: 'Campaign 2', + fundingGoal: 200, + startDate: '2021-01-01', + endDate: '2021-01-01', + currency: 'USD', + }, + ], }, }, }, }, { request: { - query: UPDATE_CAMPAIGN_MUTATION, + query: FUND_CAMPAIGN, variables: { - id: '1', - name: 'Campaign 4', - startDate: '2024-03-10', - endDate: '2024-03-10', - fundingGoal: 1000, - currency: 'INR', + id: 'fundId', + orderBy: 'endDate_DESC', + where: { name_contains: '' }, }, }, result: { data: { - updateFundraisingCampaign: { - _id: '1', + getFundById: { + campaigns: [ + { + _id: '1', + name: 'Campaign 1', + fundingGoal: 100, + startDate: '2024-01-01', + endDate: '2024-01-01', + currency: 'USD', + }, + { + _id: '2', + name: 'Campaign 2', + fundingGoal: 200, + startDate: '2021-01-01', + endDate: '2021-01-01', + currency: 'USD', + }, + ], }, }, }, }, { request: { - query: DELETE_CAMPAIGN_MUTATION, + query: FUND_CAMPAIGN, variables: { - id: '1', + id: 'fundId', + orderBy: 'endDate_ASC', + where: { name_contains: '' }, }, }, result: { data: { - removeFundraisingCampaign: { - _id: '1', + getFundById: { + campaigns: [ + { + _id: '2', + name: 'Campaign 2', + fundingGoal: 200, + startDate: '2021-01-01', + endDate: '2021-01-01', + currency: 'USD', + }, + { + _id: '1', + name: 'Campaign 1', + fundingGoal: 100, + startDate: '2024-01-01', + endDate: '2024-01-01', + currency: 'USD', + }, + ], }, }, }, }, -]; - -export const MOCK_FUND_CAMPAIGN_ERROR = [ { request: { query: FUND_CAMPAIGN, variables: { - id: undefined, - }, - }, - error: new Error('An error occurred'), - }, -]; -export const MOCKS_ERROR_CREATE_CAMPAIGN = [ - { - request: { - query: FUND_CAMPAIGN, - variables: { - id: undefined, + id: 'fundId', + orderBy: 'fundingGoal_DESC', + where: { name_contains: '' }, }, }, result: { data: { getFundById: { campaigns: [ - { - _id: '1', - name: 'Campaign 1', - fundingGoal: 100, - startDate: '2021-01-01', - endDate: '2021-01-01', - currency: 'USD', - }, { _id: '2', name: 'Campaign 2', @@ -134,33 +155,26 @@ export const MOCKS_ERROR_CREATE_CAMPAIGN = [ endDate: '2021-01-01', currency: 'USD', }, + { + _id: '1', + name: 'Campaign 1', + fundingGoal: 100, + startDate: '2024-01-01', + endDate: '2024-01-01', + currency: 'USD', + }, ], }, }, }, }, - - { - request: { - query: CREATE_CAMPAIGN_MUTATION, - variables: { - fundId: undefined, - name: 'Campaign 1', - fundingGoal: 100, - startDate: '2024-03-10', - endDate: '2024-03-10', - currency: 'USD', - }, - }, - error: new Error('An error occurred'), - }, -]; -export const MOCKS_ERROR_UPDATE_CAMPAIGN = [ { request: { query: FUND_CAMPAIGN, variables: { - id: undefined, + id: 'fundId', + orderBy: 'fundingGoal_ASC', + where: { name_contains: '' }, }, }, result: { @@ -171,8 +185,8 @@ export const MOCKS_ERROR_UPDATE_CAMPAIGN = [ _id: '1', name: 'Campaign 1', fundingGoal: 100, - startDate: '2021-01-01', - endDate: '2021-01-01', + startDate: '2024-01-01', + endDate: '2024-01-01', currency: 'USD', }, { @@ -190,48 +204,39 @@ export const MOCKS_ERROR_UPDATE_CAMPAIGN = [ }, { request: { - query: UPDATE_CAMPAIGN_MUTATION, + query: CREATE_CAMPAIGN_MUTATION, variables: { - id: undefined, - name: 'Campaign 1', - fundingGoal: 100, - startDate: '2024-03-10', - endDate: '2024-03-10', + fundId: 'fundId', + name: 'Campaign 2', + fundingGoal: 200, + startDate: '2024-01-02', + endDate: '2024-02-02', currency: 'USD', }, }, - error: new Error('An error occurred'), + result: { + data: { + createFundraisingCampaign: { + _id: 'fundId', + }, + }, + }, }, -]; -export const MOCKS_ERROR_DELETE_CAMPAIGN = [ { request: { - query: FUND_CAMPAIGN, + query: UPDATE_CAMPAIGN_MUTATION, variables: { - id: undefined, + id: 'campaignId1', + name: 'Campaign 4', + fundingGoal: 400, + startDate: '2023-01-02', + endDate: '2023-02-02', }, }, result: { data: { - getFundById: { - campaigns: [ - { - _id: '1', - name: 'Campaign 1', - fundingGoal: 100, - startDate: '2021-01-01', - endDate: '2021-01-01', - currency: 'USD', - }, - { - _id: '2', - name: 'Campaign 2', - fundingGoal: 200, - startDate: '2021-01-01', - endDate: '2021-01-01', - currency: 'USD', - }, - ], + updateFundraisingCampaign: { + _id: 'campaignId1', }, }, }, @@ -240,18 +245,76 @@ export const MOCKS_ERROR_DELETE_CAMPAIGN = [ request: { query: DELETE_CAMPAIGN_MUTATION, variables: { - id: '1', + id: 'campaignId1', + }, + }, + result: { + data: { + removeFundraisingCampaign: { + _id: 'campaignId1', + }, + }, + }, + }, +]; + +export const MOCK_ERROR = [ + { + request: { + query: FUND_CAMPAIGN, + variables: { + id: 'fundId', + orderBy: null, + where: { name_contains: '2' }, }, }, error: new Error('An error occurred'), }, + { + request: { + query: CREATE_CAMPAIGN_MUTATION, + variables: { + fundId: 'fundId', + name: 'Campaign 2', + fundingGoal: 200, + startDate: '2024-01-02', + endDate: '2024-02-02', + currency: 'USD', + }, + }, + error: new Error('Mock graphql error'), + }, + { + request: { + query: UPDATE_CAMPAIGN_MUTATION, + variables: { + id: 'campaignId1', + name: 'Campaign 4', + fundingGoal: 400, + startDate: '2023-01-02', + endDate: '2023-02-02', + }, + }, + error: new Error('Mock graphql error'), + }, + { + request: { + query: DELETE_CAMPAIGN_MUTATION, + variables: { + id: 'campaignId1', + }, + }, + error: new Error('Mock graphql error'), + }, ]; export const EMPTY_MOCKS = [ { request: { query: FUND_CAMPAIGN, variables: { - id: undefined, + id: 'fundId', + orderBy: null, + where: { name_contains: '' }, }, }, result: { diff --git a/src/screens/OrganizationFunds/FundDeleteModal.test.tsx b/src/screens/OrganizationFunds/FundDeleteModal.test.tsx new file mode 100644 index 0000000000..7f0e75cbeb --- /dev/null +++ b/src/screens/OrganizationFunds/FundDeleteModal.test.tsx @@ -0,0 +1,108 @@ +import React from 'react'; +import type { ApolloLink } from '@apollo/client'; +import { MockedProvider } from '@apollo/react-testing'; +import { LocalizationProvider } from '@mui/x-date-pickers'; +import type { RenderResult } from '@testing-library/react'; +import { fireEvent, render, screen, waitFor } from '@testing-library/react'; +import { I18nextProvider } from 'react-i18next'; +import { Provider } from 'react-redux'; +import { BrowserRouter } from 'react-router-dom'; +import { store } from 'state/store'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import i18nForTest from '../../utils/i18nForTest'; +import { StaticMockLink } from 'utils/StaticMockLink'; +import { toast } from 'react-toastify'; +import type { InterfaceDeleteFundModal } from './FundDeleteModal'; +import FundDeleteModal from './FundDeleteModal'; +import { MOCKS, MOCKS_ERROR } from './OrganizationFundsMocks'; + +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, + }; +}); + +const link1 = new StaticMockLink(MOCKS); +const link2 = new StaticMockLink(MOCKS_ERROR); +const translations = JSON.parse( + JSON.stringify(i18nForTest.getDataByLanguage('en')?.translation.funds), +); + +const fundProps: InterfaceDeleteFundModal = { + isOpen: true, + hide: jest.fn(), + fund: { + _id: 'fundId', + name: 'Fund 1', + refrenceNumber: '1111', + taxDeductible: true, + isArchived: false, + isDefault: false, + createdAt: '2024-06-22', + organizationId: 'orgId', + creator: { + _id: 'creatorId1', + firstName: 'John', + lastName: 'Doe', + }, + }, + refetchFunds: jest.fn(), +}; +const renderFundDeleteModal = ( + link: ApolloLink, + props: InterfaceDeleteFundModal, +): RenderResult => { + return render( + + + + + + + + + + + , + ); +}; + +describe('FundDeleteModal', () => { + it('should render FundDeleteModal', () => { + renderFundDeleteModal(link1, fundProps); + expect(screen.getByTestId('deleteFundCloseBtn')).toBeInTheDocument(); + }); + + it('should successfully Delete Fund', async () => { + renderFundDeleteModal(link1, fundProps); + expect(screen.getByTestId('deleteFundCloseBtn')).toBeInTheDocument(); + + fireEvent.click(screen.getByTestId('deleteyesbtn')); + + await waitFor(() => { + expect(fundProps.refetchFunds).toHaveBeenCalled(); + expect(fundProps.hide).toHaveBeenCalled(); + expect(toast.success).toHaveBeenCalledWith(translations.fundDeleted); + }); + }); + + it('should fail to Delete Fund', async () => { + renderFundDeleteModal(link2, fundProps); + expect(screen.getByTestId('deleteFundCloseBtn')).toBeInTheDocument(); + + fireEvent.click(screen.getByTestId('deleteyesbtn')); + + await waitFor(() => { + expect(toast.error).toHaveBeenCalledWith('Mock graphql error'); + }); + }); +}); diff --git a/src/screens/OrganizationFunds/FundDeleteModal.tsx b/src/screens/OrganizationFunds/FundDeleteModal.tsx index b5ce303d66..fb13206f1a 100644 --- a/src/screens/OrganizationFunds/FundDeleteModal.tsx +++ b/src/screens/OrganizationFunds/FundDeleteModal.tsx @@ -38,9 +38,7 @@ const FundDeleteModal: React.FC = ({ hide(); toast.success(t('fundDeleted')); } catch (error: unknown) { - if (error instanceof Error) { - toast.error(error.message); - } + toast.error((error as Error).message); } }; diff --git a/src/screens/OrganizationFunds/FundModal.test.tsx b/src/screens/OrganizationFunds/FundModal.test.tsx new file mode 100644 index 0000000000..778ff55d17 --- /dev/null +++ b/src/screens/OrganizationFunds/FundModal.test.tsx @@ -0,0 +1,268 @@ +import React from 'react'; +import type { ApolloLink } from '@apollo/client'; +import { MockedProvider } from '@apollo/react-testing'; +import { LocalizationProvider } from '@mui/x-date-pickers'; +import type { RenderResult } from '@testing-library/react'; +import { + cleanup, + fireEvent, + render, + screen, + waitFor, +} from '@testing-library/react'; +import { I18nextProvider } from 'react-i18next'; +import { Provider } from 'react-redux'; +import { BrowserRouter } from 'react-router-dom'; +import { store } from 'state/store'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import i18nForTest from '../../utils/i18nForTest'; +import { StaticMockLink } from 'utils/StaticMockLink'; +import { toast } from 'react-toastify'; +import { MOCKS, MOCKS_ERROR } from './OrganizationFundsMocks'; +import type { InterfaceFundModal } from './FundModal'; +import FundModal from './FundModal'; + +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, + }; +}); + +const link1 = new StaticMockLink(MOCKS); +const link2 = new StaticMockLink(MOCKS_ERROR); +const translations = JSON.parse( + JSON.stringify(i18nForTest.getDataByLanguage('en')?.translation.funds), +); + +const fundProps: InterfaceFundModal[] = [ + { + isOpen: true, + hide: jest.fn(), + fund: { + _id: 'fundId', + name: 'Fund 1', + refrenceNumber: '1111', + taxDeductible: true, + isArchived: false, + isDefault: false, + createdAt: '2024-06-22', + organizationId: 'orgId', + creator: { + _id: 'creatorId1', + firstName: 'John', + lastName: 'Doe', + }, + }, + refetchFunds: jest.fn(), + orgId: 'orgId', + mode: 'create', + }, + { + isOpen: true, + hide: jest.fn(), + fund: { + _id: 'fundId', + name: 'Fund 1', + refrenceNumber: '1111', + taxDeductible: true, + isArchived: false, + isDefault: false, + createdAt: '2024-06-22', + organizationId: 'orgId', + creator: { + _id: 'creatorId1', + firstName: 'John', + lastName: 'Doe', + }, + }, + refetchFunds: jest.fn(), + orgId: 'orgId', + mode: 'edit', + }, +]; + +const renderFundModal = ( + link: ApolloLink, + props: InterfaceFundModal, +): RenderResult => { + return render( + + + + + + + + + + + , + ); +}; + +describe('PledgeModal', () => { + afterEach(() => { + cleanup(); + }); + + it('should populate form fields with correct values in edit mode', async () => { + renderFundModal(link1, fundProps[1]); + await waitFor(() => + expect( + screen.getAllByText(translations.fundUpdate)[0], + ).toBeInTheDocument(), + ); + expect(screen.getByLabelText(translations.fundName)).toHaveValue('Fund 1'); + expect(screen.getByLabelText(translations.fundId)).toHaveValue('1111'); + expect(screen.getByTestId('setTaxDeductibleSwitch')).toBeChecked(); + expect(screen.getByTestId('setDefaultSwitch')).not.toBeChecked(); + expect(screen.getByTestId('archivedSwitch')).not.toBeChecked(); + }); + + it('should update Fund Name when input value changes', async () => { + renderFundModal(link1, fundProps[1]); + const fundNameInput = screen.getByLabelText(translations.fundName); + expect(fundNameInput).toHaveValue('Fund 1'); + fireEvent.change(fundNameInput, { target: { value: 'Fund 2' } }); + expect(fundNameInput).toHaveValue('Fund 2'); + }); + + it('should update Fund Reference ID when input value changes', async () => { + renderFundModal(link1, fundProps[1]); + const fundIdInput = screen.getByLabelText(translations.fundId); + expect(fundIdInput).toHaveValue('1111'); + fireEvent.change(fundIdInput, { target: { value: '2222' } }); + expect(fundIdInput).toHaveValue('2222'); + }); + + it('should update Tax Deductible Switch when input value changes', async () => { + renderFundModal(link1, fundProps[1]); + const taxDeductibleSwitch = screen.getByTestId('setTaxDeductibleSwitch'); + expect(taxDeductibleSwitch).toBeChecked(); + fireEvent.click(taxDeductibleSwitch); + expect(taxDeductibleSwitch).not.toBeChecked(); + }); + + it('should update Tax Default switch when input value changes', async () => { + renderFundModal(link1, fundProps[1]); + const defaultSwitch = screen.getByTestId('setDefaultSwitch'); + expect(defaultSwitch).not.toBeChecked(); + fireEvent.click(defaultSwitch); + expect(defaultSwitch).toBeChecked(); + }); + + it('should update Tax isArchived switch when input value changes', async () => { + renderFundModal(link1, fundProps[1]); + const archivedSwitch = screen.getByTestId('archivedSwitch'); + expect(archivedSwitch).not.toBeChecked(); + fireEvent.click(archivedSwitch); + expect(archivedSwitch).toBeChecked(); + }); + + it('should create fund', async () => { + renderFundModal(link1, fundProps[0]); + + const fundNameInput = screen.getByLabelText(translations.fundName); + fireEvent.change(fundNameInput, { target: { value: 'Fund 2' } }); + + const fundIdInput = screen.getByLabelText(translations.fundId); + fireEvent.change(fundIdInput, { target: { value: '2222' } }); + + const taxDeductibleSwitch = screen.getByTestId('setTaxDeductibleSwitch'); + fireEvent.click(taxDeductibleSwitch); + + const defaultSwitch = screen.getByTestId('setDefaultSwitch'); + fireEvent.click(defaultSwitch); + + fireEvent.click(screen.getByTestId('createFundFormSubmitBtn')); + + await waitFor(() => { + expect(toast.success).toHaveBeenCalledWith(translations.fundCreated); + expect(fundProps[0].refetchFunds).toHaveBeenCalled(); + expect(fundProps[0].hide).toHaveBeenCalled(); + }); + }); + + it('should update fund', async () => { + renderFundModal(link1, fundProps[1]); + + const fundNameInput = screen.getByLabelText(translations.fundName); + fireEvent.change(fundNameInput, { target: { value: 'Fund 2' } }); + + const fundIdInput = screen.getByLabelText(translations.fundId); + fireEvent.change(fundIdInput, { target: { value: '2222' } }); + + const taxDeductibleSwitch = screen.getByTestId('setTaxDeductibleSwitch'); + fireEvent.click(taxDeductibleSwitch); + + const defaultSwitch = screen.getByTestId('setDefaultSwitch'); + fireEvent.click(defaultSwitch); + + const archivedSwitch = screen.getByTestId('archivedSwitch'); + fireEvent.click(archivedSwitch); + + fireEvent.click(screen.getByTestId('createFundFormSubmitBtn')); + + await waitFor(() => { + expect(toast.success).toHaveBeenCalledWith(translations.fundUpdated); + expect(fundProps[1].refetchFunds).toHaveBeenCalled(); + expect(fundProps[1].hide).toHaveBeenCalled(); + }); + }); + + it('Error: should create fund', async () => { + renderFundModal(link2, fundProps[0]); + + const fundNameInput = screen.getByLabelText(translations.fundName); + fireEvent.change(fundNameInput, { target: { value: 'Fund 2' } }); + + const fundIdInput = screen.getByLabelText(translations.fundId); + fireEvent.change(fundIdInput, { target: { value: '2222' } }); + + const taxDeductibleSwitch = screen.getByTestId('setTaxDeductibleSwitch'); + fireEvent.click(taxDeductibleSwitch); + + const defaultSwitch = screen.getByTestId('setDefaultSwitch'); + fireEvent.click(defaultSwitch); + + fireEvent.click(screen.getByTestId('createFundFormSubmitBtn')); + + await waitFor(() => { + expect(toast.error).toHaveBeenCalledWith('Mock graphql error'); + }); + }); + + it('Error: should update fund', async () => { + renderFundModal(link2, fundProps[1]); + + const fundNameInput = screen.getByLabelText(translations.fundName); + fireEvent.change(fundNameInput, { target: { value: 'Fund 2' } }); + + const fundIdInput = screen.getByLabelText(translations.fundId); + fireEvent.change(fundIdInput, { target: { value: '2222' } }); + + const taxDeductibleSwitch = screen.getByTestId('setTaxDeductibleSwitch'); + fireEvent.click(taxDeductibleSwitch); + + const defaultSwitch = screen.getByTestId('setDefaultSwitch'); + fireEvent.click(defaultSwitch); + + const archivedSwitch = screen.getByTestId('archivedSwitch'); + fireEvent.click(archivedSwitch); + + fireEvent.click(screen.getByTestId('createFundFormSubmitBtn')); + + await waitFor(() => { + expect(toast.error).toHaveBeenCalledWith('Mock graphql error'); + }); + }); +}); diff --git a/src/screens/OrganizationFunds/FundModal.tsx b/src/screens/OrganizationFunds/FundModal.tsx index 3cfbecb05a..f4c6350a1c 100644 --- a/src/screens/OrganizationFunds/FundModal.tsx +++ b/src/screens/OrganizationFunds/FundModal.tsx @@ -12,7 +12,7 @@ import { import { toast } from 'react-toastify'; import { FormControl, TextField } from '@mui/material'; -interface InterfaceFundModal { +export interface InterfaceFundModal { isOpen: boolean; hide: () => void; refetchFunds: () => void; @@ -83,12 +83,11 @@ const FundModal: React.FC = ({ refetchFunds(); hide(); } catch (error: unknown) { - if (error instanceof Error) { - toast.error(error.message); - } + toast.error((error as Error).message); } }; + /*istanbul ignore next*/ const updateFundHandler = async ( e: ChangeEvent, ): Promise => { diff --git a/src/screens/OrganizationFunds/OrganizationFunds.module.css b/src/screens/OrganizationFunds/OrganizationFunds.module.css index b3739f3e13..aa9d89dfb1 100644 --- a/src/screens/OrganizationFunds/OrganizationFunds.module.css +++ b/src/screens/OrganizationFunds/OrganizationFunds.module.css @@ -21,15 +21,9 @@ border: 1px solid #31bb6b; position: relative; display: inline-block; - margin-top: 10px; - margin-bottom: 10px; color: #31bb6b; } -.createFundBtn { - margin-top: 10px; -} - .fundName { font-weight: 600; cursor: pointer; @@ -111,22 +105,12 @@ .btnsContainer { display: flex; - margin: 2.5rem 0 2.5rem 0; -} - -.btnsContainer .btnsBlock { - display: flex; -} - -.btnsContainer .btnsBlock div button { - display: flex; - margin-left: 1rem; - justify-content: center; - align-items: center; + margin: 2rem 0 2.5rem 0; } .btnsContainer .input { flex: 1; + min-width: 18rem; position: relative; } @@ -156,49 +140,3 @@ color: var(--bs-white); font-size: 1rem; } - -@media (max-width: 1020px) { - .btnsContainer { - flex-direction: column; - margin: 1.5rem 0; - } - - .btnsContainer .btnsBlock { - margin: 1.5rem 0 0 0; - justify-content: space-between; - } - - .btnsContainer .btnsBlock div button { - margin: 0; - } - - .createFundBtn { - margin-top: 0; - } -} - -@media screen and (max-width: 575.5px) { - .mainpageright { - width: 98%; - } -} - -/* For mobile devices */ - -@media (max-width: 520px) { - .btnsContainer { - margin-bottom: 0; - } - - .btnsContainer .btnsBlock { - display: block; - margin-top: 1rem; - margin-right: 0; - } - - .btnsContainer .btnsBlock div button { - margin-bottom: 1rem; - margin-right: 0; - width: 100%; - } -} diff --git a/src/screens/OrganizationFunds/OrganizationFunds.test.tsx b/src/screens/OrganizationFunds/OrganizationFunds.test.tsx index 93be2c281c..1933d286fa 100644 --- a/src/screens/OrganizationFunds/OrganizationFunds.test.tsx +++ b/src/screens/OrganizationFunds/OrganizationFunds.test.tsx @@ -1,407 +1,262 @@ import React from 'react'; import { MockedProvider } from '@apollo/client/testing'; +import type { RenderResult } from '@testing-library/react'; import { - act, + cleanup, fireEvent, render, screen, + wait, waitFor, - waitForElementToBeRemoved, } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { I18nextProvider } from 'react-i18next'; import { Provider } from 'react-redux'; -import { BrowserRouter } from 'react-router-dom'; -import { toast } from 'react-toastify'; +import { MemoryRouter, Route, Routes } from 'react-router-dom'; import { store } from 'state/store'; import { StaticMockLink } from 'utils/StaticMockLink'; import i18nForTest from 'utils/i18nForTest'; import OrganizationFunds from './OrganizationFunds'; -import { - MOCKS, - MOCKS_ERROR_CREATE_FUND, - MOCKS_ERROR_ORGANIZATIONS_FUNDS, - MOCKS_ERROR_REMOVE_FUND, - MOCKS_ERROR_UPDATE_FUND, - NO_FUNDS, -} from './OrganizationFundsMocks'; -const mockNavigate = jest.fn(); -jest.mock('react-router-dom', () => ({ - ...jest.requireActual('react-router-dom'), - useNavigate: () => mockNavigate, -})); +import { MOCKS, MOCKS_ERROR, NO_FUNDS } from './OrganizationFundsMocks'; +import type { ApolloLink } from '@apollo/client'; +import { LocalizationProvider } from '@mui/x-date-pickers'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import { createMemoryHistory } from 'history'; + jest.mock('react-toastify', () => ({ toast: { success: jest.fn(), error: jest.fn(), }, })); -async function wait(ms = 100): Promise { - await act(() => { - return new Promise((resolve) => { - setTimeout(resolve, ms); - }); - }); -} + const link1 = new StaticMockLink(MOCKS, true); -const link2 = new StaticMockLink(MOCKS_ERROR_ORGANIZATIONS_FUNDS, true); -const link3 = new StaticMockLink(MOCKS_ERROR_CREATE_FUND, true); -const link4 = new StaticMockLink(MOCKS_ERROR_UPDATE_FUND, true); -const link5 = new StaticMockLink(MOCKS_ERROR_REMOVE_FUND, true); -const link6 = new StaticMockLink(NO_FUNDS, true); +const link2 = new StaticMockLink(MOCKS_ERROR, true); +const link3 = new StaticMockLink(NO_FUNDS, true); const translations = JSON.parse( JSON.stringify(i18nForTest.getDataByLanguage('en')?.translation.funds), ); -describe('Testing OrganizationFunds screen', () => { - const formData = { - fundName: 'Test Fund', - fundRef: '1', - }; - it("loads the OrganizationFunds screen and it's components", async () => { - const { getByText } = render( - + +const renderOrganizationFunds = (link: ApolloLink): RenderResult => { + return render( + + - + - {} + + } + /> + } + /> + } + /> + - + - , - ); - await wait(); - await waitFor(() => { - expect(getByText(translations.createFund)).toBeInTheDocument(); - }); + + , + ); +}; + +describe('OrganizationFunds Screen =>', () => { + beforeEach(() => { + jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useParams: () => ({ orgId: 'orgId' }), + })); }); - it("renders the OrganizationFunds screen and it's components with error", async () => { - const { queryByText } = render( - - - - - {} - - - - , - ); - await wait(); - await waitFor(() => { - expect(queryByText(translations.createFund)).not.toBeInTheDocument(); - }); + + afterEach(() => { + jest.clearAllMocks(); }); - it('renders the Error component', async () => { - render( - - - - - {} - - - - , - ); - await wait(); - await waitFor(() => { - expect(screen.getByTestId('errorMsg')).toBeInTheDocument(); - }); + + afterEach(() => { + cleanup(); }); - it('renders the funds component based on fund type', async () => { - render( - - - - - {} - - - - , - ); - await wait(); - await waitFor(() => { - expect(screen.getAllByTestId('fundtype')[0]).toBeInTheDocument(); - }); + + it('should render the Campaign Pledge screen', async () => { + renderOrganizationFunds(link1); await waitFor(() => { - expect(screen.getAllByTestId('fundtype')[0]).toHaveTextContent( - translations.nonArchive, - ); + expect(screen.getByTestId('searchByName')).toBeInTheDocument(); }); - expect(screen.getAllByTestId('fundtype')[1]).toHaveTextContent( - translations.archived, - ); }); - it("opens and closes the 'Create Fund' modal", async () => { + + it('should redirect to fallback URL if URL params are undefined', async () => { render( - - + + - {} + + } /> + } + /> + - - + + , ); - await wait(); - await waitFor(() => { - expect(screen.getByTestId('createFundBtn')).toBeInTheDocument(); - }); - userEvent.click(screen.getByTestId('createFundBtn')); await waitFor(() => { - return expect( - screen.findByTestId('createFundModalCloseBtn'), - ).resolves.toBeInTheDocument(); + expect(screen.getByTestId('paramsError')).toBeInTheDocument(); }); - userEvent.click(screen.getByTestId('setTaxDeductibleSwitch')); - userEvent.click(screen.getByTestId('setDefaultSwitch')); - userEvent.click(screen.getByTestId('createFundModalCloseBtn')); - await waitForElementToBeRemoved(() => - screen.queryByTestId('createFundModalCloseBtn'), - ); }); - it('noFunds to be in the document', async () => { - render( - - - - - {} - - - - , + + it('open and close Create Fund modal', async () => { + renderOrganizationFunds(link1); + + const createFundBtn = await screen.findByTestId('createFundBtn'); + expect(createFundBtn).toBeInTheDocument(); + userEvent.click(createFundBtn); + + await waitFor(() => + expect(screen.getAllByText(translations.fundCreate)).toHaveLength(3), ); - await wait(); - await waitFor(() => { - expect(screen.getByText(translations.noFundsFound)).toBeInTheDocument(); - }); - }); - it('creates a new fund', async () => { - render( - - - - - {} - - - - , + userEvent.click(screen.getByTestId('fundModalCloseBtn')); + await waitFor(() => + expect(screen.queryByTestId('fundModalCloseBtn')).toBeNull(), ); - await wait(); - await waitFor(() => { - expect(screen.getByTestId('createFundBtn')).toBeInTheDocument(); - }); - userEvent.click(screen.getByTestId('createFundBtn')); + }); + + it('open and close update fund modal', async () => { + renderOrganizationFunds(link1); + await waitFor(() => { - return expect( - screen.findByTestId('createFundModalCloseBtn'), - ).resolves.toBeInTheDocument(); + expect(screen.getByTestId('searchByName')).toBeInTheDocument(); }); - userEvent.type( - screen.getByPlaceholderText(translations.enterfundName), - formData.fundName, + + const editFundBtn = await screen.findAllByTestId('editFundBtn'); + await waitFor(() => expect(editFundBtn[0]).toBeInTheDocument()); + userEvent.click(editFundBtn[0]); + + await waitFor(() => + expect( + screen.getAllByText(translations.fundUpdate)[0], + ).toBeInTheDocument(), ); - userEvent.type( - screen.getByPlaceholderText(translations.enterfundId), - formData.fundRef, + userEvent.click(screen.getByTestId('fundModalCloseBtn')); + await waitFor(() => + expect(screen.queryByTestId('fundModalCloseBtn')).toBeNull(), ); - userEvent.click(screen.getByTestId('setTaxDeductibleSwitch')); - userEvent.click(screen.getByTestId('setDefaultSwitch')); - userEvent.click(screen.getByTestId('setTaxDeductibleSwitch')); - userEvent.click(screen.getByTestId('setDefaultSwitch')); - await wait(); - userEvent.click(screen.getByTestId('createFundFormSubmitBtn')); - - await waitFor(() => { - expect(toast.success).toBeCalledWith(translations.fundCreated); - }); }); - it('updates fund successfully', async () => { - render( - - - - - {} - - - - , + + it('open and closes delete fund modal', async () => { + renderOrganizationFunds(link1); + + const deleteFundBtn = await screen.findAllByTestId('deleteFundBtn'); + await waitFor(() => expect(deleteFundBtn[0]).toBeInTheDocument()); + userEvent.click(deleteFundBtn[0]); + + await waitFor(() => + expect(screen.getByText(translations.fundDelete)).toBeInTheDocument(), ); - await wait(); - userEvent.click(screen.getAllByTestId('editFundBtn')[0]); - await wait(); - userEvent.clear(screen.getByTestId('fundNameInput')); - userEvent.clear(screen.getByTestId('fundIdInput')); - userEvent.type(screen.getByTestId('fundNameInput'), 'Test Fund'); - userEvent.type(screen.getByTestId('fundIdInput'), '1'); - expect(screen.getByTestId('taxDeductibleSwitch')).toBeInTheDocument(); - expect(screen.getByTestId('defaultSwitch')).toBeInTheDocument(); - expect(screen.getByTestId('archivedSwitch')).toBeInTheDocument(); - expect(screen.getByTestId('updateFormBtn')).toBeInTheDocument(); - userEvent.click(screen.getByTestId('taxDeductibleSwitch')); - userEvent.click(screen.getByTestId('defaultSwitch')); - userEvent.click(screen.getByTestId('archivedSwitch')); - await wait(); - userEvent.click(screen.getByTestId('updateFormBtn')); - await wait(); - await waitFor(() => { - expect(toast.success).toBeCalledWith(translations.fundUpdated); - }); - }); - it('toast error on unsuccessful fund creation', async () => { - render( - - - - - {} - - - - , + userEvent.click(screen.getByTestId('deleteFundCloseBtn')); + await waitFor(() => + expect(screen.queryByTestId('deleteFundCloseBtn')).toBeNull(), ); - await wait(); - await waitFor(() => { - expect(screen.getByTestId('createFundBtn')).toBeInTheDocument(); + }); + + it('Search the Funds list by name', async () => { + renderOrganizationFunds(link1); + const searchField = await screen.findByTestId('searchByName'); + fireEvent.change(searchField, { + target: { value: '2' }, }); - userEvent.click(screen.getByTestId('createFundBtn')); + await waitFor(() => { - return expect( - screen.findByTestId('createFundModalCloseBtn'), - ).resolves.toBeInTheDocument(); + expect(screen.getByText('Fund 2')).toBeInTheDocument(); + expect(screen.queryByText('Fund 1')).toBeNull(); }); - userEvent.type( - screen.getByPlaceholderText(translations.enterfundName), - formData.fundName, - ); - userEvent.type( - screen.getByPlaceholderText(translations.enterfundId), - formData.fundRef, - ); - userEvent.click(screen.getByTestId('createFundFormSubmitBtn')); + }); + it('should render the Fund screen with error', async () => { + renderOrganizationFunds(link2); await waitFor(() => { - expect(toast.error).toHaveBeenCalled(); + expect(screen.getByTestId('errorMsg')).toBeInTheDocument(); }); }); - it('toast error on unsuccessful fund update', async () => { - render( - - - - - {} - - - - , + + it('renders the empty fund component', async () => { + renderOrganizationFunds(link3); + await waitFor(() => + expect(screen.getByText(translations.noFundsFound)).toBeInTheDocument(), ); - await wait(); - await waitFor(() => { - expect(screen.getAllByTestId('editFundBtn')[0]).toBeInTheDocument(); - }); - userEvent.click(screen.getAllByTestId('editFundBtn')[0]); + }); + + it('Sort the Pledges list by Latest created Date', async () => { + renderOrganizationFunds(link1); + + const sortBtn = await screen.findByTestId('filter'); + expect(sortBtn).toBeInTheDocument(); + + fireEvent.click(sortBtn); + fireEvent.click(screen.getByTestId('createdAt_DESC')); + await waitFor(() => { - return expect( - screen.findByTestId('editFundModalCloseBtn'), - ).resolves.toBeInTheDocument(); + expect(screen.getByText('Fund 1')).toBeInTheDocument(); + expect(screen.queryByText('Fund 2')).toBeInTheDocument(); }); - userEvent.type( - screen.getByPlaceholderText(translations.enterfundName), - 'Test Fund Updated', - ); - userEvent.click(screen.getByTestId('updateFormBtn')); await waitFor(() => { - expect(toast.error).toHaveBeenCalled(); + expect(screen.getAllByTestId('createdOn')[0]).toHaveTextContent( + '22/06/2024', + ); }); }); - it('redirects to campaign screen when clicked on fund name', async () => { - render( - - - - - {} - - - - , - ); - await wait(); + + it('Sort the Pledges list by Earliest created Date', async () => { + renderOrganizationFunds(link1); + + const sortBtn = await screen.findByTestId('filter'); + expect(sortBtn).toBeInTheDocument(); + + fireEvent.click(sortBtn); + fireEvent.click(screen.getByTestId('createdAt_ASC')); + await waitFor(() => { - expect(screen.getAllByTestId('fundName')[0]).toBeInTheDocument(); + expect(screen.getByText('Fund 1')).toBeInTheDocument(); + expect(screen.queryByText('Fund 2')).toBeInTheDocument(); }); - userEvent.click(screen.getAllByTestId('fundName')[0]); + await waitFor(() => { - expect(mockNavigate).toBeCalledWith('/orgfundcampaign/undefined/1'); + expect(screen.getAllByTestId('createdOn')[0]).toHaveTextContent( + '21/06/2024', + ); }); }); - it('delete fund succesfully', async () => { - render( - - - - - {} - - - - , - ); - await wait(); - userEvent.click(screen.getAllByTestId('editFundBtn')[0]); - await wait(); - userEvent.click(screen.getByTestId('fundDeleteModalDeleteBtn')); + + it('Click on Fund Name', async () => { + renderOrganizationFunds(link1); + + const fundName = await screen.findAllByTestId('fundName'); + expect(fundName[0]).toBeInTheDocument(); + fireEvent.click(fundName[0]); + await waitFor(() => { - expect(toast.success).toBeCalledWith(translations.fundDeleted); + expect(screen.getByTestId('campaignScreen')).toBeInTheDocument(); }); }); - it('throws error on unsuccessful fund deletion', async () => { - render( - - - - - {} - - - - , - ); - await wait(); - userEvent.click(screen.getAllByTestId('editFundBtn')[0]); - await wait(); - userEvent.click(screen.getByTestId('fundDeleteModalDeleteBtn')); + + it('Click on View Campaign', async () => { + renderOrganizationFunds(link1); + + const viewBtn = await screen.findAllByTestId('viewBtn'); + expect(viewBtn[0]).toBeInTheDocument(); + fireEvent.click(viewBtn[0]); + await waitFor(() => { - expect(toast.error).toBeCalled(); + expect(screen.getByTestId('campaignScreen')).toBeInTheDocument(); }); }); - it('search funds by name', async () => { - render( - - - - - {} - - - - , - ); - await wait(); - userEvent.click(screen.getAllByTestId('editFundBtn')[0]); - await wait(); - userEvent.click(screen.getByTestId('editFundModalCloseBtn')); - await wait(); - userEvent.type(screen.getByTestId('searchFullName'), 'Funndds'); - await wait(); - userEvent.click(screen.getByTestId('searchBtn')); - }); }); diff --git a/src/screens/OrganizationFunds/OrganizationFunds.tsx b/src/screens/OrganizationFunds/OrganizationFunds.tsx index e4439221eb..922a6e4568 100644 --- a/src/screens/OrganizationFunds/OrganizationFunds.tsx +++ b/src/screens/OrganizationFunds/OrganizationFunds.tsx @@ -1,8 +1,8 @@ import { useQuery } from '@apollo/client'; import { Search, Sort, WarningAmberRounded } from '@mui/icons-material'; import Loader from 'components/Loader/Loader'; -import React, { useCallback, useEffect, useMemo, useState } from 'react'; -import { Button, Dropdown, Form, Row } from 'react-bootstrap'; +import React, { useCallback, useMemo, useState } from 'react'; +import { Button, Dropdown, Form } from 'react-bootstrap'; import { useTranslation } from 'react-i18next'; import { Navigate, useNavigate, useParams } from 'react-router-dom'; import type { InterfaceFundInfo } from 'utils/interfaces'; @@ -58,9 +58,10 @@ const organizationFunds = (): JSX.Element => { } const [fund, setFund] = useState(null); - const [funds, setFunds] = useState([]); const [searchTerm, setSearchTerm] = useState(''); - const [sortBy, setSortBy] = useState(null); + const [sortBy, setSortBy] = useState<'createdAt_ASC' | 'createdAt_DESC'>( + 'createdAt_DESC', + ); const [modalState, setModalState] = useState<{ [key in Modal]: boolean }>({ [Modal.SAME]: false, @@ -112,13 +113,7 @@ const organizationFunds = (): JSX.Element => { [openModal], ); - // const funds = useMemo(() => fundData?.fundsByOrganization ?? [], [fundData]); - - useEffect(() => { - if (fundData) { - setFunds(fundData.fundsByOrganization); - } - }, [fundData]); + const funds = useMemo(() => fundData?.fundsByOrganization ?? [], [fundData]); const handleClick = (fundId: string): void => { navigate(`/orgfundcampaign/${orgId}/${fundId}`); @@ -160,19 +155,19 @@ const organizationFunds = (): JSX.Element => { field: 'fundName', headerName: 'Fund Name', flex: 2, - minWidth: 150, align: 'center', + minWidth: 100, headerAlign: 'center', - headerClassName: `${styles.tableHeader}`, sortable: false, + headerClassName: `${styles.tableHeader}`, renderCell: (params: GridCellParams) => { return (
handleClick(params.row.fund._id as string)} + onClick={() => handleClick(params.row._id as string)} > - {params.row.fund.name} + {params.row.name}
); }, @@ -181,54 +176,54 @@ const organizationFunds = (): JSX.Element => { field: 'createdBy', headerName: 'Created By', flex: 2, - minWidth: 150, align: 'center', + minWidth: 100, headerAlign: 'center', - headerClassName: `${styles.tableHeader}`, sortable: false, + headerClassName: `${styles.tableHeader}`, renderCell: (params: GridCellParams) => { - return ( - params.row.fund.creator.firstName + - ' ' + - params.row.fund.creator.lastName - ); + return params.row.creator.firstName + ' ' + params.row.creator.lastName; }, }, { field: 'createdOn', headerName: 'Created On', - minWidth: 150, align: 'center', + minWidth: 100, headerAlign: 'center', + sortable: false, headerClassName: `${styles.tableHeader}`, flex: 2, - sortable: false, renderCell: (params: GridCellParams) => { - return dayjs(params.row.fund.createdAt).format('DD/MM/YYYY'); + return ( +
+ {dayjs(params.row.createdAt).format('DD/MM/YYYY')} +
+ ); }, }, { field: 'status', headerName: 'Status', flex: 2, - minWidth: 100, align: 'center', + minWidth: 100, headerAlign: 'center', - headerClassName: `${styles.tableHeader}`, sortable: false, + headerClassName: `${styles.tableHeader}`, renderCell: (params: GridCellParams) => { - return params.row.fund.isArchived ? 'Archived' : 'Active'; + return params.row.isArchived ? 'Archived' : 'Active'; }, }, { field: 'action', headerName: 'Action', flex: 2, - minWidth: 100, align: 'center', + minWidth: 100, headerAlign: 'center', - headerClassName: `${styles.tableHeader}`, sortable: false, + headerClassName: `${styles.tableHeader}`, renderCell: (params: GridCellParams) => { return ( <> @@ -238,7 +233,7 @@ const organizationFunds = (): JSX.Element => { className="me-2 rounded" data-testid="editFundBtn" onClick={() => - handleOpenModal(params.row.fund as InterfaceFundInfo, 'edit') + handleOpenModal(params.row as InterfaceFundInfo, 'edit') } > @@ -248,9 +243,7 @@ const organizationFunds = (): JSX.Element => { variant="danger" className="rounded" data-testid="deleteFundBtn" - onClick={() => - handleDeleteClick(params.row.fund as InterfaceFundInfo) - } + onClick={() => handleDeleteClick(params.row as InterfaceFundInfo)} > @@ -262,18 +255,19 @@ const organizationFunds = (): JSX.Element => { field: 'assocCampaigns', headerName: 'Associated Campaigns', flex: 2, - minWidth: 100, align: 'center', + minWidth: 100, headerAlign: 'center', - headerClassName: `${styles.tableHeader}`, sortable: false, + headerClassName: `${styles.tableHeader}`, renderCell: (params: GridCellParams) => { return ( + +
+
+ + - - -
-
-
- - - - {tCommon('sort')} - - - setSortBy('createdAt_DESC')} - data-testid="createdAt_DESC" - > - {t('createdLatest')} - - setSortBy('createdAt_ASC')} - data-testid="createdAt_ASC" - > - {t('createdEarliest')} - - - -
-
- -
-
+ {t('createdEarliest')} + + + +
+
+
- + + row.fund._id} + getRowId={(row) => row._id} components={{ NoRowsOverlay: () => ( @@ -368,12 +360,11 @@ const organizationFunds = (): JSX.Element => { rowHeight={65} rows={funds.map((fund, index) => ({ id: index + 1, - fund, + ...fund, }))} columns={columns} isRowSelectable={() => false} /> - closeModal(Modal.SAME)} diff --git a/src/screens/OrganizationFunds/OrganizationFundsMocks.ts b/src/screens/OrganizationFunds/OrganizationFundsMocks.ts index cb1e07d78f..6c7c92f75b 100644 --- a/src/screens/OrganizationFunds/OrganizationFundsMocks.ts +++ b/src/screens/OrganizationFunds/OrganizationFundsMocks.ts @@ -10,21 +10,23 @@ export const MOCKS = [ request: { query: FUND_LIST, variables: { - id: undefined, + organizationId: 'orgId', + orderBy: 'createdAt_DESC', + filter: '', }, }, result: { data: { fundsByOrganization: [ { - _id: '1', + _id: 'fundId', name: 'Fund 1', - refrenceNumber: '123', + refrenceNumber: '1111', taxDeductible: true, isArchived: false, isDefault: false, - createdAt: '2021-07-01T00:00:00.000Z', - organizationId: 'organizationId1', + createdAt: '2024-06-22', + organizationId: 'orgId', creator: { _id: 'creatorId1', firstName: 'John', @@ -32,14 +34,14 @@ export const MOCKS = [ }, }, { - _id: '99', - name: 'Funndds', - refrenceNumber: '1234', + _id: 'fundId2', + name: 'Fund 2', + refrenceNumber: '2222', taxDeductible: true, isArchived: true, isDefault: false, - createdAt: '2021-07-01T00:00:00.000Z', - organizationId: 'organizationId1', + createdAt: '2024-06-21', + organizationId: 'orgId', creator: { _id: 'creatorId1', firstName: 'John', @@ -50,108 +52,27 @@ export const MOCKS = [ }, }, }, - { - request: { - query: CREATE_FUND_MUTATION, - variables: { - name: 'Test Fund', - refrenceNumber: '1', - taxDeductible: true, - isArchived: false, - isDefault: false, - organizationId: undefined, - }, - }, - result: { - data: { - createFund: { - _id: '3', - }, - }, - }, - }, - { - request: { - query: UPDATE_FUND_MUTATION, - variables: { - id: '1', - name: 'Test Fund', - refrenceNumber: '1', - taxDeductible: false, - isArchived: true, - isDefault: true, - }, - }, - result: { - data: { - updateFund: { - _id: '1', - }, - }, - }, - }, - { - request: { - query: REMOVE_FUND_MUTATION, - variables: { - id: '1', - }, - }, - result: { - data: { - removeFund: { - _id: '1', - }, - }, - }, - }, -]; -export const NO_FUNDS = [ - { - request: { - query: FUND_LIST, - variables: { - id: undefined, - }, - }, - result: { - data: { - fundsByOrganization: [], - }, - }, - }, -]; -export const MOCKS_ERROR_ORGANIZATIONS_FUNDS = [ - { - request: { - query: FUND_LIST, - variables: { - organizationId: '1', - }, - }, - error: new Error('Mock graphql error'), - }, -]; -export const MOCKS_ERROR_CREATE_FUND = [ { request: { query: FUND_LIST, variables: { - id: undefined, + organizationId: 'orgId', + orderBy: 'createdAt_ASC', + filter: '', }, }, result: { data: { fundsByOrganization: [ { - _id: '1', - name: 'Fund 1', - refrenceNumber: '123', + _id: 'fundId', + name: 'Fund 2', + refrenceNumber: '2222', taxDeductible: true, - isArchived: false, + isArchived: true, isDefault: false, - createdAt: '2021-07-01T00:00:00.000Z', - organizationId: 'organizationId1', + createdAt: '2024-06-21', + organizationId: 'orgId', creator: { _id: 'creatorId1', firstName: 'John', @@ -159,17 +80,17 @@ export const MOCKS_ERROR_CREATE_FUND = [ }, }, { - _id: '2', - name: 'Fund 2', - refrenceNumber: '456', - taxDeductible: false, + _id: 'fundId2', + name: 'Fund 1', + refrenceNumber: '1111', + taxDeductible: true, isArchived: false, isDefault: false, - createdAt: '2021-07-01T00:00:00.000Z', - organizationId: 'organizationId2', + createdAt: '2024-06-22', + organizationId: 'orgId', creator: { - _id: 'creatorId2', - firstName: 'Jane', + _id: 'creatorId1', + firstName: 'John', lastName: 'Doe', }, }, @@ -177,62 +98,33 @@ export const MOCKS_ERROR_CREATE_FUND = [ }, }, }, - { - request: { - query: CREATE_FUND_MUTATION, - variables: { - name: 'Fund 3', - refrenceNumber: '789', - taxDeductible: true, - isArchived: false, - isDefault: false, - organizationId: undefined, - }, - }, - error: new Error('Mock graphql error'), - }, -]; -export const MOCKS_ERROR_UPDATE_FUND = [ { request: { query: FUND_LIST, variables: { - id: undefined, + organizationId: 'orgId', + orderBy: 'createdAt_DESC', + filter: '2', }, }, result: { data: { fundsByOrganization: [ { - _id: '1', - name: 'Fund 1', - refrenceNumber: '123', + _id: 'fundId', + name: 'Fund 2', + refrenceNumber: '2222', taxDeductible: true, - isArchived: false, + isArchived: true, isDefault: false, - createdAt: '2021-07-01T00:00:00.000Z', - organizationId: 'organizationId1', + createdAt: '2024-06-21', + organizationId: 'orgId', creator: { _id: 'creatorId1', firstName: 'John', lastName: 'Doe', }, }, - { - _id: '2', - name: 'Fund 2', - refrenceNumber: '456', - taxDeductible: false, - isArchived: false, - isDefault: false, - createdAt: '2021-07-01T00:00:00.000Z', - organizationId: 'organizationId2', - creator: { - _id: 'creatorId2', - firstName: 'Jane', - lastName: 'Doe', - }, - }, ], }, }, @@ -241,18 +133,18 @@ export const MOCKS_ERROR_UPDATE_FUND = [ request: { query: CREATE_FUND_MUTATION, variables: { - name: 'Fund 3', - refrenceNumber: '789', - taxDeductible: true, + name: 'Fund 2', + refrenceNumber: '2222', + taxDeductible: false, isArchived: false, - isDefault: false, - organizationId: undefined, + isDefault: true, + organizationId: 'orgId', }, }, result: { data: { createFund: { - _id: '3', + _id: '2222', }, }, }, @@ -261,106 +153,100 @@ export const MOCKS_ERROR_UPDATE_FUND = [ request: { query: UPDATE_FUND_MUTATION, variables: { - id: undefined, - name: 'Fund 1', - refrenceNumber: '789', - taxDeductible: true, - isArchived: false, - isDefault: false, + id: 'fundId', + name: 'Fund 2', + refrenceNumber: '2222', + taxDeductible: false, + isArchived: true, + isDefault: true, + }, + }, + result: { + data: { + updateFund: { + _id: 'fundId', + }, }, }, - error: new Error('Mock graphql error'), }, -]; -export const MOCKS_ERROR_REMOVE_FUND = [ { request: { - query: FUND_LIST, + query: REMOVE_FUND_MUTATION, variables: { - id: undefined, + id: 'fundId', }, }, result: { data: { - fundsByOrganization: [ - { - _id: '3', - name: 'Fund 1', - refrenceNumber: '123', - taxDeductible: true, - isArchived: false, - isDefault: false, - createdAt: '2021-07-01T00:00:00.000Z', - organizationId: 'organizationId1', - creator: { - _id: 'creatorId1', - firstName: 'John', - lastName: 'Doe', - }, - }, - { - _id: '2', - name: 'Fund 2', - refrenceNumber: '456', - taxDeductible: false, - isArchived: false, - isDefault: false, - createdAt: '2021-07-01T00:00:00.000Z', - organizationId: 'organizationId2', - creator: { - _id: 'creatorId2', - firstName: 'Jane', - lastName: 'Doe', - }, - }, - ], + removeFund: { + _id: 'fundId', + }, }, }, }, +]; +export const NO_FUNDS = [ { request: { - query: CREATE_FUND_MUTATION, + query: FUND_LIST, variables: { - name: 'Fund 3', - refrenceNumber: '789', - taxDeductible: true, - isArchived: false, - isDefault: false, - organizationId: undefined, + organizationId: 'orgId', + orderBy: 'createdAt_DESC', + filter: '', }, }, result: { data: { - createFund: { - _id: '3', - }, + fundsByOrganization: [], }, }, }, +]; +export const MOCKS_ERROR = [ { request: { - query: UPDATE_FUND_MUTATION, + query: FUND_LIST, variables: { - id: undefined, - name: 'Fund 1', - taxDeductible: true, - isArchived: false, - isDefault: false, + organizationId: 'orgId', + orderBy: 'createdAt_DESC', + filter: '', }, }, - result: { - data: { - updateFund: { - _id: '1', - }, + error: new Error('Mock graphql error'), + }, + { + request: { + query: CREATE_FUND_MUTATION, + variables: { + name: 'Fund 2', + refrenceNumber: '2222', + taxDeductible: false, + isArchived: false, + isDefault: true, + organizationId: 'orgId', }, }, + error: new Error('Mock graphql error'), }, { request: { query: REMOVE_FUND_MUTATION, variables: { - id: undefined, + id: 'fundId', + }, + }, + error: new Error('Mock graphql error'), + }, + { + request: { + query: UPDATE_FUND_MUTATION, + variables: { + id: 'fundId', + name: 'Fund 2', + refrenceNumber: '2222', + taxDeductible: false, + isArchived: true, + isDefault: true, }, }, error: new Error('Mock graphql error'), From 3e96ab002161d20551ed624c2b9b851ba365dcb1 Mon Sep 17 00:00:00 2001 From: Glen Date: Sun, 30 Jun 2024 23:37:29 +0530 Subject: [PATCH 07/17] add translations --- public/locales/fr/translation.json | 3 ++- public/locales/hi/translation.json | 3 ++- public/locales/sp/translation.json | 3 ++- public/locales/zh/translation.json | 3 ++- .../FundCampaignPledge/FundCampaignPledge.tsx | 14 ++++++++++---- 5 files changed, 18 insertions(+), 8 deletions(-) diff --git a/public/locales/fr/translation.json b/public/locales/fr/translation.json index 9dc040a714..2fcd8a197b 100644 --- a/public/locales/fr/translation.json +++ b/public/locales/fr/translation.json @@ -397,7 +397,8 @@ "highestAmount": "Montant le plus élevé", "lowestAmount": "Montant le plus bas", "latestEndDate": "Date de fin la plus récente", - "earliestEndDate": "Date de fin la plus proche" + "earliestEndDate": "Date de fin la plus proche", + "campaigns": "Campagnes" }, "orgPost": { "title": "Des postes", diff --git a/public/locales/hi/translation.json b/public/locales/hi/translation.json index d83f546305..07f6c52203 100644 --- a/public/locales/hi/translation.json +++ b/public/locales/hi/translation.json @@ -397,7 +397,8 @@ "highestAmount": "सबसे अधिक राशि", "lowestAmount": "सबसे कम राशि", "latestEndDate": "नवीनतम समाप्ति तिथि", - "earliestEndDate": "सबसे प्रारंभिक समाप्ति तिथि" + "earliestEndDate": "सबसे प्रारंभिक समाप्ति तिथि", + "campaigns": "अभियान" }, "orgPost": { "title": "पदों", diff --git a/public/locales/sp/translation.json b/public/locales/sp/translation.json index 2e5b1a4922..2d4a0ea2f4 100644 --- a/public/locales/sp/translation.json +++ b/public/locales/sp/translation.json @@ -525,7 +525,8 @@ "highestAmount": "Cantidad más alta", "lowestAmount": "Cantidad más baja", "latestEndDate": "Fecha de finalización más reciente", - "earliestEndDate": "Fecha de finalización más cercana" + "earliestEndDate": "Fecha de finalización más cercana", + "campaigns": "Campañas" }, "orgPost": { diff --git a/public/locales/zh/translation.json b/public/locales/zh/translation.json index c7b1376fb0..83d6d42be9 100644 --- a/public/locales/zh/translation.json +++ b/public/locales/zh/translation.json @@ -397,7 +397,8 @@ "highestAmount": "最高金额", "lowestAmount": "最低金额", "latestEndDate": "最新结束日期", - "earliestEndDate": "最早结束日期" + "earliestEndDate": "最早结束日期", + "campaigns": "活动" }, "orgPost": { "title": "帖子", diff --git a/src/screens/FundCampaignPledge/FundCampaignPledge.tsx b/src/screens/FundCampaignPledge/FundCampaignPledge.tsx index 97f3735f1c..0d297c5898 100644 --- a/src/screens/FundCampaignPledge/FundCampaignPledge.tsx +++ b/src/screens/FundCampaignPledge/FundCampaignPledge.tsx @@ -299,17 +299,23 @@ const fundCampaignPledge = (): JSX.Element => { underline="hover" color="inherit" component="button" - onClick={() => history.go(-2)} + onClick={ + /* istanbul ignore next */ + () => history.go(-2) + } > - Funds + {tCommon('Funds')} history.back()} + onClick={ + /* istanbul ignore next */ + () => history.back() + } > - Campaigns + {t('campaigns')} Fund Campaign Pledges From 3530262edc9e3403388a1568a3335f205ffa42be Mon Sep 17 00:00:00 2001 From: Glen Date: Sun, 30 Jun 2024 23:49:44 +0530 Subject: [PATCH 08/17] cleanup --- .../FundCampaignPledge/PledgeDeleteModal.test.tsx | 9 +-------- .../OrganizationFundCampagins.tsx | 8 ++++---- .../OrganizationFundCampaign.test.tsx | 12 +----------- .../OrganizationFundCampaignMocks.ts | 1 + .../OrganizationFunds/FundDeleteModal.test.tsx | 8 -------- src/screens/OrganizationFunds/FundModal.test.tsx | 8 -------- .../OrganizationFunds/OrganizationFunds.test.tsx | 2 -- .../OrganizationFunds/OrganizationFundsMocks.ts | 2 ++ 8 files changed, 9 insertions(+), 41 deletions(-) diff --git a/src/screens/FundCampaignPledge/PledgeDeleteModal.test.tsx b/src/screens/FundCampaignPledge/PledgeDeleteModal.test.tsx index c9d5c5f50e..dbaae76504 100644 --- a/src/screens/FundCampaignPledge/PledgeDeleteModal.test.tsx +++ b/src/screens/FundCampaignPledge/PledgeDeleteModal.test.tsx @@ -23,14 +23,6 @@ jest.mock('react-toastify', () => ({ }, })); -jest.mock('@mui/x-date-pickers/DateTimePicker', () => { - return { - DateTimePicker: jest.requireActual( - '@mui/x-date-pickers/DesktopDateTimePicker', - ).DesktopDateTimePicker, - }; -}); - const link = new StaticMockLink(MOCKS); const link2 = new StaticMockLink(MOCKS_DELETE_PLEDGE_ERROR); const translations = JSON.parse( @@ -57,6 +49,7 @@ const pledgeProps: InterfaceDeletePledgeModal = { }, refetchPledge: jest.fn(), }; + const renderPledgeDeleteModal = ( link: ApolloLink, props: InterfaceDeletePledgeModal, diff --git a/src/screens/OrganizationFundCampaign/OrganizationFundCampagins.tsx b/src/screens/OrganizationFundCampaign/OrganizationFundCampagins.tsx index e84cbfe368..37009e7259 100644 --- a/src/screens/OrganizationFundCampaign/OrganizationFundCampagins.tsx +++ b/src/screens/OrganizationFundCampaign/OrganizationFundCampagins.tsx @@ -1,10 +1,9 @@ -/*eslint-disable*/ import { useQuery } from '@apollo/client'; import { Search, Sort, WarningAmberRounded } from '@mui/icons-material'; import { FUND_CAMPAIGN } from 'GraphQl/Queries/fundQueries'; import Loader from 'components/Loader/Loader'; import dayjs from 'dayjs'; -import { useCallback, useMemo, useState } from 'react'; +import React, { useCallback, useMemo, useState } from 'react'; import { Button, Dropdown, Form } from 'react-bootstrap'; import { useTranslation } from 'react-i18next'; import { Navigate, useNavigate, useParams } from 'react-router-dom'; @@ -15,7 +14,8 @@ import type { import CampaignModal from './CampaignModal'; import CampaignDeleteModal from './CampaignDeleteModal'; import styles from './OrganizationFundCampaign.module.css'; -import { DataGrid, GridCellParams, GridColDef } from '@mui/x-data-grid'; +import type { GridCellParams, GridColDef } from '@mui/x-data-grid'; +import { DataGrid } from '@mui/x-data-grid'; import { currencySymbols } from 'utils/currency'; import { Stack, Typography, Breadcrumbs, Link } from '@mui/material'; @@ -114,7 +114,7 @@ const orgFundCampaign = (): JSX.Element => { }, }); - const handleClick = (campaignId: String) => { + const handleClick = (campaignId: string): void => { navigate(`/fundCampaignPledge/${orgId}/${campaignId}`); }; diff --git a/src/screens/OrganizationFundCampaign/OrganizationFundCampaign.test.tsx b/src/screens/OrganizationFundCampaign/OrganizationFundCampaign.test.tsx index b2e996c2d2..74f646f51a 100644 --- a/src/screens/OrganizationFundCampaign/OrganizationFundCampaign.test.tsx +++ b/src/screens/OrganizationFundCampaign/OrganizationFundCampaign.test.tsx @@ -9,14 +9,11 @@ import { render, screen, waitFor, - waitForElementToBeRemoved, } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -import { act } from 'react-dom/test-utils'; import { I18nextProvider } from 'react-i18next'; import { Provider } from 'react-redux'; -import { BrowserRouter, MemoryRouter, Route, Routes } from 'react-router-dom'; -import { toast } from 'react-toastify'; +import { MemoryRouter, Route, Routes } from 'react-router-dom'; import { store } from '../../state/store'; import { StaticMockLink } from '../../utils/StaticMockLink'; import i18nForTest from '../../utils/i18nForTest'; @@ -43,13 +40,6 @@ jest.mock('@mui/x-date-pickers/DateTimePicker', () => { }; }); -async function wait(ms = 100): Promise { - await act(() => { - return new Promise((resolve) => { - setTimeout(resolve, ms); - }); - }); -} const link1 = new StaticMockLink(MOCKS, true); const link2 = new StaticMockLink(MOCK_ERROR, true); const link3 = new StaticMockLink(EMPTY_MOCKS, true); diff --git a/src/screens/OrganizationFundCampaign/OrganizationFundCampaignMocks.ts b/src/screens/OrganizationFundCampaign/OrganizationFundCampaignMocks.ts index 64ac21340f..160dfad5fb 100644 --- a/src/screens/OrganizationFundCampaign/OrganizationFundCampaignMocks.ts +++ b/src/screens/OrganizationFundCampaign/OrganizationFundCampaignMocks.ts @@ -307,6 +307,7 @@ export const MOCK_ERROR = [ error: new Error('Mock graphql error'), }, ]; + export const EMPTY_MOCKS = [ { request: { diff --git a/src/screens/OrganizationFunds/FundDeleteModal.test.tsx b/src/screens/OrganizationFunds/FundDeleteModal.test.tsx index 7f0e75cbeb..41fcd3ec5e 100644 --- a/src/screens/OrganizationFunds/FundDeleteModal.test.tsx +++ b/src/screens/OrganizationFunds/FundDeleteModal.test.tsx @@ -23,14 +23,6 @@ jest.mock('react-toastify', () => ({ }, })); -jest.mock('@mui/x-date-pickers/DateTimePicker', () => { - return { - DateTimePicker: jest.requireActual( - '@mui/x-date-pickers/DesktopDateTimePicker', - ).DesktopDateTimePicker, - }; -}); - const link1 = new StaticMockLink(MOCKS); const link2 = new StaticMockLink(MOCKS_ERROR); const translations = JSON.parse( diff --git a/src/screens/OrganizationFunds/FundModal.test.tsx b/src/screens/OrganizationFunds/FundModal.test.tsx index 778ff55d17..c74b0434c3 100644 --- a/src/screens/OrganizationFunds/FundModal.test.tsx +++ b/src/screens/OrganizationFunds/FundModal.test.tsx @@ -29,14 +29,6 @@ jest.mock('react-toastify', () => ({ }, })); -jest.mock('@mui/x-date-pickers/DateTimePicker', () => { - return { - DateTimePicker: jest.requireActual( - '@mui/x-date-pickers/DesktopDateTimePicker', - ).DesktopDateTimePicker, - }; -}); - const link1 = new StaticMockLink(MOCKS); const link2 = new StaticMockLink(MOCKS_ERROR); const translations = JSON.parse( diff --git a/src/screens/OrganizationFunds/OrganizationFunds.test.tsx b/src/screens/OrganizationFunds/OrganizationFunds.test.tsx index 1933d286fa..a1b49e68eb 100644 --- a/src/screens/OrganizationFunds/OrganizationFunds.test.tsx +++ b/src/screens/OrganizationFunds/OrganizationFunds.test.tsx @@ -6,7 +6,6 @@ import { fireEvent, render, screen, - wait, waitFor, } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; @@ -21,7 +20,6 @@ import { MOCKS, MOCKS_ERROR, NO_FUNDS } from './OrganizationFundsMocks'; import type { ApolloLink } from '@apollo/client'; import { LocalizationProvider } from '@mui/x-date-pickers'; import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; -import { createMemoryHistory } from 'history'; jest.mock('react-toastify', () => ({ toast: { diff --git a/src/screens/OrganizationFunds/OrganizationFundsMocks.ts b/src/screens/OrganizationFunds/OrganizationFundsMocks.ts index 6c7c92f75b..cd03dc7f31 100644 --- a/src/screens/OrganizationFunds/OrganizationFundsMocks.ts +++ b/src/screens/OrganizationFunds/OrganizationFundsMocks.ts @@ -185,6 +185,7 @@ export const MOCKS = [ }, }, ]; + export const NO_FUNDS = [ { request: { @@ -202,6 +203,7 @@ export const NO_FUNDS = [ }, }, ]; + export const MOCKS_ERROR = [ { request: { From 0e409f403dec70138aa2c1260303e215c5dd9982 Mon Sep 17 00:00:00 2001 From: Glen Date: Mon, 1 Jul 2024 00:14:44 +0530 Subject: [PATCH 09/17] missing translation --- public/locales/en/translation.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index 4281d76232..1e41714ed5 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -391,7 +391,8 @@ "highestAmount": "Highest Amount", "lowestAmount": "Lowest Amount", "latestEndDate": "Latest End Date", - "earliestEndDate": "Earliest End Date" + "earliestEndDate": "Earliest End Date", + "campaigns": "Campaigns" }, "orgPost": { "title": "Posts", From 48e121238101ae4e411562feae9e70afe4bffcd1 Mon Sep 17 00:00:00 2001 From: Glen Date: Mon, 1 Jul 2024 02:15:43 +0530 Subject: [PATCH 10/17] Add progress indicator in pledges screen --- public/locales/en/translation.json | 5 +- public/locales/fr/translation.json | 5 +- public/locales/hi/translation.json | 5 +- public/locales/sp/translation.json | 5 +- public/locales/zh/translation.json | 5 +- src/GraphQl/Queries/fundQueries.ts | 3 + .../FundCampaignPledge.module.css | 76 ++++++++++++- .../FundCampaignPledge/FundCampaignPledge.tsx | 103 +++++++++++++++++- .../FundCampaignPledge/PledgesMocks.ts | 15 +++ .../OrganizationFundCampagins.tsx | 1 + .../OrganizationFundCampaign.module.css | 6 +- src/utils/interfaces.ts | 3 + 12 files changed, 219 insertions(+), 13 deletions(-) diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index 1e41714ed5..d006fd3140 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -392,7 +392,10 @@ "lowestAmount": "Lowest Amount", "latestEndDate": "Latest End Date", "earliestEndDate": "Earliest End Date", - "campaigns": "Campaigns" + "campaigns": "Campaigns", + "pledges": "Pledges", + "endsOn": "Ends on", + "raised": "Raised" }, "orgPost": { "title": "Posts", diff --git a/public/locales/fr/translation.json b/public/locales/fr/translation.json index 2fcd8a197b..57009be21f 100644 --- a/public/locales/fr/translation.json +++ b/public/locales/fr/translation.json @@ -398,7 +398,10 @@ "lowestAmount": "Montant le plus bas", "latestEndDate": "Date de fin la plus récente", "earliestEndDate": "Date de fin la plus proche", - "campaigns": "Campagnes" + "campaigns": "Campagnes", + "pledges": "Promesses de dons", + "endsOn": "Se termine le", + "raised": "Levé" }, "orgPost": { "title": "Des postes", diff --git a/public/locales/hi/translation.json b/public/locales/hi/translation.json index 07f6c52203..4857816985 100644 --- a/public/locales/hi/translation.json +++ b/public/locales/hi/translation.json @@ -398,7 +398,10 @@ "lowestAmount": "सबसे कम राशि", "latestEndDate": "नवीनतम समाप्ति तिथि", "earliestEndDate": "सबसे प्रारंभिक समाप्ति तिथि", - "campaigns": "अभियान" + "campaigns": "अभियान", + "pledges": "प्रतिज्ञाएँ", + "endsOn": "पर समाप्त होता है", + "raised": "उठाया गया" }, "orgPost": { "title": "पदों", diff --git a/public/locales/sp/translation.json b/public/locales/sp/translation.json index 2d4a0ea2f4..c289588a5d 100644 --- a/public/locales/sp/translation.json +++ b/public/locales/sp/translation.json @@ -526,7 +526,10 @@ "lowestAmount": "Cantidad más baja", "latestEndDate": "Fecha de finalización más reciente", "earliestEndDate": "Fecha de finalización más cercana", - "campaigns": "Campañas" + "campaigns": "Campañas", + "pledges": "Compromisos", + "endsOn": "Finaliza el", + "raised": "Recaudado" }, "orgPost": { diff --git a/public/locales/zh/translation.json b/public/locales/zh/translation.json index 83d6d42be9..fd2fd235c5 100644 --- a/public/locales/zh/translation.json +++ b/public/locales/zh/translation.json @@ -398,7 +398,10 @@ "lowestAmount": "最低金额", "latestEndDate": "最新结束日期", "earliestEndDate": "最早结束日期", - "campaigns": "活动" + "campaigns": "活动", + "pledges": "承诺", + "endsOn": "结束于", + "raised": "筹集到" }, "orgPost": { "title": "帖子", diff --git a/src/GraphQl/Queries/fundQueries.ts b/src/GraphQl/Queries/fundQueries.ts index bed8d027ff..daec99a8c9 100644 --- a/src/GraphQl/Queries/fundQueries.ts +++ b/src/GraphQl/Queries/fundQueries.ts @@ -58,6 +58,9 @@ export const FUND_CAMPAIGN = gql` export const FUND_CAMPAIGN_PLEDGE = gql` query GetFundraisingCampaignById($id: ID!, $orderBy: PledgeOrderByInput) { getFundraisingCampaignById(id: $id, orderBy: $orderBy) { + name + fundingGoal + currency startDate endDate pledges { diff --git a/src/screens/FundCampaignPledge/FundCampaignPledge.module.css b/src/screens/FundCampaignPledge/FundCampaignPledge.module.css index 51ce5a3ea5..646fbb54c3 100644 --- a/src/screens/FundCampaignPledge/FundCampaignPledge.module.css +++ b/src/screens/FundCampaignPledge/FundCampaignPledge.module.css @@ -58,7 +58,8 @@ .btnsContainer { display: flex; - margin: 2rem 0 2.5rem 0; + gap: 0.8rem; + margin: 2.2rem 0 0.8rem 0; } .btnsContainer .input { @@ -125,3 +126,76 @@ .noOutline input { outline: none; } + +.overviewContainer { + display: flex; + gap: 7rem; + width: 100%; + justify-content: space-between; + margin: 1.5rem 0 0 0; + padding: 1.25rem 2rem; + background-color: rgba(255, 255, 255, 0.591); + + box-shadow: rgba(0, 0, 0, 0.16) 0px 1px 4px; + border-radius: 0.5rem; +} + +.titleContainer { + display: flex; + flex-direction: column; + gap: 0.6rem; +} + +.titleContainer h3 { + font-size: 1.75rem; + font-weight: 750; + color: #5e5e5e; + margin-top: 0.2rem; +} + +.titleContainer span { + font-size: 0.9rem; + margin-left: 0.5rem; + font-weight: lighter; + color: #707070; +} + +.raisedAmount { + display: flex; + justify-content: center; + align-items: center; + font-size: 1.25rem; + font-weight: 750; + color: #5e5e5e; +} + +.progressContainer { + display: flex; + flex-direction: column; + gap: 0.5rem; + flex-grow: 1; +} + +.progress { + margin-top: 0.2rem; + display: flex; + flex-direction: column; + gap: 0.3rem; +} + +.endpoints { + display: flex; + position: relative; + font-size: 0.85rem; +} + +.start { + position: absolute; + top: 0px; +} + +.end { + position: absolute; + top: 0px; + right: 0px; +} diff --git a/src/screens/FundCampaignPledge/FundCampaignPledge.tsx b/src/screens/FundCampaignPledge/FundCampaignPledge.tsx index 0d297c5898..2c1ad0045d 100644 --- a/src/screens/FundCampaignPledge/FundCampaignPledge.tsx +++ b/src/screens/FundCampaignPledge/FundCampaignPledge.tsx @@ -11,7 +11,14 @@ import { currencySymbols } from 'utils/currency'; import styles from './FundCampaignPledge.module.css'; import PledgeDeleteModal from './PledgeDeleteModal'; import PledgeModal from './PledgeModal'; -import { Breadcrumbs, Link, Stack, Typography } from '@mui/material'; +import { + Breadcrumbs, + LinearProgress, + Link, + Stack, + Typography, + linearProgressClasses, +} from '@mui/material'; import { DataGrid } from '@mui/x-data-grid'; import Avatar from 'components/Avatar/Avatar'; import type { GridCellParams, GridColDef } from '@mui/x-data-grid'; @@ -20,6 +27,15 @@ import type { InterfacePledgeVolunteer, InterfaceQueryFundCampaignsPledges, } from 'utils/interfaces'; +import { styled } from '@mui/system'; + +interface InterfaceCampaignInfo { + name: string; + goal: number; + startDate: Date; + endDate: Date; + currency: string; +} enum Modal { SAME = 'same', @@ -47,6 +63,18 @@ const dataGridStyle = { }, }; +const BorderLinearProgress = styled(LinearProgress)(() => ({ + height: 15, + borderRadius: 4, + [`&.${linearProgressClasses.colorPrimary}`]: { + backgroundColor: '#e0e0e0', + }, + [`& .${linearProgressClasses.bar}`]: { + borderRadius: 5, + backgroundColor: '#31bb6b', + }, +})); + const fundCampaignPledge = (): JSX.Element => { const { t } = useTranslation('translation', { keyPrefix: 'pledges', @@ -58,6 +86,14 @@ const fundCampaignPledge = (): JSX.Element => { return ; } + const [campaignInfo, setCampaignInfo] = useState({ + name: '', + goal: 0, + startDate: new Date(), + endDate: new Date(), + currency: '', + }); + const [modalState, setModalState] = useState<{ [key in Modal]: boolean }>({ [Modal.SAME]: false, [Modal.DELETE]: false, @@ -113,6 +149,18 @@ const fundCampaignPledge = (): JSX.Element => { ); }, [pledgeData, searchTerm]); + useEffect(() => { + if (pledgeData) { + setCampaignInfo({ + name: pledgeData.getFundraisingCampaignById.name, + goal: pledgeData.getFundraisingCampaignById.fundingGoal, + startDate: pledgeData.getFundraisingCampaignById.startDate, + endDate: pledgeData.getFundraisingCampaignById.endDate, + currency: pledgeData.getFundraisingCampaignById.currency, + }); + } + }, [pledgeData]); + useEffect(() => { refetchPledge(); }, [sortBy, refetchPledge]); @@ -228,7 +276,7 @@ const fundCampaignPledge = (): JSX.Element => { }, { field: 'amount', - headerName: 'Pledge Amount', + headerName: 'Pledged', flex: 1, minWidth: 100, align: 'center', @@ -251,6 +299,31 @@ const fundCampaignPledge = (): JSX.Element => { ); }, }, + { + field: 'paid', + headerName: 'Paid', + flex: 1, + minWidth: 100, + align: 'center', + headerAlign: 'center', + headerClassName: `${styles.tableHeader}`, + sortable: false, + renderCell: (params: GridCellParams) => { + return ( +
+ { + currencySymbols[ + params.row.currency as keyof typeof currencySymbols + ] + } + 0 +
+ ); + }, + }, { field: 'action', headerName: 'Action', @@ -294,7 +367,7 @@ const fundCampaignPledge = (): JSX.Element => { return (
- + { > {t('campaigns')} - Fund Campaign Pledges + {t('pledges')} + +
+
+

{campaignInfo?.name}

+ + {t('endsOn')} {campaignInfo?.endDate.toString()} + +
+
+
$1,000 {t('raised')}
+
+ +
+
$0
+
${campaignInfo?.goal}
+
+
+
+
+
{ row._id} components={{ diff --git a/src/screens/FundCampaignPledge/PledgesMocks.ts b/src/screens/FundCampaignPledge/PledgesMocks.ts index cf07d1cf28..5a913ff3d6 100644 --- a/src/screens/FundCampaignPledge/PledgesMocks.ts +++ b/src/screens/FundCampaignPledge/PledgesMocks.ts @@ -18,6 +18,9 @@ export const MOCKS = [ result: { data: { getFundraisingCampaignById: { + name: 'Campaign Name', + fundingGoal: 1000, + currency: 'USD', startDate: '2024-01-01', endDate: '2024-08-08', pledges: [ @@ -67,6 +70,9 @@ export const MOCKS = [ result: { data: { getFundraisingCampaignById: { + name: 'Campaign Name', + fundingGoal: 1000, + currency: 'USD', startDate: '2024-01-01', endDate: '2024-08-08', pledges: [ @@ -116,6 +122,9 @@ export const MOCKS = [ result: { data: { getFundraisingCampaignById: { + name: 'Campaign Name', + fundingGoal: 1000, + currency: 'USD', startDate: '2024-01-01', endDate: '2024-08-08', pledges: [ @@ -165,6 +174,9 @@ export const MOCKS = [ result: { data: { getFundraisingCampaignById: { + name: 'Campaign Name', + fundingGoal: 1000, + currency: 'USD', startDate: '2024-01-01', endDate: '2024-08-08', pledges: [ @@ -422,6 +434,9 @@ export const EMPTY_MOCKS = [ result: { data: { getFundraisingCampaignById: { + name: 'Campaign Name', + fundingGoal: 1000, + currency: 'USD', startDate: '2024-01-01', endDate: '2024-01-01', pledges: [], diff --git a/src/screens/OrganizationFundCampaign/OrganizationFundCampagins.tsx b/src/screens/OrganizationFundCampaign/OrganizationFundCampagins.tsx index 37009e7259..86a9d7d290 100644 --- a/src/screens/OrganizationFundCampaign/OrganizationFundCampagins.tsx +++ b/src/screens/OrganizationFundCampaign/OrganizationFundCampagins.tsx @@ -313,6 +313,7 @@ const orgFundCampaign = (): JSX.Element => { FundRaising Campaign +
Date: Mon, 1 Jul 2024 03:23:30 +0530 Subject: [PATCH 11/17] incorp coderab changes --- .../FundCampaignPledge/FundCampaignPledge.tsx | 30 ++++++----- .../OrganizationFundCampagins.tsx | 53 ++++++++++--------- .../OrganizationFunds/OrganizationFunds.tsx | 46 ++++++++-------- 3 files changed, 69 insertions(+), 60 deletions(-) diff --git a/src/screens/FundCampaignPledge/FundCampaignPledge.tsx b/src/screens/FundCampaignPledge/FundCampaignPledge.tsx index 2c1ad0045d..ea5b329d34 100644 --- a/src/screens/FundCampaignPledge/FundCampaignPledge.tsx +++ b/src/screens/FundCampaignPledge/FundCampaignPledge.tsx @@ -37,7 +37,7 @@ interface InterfaceCampaignInfo { currency: string; } -enum Modal { +enum ModalState { SAME = 'same', DELETE = 'delete', } @@ -94,9 +94,11 @@ const fundCampaignPledge = (): JSX.Element => { currency: '', }); - const [modalState, setModalState] = useState<{ [key in Modal]: boolean }>({ - [Modal.SAME]: false, - [Modal.DELETE]: false, + const [modalState, setModalState] = useState<{ + [key in ModalState]: boolean; + }>({ + [ModalState.SAME]: false, + [ModalState.DELETE]: false, }); const [pledgeModalMode, setPledgeModalMode] = useState<'edit' | 'create'>( @@ -165,11 +167,11 @@ const fundCampaignPledge = (): JSX.Element => { refetchPledge(); }, [sortBy, refetchPledge]); - const openModal = (modal: Modal): void => { + const openModal = (modal: ModalState): void => { setModalState((prevState) => ({ ...prevState, [modal]: true })); }; - const closeModal = (modal: Modal): void => { + const closeModal = (modal: ModalState): void => { setModalState((prevState) => ({ ...prevState, [modal]: false })); }; @@ -177,7 +179,7 @@ const fundCampaignPledge = (): JSX.Element => { (pledge: InterfacePledgeInfo | null, mode: 'edit' | 'create'): void => { setPledge(pledge); setPledgeModalMode(mode); - openModal(Modal.SAME); + openModal(ModalState.SAME); }, [openModal], ); @@ -185,7 +187,7 @@ const fundCampaignPledge = (): JSX.Element => { const handleDeleteClick = useCallback( (pledge: InterfacePledgeInfo): void => { setPledge(pledge); - openModal(Modal.DELETE); + openModal(ModalState.DELETE); }, [openModal], ); @@ -515,10 +517,10 @@ const fundCampaignPledge = (): JSX.Element => { isRowSelectable={() => false} /> - {/* Update Pledge Modal */} + {/* Update Pledge ModalState */} closeModal(Modal.SAME)} + isOpen={modalState[ModalState.SAME]} + hide={() => closeModal(ModalState.SAME)} campaignId={fundCampaignId} orgId={orgId} pledge={pledge} @@ -527,10 +529,10 @@ const fundCampaignPledge = (): JSX.Element => { mode={pledgeModalMode} /> - {/* Delete Pledge Modal */} + {/* Delete Pledge ModalState */} closeModal(Modal.DELETE)} + isOpen={modalState[ModalState.DELETE]} + hide={() => closeModal(ModalState.DELETE)} pledge={pledge} refetchPledge={refetchPledge} /> diff --git a/src/screens/OrganizationFundCampaign/OrganizationFundCampagins.tsx b/src/screens/OrganizationFundCampaign/OrganizationFundCampagins.tsx index 86a9d7d290..5b615392c4 100644 --- a/src/screens/OrganizationFundCampaign/OrganizationFundCampagins.tsx +++ b/src/screens/OrganizationFundCampaign/OrganizationFundCampagins.tsx @@ -1,23 +1,26 @@ import { useQuery } from '@apollo/client'; import { Search, Sort, WarningAmberRounded } from '@mui/icons-material'; -import { FUND_CAMPAIGN } from 'GraphQl/Queries/fundQueries'; -import Loader from 'components/Loader/Loader'; -import dayjs from 'dayjs'; -import React, { useCallback, useMemo, useState } from 'react'; +import { Stack, Typography, Breadcrumbs, Link } from '@mui/material'; +import { + DataGrid, + type GridCellParams, + type GridColDef, +} from '@mui/x-data-grid'; import { Button, Dropdown, Form } from 'react-bootstrap'; import { useTranslation } from 'react-i18next'; import { Navigate, useNavigate, useParams } from 'react-router-dom'; -import type { - InterfaceCampaignInfo, - InterfaceQueryOrganizationFundCampaigns, -} from 'utils/interfaces'; +import React, { useCallback, useMemo, useState } from 'react'; +import dayjs from 'dayjs'; +import Loader from 'components/Loader/Loader'; import CampaignModal from './CampaignModal'; import CampaignDeleteModal from './CampaignDeleteModal'; +import { FUND_CAMPAIGN } from 'GraphQl/Queries/fundQueries'; import styles from './OrganizationFundCampaign.module.css'; -import type { GridCellParams, GridColDef } from '@mui/x-data-grid'; -import { DataGrid } from '@mui/x-data-grid'; import { currencySymbols } from 'utils/currency'; -import { Stack, Typography, Breadcrumbs, Link } from '@mui/material'; +import type { + InterfaceCampaignInfo, + InterfaceQueryOrganizationFundCampaigns, +} from 'utils/interfaces'; const dataGridStyle = { '&.MuiDataGrid-root .MuiDataGrid-cell:focus-within': { @@ -40,7 +43,7 @@ const dataGridStyle = { }, }; -enum Modal { +enum ModalState { SAME = 'same', DELETE = 'delete', } @@ -62,24 +65,26 @@ const orgFundCampaign = (): JSX.Element => { const [searchTerm, setSearchTerm] = useState(''); const [sortBy, setSortBy] = useState(null); - const [modalState, setModalState] = useState<{ [key in Modal]: boolean }>({ - [Modal.SAME]: false, - [Modal.DELETE]: false, + const [modalState, setModalState] = useState<{ + [key in ModalState]: boolean; + }>({ + [ModalState.SAME]: false, + [ModalState.DELETE]: false, }); const [campaignModalMode, setCampaignModalMode] = useState<'edit' | 'create'>( 'create', ); - const openModal = (modal: Modal): void => + const openModal = (modal: ModalState): void => setModalState((prevState) => ({ ...prevState, [modal]: true })); - const closeModal = (modal: Modal): void => + const closeModal = (modal: ModalState): void => setModalState((prevState) => ({ ...prevState, [modal]: false })); const handleOpenModal = useCallback( (campaign: InterfaceCampaignInfo | null, mode: 'edit' | 'create'): void => { setCampaign(campaign); setCampaignModalMode(mode); - openModal(Modal.SAME); + openModal(ModalState.SAME); }, [openModal], ); @@ -87,7 +92,7 @@ const orgFundCampaign = (): JSX.Element => { const handleDeleteClick = useCallback( (campaign: InterfaceCampaignInfo): void => { setCampaign(campaign); - openModal(Modal.DELETE); + openModal(ModalState.DELETE); }, [openModal], ); @@ -411,10 +416,10 @@ const orgFundCampaign = (): JSX.Element => { isRowSelectable={() => false} /> - {/* Create Campaign Modal */} + {/* Create Campaign ModalState */} closeModal(Modal.SAME)} + isOpen={modalState[ModalState.SAME]} + hide={() => closeModal(ModalState.SAME)} refetchCampaign={refetchCampaign} fundId={fundId} campaign={campaign} @@ -422,8 +427,8 @@ const orgFundCampaign = (): JSX.Element => { /> closeModal(Modal.DELETE)} + isOpen={modalState[ModalState.DELETE]} + hide={() => closeModal(ModalState.DELETE)} campaign={campaign} refetchCampaign={refetchCampaign} /> diff --git a/src/screens/OrganizationFunds/OrganizationFunds.tsx b/src/screens/OrganizationFunds/OrganizationFunds.tsx index 922a6e4568..11a5f09d55 100644 --- a/src/screens/OrganizationFunds/OrganizationFunds.tsx +++ b/src/screens/OrganizationFunds/OrganizationFunds.tsx @@ -1,22 +1,22 @@ import { useQuery } from '@apollo/client'; import { Search, Sort, WarningAmberRounded } from '@mui/icons-material'; -import Loader from 'components/Loader/Loader'; -import React, { useCallback, useMemo, useState } from 'react'; -import { Button, Dropdown, Form } from 'react-bootstrap'; -import { useTranslation } from 'react-i18next'; -import { Navigate, useNavigate, useParams } from 'react-router-dom'; -import type { InterfaceFundInfo } from 'utils/interfaces'; -import FundModal from './FundModal'; -import styles from './OrganizationFunds.module.css'; import { Stack } from '@mui/material'; -import dayjs from 'dayjs'; -import { FUND_LIST } from 'GraphQl/Queries/fundQueries'; import { DataGrid, type GridCellParams, type GridColDef, } from '@mui/x-data-grid'; +import { Button, Dropdown, Form } from 'react-bootstrap'; +import { useTranslation } from 'react-i18next'; +import { Navigate, useNavigate, useParams } from 'react-router-dom'; +import React, { useCallback, useMemo, useState } from 'react'; +import dayjs from 'dayjs'; +import Loader from 'components/Loader/Loader'; +import FundModal from './FundModal'; import FundDeleteModal from './FundDeleteModal'; +import { FUND_LIST } from 'GraphQl/Queries/fundQueries'; +import styles from './OrganizationFunds.module.css'; +import type { InterfaceFundInfo } from 'utils/interfaces'; const dataGridStyle = { '&.MuiDataGrid-root .MuiDataGrid-cell:focus-within': { @@ -39,7 +39,7 @@ const dataGridStyle = { }, }; -enum Modal { +enum ModalState { SAME = 'same', DELETE = 'delete', } @@ -63,24 +63,26 @@ const organizationFunds = (): JSX.Element => { 'createdAt_DESC', ); - const [modalState, setModalState] = useState<{ [key in Modal]: boolean }>({ - [Modal.SAME]: false, - [Modal.DELETE]: false, + const [modalState, setModalState] = useState<{ + [key in ModalState]: boolean; + }>({ + [ModalState.SAME]: false, + [ModalState.DELETE]: false, }); const [fundModalMode, setFundModalMode] = useState<'edit' | 'create'>( 'create', ); - const openModal = (modal: Modal): void => + const openModal = (modal: ModalState): void => setModalState((prevState) => ({ ...prevState, [modal]: true })); - const closeModal = (modal: Modal): void => + const closeModal = (modal: ModalState): void => setModalState((prevState) => ({ ...prevState, [modal]: false })); const handleOpenModal = useCallback( (fund: InterfaceFundInfo | null, mode: 'edit' | 'create'): void => { setFund(fund); setFundModalMode(mode); - openModal(Modal.SAME); + openModal(ModalState.SAME); }, [openModal], ); @@ -108,7 +110,7 @@ const organizationFunds = (): JSX.Element => { const handleDeleteClick = useCallback( (fund: InterfaceFundInfo): void => { setFund(fund); - openModal(Modal.DELETE); + openModal(ModalState.DELETE); }, [openModal], ); @@ -366,8 +368,8 @@ const organizationFunds = (): JSX.Element => { isRowSelectable={() => false} /> closeModal(Modal.SAME)} + isOpen={modalState[ModalState.SAME]} + hide={() => closeModal(ModalState.SAME)} refetchFunds={refetchFunds} fund={fund} orgId={orgId} @@ -375,8 +377,8 @@ const organizationFunds = (): JSX.Element => { /> closeModal(Modal.DELETE)} + isOpen={modalState[ModalState.DELETE]} + hide={() => closeModal(ModalState.DELETE)} fund={fund} refetchFunds={refetchFunds} /> From 707e81cd09df987ca308fa8074139f595e1d2973 Mon Sep 17 00:00:00 2001 From: Glen Date: Fri, 5 Jul 2024 20:57:48 +0530 Subject: [PATCH 12/17] Added popup to view all volunteers --- .../FundCampaignPledge.module.css | 32 ++++++++ .../FundCampaignPledge.test.tsx | 34 ++++++++- .../FundCampaignPledge/FundCampaignPledge.tsx | 76 ++++++++++++++++--- .../FundCampaignPledge/PledgesMocks.ts | 54 +++++++++++++ 4 files changed, 185 insertions(+), 11 deletions(-) diff --git a/src/screens/FundCampaignPledge/FundCampaignPledge.module.css b/src/screens/FundCampaignPledge/FundCampaignPledge.module.css index 646fbb54c3..27d3149b71 100644 --- a/src/screens/FundCampaignPledge/FundCampaignPledge.module.css +++ b/src/screens/FundCampaignPledge/FundCampaignPledge.module.css @@ -199,3 +199,35 @@ top: 0px; right: 0px; } + +.moreContainer { + display: flex; + align-items: center; +} + +.moreContainer:hover { + text-decoration: underline; + cursor: pointer; +} + +.popup { + z-index: 50; + border-radius: 0.5rem; + font-family: sans-serif; + font-weight: 500; + font-size: 0.875rem; + margin-top: 0.5rem; + padding: 0.75rem; + border: 1px solid #e2e8f0; + background-color: white; + color: #1e293b; + box-shadow: 0 0.5rem 1rem rgb(0 0 0 / 0.15); + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.popupExtra { + max-height: 15rem; + overflow-y: auto; +} diff --git a/src/screens/FundCampaignPledge/FundCampaignPledge.test.tsx b/src/screens/FundCampaignPledge/FundCampaignPledge.test.tsx index 7c61a1b48a..0fbea5ad37 100644 --- a/src/screens/FundCampaignPledge/FundCampaignPledge.test.tsx +++ b/src/screens/FundCampaignPledge/FundCampaignPledge.test.tsx @@ -279,8 +279,40 @@ describe('Testing Campaign Pledge Screen', () => { expect(screen.getByTestId('searchVolunteer')).toBeInTheDocument(); }); - const image = await screen.findByAltText('volunteer'); + const image = await screen.findByTestId('image1'); expect(image).toBeInTheDocument(); expect(image).toHaveAttribute('src', 'img-url'); }); + + it('should render extraUserDetails in Popup', async () => { + renderFundCampaignPledge(link1); + await waitFor(() => { + expect(screen.getByTestId('searchVolunteer')).toBeInTheDocument(); + }); + + expect(screen.getByText('John Doe')).toBeInTheDocument(); + expect(screen.getByText('John Doe2')).toBeInTheDocument(); + expect(screen.queryByText('John Doe3')).toBeNull(); + expect(screen.queryByText('John Doe4')).toBeNull(); + + const moreContainer = await screen.findAllByTestId('moreContainer'); + userEvent.click(moreContainer[0]); + + await waitFor(() => { + expect(screen.getByText('John Doe3')).toBeInTheDocument(); + expect(screen.getByText('John Doe4')).toBeInTheDocument(); + expect(screen.getByTestId('extra1')).toBeInTheDocument(); + expect(screen.getByTestId('extra2')).toBeInTheDocument(); + expect(screen.getByTestId('extraAvatar8')).toBeInTheDocument(); + const image = screen.getByTestId('extraImage1'); + expect(image).toBeInTheDocument(); + expect(image).toHaveAttribute('src', 'img-url3'); + }); + + userEvent.click(moreContainer[0]); + await waitFor(() => { + expect(screen.queryByText('John Doe3')).toBeNull(); + expect(screen.queryByText('John Doe4')).toBeNull(); + }); + }); }); diff --git a/src/screens/FundCampaignPledge/FundCampaignPledge.tsx b/src/screens/FundCampaignPledge/FundCampaignPledge.tsx index ea5b329d34..27101cd00e 100644 --- a/src/screens/FundCampaignPledge/FundCampaignPledge.tsx +++ b/src/screens/FundCampaignPledge/FundCampaignPledge.tsx @@ -2,6 +2,7 @@ import { useQuery, type ApolloQueryResult } from '@apollo/client'; import { Search, Sort, WarningAmberRounded } from '@mui/icons-material'; import { FUND_CAMPAIGN_PLEDGE } from 'GraphQl/Queries/fundQueries'; import Loader from 'components/Loader/Loader'; +import { Unstable_Popup as BasePopup } from '@mui/base/Unstable_Popup'; import dayjs from 'dayjs'; import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { Button, Dropdown, Form } from 'react-bootstrap'; @@ -101,6 +102,10 @@ const fundCampaignPledge = (): JSX.Element => { [ModalState.DELETE]: false, }); + const [anchor, setAnchor] = useState(null); + const [extraUsers, setExtraUsers] = useState([]); + const open = Boolean(anchor); + const id = open ? 'simple-popup' : undefined; const [pledgeModalMode, setPledgeModalMode] = useState<'edit' | 'create'>( 'create', ); @@ -192,6 +197,14 @@ const fundCampaignPledge = (): JSX.Element => { [openModal], ); + const handleClick = ( + event: React.MouseEvent, + users: InterfacePledgeVolunteer[], + ): void => { + setExtraUsers(users); + setAnchor(anchor ? null : event.currentTarget); + }; + if (pledgeLoading) return ; if (pledgeError) { return ( @@ -212,7 +225,7 @@ const fundCampaignPledge = (): JSX.Element => { { field: 'volunteers', headerName: 'Volunteers', - flex: 2, + flex: 3, minWidth: 50, align: 'left', headerAlign: 'center', @@ -221,13 +234,15 @@ const fundCampaignPledge = (): JSX.Element => { renderCell: (params: GridCellParams) => { return (
- {params.row.users.map( - (user: InterfacePledgeVolunteer, index: number) => ( + {params.row.users + .slice(0, 2) + .map((user: InterfacePledgeVolunteer, index: number) => (
{user.image ? ( volunteer ) : ( @@ -244,7 +259,16 @@ const fundCampaignPledge = (): JSX.Element => { {user.firstName + ' ' + user.lastName}
- ), + ))} + {params.row.users.length > 2 && ( +
handleClick(e, params.row.users.slice(2))} + > + +{params.row.users.length - 2} more... +
)}
); @@ -394,7 +418,6 @@ const fundCampaignPledge = (): JSX.Element => { {t('pledges')} -

{campaignInfo?.name}

@@ -413,7 +436,6 @@ const fundCampaignPledge = (): JSX.Element => {
-
{
- row._id} components={{ @@ -516,7 +537,6 @@ const fundCampaignPledge = (): JSX.Element => { columns={columns} isRowSelectable={() => false} /> - {/* Update Pledge ModalState */} { endDate={pledgeData?.getFundraisingCampaignById.endDate as Date} mode={pledgeModalMode} /> - {/* Delete Pledge ModalState */} { pledge={pledge} refetchPledge={refetchPledge} /> + 4 ? styles.popupExtra : ''}`} + > + {extraUsers.map((user: InterfacePledgeVolunteer, index: number) => ( +
+ {user.image ? ( + volunteer + ) : ( +
+ +
+ )} + + {user.firstName + ' ' + user.lastName} + +
+ ))} +
); }; diff --git a/src/screens/FundCampaignPledge/PledgesMocks.ts b/src/screens/FundCampaignPledge/PledgesMocks.ts index 5a913ff3d6..71d74b1805 100644 --- a/src/screens/FundCampaignPledge/PledgesMocks.ts +++ b/src/screens/FundCampaignPledge/PledgesMocks.ts @@ -37,6 +37,60 @@ export const MOCKS = [ lastName: 'Doe', image: 'img-url', }, + { + _id: '2', + firstName: 'John', + lastName: 'Doe2', + image: 'img-url2', + }, + { + _id: '3', + firstName: 'John', + lastName: 'Doe3', + image: 'img-url3', + }, + { + _id: '4', + firstName: 'John', + lastName: 'Doe4', + image: 'img-url4', + }, + { + _id: '5', + firstName: 'John', + lastName: 'Doe5', + image: 'img-url5', + }, + { + _id: '6', + firstName: 'John', + lastName: 'Doe6', + image: 'img-url6', + }, + { + _id: '7', + firstName: 'John', + lastName: 'Doe7', + image: 'img-url7', + }, + { + _id: '8', + firstName: 'John', + lastName: 'Doe8', + image: 'img-url8', + }, + { + _id: '9', + firstName: 'John', + lastName: 'Doe9', + image: 'img-url9', + }, + { + _id: '10', + firstName: 'John', + lastName: 'Doe10', + image: null, + }, ], }, { From 85a7b282aab8b010526d8a07af4d764f2f674285 Mon Sep 17 00:00:00 2001 From: Glen Date: Sat, 6 Jul 2024 23:35:42 +0530 Subject: [PATCH 13/17] Add progress bar for Raised & Pledged --- public/locales/en/translation.json | 6 +- public/locales/fr/translation.json | 6 +- public/locales/hi/translation.json | 6 +- public/locales/sp/translation.json | 6 +- public/locales/zh/translation.json | 6 +- .../FundCampaignPledge.module.css | 28 +++- .../FundCampaignPledge.test.tsx | 145 +++++++++++------- .../FundCampaignPledge/FundCampaignPledge.tsx | 114 +++++++++----- .../FundCampaignPledge/PledgeModal.test.tsx | 2 +- .../FundCampaignPledge/PledgeModal.tsx | 20 +-- src/utils/interfaces.ts | 8 +- 11 files changed, 220 insertions(+), 127 deletions(-) diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index d8f7c0af1d..7cff1c8ed2 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -406,7 +406,6 @@ }, "pledges": { "title": "Fund Campaign Pledges", - "volunteers": "Volunteers", "pledgeAmount": "Pledge Amount", "pledgeOptions": "Options", "pledgeCreated": "Pledge created successfully", @@ -422,7 +421,7 @@ "editPledge": "Edit Pledge", "deletePledgeMsg": "Are you sure you want to delete this pledge?", "noPledges": "No Pledges Found", - "searchVolunteer": "Search By Volunteer", + "searchPledger": "Search By Pledgers", "highestAmount": "Highest Amount", "lowestAmount": "Lowest Amount", "latestEndDate": "Latest End Date", @@ -430,7 +429,8 @@ "campaigns": "Campaigns", "pledges": "Pledges", "endsOn": "Ends on", - "raised": "Raised" + "fundsRaised": "Funds Raised", + "fundsPledged": "Funds Pledged" }, "orgPost": { "title": "Posts", diff --git a/public/locales/fr/translation.json b/public/locales/fr/translation.json index a6e659ed5e..b8f6cf98fd 100644 --- a/public/locales/fr/translation.json +++ b/public/locales/fr/translation.json @@ -412,7 +412,6 @@ }, "pledges": { "title": "Engagements de campagne de financement", - "volunteers": "Bénévoles", "pledgeAmount": "Montant de la promesse de don", "pledgeOptions": "Possibilités", "pledgeCreated": "Engagement créé avec succès", @@ -428,7 +427,7 @@ "editPledge": "Modifier l'engagement", "deletePledgeMsg": "Etes-vous sûr de vouloir supprimer cet engagement ?", "noPledges": "Aucun engagement trouvé", - "searchVolunteer": "Rechercher par bénévole", + "searchPledger": "Rechercher par les bailleurs de fonds", "highestAmount": "Montant le plus élevé", "lowestAmount": "Montant le plus bas", "latestEndDate": "Date de fin la plus récente", @@ -436,7 +435,8 @@ "campaigns": "Campagnes", "pledges": "Promesses de dons", "endsOn": "Se termine le", - "raised": "Levé" + "fundsRaised": "Fonds collectés", + "fundsPledged": "Fonds promis" }, "orgPost": { "title": "Des postes", diff --git a/public/locales/hi/translation.json b/public/locales/hi/translation.json index bf09cac68e..bf63963043 100644 --- a/public/locales/hi/translation.json +++ b/public/locales/hi/translation.json @@ -412,7 +412,6 @@ }, "pledges": { "title": "निधि अभियान प्रतिज्ञाएँ", - "volunteers": "स्वयंसेवकों", "pledgeAmount": "प्रतिज्ञा राशि", "pledgeOptions": "विकल्प", "pledgeCreated": "प्रतिज्ञा सफलतापूर्वक बनाई गई", @@ -428,7 +427,7 @@ "editPledge": "प्रतिज्ञा संपादित करें", "deletePledgeMsg": "क्या आप वाकई इस प्रतिज्ञा को हटाना चाहते हैं?", "noPledges": "कोई प्रतिज्ञा नहीं मिली", - "searchVolunteer": "स्वयंसेवक द्वारा खोजें", + "searchPledger": "प्लेजर्स के द्वारा खोजें", "highestAmount": "सबसे अधिक राशि", "lowestAmount": "सबसे कम राशि", "latestEndDate": "नवीनतम समाप्ति तिथि", @@ -436,7 +435,8 @@ "campaigns": "अभियान", "pledges": "प्रतिज्ञाएँ", "endsOn": "पर समाप्त होता है", - "raised": "उठाया गया" + "fundsRaised": "आमंत्रित किस्म के धन", + "fundsPledged": "धन प्रतिबद्ध" }, "orgPost": { "title": "पदों", diff --git a/public/locales/sp/translation.json b/public/locales/sp/translation.json index b42ca0db5f..7972c02147 100644 --- a/public/locales/sp/translation.json +++ b/public/locales/sp/translation.json @@ -537,7 +537,6 @@ }, "pledges": { "title": "Compromisos de Campaña de Financiamiento", - "volunteers": "Voluntarios", "startDate": "Fecha de Inicio", "endDate": "Fecha de Finalización", "pledgeAmount": "Monto del Compromiso", @@ -555,7 +554,7 @@ "editPledge": "Editar Compromiso", "deletePledgeMsg": "¿Estás seguro de que quieres eliminar este compromiso?", "noPledges": "No se encontraron compromisos", - "searchVolunteer": "Buscar por voluntario", + "searchPledger": "Buscar por compromisos", "highestAmount": "Cantidad más alta", "lowestAmount": "Cantidad más baja", "latestEndDate": "Fecha de finalización más reciente", @@ -563,7 +562,8 @@ "campaigns": "Campañas", "pledges": "Compromisos", "endsOn": "Finaliza el", - "raised": "Recaudado" + "fundsRaised": "Fondos recaudados", + "fundsPledged": "Fondos comprometidos" }, "orgPost": { diff --git a/public/locales/zh/translation.json b/public/locales/zh/translation.json index a8c8abc954..5424519b69 100644 --- a/public/locales/zh/translation.json +++ b/public/locales/zh/translation.json @@ -412,7 +412,6 @@ }, "pledges": { "title": "基金活动承诺", - "volunteers": "志愿者", "pledgeAmount": "质押金额", "pledgeOptions": "选项", "pledgeCreated": "质押创建成功", @@ -428,7 +427,7 @@ "editPledge": "编辑承诺", "deletePledgeMsg": "您确定要删除此承诺吗?", "noPledges": "未找到承诺", - "searchVolunteer": "按志愿者搜索", + "searchPledger": "按承诺搜索", "highestAmount": "最高金额", "lowestAmount": "最低金额", "latestEndDate": "最新结束日期", @@ -436,7 +435,8 @@ "campaigns": "活动", "pledges": "承诺", "endsOn": "结束于", - "raised": "筹集到" + "fundsRaised": "募集到的资金", + "fundsPledged": "承诺的资金" }, "orgPost": { "title": "帖子", diff --git a/src/screens/FundCampaignPledge/FundCampaignPledge.module.css b/src/screens/FundCampaignPledge/FundCampaignPledge.module.css index 27d3149b71..c46adfada3 100644 --- a/src/screens/FundCampaignPledge/FundCampaignPledge.module.css +++ b/src/screens/FundCampaignPledge/FundCampaignPledge.module.css @@ -112,7 +112,7 @@ height: 26px; } -.volunteerContainer { +.pledgerContainer { display: flex; align-items: center; justify-content: center; @@ -231,3 +231,29 @@ max-height: 15rem; overflow-y: auto; } + +.toggleGroup { + width: 50%; + min-width: 27.75rem; + margin: 0.5rem 0rem; +} + +.toggleBtn { + padding: 0rem; + height: 30px; + display: flex; + justify-content: center; + align-items: center; +} + +.toggleBtn:hover { + color: #31bb6b !important; +} + +input[type='radio']:checked + label { + background-color: #31bb6a50 !important; +} + +input[type='radio']:checked + label:hover { + color: black !important; +} diff --git a/src/screens/FundCampaignPledge/FundCampaignPledge.test.tsx b/src/screens/FundCampaignPledge/FundCampaignPledge.test.tsx index 0fbea5ad37..50f68cfdb6 100644 --- a/src/screens/FundCampaignPledge/FundCampaignPledge.test.tsx +++ b/src/screens/FundCampaignPledge/FundCampaignPledge.test.tsx @@ -92,7 +92,7 @@ describe('Testing Campaign Pledge Screen', () => { it('should render the Campaign Pledge screen', async () => { renderFundCampaignPledge(link1); await waitFor(() => { - expect(screen.getByTestId('searchVolunteer')).toBeInTheDocument(); + expect(screen.getByTestId('searchPledger')).toBeInTheDocument(); }); }); @@ -172,8 +172,8 @@ describe('Testing Campaign Pledge Screen', () => { it('Search the Pledges list by Users', async () => { renderFundCampaignPledge(link1); - const searchVolunteer = await screen.findByTestId('searchVolunteer'); - fireEvent.change(searchVolunteer, { + const searchPledger = await screen.findByTestId('searchPledger'); + fireEvent.change(searchPledger, { target: { value: 'John' }, }); @@ -197,52 +197,85 @@ describe('Testing Campaign Pledge Screen', () => { ); }); - it('Sort the Pledges list by Lowest Amount', async () => { + it('check if user image renders', async () => { renderFundCampaignPledge(link1); + await waitFor(() => { + expect(screen.getByTestId('searchPledger')).toBeInTheDocument(); + }); - const searchVolunteer = await screen.findByTestId('searchVolunteer'); - expect(searchVolunteer).toBeInTheDocument(); + const image = await screen.findByTestId('image1'); + expect(image).toBeInTheDocument(); + expect(image).toHaveAttribute('src', 'img-url'); + }); - fireEvent.click(screen.getByTestId('filter')); - fireEvent.click(screen.getByTestId('amount_ASC')); + it('should render extraUserDetails in Popup', async () => { + renderFundCampaignPledge(link1); + await waitFor(() => { + expect(screen.getByTestId('searchPledger')).toBeInTheDocument(); + }); + + expect(screen.getByText('John Doe')).toBeInTheDocument(); + expect(screen.getByText('John Doe2')).toBeInTheDocument(); + expect(screen.queryByText('John Doe3')).toBeNull(); + expect(screen.queryByText('John Doe4')).toBeNull(); + + const moreContainer = await screen.findAllByTestId('moreContainer'); + userEvent.click(moreContainer[0]); await waitFor(() => { - expect(screen.getByText('John Doe')).toBeInTheDocument(); - expect(screen.queryByText('Jane Doe')).toBeInTheDocument(); + expect(screen.getByText('John Doe3')).toBeInTheDocument(); + expect(screen.getByText('John Doe4')).toBeInTheDocument(); + expect(screen.getByTestId('extra1')).toBeInTheDocument(); + expect(screen.getByTestId('extra2')).toBeInTheDocument(); + expect(screen.getByTestId('extraAvatar8')).toBeInTheDocument(); + const image = screen.getByTestId('extraImage1'); + expect(image).toBeInTheDocument(); + expect(image).toHaveAttribute('src', 'img-url3'); }); + userEvent.click(moreContainer[0]); await waitFor(() => { - expect(screen.getAllByTestId('amountCell')[0]).toHaveTextContent('100'); + expect(screen.queryByText('John Doe3')).toBeNull(); + expect(screen.queryByText('John Doe4')).toBeNull(); }); }); - it('Sort the Pledges list by Highest Amount', async () => { + it('should render Progress Bar with Raised amount (CONSTANT) & Pledged Amount', async () => { renderFundCampaignPledge(link1); + await waitFor(() => { + expect(screen.getByTestId('searchPledger')).toBeInTheDocument(); + }); + const raised = screen.getByText('Funds Raised'); + const pledged = screen.getByText('Funds Pledged'); + expect(pledged).toBeInTheDocument(); + expect(raised).toBeInTheDocument(); - const searchVolunteer = await screen.findByTestId('searchVolunteer'); - expect(searchVolunteer).toBeInTheDocument(); - - fireEvent.click(screen.getByTestId('filter')); - fireEvent.click(screen.getByTestId('amount_DESC')); + userEvent.click(raised); await waitFor(() => { - expect(screen.getByText('John Doe')).toBeInTheDocument(); - expect(screen.queryByText('Jane Doe')).toBeInTheDocument(); + expect(screen.getByTestId('progressBar')).toBeInTheDocument(); + expect(screen.getByTestId('progressBar')).toHaveTextContent('$500'); }); + userEvent.click(pledged); + await waitFor(() => { - expect(screen.getAllByTestId('amountCell')[0]).toHaveTextContent('200'); + expect(screen.getByTestId('progressBar')).toBeInTheDocument(); + expect(screen.getByTestId('progressBar')).toHaveTextContent('$300'); }); }); - it('Sort the Pledges list by latest endDate', async () => { + it('Sort the Pledges list by Lowest Amount', async () => { renderFundCampaignPledge(link1); - const searchVolunteer = await screen.findByTestId('searchVolunteer'); - expect(searchVolunteer).toBeInTheDocument(); + const searchPledger = await screen.findByTestId('searchPledger'); + expect(searchPledger).toBeInTheDocument(); fireEvent.click(screen.getByTestId('filter')); - fireEvent.click(screen.getByTestId('endDate_DESC')); + await waitFor(() => { + expect(screen.getByTestId('amount_ASC')).toBeInTheDocument(); + }); + fireEvent.click(screen.getByTestId('amount_ASC')); await waitFor(() => { expect(screen.getByText('John Doe')).toBeInTheDocument(); @@ -254,14 +287,17 @@ describe('Testing Campaign Pledge Screen', () => { }); }); - it('Sort the Pledges list by earliest endDate', async () => { + it('Sort the Pledges list by Highest Amount', async () => { renderFundCampaignPledge(link1); - const searchVolunteer = await screen.findByTestId('searchVolunteer'); - expect(searchVolunteer).toBeInTheDocument(); + const searchPledger = await screen.findByTestId('searchPledger'); + expect(searchPledger).toBeInTheDocument(); fireEvent.click(screen.getByTestId('filter')); - fireEvent.click(screen.getByTestId('endDate_ASC')); + await waitFor(() => { + expect(screen.getByTestId('amount_DESC')).toBeInTheDocument(); + }); + fireEvent.click(screen.getByTestId('amount_DESC')); await waitFor(() => { expect(screen.getByText('John Doe')).toBeInTheDocument(); @@ -273,46 +309,47 @@ describe('Testing Campaign Pledge Screen', () => { }); }); - it('check if user image renders', async () => { + it('Sort the Pledges list by latest endDate', async () => { renderFundCampaignPledge(link1); + + const searchPledger = await screen.findByTestId('searchPledger'); + expect(searchPledger).toBeInTheDocument(); + + fireEvent.click(screen.getByTestId('filter')); await waitFor(() => { - expect(screen.getByTestId('searchVolunteer')).toBeInTheDocument(); + expect(screen.getByTestId('endDate_DESC')).toBeInTheDocument(); }); + fireEvent.click(screen.getByTestId('endDate_DESC')); - const image = await screen.findByTestId('image1'); - expect(image).toBeInTheDocument(); - expect(image).toHaveAttribute('src', 'img-url'); - }); + await waitFor(() => { + expect(screen.getByText('John Doe')).toBeInTheDocument(); + expect(screen.queryByText('Jane Doe')).toBeInTheDocument(); + }); - it('should render extraUserDetails in Popup', async () => { - renderFundCampaignPledge(link1); await waitFor(() => { - expect(screen.getByTestId('searchVolunteer')).toBeInTheDocument(); + expect(screen.getAllByTestId('amountCell')[0]).toHaveTextContent('100'); }); + }); - expect(screen.getByText('John Doe')).toBeInTheDocument(); - expect(screen.getByText('John Doe2')).toBeInTheDocument(); - expect(screen.queryByText('John Doe3')).toBeNull(); - expect(screen.queryByText('John Doe4')).toBeNull(); + it('Sort the Pledges list by earliest endDate', async () => { + renderFundCampaignPledge(link1); - const moreContainer = await screen.findAllByTestId('moreContainer'); - userEvent.click(moreContainer[0]); + const searchPledger = await screen.findByTestId('searchPledger'); + expect(searchPledger).toBeInTheDocument(); + fireEvent.click(screen.getByTestId('filter')); await waitFor(() => { - expect(screen.getByText('John Doe3')).toBeInTheDocument(); - expect(screen.getByText('John Doe4')).toBeInTheDocument(); - expect(screen.getByTestId('extra1')).toBeInTheDocument(); - expect(screen.getByTestId('extra2')).toBeInTheDocument(); - expect(screen.getByTestId('extraAvatar8')).toBeInTheDocument(); - const image = screen.getByTestId('extraImage1'); - expect(image).toBeInTheDocument(); - expect(image).toHaveAttribute('src', 'img-url3'); + expect(screen.getByTestId('endDate_ASC')).toBeInTheDocument(); }); + fireEvent.click(screen.getByTestId('endDate_ASC')); - userEvent.click(moreContainer[0]); await waitFor(() => { - expect(screen.queryByText('John Doe3')).toBeNull(); - expect(screen.queryByText('John Doe4')).toBeNull(); + expect(screen.getByText('John Doe')).toBeInTheDocument(); + expect(screen.queryByText('Jane Doe')).toBeInTheDocument(); + }); + + await waitFor(() => { + expect(screen.getAllByTestId('amountCell')[0]).toHaveTextContent('200'); }); }); }); diff --git a/src/screens/FundCampaignPledge/FundCampaignPledge.tsx b/src/screens/FundCampaignPledge/FundCampaignPledge.tsx index 27101cd00e..dd3b26b7a5 100644 --- a/src/screens/FundCampaignPledge/FundCampaignPledge.tsx +++ b/src/screens/FundCampaignPledge/FundCampaignPledge.tsx @@ -12,23 +12,16 @@ import { currencySymbols } from 'utils/currency'; import styles from './FundCampaignPledge.module.css'; import PledgeDeleteModal from './PledgeDeleteModal'; import PledgeModal from './PledgeModal'; -import { - Breadcrumbs, - LinearProgress, - Link, - Stack, - Typography, - linearProgressClasses, -} from '@mui/material'; +import { Breadcrumbs, Link, Stack, Typography } from '@mui/material'; import { DataGrid } from '@mui/x-data-grid'; import Avatar from 'components/Avatar/Avatar'; import type { GridCellParams, GridColDef } from '@mui/x-data-grid'; import type { InterfacePledgeInfo, - InterfacePledgeVolunteer, + InterfacePledger, InterfaceQueryFundCampaignsPledges, } from 'utils/interfaces'; -import { styled } from '@mui/system'; +import ProgressBar from 'react-bootstrap/ProgressBar'; interface InterfaceCampaignInfo { name: string; @@ -64,18 +57,6 @@ const dataGridStyle = { }, }; -const BorderLinearProgress = styled(LinearProgress)(() => ({ - height: 15, - borderRadius: 4, - [`&.${linearProgressClasses.colorPrimary}`]: { - backgroundColor: '#e0e0e0', - }, - [`& .${linearProgressClasses.bar}`]: { - borderRadius: 5, - backgroundColor: '#31bb6b', - }, -})); - const fundCampaignPledge = (): JSX.Element => { const { t } = useTranslation('translation', { keyPrefix: 'pledges', @@ -103,7 +84,10 @@ const fundCampaignPledge = (): JSX.Element => { }); const [anchor, setAnchor] = useState(null); - const [extraUsers, setExtraUsers] = useState([]); + const [extraUsers, setExtraUsers] = useState([]); + const [progressIndicator, setProgressIndicator] = useState< + 'raised' | 'pledged' + >('pledged'); const open = Boolean(anchor); const id = open ? 'simple-popup' : undefined; const [pledgeModalMode, setPledgeModalMode] = useState<'edit' | 'create'>( @@ -144,16 +128,18 @@ const fundCampaignPledge = (): JSX.Element => { 'YYYY-MM-DD', ).toDate(); - const pledges = useMemo(() => { - return ( + const { pledges, totalPledged } = useMemo(() => { + let totalPledged = 0; + const pledges = pledgeData?.getFundraisingCampaignById.pledges.filter((pledge) => { + totalPledged += pledge.amount; const search = searchTerm.toLowerCase(); return pledge.users.some((user) => { const fullName = `${user.firstName} ${user.lastName}`; return fullName.toLowerCase().includes(search); }); - }) ?? [] - ); + }) ?? []; + return { pledges, totalPledged }; }, [pledgeData, searchTerm]); useEffect(() => { @@ -199,7 +185,7 @@ const fundCampaignPledge = (): JSX.Element => { const handleClick = ( event: React.MouseEvent, - users: InterfacePledgeVolunteer[], + users: InterfacePledger[], ): void => { setExtraUsers(users); setAnchor(anchor ? null : event.currentTarget); @@ -223,8 +209,8 @@ const fundCampaignPledge = (): JSX.Element => { const columns: GridColDef[] = [ { - field: 'volunteers', - headerName: 'Volunteers', + field: 'pledgers', + headerName: 'Pledgers', flex: 3, minWidth: 50, align: 'left', @@ -236,12 +222,12 @@ const fundCampaignPledge = (): JSX.Element => {
{params.row.users .slice(0, 2) - .map((user: InterfacePledgeVolunteer, index: number) => ( -
+ .map((user: InterfacePledger, index: number) => ( +
{user.image ? ( volunteer @@ -326,8 +312,8 @@ const fundCampaignPledge = (): JSX.Element => { }, }, { - field: 'paid', - headerName: 'Paid', + field: 'donated', + headerName: 'Donated', flex: 1, minWidth: 100, align: 'center', @@ -426,9 +412,53 @@ const fundCampaignPledge = (): JSX.Element => {
-
$1,000 {t('raised')}
+
+
+ setProgressIndicator('pledged')} + /> + + + setProgressIndicator('raised')} + checked={progressIndicator === 'raised'} + /> + +
+
+
- +
$0
${campaignInfo?.goal}
@@ -440,13 +470,13 @@ const fundCampaignPledge = (): JSX.Element => {
setSearchTerm(e.target.value)} - data-testid="searchVolunteer" + data-testid="searchPledger" />