From 99d1a1377c686f030a25425a353b08b6e5e61f91 Mon Sep 17 00:00:00 2001 From: Glen Dsouza Date: Tue, 18 Jun 2024 01:05:27 +0530 Subject: [PATCH] Redesign FundPledge Screen & Add Search/Sort (#2040) * Add new UI Changes * Add test cases for Pledge UI changes * CHange Bug misplaced label for amount * Add comments to code * minor changes --- public/locales/en/translation.json | 8 +- public/locales/fr/translation.json | 8 +- public/locales/hi/translation.json | 8 +- public/locales/sp/translation.json | 8 +- public/locales/zh/translation.json | 8 +- src/GraphQl/Mutations/PledgeMutation.ts | 2 + src/GraphQl/Queries/fundQueries.ts | 6 +- .../FundCampaignPledge.module.css | 94 ++- .../FundCampaignPledge.test.tsx | 544 ++++++----------- .../FundCampaignPledge/FundCampaignPledge.tsx | 558 ++++++++++-------- .../FundCampaignPledge/PledgeCreateModal.tsx | 150 ----- .../PledgeDeleteModal.test.tsx | 108 ++++ .../FundCampaignPledge/PledgeDeleteModal.tsx | 63 +- .../FundCampaignPledge/PledgeEditModal.tsx | 149 ----- .../FundCampaignPledge/PledgeModal.test.tsx | 213 +++++++ .../FundCampaignPledge/PledgeModal.tsx | 322 ++++++++++ .../FundCampaignPledge/PledgesMocks.ts | 256 +++++++- src/screens/LoginPage/LoginPage.tsx | 2 +- src/utils/currency.ts | 2 +- src/utils/interfaces.ts | 17 +- 20 files changed, 1547 insertions(+), 979 deletions(-) delete mode 100644 src/screens/FundCampaignPledge/PledgeCreateModal.tsx create mode 100644 src/screens/FundCampaignPledge/PledgeDeleteModal.test.tsx delete mode 100644 src/screens/FundCampaignPledge/PledgeEditModal.tsx create mode 100644 src/screens/FundCampaignPledge/PledgeModal.test.tsx create mode 100644 src/screens/FundCampaignPledge/PledgeModal.tsx diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index 8ee4040568..d94d050e53 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -390,7 +390,13 @@ "amount": "Amount", "editPledge": "Edit Pledge", "deletePledgeMsg": "Are you sure you want to delete this pledge?", - "noPledges": "No Pledges Found" + "noPledges": "No Pledges Found", + "sort": "Sort", + "searchVolunteer": "Search By Volunteer", + "highestAmount": "Highest Amount", + "lowestAmount": "Lowest Amount", + "latestEndDate": "Latest End Date", + "earliestEndDate": "Earliest End Date" }, "orgPost": { "title": "Posts", diff --git a/public/locales/fr/translation.json b/public/locales/fr/translation.json index cb0c7798d7..1d41e2c54a 100644 --- a/public/locales/fr/translation.json +++ b/public/locales/fr/translation.json @@ -395,7 +395,13 @@ "amount": "Montant", "editPledge": "Modifier l'engagement", "deletePledgeMsg": "Etes-vous sûr de vouloir supprimer cet engagement ?", - "noPledges": "Aucun engagement trouvé" + "noPledges": "Aucun engagement trouvé", + "sort": "Trier", + "searchVolunteer": "Rechercher par bénévole", + "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" }, "orgPost": { "title": "Des postes", diff --git a/public/locales/hi/translation.json b/public/locales/hi/translation.json index 69144d09ef..b01838a702 100644 --- a/public/locales/hi/translation.json +++ b/public/locales/hi/translation.json @@ -395,7 +395,13 @@ "amount": "मात्रा", "editPledge": "प्रतिज्ञा संपादित करें", "deletePledgeMsg": "क्या आप वाकई इस प्रतिज्ञा को हटाना चाहते हैं?", - "noPledges": "कोई प्रतिज्ञा नहीं मिली" + "noPledges": "कोई प्रतिज्ञा नहीं मिली", + "sort": "क्रमबद्ध करें", + "searchVolunteer": "स्वयंसेवक द्वारा खोजें", + "highestAmount": "सबसे अधिक राशि", + "lowestAmount": "सबसे कम राशि", + "latestEndDate": "नवीनतम समाप्ति तिथि", + "earliestEndDate": "सबसे प्रारंभिक समाप्ति तिथि" }, "orgPost": { "title": "पदों", diff --git a/public/locales/sp/translation.json b/public/locales/sp/translation.json index c4ba57ab81..96345b1567 100644 --- a/public/locales/sp/translation.json +++ b/public/locales/sp/translation.json @@ -533,7 +533,13 @@ "deletePledgeMsg": "¿Estás seguro de que quieres eliminar este compromiso?", "no": "No", "yes": "Sí", - "noPledges": "No se encontraron compromisos" + "noPledges": "No se encontraron compromisos", + "sort": "Ordenar", + "searchVolunteer": "Buscar por voluntario", + "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" }, "orgPost": { diff --git a/public/locales/zh/translation.json b/public/locales/zh/translation.json index c29824067c..e8ea831a91 100644 --- a/public/locales/zh/translation.json +++ b/public/locales/zh/translation.json @@ -395,7 +395,13 @@ "amount": "数量", "editPledge": "编辑承诺", "deletePledgeMsg": "您确定要删除此承诺吗?", - "noPledges": "未找到承诺" + "noPledges": "未找到承诺", + "sort": "排序", + "searchVolunteer": "按志愿者搜索", + "highestAmount": "最高金额", + "lowestAmount": "最低金额", + "latestEndDate": "最新结束日期", + "earliestEndDate": "最早结束日期" }, "orgPost": { "title": "帖子", diff --git a/src/GraphQl/Mutations/PledgeMutation.ts b/src/GraphQl/Mutations/PledgeMutation.ts index aca0663635..321f7aa697 100644 --- a/src/GraphQl/Mutations/PledgeMutation.ts +++ b/src/GraphQl/Mutations/PledgeMutation.ts @@ -52,10 +52,12 @@ export const UPDATE_PLEDGE = gql` $currency: Currency $startDate: Date $endDate: Date + $users: [ID!] ) { updateFundraisingCampaignPledge( id: $id data: { + users: $users amount: $amount currency: $currency startDate: $startDate diff --git a/src/GraphQl/Queries/fundQueries.ts b/src/GraphQl/Queries/fundQueries.ts index 85015f5f63..3648060d2e 100644 --- a/src/GraphQl/Queries/fundQueries.ts +++ b/src/GraphQl/Queries/fundQueries.ts @@ -47,8 +47,8 @@ export const FUND_CAMPAIGN = gql` `; export const FUND_CAMPAIGN_PLEDGE = gql` - query GetFundraisingCampaignById($id: ID!) { - getFundraisingCampaignById(id: $id) { + query GetFundraisingCampaignById($id: ID!, $orderBy: PledgeOrderByInput) { + getFundraisingCampaignById(id: $id, orderBy: $orderBy) { startDate endDate pledges { @@ -60,6 +60,8 @@ export const FUND_CAMPAIGN_PLEDGE = gql` users { _id firstName + lastName + image } } } diff --git a/src/screens/FundCampaignPledge/FundCampaignPledge.module.css b/src/screens/FundCampaignPledge/FundCampaignPledge.module.css index 8ddc6c01b1..0e9f3187b7 100644 --- a/src/screens/FundCampaignPledge/FundCampaignPledge.module.css +++ b/src/screens/FundCampaignPledge/FundCampaignPledge.module.css @@ -2,11 +2,6 @@ margin: 0.6rem 0; } -.createPledgeBtn { - position: absolute; - top: 1.3rem; - right: 2rem; -} .container { min-height: 100vh; } @@ -16,15 +11,24 @@ margin-top: 2vh; 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; +} + +.modalCloseBtn { + width: 40px; + height: 40px; + padding: 1rem; + display: flex; + justify-content: center; + align-items: center; } + .greenregbtn { margin: 1rem 0 0; margin-top: 15px; @@ -51,3 +55,73 @@ align-items: center; flex-direction: column; } + +.btnsContainer { + display: flex; + margin: 2.5rem 0 2.5rem 0; +} + +.btnsContainer .input { + flex: 1; + min-width: 18rem; + position: relative; +} + +.btnsContainer input { + outline: 1px solid var(--bs-gray-400); +} + +.btnsContainer .input button { + width: 52px; +} + +.inputField { + background-color: white; + box-shadow: 0 1px 1px #31bb6b; +} + +.dropdown { + background-color: white; + border: 1px solid #31bb6b; + position: relative; + display: inline-block; + color: #31bb6b; +} + +.tableHeader { + background-color: var(--bs-primary); + color: var(--bs-white); + font-size: 1rem; +} + +.rowBackground { + background-color: var(--bs-white); + max-height: 120px; +} + +.TableImage { + object-fit: cover; + width: 25px !important; + height: 25px !important; + border-radius: 100% !important; +} + +.avatarContainer { + width: 28px; + height: 26px; +} + +.volunteerContainer { + display: flex; + align-items: center; + justify-content: center; + margin: 0.1rem 0.25rem; + gap: 0.25rem; + padding: 0.25rem 0.45rem; + border-radius: 0.35rem; + background-color: #31bb6b33; +} + +.noOutline input { + outline: none; +} diff --git a/src/screens/FundCampaignPledge/FundCampaignPledge.test.tsx b/src/screens/FundCampaignPledge/FundCampaignPledge.test.tsx index f949168c8a..7c61a1b48a 100644 --- a/src/screens/FundCampaignPledge/FundCampaignPledge.test.tsx +++ b/src/screens/FundCampaignPledge/FundCampaignPledge.test.tsx @@ -1,32 +1,29 @@ import { MockedProvider } from '@apollo/react-testing'; import { LocalizationProvider } from '@mui/x-date-pickers'; import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import type { RenderResult } from '@testing-library/react'; import { + cleanup, fireEvent, 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 } 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 i18n from '../../utils/i18nForTest'; +import i18nForTest from '../../utils/i18nForTest'; import FundCampaignPledge from './FundCampaignPledge'; import { EMPTY_MOCKS, MOCKS, - MOCKS_CREATE_PLEDGE_ERROR, - MOCKS_DELETE_PLEDGE_ERROR, MOCKS_FUND_CAMPAIGN_PLEDGE_ERROR, - MOCKS_UPDATE_PLEDGE_ERROR, } from './PledgesMocks'; import React from 'react'; +import type { ApolloLink } from '@apollo/client'; jest.mock('react-toastify', () => ({ toast: { @@ -42,407 +39,248 @@ 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); const link2 = new StaticMockLink(MOCKS_FUND_CAMPAIGN_PLEDGE_ERROR); -const link3 = new StaticMockLink(MOCKS_CREATE_PLEDGE_ERROR); -const link4 = new StaticMockLink(MOCKS_UPDATE_PLEDGE_ERROR); -const link5 = new StaticMockLink(MOCKS_DELETE_PLEDGE_ERROR); -const link6 = new StaticMockLink(EMPTY_MOCKS); - -const translations = { - ...JSON.parse( - JSON.stringify(i18n.getDataByLanguage('en')?.translation.pledges ?? {}), - ), - ...JSON.parse(JSON.stringify(i18n.getDataByLanguage('en')?.common ?? {})), - ...JSON.parse(JSON.stringify(i18n.getDataByLanguage('en')?.errors ?? {})), +const link3 = new StaticMockLink(EMPTY_MOCKS); +const translations = JSON.parse( + JSON.stringify(i18nForTest.getDataByLanguage('en')?.translation.pledges), +); + +const renderFundCampaignPledge = (link: ApolloLink): RenderResult => { + return render( + + + + + + + } + /> + } + /> + + + + + + , + ); }; describe('Testing Campaign Pledge Screen', () => { - const formData = { - pledgeAmount: 100, - pledgeCurrency: 'USD', - pledgeEndDate: '03/10/2024', - pledgeStartDate: '03/10/2024', - }; + beforeAll(() => { + jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useParams: () => ({ orgId: 'orgId', fundCampaignId: 'fundCampaignId' }), + })); + }); + + afterAll(() => { + jest.clearAllMocks(); + }); + + afterEach(() => { + cleanup(); + }); it('should render the Campaign Pledge screen', async () => { - const { getByText } = render( - - - - - {} - - - - , - ); - await wait(); + renderFundCampaignPledge(link1); await waitFor(() => { - expect(getByText(translations.addPledge)).toBeInTheDocument(); + expect(screen.getByTestId('searchVolunteer')).toBeInTheDocument(); }); }); - it('should render the Campaign Pledge screen with error', async () => { - const { queryByText } = render( - - - - - {} + + it('should redirect to fallback URL if URL params are undefined', async () => { + render( + + + + + + } + /> + } + /> + - - + + , ); - await wait(); - await waitFor(() => - expect(queryByText(translations.addPledge)).not.toBeInTheDocument(), - ); - await waitFor(() => - expect(screen.getByTestId('errorMsg')).toBeInTheDocument(), - ); + await waitFor(() => { + expect(screen.getByTestId('paramsError')).toBeInTheDocument(); + }); }); it('open and closes Create Pledge modal', async () => { - render( - - - - - - {} - - - - - , - ); - await wait(); + renderFundCampaignPledge(link1); + + const addPledgeBtn = await screen.findByTestId('addPledgeBtn'); + expect(addPledgeBtn).toBeInTheDocument(); + userEvent.click(addPledgeBtn); + await waitFor(() => - expect(screen.getByTestId('addPledgeBtn')).toBeInTheDocument(), + expect(screen.getAllByText(translations.createPledge)).toHaveLength(2), ); - userEvent.click(screen.getByTestId('addPledgeBtn')); - await waitFor(() => { - return expect( - screen.findByTestId('createPledgeCloseBtn'), - ).resolves.toBeInTheDocument(); - }); - userEvent.click(screen.getByTestId('createPledgeCloseBtn')); - await waitForElementToBeRemoved(() => - screen.queryByTestId('createPledgeCloseBtn'), + userEvent.click(screen.getByTestId('pledgeModalCloseBtn')); + await waitFor(() => + expect(screen.queryByTestId('pledgeModalCloseBtn')).toBeNull(), ); }); - it('creates a pledge', async () => { - render( - - - - - - {} - - - - - , - ); - await wait(); + + it('open and closes update pledge modal', async () => { + renderFundCampaignPledge(link1); + + const editPledgeBtn = await screen.findAllByTestId('editPledgeBtn'); + await waitFor(() => expect(editPledgeBtn[0]).toBeInTheDocument()); + userEvent.click(editPledgeBtn[0]); + await waitFor(() => - expect(screen.getByTestId('addPledgeBtn')).toBeInTheDocument(), + expect(screen.getByText(translations.editPledge)).toBeInTheDocument(), ); - userEvent.click(screen.getByTestId('addPledgeBtn')); - await waitFor(() => { - return expect( - screen.findByTestId('createPledgeCloseBtn'), - ).resolves.toBeInTheDocument(); - }); - const currency = screen.getByTestId('currencySelect'); - fireEvent.change(currency, { target: { value: formData.pledgeCurrency } }); - const startDate = screen.getByLabelText(translations.startDate); - const endDate = screen.getByLabelText(translations.endDate); - fireEvent.change(startDate, { - target: { value: formData.pledgeStartDate }, - }); - fireEvent.change(endDate, { target: { value: formData.pledgeEndDate } }); - userEvent.type( - screen.getByPlaceholderText('Enter Pledge Amount'), - formData.pledgeAmount.toString(), + userEvent.click(screen.getByTestId('pledgeModalCloseBtn')); + await waitFor(() => + expect(screen.queryByTestId('pledgeModalCloseBtn')).toBeNull(), ); - userEvent.click(screen.getByTestId('createPledgeBtn')); - await waitFor(() => { - return expect(toast.success).toHaveBeenCalledWith( - translations.pledgeCreated, - ); - }); }); - it('toasts an error on unsuccessful pledge creation', async () => { - render( - - - - - - {} - - - - - , + + it('open and closes delete pledge modal', async () => { + renderFundCampaignPledge(link1); + + const deletePledgeBtn = await screen.findAllByTestId('deletePledgeBtn'); + await waitFor(() => expect(deletePledgeBtn[0]).toBeInTheDocument()); + userEvent.click(deletePledgeBtn[0]); + + await waitFor(() => + expect(screen.getByText(translations.deletePledge)).toBeInTheDocument(), ); - await wait(); + userEvent.click(screen.getByTestId('deletePledgeCloseBtn')); await waitFor(() => - expect(screen.getByTestId('addPledgeBtn')).toBeInTheDocument(), + expect(screen.queryByTestId('deletePledgeCloseBtn')).toBeNull(), ); - userEvent.click(screen.getByTestId('addPledgeBtn')); - await waitFor(() => { - return expect( - screen.findByTestId('createPledgeCloseBtn'), - ).resolves.toBeInTheDocument(); - }); - const currency = screen.getByTestId('currencySelect'); - fireEvent.change(currency, { target: { value: formData.pledgeCurrency } }); - const startDate = screen.getByLabelText(translations.startDate); - const endDate = screen.getByLabelText(translations.endDate); - fireEvent.change(startDate, { - target: { value: formData.pledgeStartDate }, + }); + + it('Search the Pledges list by Users', async () => { + renderFundCampaignPledge(link1); + const searchVolunteer = await screen.findByTestId('searchVolunteer'); + fireEvent.change(searchVolunteer, { + target: { value: 'John' }, }); - fireEvent.change(endDate, { target: { value: formData.pledgeEndDate } }); - userEvent.type( - screen.getByPlaceholderText('Enter Pledge Amount'), - formData.pledgeAmount.toString(), - ); - userEvent.click(screen.getByTestId('createPledgeBtn')); + await waitFor(() => { - return expect(toast.error).toHaveBeenCalled(); + expect(screen.getByText('John Doe')).toBeInTheDocument(); + expect(screen.queryByText('Jane Doe')).toBeNull(); }); }); - it('open and closes update pledge modal', async () => { - render( - - - - - - {} - - - - - , - ); - await wait(); - await waitFor(() => - expect(screen.getAllByTestId('editPledgeBtn')[0]).toBeInTheDocument(), - ); - userEvent.click(screen.getAllByTestId('editPledgeBtn')[0]); + it('should render the Campaign Pledge screen with error', async () => { + renderFundCampaignPledge(link2); await waitFor(() => { - return expect( - screen.findByTestId('updatePledgeCloseBtn'), - ).resolves.toBeInTheDocument(); + expect(screen.getByTestId('errorMsg')).toBeInTheDocument(); }); - userEvent.click(screen.getByTestId('updatePledgeCloseBtn')); - await waitForElementToBeRemoved(() => - screen.queryByTestId('updatePledgeCloseBtn'), - ); }); - it('updates a pledge', async () => { - render( - - - - - - {} - - - - - , - ); - await wait(); + + it('renders the empty pledge component', async () => { + renderFundCampaignPledge(link3); await waitFor(() => - expect(screen.getAllByTestId('editPledgeBtn')[0]).toBeInTheDocument(), + expect(screen.getByText(translations.noPledges)).toBeInTheDocument(), ); - userEvent.click(screen.getAllByTestId('editPledgeBtn')[0]); + }); + + it('Sort the Pledges list by Lowest Amount', async () => { + renderFundCampaignPledge(link1); + + const searchVolunteer = await screen.findByTestId('searchVolunteer'); + expect(searchVolunteer).toBeInTheDocument(); + + fireEvent.click(screen.getByTestId('filter')); + fireEvent.click(screen.getByTestId('amount_ASC')); + await waitFor(() => { - return expect( - screen.findByTestId('updatePledgeCloseBtn'), - ).resolves.toBeInTheDocument(); - }); - const currency = screen.getByTestId('currencySelect'); - fireEvent.change(currency, { target: { value: 'INR' } }); - const startDate = screen.getByLabelText(translations.startDate); - const endDate = screen.getByLabelText(translations.endDate); - fireEvent.change(startDate, { - target: { value: formData.pledgeStartDate }, + expect(screen.getByText('John Doe')).toBeInTheDocument(); + expect(screen.queryByText('Jane Doe')).toBeInTheDocument(); }); - fireEvent.change(endDate, { target: { value: formData.pledgeEndDate } }); - userEvent.type( - screen.getByPlaceholderText('Enter Pledge Amount'), - formData.pledgeAmount.toString(), - ); - userEvent.click(screen.getByTestId('updatePledgeBtn')); + await waitFor(() => { - return expect(toast.success).toHaveBeenCalledWith( - translations.pledgeUpdated, - ); + expect(screen.getAllByTestId('amountCell')[0]).toHaveTextContent('100'); }); }); - it('toasts an error on unsuccessful pledge update', async () => { - render( - - - - - - {} - - - - - , - ); - await wait(); - await waitFor(() => - expect(screen.getAllByTestId('editPledgeBtn')[0]).toBeInTheDocument(), - ); - userEvent.click(screen.getAllByTestId('editPledgeBtn')[0]); + + it('Sort the Pledges list by Highest Amount', async () => { + renderFundCampaignPledge(link1); + + const searchVolunteer = await screen.findByTestId('searchVolunteer'); + expect(searchVolunteer).toBeInTheDocument(); + + fireEvent.click(screen.getByTestId('filter')); + fireEvent.click(screen.getByTestId('amount_DESC')); + await waitFor(() => { - return expect( - screen.findByTestId('updatePledgeCloseBtn'), - ).resolves.toBeInTheDocument(); - }); - const currency = screen.getByTestId('currencySelect'); - fireEvent.change(currency, { target: { value: 'INR' } }); - const startDate = screen.getByLabelText(translations.startDate); - const endDate = screen.getByLabelText(translations.endDate); - fireEvent.change(startDate, { - target: { value: formData.pledgeStartDate }, + expect(screen.getByText('John Doe')).toBeInTheDocument(); + expect(screen.queryByText('Jane Doe')).toBeInTheDocument(); }); - fireEvent.change(endDate, { target: { value: formData.pledgeEndDate } }); - userEvent.type( - screen.getByPlaceholderText('Enter Pledge Amount'), - formData.pledgeAmount.toString(), - ); - userEvent.click(screen.getByTestId('updatePledgeBtn')); + await waitFor(() => { - return expect(toast.error).toHaveBeenCalled(); + expect(screen.getAllByTestId('amountCell')[0]).toHaveTextContent('200'); }); }); - it('open and closes delete pledge modal', async () => { - render( - - - - - - {} - - - - - , - ); - await wait(); - await waitFor(() => - expect(screen.getAllByTestId('deletePledgeBtn')[0]).toBeInTheDocument(), - ); - userEvent.click(screen.getAllByTestId('deletePledgeBtn')[0]); - await waitFor(() => { - return expect( - screen.findByTestId('deletePledgeCloseBtn'), - ).resolves.toBeInTheDocument(); - }); - userEvent.click(screen.getByTestId('deletePledgeCloseBtn')); - await waitForElementToBeRemoved(() => - screen.queryByTestId('deletePledgeCloseBtn'), - ); - }); - it('deletes a pledge', async () => { - render( - - - - - - {} - - - - - , - ); - await wait(); - await waitFor(() => - expect(screen.getAllByTestId('deletePledgeBtn')[0]).toBeInTheDocument(), - ); - userEvent.click(screen.getAllByTestId('deletePledgeBtn')[0]); + + it('Sort the Pledges list by latest endDate', async () => { + renderFundCampaignPledge(link1); + + const searchVolunteer = await screen.findByTestId('searchVolunteer'); + expect(searchVolunteer).toBeInTheDocument(); + + fireEvent.click(screen.getByTestId('filter')); + fireEvent.click(screen.getByTestId('endDate_DESC')); + await waitFor(() => { - return expect( - screen.findByTestId('deletePledgeCloseBtn'), - ).resolves.toBeInTheDocument(); + expect(screen.getByText('John Doe')).toBeInTheDocument(); + expect(screen.queryByText('Jane Doe')).toBeInTheDocument(); }); - userEvent.click(screen.getByTestId('deleteyesbtn')); + await waitFor(() => { - return expect(toast.success).toHaveBeenCalledWith( - translations.pledgeDeleted, - ); + expect(screen.getAllByTestId('amountCell')[0]).toHaveTextContent('100'); }); }); - it('toasts an error on unsuccessful pledge deletion', async () => { - render( - - - - - - {} - - - - - , - ); - await wait(); - await waitFor(() => - expect(screen.getAllByTestId('deletePledgeBtn')[0]).toBeInTheDocument(), - ); - userEvent.click(screen.getAllByTestId('deletePledgeBtn')[0]); + + it('Sort the Pledges list by earliest endDate', async () => { + renderFundCampaignPledge(link1); + + const searchVolunteer = await screen.findByTestId('searchVolunteer'); + expect(searchVolunteer).toBeInTheDocument(); + + fireEvent.click(screen.getByTestId('filter')); + fireEvent.click(screen.getByTestId('endDate_ASC')); + await waitFor(() => { - return expect( - screen.findByTestId('deletePledgeCloseBtn'), - ).resolves.toBeInTheDocument(); + expect(screen.getByText('John Doe')).toBeInTheDocument(); + expect(screen.queryByText('Jane Doe')).toBeInTheDocument(); }); - userEvent.click(screen.getByTestId('deleteyesbtn')); + await waitFor(() => { - return expect(toast.error).toHaveBeenCalled(); + expect(screen.getAllByTestId('amountCell')[0]).toHaveTextContent('200'); }); }); - it('renders the empty pledge component', async () => { - render( - - - - - - {} - - - - - , - ); - await wait(); - await waitFor(() => - expect(screen.getByText(translations.noPledges)).toBeInTheDocument(), - ); + + it('check if user image renders', async () => { + renderFundCampaignPledge(link1); + await waitFor(() => { + expect(screen.getByTestId('searchVolunteer')).toBeInTheDocument(); + }); + + const image = await screen.findByAltText('volunteer'); + expect(image).toBeInTheDocument(); + expect(image).toHaveAttribute('src', 'img-url'); }); }); diff --git a/src/screens/FundCampaignPledge/FundCampaignPledge.tsx b/src/screens/FundCampaignPledge/FundCampaignPledge.tsx index 9b983ed2b8..c8d1c95d5b 100644 --- a/src/screens/FundCampaignPledge/FundCampaignPledge.tsx +++ b/src/screens/FundCampaignPledge/FundCampaignPledge.tsx @@ -1,62 +1,56 @@ -import { useMutation, useQuery } from '@apollo/client'; -import { WarningAmberRounded } from '@mui/icons-material'; -import { - CREATE_PlEDGE, - DELETE_PLEDGE, - UPDATE_PLEDGE, -} from 'GraphQl/Mutations/PledgeMutation'; +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 dayjs from 'dayjs'; -import React, { useCallback, useMemo, useState, type ChangeEvent } from 'react'; -import { Button, Col, Row } from 'react-bootstrap'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import { Button, Dropdown, Form } from 'react-bootstrap'; import { useTranslation } from 'react-i18next'; -import { useParams } from 'react-router-dom'; -import { toast } from 'react-toastify'; +import { Navigate, useParams } from 'react-router-dom'; import { currencySymbols } from 'utils/currency'; +import styles from './FundCampaignPledge.module.css'; +import PledgeDeleteModal from './PledgeDeleteModal'; +import PledgeModal from './PledgeModal'; +import { Stack } 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 { - InterfaceCreatePledge, InterfacePledgeInfo, + InterfacePledgeVolunteer, InterfaceQueryFundCampaignsPledges, } from 'utils/interfaces'; -import type { ApolloQueryResult } from '@apollo/client'; -import useLocalStorage from 'utils/useLocalstorage'; -import styles from './FundCampaignPledge.module.css'; -import PledgeCreateModal from './PledgeCreateModal'; -import PledgeDeleteModal from './PledgeDeleteModal'; -import PledgeEditModal from './PledgeEditModal'; enum Modal { - CREATE = 'create', - UPDATE = 'update', + SAME = 'same', DELETE = 'delete', } const fundCampaignPledge = (): JSX.Element => { - const { fundCampaignId: currentUrl } = useParams(); - const { getItem } = useLocalStorage(); const { t } = useTranslation('translation', { keyPrefix: 'pledges', }); const { t: tCommon } = useTranslation('common'); + const { fundCampaignId, orgId } = useParams(); + if (!fundCampaignId || !orgId) { + return ; + } + const [modalState, setModalState] = useState<{ [key in Modal]: boolean }>({ - [Modal.CREATE]: false, - [Modal.UPDATE]: false, + [Modal.SAME]: false, [Modal.DELETE]: false, }); + const [pledgeModalMode, setPledgeModalMode] = useState<'edit' | 'create'>( + 'create', + ); const [pledge, setPledge] = useState(null); + const [searchTerm, setSearchTerm] = useState(''); - const [formState, setFormState] = useState({ - pledgeAmount: 0, - pledgeCurrency: 'USD', - pledgeEndDate: new Date(), - pledgeStartDate: new Date(), - }); - const [createPledge] = useMutation(CREATE_PlEDGE); - const [updatePledge] = useMutation(UPDATE_PLEDGE); - const [deletePledge] = useMutation(DELETE_PLEDGE); + const [sortBy, setSortBy] = useState< + 'amount_ASC' | 'amount_DESC' | 'endDate_ASC' | 'endDate_DESC' + >('endDate_DESC'); const { data: pledgeData, @@ -76,13 +70,31 @@ const fundCampaignPledge = (): JSX.Element => { >; } = useQuery(FUND_CAMPAIGN_PLEDGE, { variables: { - id: currentUrl, + id: fundCampaignId, + orderBy: sortBy, }, }); + const endDate = dayjs( + pledgeData?.getFundraisingCampaignById?.endDate, + 'YYYY-MM-DD', + ).toDate(); + const pledges = useMemo(() => { - return pledgeData?.getFundraisingCampaignById.pledges ?? []; - }, [pledgeData]); + return ( + pledgeData?.getFundraisingCampaignById.pledges.filter((pledge) => { + const search = searchTerm.toLowerCase(); + return pledge.users.some((user) => { + const fullName = `${user.firstName} ${user.lastName}`; + return fullName.toLowerCase().includes(search); + }); + }) ?? [] + ); + }, [pledgeData, searchTerm]); + + useEffect(() => { + refetchPledge(); + }, [sortBy, refetchPledge]); const openModal = (modal: Modal): void => { setModalState((prevState) => ({ ...prevState, [modal]: true })); @@ -92,19 +104,15 @@ const fundCampaignPledge = (): JSX.Element => { setModalState((prevState) => ({ ...prevState, [modal]: false })); }; - const handleEditClick = useCallback( - (pledge: InterfacePledgeInfo): void => { - setFormState({ - pledgeAmount: pledge.amount, - pledgeCurrency: pledge.currency, - pledgeEndDate: new Date(pledge.endDate), - pledgeStartDate: new Date(pledge.startDate), - }); + const handleOpenModal = useCallback( + (pledge: InterfacePledgeInfo | null, mode: 'edit' | 'create'): void => { setPledge(pledge); - openModal(Modal.UPDATE); + setPledgeModalMode(mode); + openModal(Modal.SAME); }, [openModal], ); + const handleDeleteClick = useCallback( (pledge: InterfacePledgeInfo): void => { setPledge(pledge); @@ -112,91 +120,7 @@ const fundCampaignPledge = (): JSX.Element => { }, [openModal], ); - const createPledgeHandler = async ( - e: ChangeEvent, - ): Promise => { - try { - e.preventDefault(); - const userId = getItem('id'); - await createPledge({ - variables: { - campaignId: currentUrl, - amount: formState.pledgeAmount, - currency: formState.pledgeCurrency, - startDate: dayjs(formState.pledgeStartDate).format('YYYY-MM-DD'), - endDate: dayjs(formState.pledgeEndDate).format('YYYY-MM-DD'), - userIds: [userId], - }, - }); - await refetchPledge(); - closeModal(Modal.CREATE); - toast.success(t('pledgeCreated')); - setFormState({ - pledgeAmount: 0, - pledgeCurrency: 'USD', - pledgeEndDate: new Date(), - pledgeStartDate: new Date(), - }); - } catch (error: unknown) { - toast.error((error as Error).message); - } - }; - const updatePledgeHandler = async ( - e: ChangeEvent, - ): Promise => { - try { - e.preventDefault(); - const updatedFields: { [key: string]: number | string | undefined } = {}; - if (formState.pledgeAmount !== pledge?.amount) { - updatedFields.amount = formState.pledgeAmount; - } - if (formState.pledgeCurrency !== pledge?.currency) { - updatedFields.currency = formState.pledgeCurrency; - } - if ( - dayjs(formState.pledgeStartDate).format('YYYY-MM-DD') !== - dayjs(pledge?.startDate).format('YYYY-MM-DD') - ) { - updatedFields.startDate = dayjs(formState.pledgeStartDate).format( - 'YYYY-MM-DD', - ); - } - if ( - dayjs(formState.pledgeEndDate).format('YYYY-MM-DD') !== - dayjs(pledge?.endDate).format('YYYY-MM-DD') - ) { - updatedFields.endDate = dayjs(formState.pledgeEndDate).format( - 'YYYY-MM-DD', - ); - } - await updatePledge({ - variables: { - id: pledge?._id, - ...updatedFields, - }, - }); - await refetchPledge(); - closeModal(Modal.UPDATE); - toast.success(t('pledgeUpdated')); - } catch (error: unknown) { - toast.error((error as Error).message); - } - }; - const deleteHandler = async (): Promise => { - try { - await deletePledge({ - variables: { - id: pledge?._id, - }, - }); - await refetchPledge(); - closeModal(Modal.DELETE); - toast.success(t('pledgeDeleted')); - } catch (error: unknown) { - toast.error((error as Error).message); - } - }; if (pledgeLoading) return ; if (pledgeError) { return ( @@ -212,139 +136,277 @@ const fundCampaignPledge = (): JSX.Element => { ); } - return ( -
- -
-
- - -
{t('volunteers')}
- - -
{tCommon('startDate')}
- - -
{tCommon('endDate')}
- - -
{t('pledgeAmount')}
- - -
{t('pledgeOptions')}
- -
-
- {pledges.map((pledge, index) => ( -
- - -
- {pledge.users.map((user, index) => ( - - {user.firstName} - {index !== pledge.users.length - 1 && ', '} - - ))} -
- - -
- {dayjs(pledge.startDate).format('DD/MM/YYYY')} -
- - -
{dayjs(pledge.endDate).format('DD/MM/YYYY')}
- - -
- { - currencySymbols[ - pledge.currency as keyof typeof currencySymbols - ] - } - {pledge.amount} -
- - - - - -
- {pledges && index !== pledges.length - 1 && ( -
- )} -
- ))} - {pledges.length === 0 && ( -
-
{t('noPledges')}
-
+ const columns: GridColDef[] = [ + { + field: 'volunteers', + headerName: 'Volunteers', + flex: 2, + minWidth: 50, + align: 'left', + headerAlign: 'center', + headerClassName: `${styles.tableHeader}`, + sortable: false, + renderCell: (params: GridCellParams) => { + return ( +
+ {params.row.users.map( + (user: InterfacePledgeVolunteer, index: number) => ( +
+ {user.image ? ( + volunteer + ) : ( +
+ +
+ )} + + {user.firstName + ' ' + user.lastName} + +
+ ), )}
+ ); + }, + }, + { + 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.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.endDate).format('DD/MM/YYYY'); + }, + }, + { + field: 'amount', + headerName: 'Pledge Amount', + 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 + ] + } + {params.row.amount} +
+ ); + }, + }, + { + field: 'action', + headerName: 'Action', + flex: 1, + minWidth: 100, + align: 'center', + headerAlign: 'center', + headerClassName: `${styles.tableHeader}`, + sortable: false, + renderCell: (params: GridCellParams) => { + return ( + <> + + + + ); + }, + }, + ]; + + return ( +
+
+
+ setSearchTerm(e.target.value)} + data-testid="searchVolunteer" + /> + +
+
+
+ + + + {tCommon('sort')} + + + setSortBy('amount_ASC')} + data-testid="amount_ASC" + > + {t('lowestAmount')} + + setSortBy('amount_DESC')} + data-testid="amount_DESC" + > + {t('highestAmount')} + + setSortBy('endDate_DESC')} + data-testid="endDate_DESC" + > + {t('latestEndDate')} + + setSortBy('endDate_ASC')} + data-testid="endDate_ASC" + > + {t('earliestEndDate')} + + + +
+
+ +
- {/* Create Pledge Modal */} - closeModal(Modal.CREATE)} - formState={formState} - setFormState={setFormState} - createPledgeHandler={createPledgeHandler} - startDate={pledgeData?.getFundraisingCampaignById.startDate as Date} - endDate={pledgeData?.getFundraisingCampaignById.endDate as Date} - t={t} - tCommon={tCommon} + row._id} + components={{ + NoRowsOverlay: () => ( + + {t('noPledges')} + + ), + }} + sx={{ + '&.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', + }, + }} + getRowClassName={() => `${styles.rowBackground}`} + autoHeight + rowHeight={65} + rows={pledges.map((pledge) => ({ + _id: pledge._id, + users: pledge.users, + startDate: pledge.startDate, + endDate: pledge.endDate, + amount: pledge.amount, + currency: pledge.currency, + }))} + columns={columns} + isRowSelectable={() => false} /> {/* Update Pledge Modal */} - closeModal(Modal.UPDATE)} - formState={formState} - setFormState={setFormState} - updatePledgeHandler={updatePledgeHandler} - startDate={pledgeData?.getFundraisingCampaignById.startDate as Date} + closeModal(Modal.SAME)} + campaignId={fundCampaignId} + orgId={orgId} + pledge={pledge} + refetchPledge={refetchPledge} endDate={pledgeData?.getFundraisingCampaignById.endDate as Date} - t={t} - tCommon={tCommon} + mode={pledgeModalMode} /> {/* Delete Pledge Modal */} closeModal(Modal.DELETE)} - deletePledgeHandler={deleteHandler} - t={t} - tCommon={tCommon} + isOpen={modalState[Modal.DELETE]} + hide={() => closeModal(Modal.DELETE)} + pledge={pledge} + refetchPledge={refetchPledge} />
); diff --git a/src/screens/FundCampaignPledge/PledgeCreateModal.tsx b/src/screens/FundCampaignPledge/PledgeCreateModal.tsx deleted file mode 100644 index 53d78249a8..0000000000 --- a/src/screens/FundCampaignPledge/PledgeCreateModal.tsx +++ /dev/null @@ -1,150 +0,0 @@ -import { DatePicker } from '@mui/x-date-pickers'; -import dayjs, { type Dayjs } from 'dayjs'; -import { type ChangeEvent } from 'react'; -import { Button, Form, Modal } from 'react-bootstrap'; -import { currencyOptions } from 'utils/currency'; -import type { InterfaceCreatePledge } from 'utils/interfaces'; -import styles from './FundCampaignPledge.module.css'; -import React from 'react'; - -interface InterfaceCreatePledgeModal { - createCamapignModalIsOpen: boolean; - hideCreateCampaignModal: () => void; - formState: InterfaceCreatePledge; - setFormState: (state: React.SetStateAction) => void; - createPledgeHandler: (e: ChangeEvent) => Promise; - startDate: Date; - endDate: Date; - t: (key: string) => string; - tCommon: (key: string) => string; -} - -const PledgeCreateModal: React.FC = ({ - createCamapignModalIsOpen, - hideCreateCampaignModal, - formState, - setFormState, - createPledgeHandler, - startDate, - endDate, - t, - tCommon, -}) => { - return ( - <> - - -

{t('createPledge')}

- -
- -
- -
- { - if (date) { - setFormState({ - ...formState, - pledgeStartDate: date.toDate(), - pledgeEndDate: - formState.pledgeEndDate && - (formState.pledgeEndDate < date?.toDate() - ? date.toDate() - : formState.pledgeEndDate), - }); - } - }} - minDate={dayjs(startDate)} - /> -
-
- { - if (date) { - setFormState({ - ...formState, - pledgeEndDate: date.toDate(), - }); - } - }} - minDate={dayjs(startDate)} - /> -
-
- - - {t('currency')} -
- { - setFormState({ - ...formState, - pledgeCurrency: e.target.value, - }); - }} - > - - {currencyOptions.map((currency) => ( - - ))} - -
-
- - {t('amount')} - { - if (parseInt(e.target.value) > 0) { - setFormState({ - ...formState, - pledgeAmount: parseInt(e.target.value), - }); - } - }} - /> - -
- -
-
-
- - ); -}; -export default PledgeCreateModal; diff --git a/src/screens/FundCampaignPledge/PledgeDeleteModal.test.tsx b/src/screens/FundCampaignPledge/PledgeDeleteModal.test.tsx new file mode 100644 index 0000000000..c9d5c5f50e --- /dev/null +++ b/src/screens/FundCampaignPledge/PledgeDeleteModal.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 type { InterfaceDeletePledgeModal } from './PledgeDeleteModal'; +import PledgeDeleteModal from './PledgeDeleteModal'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import i18nForTest from '../../utils/i18nForTest'; +import { MOCKS_DELETE_PLEDGE_ERROR, MOCKS } from './PledgesMocks'; +import { StaticMockLink } from 'utils/StaticMockLink'; +import { toast } from 'react-toastify'; + +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 link = new StaticMockLink(MOCKS); +const link2 = new StaticMockLink(MOCKS_DELETE_PLEDGE_ERROR); +const translations = JSON.parse( + JSON.stringify(i18nForTest.getDataByLanguage('en')?.translation.pledges), +); + +const pledgeProps: InterfaceDeletePledgeModal = { + isOpen: true, + hide: jest.fn(), + pledge: { + _id: '1', + amount: 100, + currency: 'USD', + startDate: '2024-01-01', + endDate: '2024-01-10', + users: [ + { + _id: '1', + firstName: 'John', + lastName: 'Doe', + image: 'img-url', + }, + ], + }, + refetchPledge: jest.fn(), +}; +const renderPledgeDeleteModal = ( + link: ApolloLink, + props: InterfaceDeletePledgeModal, +): RenderResult => { + return render( + + + + + + + + + + + , + ); +}; + +describe('PledgeDeleteModal', () => { + it('should render PledgeDeleteModal', () => { + renderPledgeDeleteModal(link, pledgeProps); + expect(screen.getByTestId('deletePledgeCloseBtn')).toBeInTheDocument(); + }); + + it('should successfully Delete pledge', async () => { + renderPledgeDeleteModal(link, pledgeProps); + expect(screen.getByTestId('deletePledgeCloseBtn')).toBeInTheDocument(); + + fireEvent.click(screen.getByTestId('deleteyesbtn')); + + await waitFor(() => { + expect(pledgeProps.refetchPledge).toHaveBeenCalled(); + expect(pledgeProps.hide).toHaveBeenCalled(); + expect(toast.success).toHaveBeenCalledWith(translations.pledgeDeleted); + }); + }); + + it('should fail to Delete pledge', async () => { + renderPledgeDeleteModal(link2, pledgeProps); + expect(screen.getByTestId('deletePledgeCloseBtn')).toBeInTheDocument(); + + fireEvent.click(screen.getByTestId('deleteyesbtn')); + + await waitFor(() => { + expect(toast.error).toHaveBeenCalledWith('Error deleting pledge'); + }); + }); +}); diff --git a/src/screens/FundCampaignPledge/PledgeDeleteModal.tsx b/src/screens/FundCampaignPledge/PledgeDeleteModal.tsx index 2cc7fdeaff..a16d0df905 100644 --- a/src/screens/FundCampaignPledge/PledgeDeleteModal.tsx +++ b/src/screens/FundCampaignPledge/PledgeDeleteModal.tsx @@ -1,33 +1,54 @@ import { Button, Modal } from 'react-bootstrap'; import styles from './FundCampaignPledge.module.css'; import React from 'react'; +import { useTranslation } from 'react-i18next'; +import { useMutation } from '@apollo/client'; +import { DELETE_PLEDGE } from 'GraphQl/Mutations/PledgeMutation'; +import type { InterfacePledgeInfo } from 'utils/interfaces'; +import { toast } from 'react-toastify'; -interface InterfaceDeletePledgeModal { - deletePledgeModalIsOpen: boolean; - hideDeletePledgeModal: () => void; - deletePledgeHandler: () => Promise; - t: (key: string) => string; - tCommon: (key: string) => string; +export interface InterfaceDeletePledgeModal { + isOpen: boolean; + hide: () => void; + pledge: InterfacePledgeInfo | null; + refetchPledge: () => void; } const PledgeDeleteModal: React.FC = ({ - deletePledgeModalIsOpen, - hideDeletePledgeModal, - deletePledgeHandler, - t, - tCommon, + isOpen, + hide, + pledge, + refetchPledge, }) => { + const { t } = useTranslation('translation', { + keyPrefix: 'pledges', + }); + const { t: tCommon } = useTranslation('common'); + + const [deletePledge] = useMutation(DELETE_PLEDGE); + + const deleteHandler = async (): Promise => { + try { + await deletePledge({ + variables: { + id: pledge?._id, + }, + }); + refetchPledge(); + hide(); + toast.success(t('pledgeDeleted')); + } catch (error: unknown) { + toast.error((error as Error).message); + } + }; return ( <> - +

{t('deletePledge')}

- diff --git a/src/screens/FundCampaignPledge/PledgeEditModal.tsx b/src/screens/FundCampaignPledge/PledgeEditModal.tsx deleted file mode 100644 index af1e743221..0000000000 --- a/src/screens/FundCampaignPledge/PledgeEditModal.tsx +++ /dev/null @@ -1,149 +0,0 @@ -import { DatePicker } from '@mui/x-date-pickers'; -import dayjs, { type Dayjs } from 'dayjs'; -import type { ChangeEvent } from 'react'; -import { Button, Form, Modal } from 'react-bootstrap'; -import { currencyOptions } from 'utils/currency'; -import type { InterfaceCreatePledge } from 'utils/interfaces'; -import styles from './FundCampaignPledge.module.css'; -import React from 'react'; - -interface InterfaceUpdatePledgeModal { - updatePledgeModalIsOpen: boolean; - hideUpdatePledgeModal: () => void; - formState: InterfaceCreatePledge; - setFormState: (state: React.SetStateAction) => void; - updatePledgeHandler: (e: ChangeEvent) => Promise; - startDate: Date; - endDate: Date; - t: (key: string) => string; - tCommon: (key: string) => string; -} -const PledgeEditModal: React.FC = ({ - updatePledgeModalIsOpen, - hideUpdatePledgeModal, - formState, - setFormState, - updatePledgeHandler, - startDate, - endDate, - t, - tCommon, -}) => { - return ( - <> - - -

{t('editPledge')}

- -
- -
- -
- { - if (date) { - setFormState({ - ...formState, - pledgeStartDate: date.toDate(), - pledgeEndDate: - formState.pledgeEndDate && - (formState.pledgeEndDate < date?.toDate() - ? date.toDate() - : formState.pledgeEndDate), - }); - } - }} - minDate={dayjs(startDate)} - /> -
-
- { - if (date) { - setFormState({ - ...formState, - pledgeEndDate: date.toDate(), - }); - } - }} - minDate={dayjs(startDate)} - /> -
-
- - - {t('currency')} -
- { - setFormState({ - ...formState, - pledgeCurrency: e.target.value, - }); - }} - > - - {currencyOptions.map((currency) => ( - - ))} - -
-
- - {t('amount')} - { - if (parseInt(e.target.value) > 0) { - setFormState({ - ...formState, - pledgeAmount: parseInt(e.target.value), - }); - } - }} - /> - -
- -
-
-
- - ); -}; -export default PledgeEditModal; diff --git a/src/screens/FundCampaignPledge/PledgeModal.test.tsx b/src/screens/FundCampaignPledge/PledgeModal.test.tsx new file mode 100644 index 0000000000..cd1e417f0a --- /dev/null +++ b/src/screens/FundCampaignPledge/PledgeModal.test.tsx @@ -0,0 +1,213 @@ +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 { PLEDGE_MODAL_MOCKS } from './PledgesMocks'; +import { StaticMockLink } from 'utils/StaticMockLink'; +import { toast } from 'react-toastify'; +import type { InterfacePledgeModal } from './PledgeModal'; +import PledgeModal from './PledgeModal'; +import React from 'react'; + +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(PLEDGE_MODAL_MOCKS); +const translations = JSON.parse( + JSON.stringify(i18nForTest.getDataByLanguage('en')?.translation.pledges), +); + +const pledgeProps: InterfacePledgeModal[] = [ + { + isOpen: true, + hide: jest.fn(), + pledge: { + _id: '1', + amount: 100, + currency: 'USD', + startDate: '2024-01-01', + endDate: '2024-01-10', + users: [ + { + _id: '1', + firstName: 'John', + lastName: 'Doe', + image: null, + }, + ], + }, + refetchPledge: jest.fn(), + campaignId: 'campaignId', + orgId: 'orgId', + endDate: new Date(), + mode: 'create', + }, + { + isOpen: true, + hide: jest.fn(), + pledge: { + _id: '1', + amount: 100, + currency: 'USD', + startDate: '2024-01-01', + endDate: '2024-01-10', + users: [ + { + _id: '1', + firstName: 'John', + lastName: 'Doe', + image: null, + }, + ], + }, + refetchPledge: jest.fn(), + campaignId: 'campaignId', + orgId: 'orgId', + endDate: new Date(), + mode: 'edit', + }, +]; +const renderPledgeModal = ( + link: ApolloLink, + props: InterfacePledgeModal, +): RenderResult => { + return render( + + + + + + + + + + + , + ); +}; + +describe('PledgeModal', () => { + beforeAll(() => { + jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useParams: () => ({ orgId: 'orgId', fundCampaignId: 'fundCampaignId' }), + })); + }); + + afterAll(() => { + jest.clearAllMocks(); + }); + + afterEach(() => { + cleanup(); + }); + it('should populate form fields with correct values in edit mode', async () => { + renderPledgeModal(link1, pledgeProps[1]); + await waitFor(() => + expect(screen.getByText(translations.editPledge)).toBeInTheDocument(), + ); + expect(screen.getByTestId('volunteerSelect')).toHaveTextContent('John Doe'); + expect(screen.getByLabelText('Start Date')).toHaveValue('01/01/2024'); + expect(screen.getByLabelText('End Date')).toHaveValue('10/01/2024'); + expect(screen.getByLabelText('Currency')).toHaveTextContent('USD ($)'); + expect(screen.getByLabelText('Amount')).toHaveValue('100'); + }); + + it('should update pledgeAmount when input value changes', async () => { + renderPledgeModal(link1, pledgeProps[1]); + const amountInput = screen.getByLabelText('Amount'); + expect(amountInput).toHaveValue('100'); + fireEvent.change(amountInput, { target: { value: '200' } }); + expect(amountInput).toHaveValue('200'); + }); + + it('should not update pledgeAmount when input value is less than or equal to 0', async () => { + renderPledgeModal(link1, pledgeProps[1]); + const amountInput = screen.getByLabelText('Amount'); + expect(amountInput).toHaveValue('100'); + fireEvent.change(amountInput, { target: { value: '-10' } }); + expect(amountInput).toHaveValue('100'); + }); + + it('should update pledgeStartDate when a new date is selected', async () => { + renderPledgeModal(link1, pledgeProps[1]); + const startDateInput = screen.getByLabelText('Start Date'); + fireEvent.change(startDateInput, { target: { value: '02/01/2024' } }); + expect(startDateInput).toHaveValue('02/01/2024'); + expect(pledgeProps[1].pledge?.startDate).toEqual('2024-01-01'); + }); + + it('pledgeStartDate onChange when its null', async () => { + renderPledgeModal(link1, pledgeProps[1]); + const startDateInput = screen.getByLabelText('Start Date'); + fireEvent.change(startDateInput, { target: { value: null } }); + expect(pledgeProps[1].pledge?.startDate).toEqual('2024-01-01'); + }); + + it('should update pledgeEndDate when a new date is selected', async () => { + renderPledgeModal(link1, pledgeProps[1]); + const startDateInput = screen.getByLabelText('End Date'); + fireEvent.change(startDateInput, { target: { value: '02/01/2024' } }); + expect(startDateInput).toHaveValue('02/01/2024'); + expect(pledgeProps[1].pledge?.endDate).toEqual('2024-01-10'); + }); + + it('pledgeEndDate onChange when its null', async () => { + renderPledgeModal(link1, pledgeProps[1]); + const endDateInput = screen.getByLabelText('End Date'); + fireEvent.change(endDateInput, { target: { value: null } }); + expect(pledgeProps[1].pledge?.endDate).toEqual('2024-01-10'); + }); + + it('should create pledge', async () => { + renderPledgeModal(link1, pledgeProps[0]); + + fireEvent.change(screen.getByLabelText('Amount'), { + target: { value: '200' }, + }); + fireEvent.change(screen.getByLabelText('Start Date'), { + target: { value: '02/01/2024' }, + }); + fireEvent.change(screen.getByLabelText('End Date'), { + target: { value: '02/01/2024' }, + }); + + expect(screen.getByLabelText('Amount')).toHaveValue('200'); + expect(screen.getByLabelText('Start Date')).toHaveValue('02/01/2024'); + expect(screen.getByLabelText('End Date')).toHaveValue('02/01/2024'); + expect(screen.getByTestId('submitPledgeBtn')).toBeInTheDocument(); + + fireEvent.click(screen.getByTestId('submitPledgeBtn')); + + await waitFor(() => { + expect(toast.success).toHaveBeenCalled(); + expect(pledgeProps[0].refetchPledge).toHaveBeenCalled(); + expect(pledgeProps[0].hide).toHaveBeenCalled(); + }); + }); +}); diff --git a/src/screens/FundCampaignPledge/PledgeModal.tsx b/src/screens/FundCampaignPledge/PledgeModal.tsx new file mode 100644 index 0000000000..2fca2379b4 --- /dev/null +++ b/src/screens/FundCampaignPledge/PledgeModal.tsx @@ -0,0 +1,322 @@ +import { DatePicker } from '@mui/x-date-pickers'; +import dayjs, { type Dayjs } from 'dayjs'; +import type { ChangeEvent } from 'react'; +import { Button, Form, Modal } from 'react-bootstrap'; +import { currencyOptions, currencySymbols } from 'utils/currency'; +import type { + InterfaceCreatePledge, + InterfacePledgeInfo, + InterfacePledgeVolunteer, +} from 'utils/interfaces'; +import styles from './FundCampaignPledge.module.css'; +import React, { useCallback, useEffect, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useMutation, useQuery } from '@apollo/client'; +import { CREATE_PlEDGE, UPDATE_PLEDGE } from 'GraphQl/Mutations/PledgeMutation'; +import { toast } from 'react-toastify'; +import { + Autocomplete, + FormControl, + InputLabel, + MenuItem, + Select, + TextField, +} from '@mui/material'; + +import { MEMBERS_LIST } from 'GraphQl/Queries/Queries'; + +export interface InterfacePledgeModal { + isOpen: boolean; + hide: () => void; + campaignId: string; + orgId: string; + pledge: InterfacePledgeInfo | null; + refetchPledge: () => void; + endDate: Date; + mode: 'create' | 'edit'; +} +const PledgeModal: React.FC = ({ + isOpen, + hide, + campaignId, + orgId, + pledge, + refetchPledge, + endDate, + mode, +}) => { + const { t } = useTranslation('translation', { + keyPrefix: 'pledges', + }); + const { t: tCommon } = useTranslation('common'); + + const [formState, setFormState] = useState({ + pledgeUsers: [], + pledgeAmount: pledge?.amount ?? 0, + pledgeCurrency: pledge?.currency ?? 'USD', + pledgeEndDate: new Date(pledge?.endDate ?? new Date()), + pledgeStartDate: new Date(pledge?.startDate ?? new Date()), + }); + const [volunteers, setVolunteers] = useState([]); + const [updatePledge] = useMutation(UPDATE_PLEDGE); + const [createPledge] = useMutation(CREATE_PlEDGE); + + const { data: memberData } = useQuery(MEMBERS_LIST, { + variables: { id: orgId }, + }); + + useEffect(() => { + setFormState({ + pledgeUsers: pledge?.users ?? [], + pledgeAmount: pledge?.amount ?? 0, + pledgeCurrency: pledge?.currency ?? 'USD', + pledgeEndDate: new Date(pledge?.endDate ?? new Date()), + pledgeStartDate: new Date(pledge?.startDate ?? new Date()), + }); + }, [pledge]); + + useEffect(() => { + if (memberData) { + /*istanbul ignore next*/ + setVolunteers(memberData.organizations[0].members); + } + }, [memberData]); + + const { + pledgeUsers, + pledgeAmount, + pledgeCurrency, + pledgeStartDate, + pledgeEndDate, + } = formState; + + /*istanbul ignore next*/ + const updatePledgeHandler = useCallback( + async (e: ChangeEvent): Promise => { + e.preventDefault(); + const startDate = dayjs(pledgeStartDate).format('YYYY-MM-DD'); + const endDate = dayjs(pledgeEndDate).format('YYYY-MM-DD'); + + const updatedFields: { + [key: string]: number | string | string[] | undefined; + } = {}; + // checks if there are changes to the pledge and adds them to the updatedFields object + if (pledgeAmount !== pledge?.amount) { + updatedFields.amount = pledgeAmount; + } + if (pledgeCurrency !== pledge?.currency) { + updatedFields.currency = pledgeCurrency; + } + if (startDate !== dayjs(pledge?.startDate).format('YYYY-MM-DD')) { + updatedFields.startDate = startDate; + } + if (endDate !== dayjs(pledge?.endDate).format('YYYY-MM-DD')) { + updatedFields.endDate = endDate; + } + if (pledgeUsers !== pledge?.users) { + updatedFields.users = pledgeUsers.map((user) => user._id); + } + try { + await updatePledge({ + variables: { + id: pledge?._id, + ...updatedFields, + }, + }); + toast.success(t('pledgeUpdated')); + refetchPledge(); + hide(); + } catch (error: unknown) { + toast.error((error as Error).message); + } + }, + [formState, pledge], + ); + + // Function to create a new pledge + const createPledgeHandler = useCallback( + async (e: ChangeEvent): Promise => { + try { + e.preventDefault(); + await createPledge({ + variables: { + campaignId, + amount: pledgeAmount, + currency: pledgeCurrency, + startDate: dayjs(pledgeStartDate).format('YYYY-MM-DD'), + endDate: dayjs(pledgeEndDate).format('YYYY-MM-DD'), + userIds: pledgeUsers.map((user) => user._id), + }, + }); + + toast.success(t('pledgeCreated')); + refetchPledge(); + setFormState({ + pledgeUsers: [], + pledgeAmount: 0, + pledgeCurrency: 'USD', + pledgeEndDate: new Date(), + pledgeStartDate: new Date(), + }); + hide(); + } catch (error: unknown) { + /*istanbul ignore next*/ + toast.error((error as Error).message); + } + }, + [formState, campaignId], + ); + + return ( + + +

+ {t(mode === 'edit' ? 'editPledge' : 'createPledge')} +

+ +
+ +
+ {/* A Multi-select dropdown enables admin to select more than one volunteer for participating in a pledge */} + + option._id === value._id} + filterSelectedOptions={true} + getOptionLabel={(member: InterfacePledgeVolunteer): string => + `${member.firstName} ${member.lastName}` + } + onChange={ + /*istanbul ignore next*/ + (_, newVolunteers): void => { + setFormState({ + ...formState, + pledgeUsers: newVolunteers, + }); + } + } + renderInput={(params) => ( + + )} + /> + + + {/* Date Calendar Component to select start date of an event */} + { + if (date) { + setFormState({ + ...formState, + pledgeStartDate: date.toDate(), + pledgeEndDate: + pledgeEndDate && + /*istanbul ignore next*/ + (pledgeEndDate < date?.toDate() + ? date.toDate() + : pledgeEndDate), + }); + } + }} + minDate={dayjs(pledgeStartDate)} + maxDate={dayjs(endDate)} + /> + {/* Date Calendar Component to select end Date of an event */} + { + if (date) { + setFormState({ + ...formState, + pledgeEndDate: date.toDate(), + }); + } + }} + minDate={dayjs(pledgeStartDate)} + maxDate={dayjs(endDate)} + /> + + + {/* Dropdown to select the currency in which amount is to be pledged */} + + + {t('currency')} + + + + {/* Input field to enter amount to be pledged */} + + { + if (parseInt(e.target.value) > 0) { + setFormState({ + ...formState, + pledgeAmount: parseInt(e.target.value), + }); + } + }} + /> + + + {/* Button to submit the pledge form */} + +
+
+
+ ); +}; +export default PledgeModal; diff --git a/src/screens/FundCampaignPledge/PledgesMocks.ts b/src/screens/FundCampaignPledge/PledgesMocks.ts index e5d668d53e..cf07d1cf28 100644 --- a/src/screens/FundCampaignPledge/PledgesMocks.ts +++ b/src/screens/FundCampaignPledge/PledgesMocks.ts @@ -3,6 +3,7 @@ import { DELETE_PLEDGE, UPDATE_PLEDGE, } from 'GraphQl/Mutations/PledgeMutation'; +import { MEMBERS_LIST } from 'GraphQl/Queries/Queries'; import { FUND_CAMPAIGN_PLEDGE } from 'GraphQl/Queries/fundQueries'; export const MOCKS = [ @@ -10,25 +11,28 @@ export const MOCKS = [ request: { query: FUND_CAMPAIGN_PLEDGE, variables: { - id: undefined, + id: 'fundCampaignId', + orderBy: 'endDate_DESC', }, }, result: { data: { getFundraisingCampaignById: { startDate: '2024-01-01', - endDate: '2024-01-01', + endDate: '2024-08-08', pledges: [ { _id: '1', amount: 100, currency: 'USD', startDate: '2024-01-01', - endDate: '2024-01-01', + endDate: '2024-01-10', users: [ { _id: '1', firstName: 'John', + lastName: 'Doe', + image: 'img-url', }, ], }, @@ -36,12 +40,14 @@ export const MOCKS = [ _id: '2', amount: 200, currency: 'USD', - startDate: '2024-03-03', - endDate: '2024-04-03', + startDate: '2024-01-01', + endDate: '2024-01-09', users: [ { _id: '2', firstName: 'Jane', + lastName: 'Doe', + image: null, }, ], }, @@ -52,39 +58,147 @@ export const MOCKS = [ }, { request: { - query: CREATE_PlEDGE, + query: FUND_CAMPAIGN_PLEDGE, variables: { - campaignId: undefined, - amount: 100, - currency: 'USD', - startDate: '2024-03-10', - endDate: '2024-03-10', - userIds: [null], + id: 'fundCampaignId', + orderBy: 'endDate_ASC', }, }, result: { data: { - createFundraisingCampaignPledge: { - _id: '3', + getFundraisingCampaignById: { + startDate: '2024-01-01', + endDate: '2024-08-08', + pledges: [ + { + _id: '2', + amount: 200, + currency: 'USD', + startDate: '2024-01-01', + endDate: '2024-01-09', + users: [ + { + _id: '2', + firstName: 'Jane', + lastName: 'Doe', + image: null, + }, + ], + }, + { + _id: '1', + amount: 100, + currency: 'USD', + startDate: '2024-01-01', + endDate: '2024-01-10', + users: [ + { + _id: '1', + firstName: 'John', + lastName: 'Doe', + image: null, + }, + ], + }, + ], }, }, }, }, { request: { - query: UPDATE_PLEDGE, + query: FUND_CAMPAIGN_PLEDGE, variables: { - id: '1', - amount: 100100, - currency: 'INR', - startDate: '2024-03-10', - endDate: '2024-03-10', + id: 'fundCampaignId', + orderBy: 'amount_DESC', }, }, result: { data: { - updateFundraisingCampaignPledge: { - _id: '1', + getFundraisingCampaignById: { + startDate: '2024-01-01', + endDate: '2024-08-08', + pledges: [ + { + _id: '2', + amount: 200, + currency: 'USD', + startDate: '2024-01-01', + endDate: '2024-01-09', + users: [ + { + _id: '2', + firstName: 'Jane', + lastName: 'Doe', + image: null, + }, + ], + }, + { + _id: '1', + amount: 100, + currency: 'USD', + startDate: '2024-01-01', + endDate: '2024-01-10', + users: [ + { + _id: '1', + firstName: 'John', + lastName: 'Doe', + image: null, + }, + ], + }, + ], + }, + }, + }, + }, + { + request: { + query: FUND_CAMPAIGN_PLEDGE, + variables: { + id: 'fundCampaignId', + orderBy: 'amount_ASC', + }, + }, + result: { + data: { + getFundraisingCampaignById: { + startDate: '2024-01-01', + endDate: '2024-08-08', + pledges: [ + { + _id: '1', + amount: 100, + currency: 'USD', + startDate: '2024-01-01', + endDate: '2024-01-10', + users: [ + { + _id: '1', + firstName: 'John', + lastName: 'Doe', + image: null, + }, + ], + }, + { + _id: '2', + amount: 200, + currency: 'USD', + startDate: '2024-01-01', + endDate: '2024-01-09', + users: [ + { + _id: '2', + firstName: 'Jane', + lastName: 'Doe', + image: null, + }, + ], + }, + ], }, }, }, @@ -105,12 +219,14 @@ export const MOCKS = [ }, }, ]; + export const MOCKS_FUND_CAMPAIGN_PLEDGE_ERROR = [ { request: { query: FUND_CAMPAIGN_PLEDGE, variables: { - id: undefined, + id: 'fundCampaignId', + orderBy: 'endDate_DESC', }, }, error: new Error('Error fetching pledges'), @@ -166,17 +282,18 @@ export const MOCKS_CREATE_PLEDGE_ERROR = [ request: { query: CREATE_PlEDGE, variables: { - campaignId: undefined, - amount: 100, + campaignId: 'campaignId', + amount: 200, currency: 'USD', - startDate: '2024-03-10', - endDate: '2024-03-10', - userIds: null, + startDate: '2024-01-02', + endDate: '2024-01-02', + userIds: ['1'], }, }, error: new Error('Error creating pledge'), }, ]; + export const MOCKS_UPDATE_PLEDGE_ERROR = [ { request: { @@ -236,6 +353,7 @@ export const MOCKS_UPDATE_PLEDGE_ERROR = [ error: new Error('Error updating pledge'), }, ]; + export const MOCKS_DELETE_PLEDGE_ERROR = [ { request: { @@ -291,12 +409,14 @@ export const MOCKS_DELETE_PLEDGE_ERROR = [ error: new Error('Error deleting pledge'), }, ]; + export const EMPTY_MOCKS = [ { request: { query: FUND_CAMPAIGN_PLEDGE, variables: { - id: undefined, + id: 'fundCampaignId', + orderBy: 'endDate_DESC', }, }, result: { @@ -310,3 +430,81 @@ export const EMPTY_MOCKS = [ }, }, ]; + +export const PLEDGE_MODAL_MOCKS = [ + { + request: { + query: MEMBERS_LIST, + variables: { + id: 'orgId', + }, + }, + result: { + data: { + organizations: [ + { + _id: 'orgId', + members: [ + { + createdAt: '2023-04-13T04:53:17.742Z', + email: 'testuser4@example.com', + firstName: 'John', + image: 'img-url', + lastName: 'Doe', + organizationsBlockedBy: [], + __typename: 'User', + _id: '1', + }, + { + createdAt: '2024-04-13T04:53:17.742Z', + email: 'testuser2@example.com', + firstName: 'Anna', + image: null, + lastName: 'Bradley', + organizationsBlockedBy: [], + __typename: 'User', + _id: '2', + }, + ], + }, + ], + }, + }, + }, + { + request: { + query: UPDATE_PLEDGE, + variables: { + id: '1', + amount: 200, + }, + }, + result: { + data: { + updateFundraisingCampaignPledge: { + _id: '1', + }, + }, + }, + }, + { + request: { + query: CREATE_PlEDGE, + variables: { + campaignId: 'campaignId', + amount: 200, + currency: 'USD', + startDate: '2024-01-02', + endDate: '2024-01-02', + userIds: ['1'], + }, + }, + result: { + data: { + createFundraisingCampaignPledge: { + _id: '3', + }, + }, + }, + }, +]; diff --git a/src/screens/LoginPage/LoginPage.tsx b/src/screens/LoginPage/LoginPage.tsx index 56f2d38159..90b2b28b49 100644 --- a/src/screens/LoginPage/LoginPage.tsx +++ b/src/screens/LoginPage/LoginPage.tsx @@ -133,7 +133,7 @@ const loginPage = (): JSX.Element => { id: string; }; tempObj['label'] = - `${org.name}(${org.address.city},${org.address.state},${org.address.countryCode})`; + `${org.name}(${org.address?.city},${org.address?.state},${org.address?.countryCode})`; tempObj['id'] = org._id; return tempObj; }, diff --git a/src/utils/currency.ts b/src/utils/currency.ts index d096177dc5..c7db3002f2 100644 --- a/src/utils/currency.ts +++ b/src/utils/currency.ts @@ -163,7 +163,7 @@ export const currencyOptions = [ { value: 'ZMW', label: 'ZMW' }, // Zambian Kwacha { value: 'ZWD', label: 'ZWD' }, // Zimbabwean Dollar ]; -export const currencySymbols = { +export const currencySymbols: { [key: string]: string } = { AED: 'د.إ', // United Arab Emirates Dirham AFN: '؋', // Afghan Afghani ALL: 'L', // Albanian Lek diff --git a/src/utils/interfaces.ts b/src/utils/interfaces.ts index 1d65b63189..3945003f40 100644 --- a/src/utils/interfaces.ts +++ b/src/utils/interfaces.ts @@ -259,10 +259,7 @@ export interface InterfaceQueryFundCampaignsPledges { currency: string; endDate: string; startDate: string; - users: { - _id: string; - firstName: string; - }[]; + users: InterfacePledgeVolunteer[]; }[]; } export interface InterfaceFundInfo { @@ -289,10 +286,7 @@ export interface InterfacePledgeInfo { currency: string; endDate: string; startDate: string; - users: { - _id: string; - firstName: string; - }[]; + users: InterfacePledgeVolunteer[]; } export interface InterfaceQueryOrganizationEventListItem { _id: string; @@ -436,6 +430,7 @@ export interface InterfaceCreateCampaign { } export interface InterfaceCreatePledge { + pledgeUsers: InterfacePledgeVolunteer[]; pledgeAmount: number; pledgeCurrency: string; pledgeStartDate: Date; @@ -457,6 +452,12 @@ export interface InterfaceQueryMembershipRequestsListItem { }[]; } +export interface InterfacePledgeVolunteer { + _id: string; + firstName: string; + lastName: string; + image: string | null; +} export interface InterfaceAgendaItemCategoryInfo { _id: string; name: string;