From 0b93ef69a7c13031344e6b0903883dcca010f05e Mon Sep 17 00:00:00 2001 From: Alexander Petkov Date: Sat, 1 Jul 2023 18:44:36 +0300 Subject: [PATCH 1/2] donations/grid: Allow admins to edit billingEmail Currently billingEmail is used to link all anonymous donations to a specific user profile. Unfortunately when user donates via bank transaction, and prefers the donation to be anonymous, there is no way to link that donation to user's profile. This commits allows billingEmail to be either set or edited manually, thus linking anonymous donations to user's profile --- src/components/admin/donations/grid/Grid.tsx | 42 ++++++- .../grid/RenderEditBillingEmailCell.tsx | 111 ++++++++++++++++++ src/gql/donations.d.ts | 1 + 3 files changed, 152 insertions(+), 2 deletions(-) create mode 100644 src/components/admin/donations/grid/RenderEditBillingEmailCell.tsx diff --git a/src/components/admin/donations/grid/Grid.tsx b/src/components/admin/donations/grid/Grid.tsx index a1f191d11..138cd49f4 100644 --- a/src/components/admin/donations/grid/Grid.tsx +++ b/src/components/admin/donations/grid/Grid.tsx @@ -24,6 +24,7 @@ import { PersonResponse } from 'gql/person' import { usePersonList } from 'common/hooks/person' import RenderEditPersonCell from './RenderEditPersonCell' import { useStores } from '../../../../common/hooks/useStores' +import RenderEditBillingEmailCell from './RenderEditBillingEmailCell' interface RenderCellProps { params: GridRenderCellParams @@ -97,6 +98,34 @@ export default observer(function Grid() { ) } + const RenderBillingEmaiCell = ({ params }: RenderCellProps) => { + return ( + <> + + {params.row.billingEmail} + {params.isEditable ? ( + + { + if (focusedRowId) { + params.api.startCellEditMode({ id: params.row.id, field: params.field }) + } + params.api.getCellMode(params.row.id, params.field) + setFocusedRowId(params.row.id) + }} + /> + + ) : ( + <> + )} + + + ) + } + const RenderMoneyCell = ({ params }: RenderCellProps) => { return <>{money(params.row.amount, params.row.currency)} } @@ -155,11 +184,20 @@ export default observer(function Grid() { { field: 'billingEmail', headerName: 'Billing Email', - width: 250, + width: 300, + editable: true, + renderCell: (params: GridRenderCellParams) => { + return + }, + + renderEditCell: (params: GridRenderEditCellParams) => { + return + }, }, { field: 'id', headerName: 'ID', + width: 320, }, { field: 'type', @@ -169,7 +207,7 @@ export default observer(function Grid() { field: 'provider', headerName: t('donations:provider'), ...commonProps, - width: 250, + width: 100, }, { field: 'targetVaultId', diff --git a/src/components/admin/donations/grid/RenderEditBillingEmailCell.tsx b/src/components/admin/donations/grid/RenderEditBillingEmailCell.tsx new file mode 100644 index 000000000..784868ec5 --- /dev/null +++ b/src/components/admin/donations/grid/RenderEditBillingEmailCell.tsx @@ -0,0 +1,111 @@ +import React from 'react' +import { AxiosError, AxiosResponse } from 'axios' +import { useMutation } from '@tanstack/react-query' +import { useTranslation } from 'next-i18next' +import { GridRenderEditCellParams } from '@mui/x-data-grid' +import { TextField, Tooltip, Box } from '@mui/material' +import Autocomplete from '@mui/material/Autocomplete' +import { Save } from '@mui/icons-material' +import { PersonResponse } from 'gql/person' +import { DonationResponse, UserDonationInput } from 'gql/donations' +import { useEditDonation } from 'service/donation' +import { ApiErrors } from 'service/apiErrors' +import { AlertStore } from 'stores/AlertStore' + +interface RenderEditCellProps { + params: GridRenderEditCellParams + personList?: PersonResponse[] + onUpdate(): void +} +const addIconStyles = { + background: '#4ac3ff', + borderRadius: '50%', + cursor: 'pointer', + padding: 0.7, + boxShadow: 3, +} + +export default function RenderEditPersonCell({ + params, + personList, + onUpdate, +}: RenderEditCellProps) { + const { t } = useTranslation() + + const initialPerson = { + firstName: params.row.billingEmail, + lastName: '', + email: params.row.email || params.row.billingEmail || null, + } + const [person, setPerson] = React.useState({ + ...initialPerson, + } as PersonResponse) + const mutationFn = useEditDonation(params.row.id) + + const mutation = useMutation< + AxiosResponse, + AxiosError, + UserDonationInput + >({ + mutationFn, + onError: () => AlertStore.show(t('donations:alerts.error'), 'error'), + onSuccess: () => { + AlertStore.show(t('donations:alerts.editDonor'), 'success') + onUpdate() + params.api.stopCellEditMode({ id: params.row.id, field: params.field }) + }, + }) + + const onClick = () => { + if (person) { + const donationData: UserDonationInput = params.row + donationData.billingEmail = person.email + mutation.mutate(donationData) + } else { + AlertStore.show(t('donations:alerts.requiredError'), 'error') + } + } + + return ( + + { + setPerson(newValue) + }} + options={personList || []} + getOptionLabel={(option: PersonResponse) => `${option.email}`} + renderInput={(params) => } + renderOption={(params, option: PersonResponse) => ( + + {`${option.firstName} ${option.lastName} (${option.email ? option.email : ''})`} + + )} + isOptionEqualToValue={(option, value) => option.email === value.email} + filterOptions={(options, state) => { + const displayOptions = options.filter( + (option) => + option.firstName + .toLowerCase() + .trim() + .includes(state.inputValue.toLowerCase().trim()) || + option.email.toLowerCase().trim().includes(state.inputValue.toLowerCase().trim()), + ) + + return displayOptions + }} + clearText={t('donations:cta.clear')} + noOptionsText={t('donations:noOptions')} + openText={t('donations:cta.open')} + closeText={t('donations:cta.close')} + /> + + + + + ) +} diff --git a/src/gql/donations.d.ts b/src/gql/donations.d.ts index 8b9e040e0..544738ec9 100644 --- a/src/gql/donations.d.ts +++ b/src/gql/donations.d.ts @@ -74,6 +74,7 @@ export type DonationInput = { } export type UserDonationInput = DonationInput & { targetPersonId?: UUID + billingEmail?: string } export type DonationBankInput = { From 31e8360d4a1598518c8bc3f5724de4ef72d8a7ee Mon Sep 17 00:00:00 2001 From: Alexander Petkov Date: Mon, 3 Jul 2023 14:25:59 +0300 Subject: [PATCH 2/2] RendedEditBillingEmailCell.tsx: Rename the default function --- .../admin/donations/grid/RenderEditBillingEmailCell.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/admin/donations/grid/RenderEditBillingEmailCell.tsx b/src/components/admin/donations/grid/RenderEditBillingEmailCell.tsx index 784868ec5..ac1a28955 100644 --- a/src/components/admin/donations/grid/RenderEditBillingEmailCell.tsx +++ b/src/components/admin/donations/grid/RenderEditBillingEmailCell.tsx @@ -25,7 +25,7 @@ const addIconStyles = { boxShadow: 3, } -export default function RenderEditPersonCell({ +export default function RenderEditBillingEmailCell({ params, personList, onUpdate,