From ae17be1163d1f530d8075987ac6fb8ae2686de5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cec=C3=ADlia=20Rom=C3=A3o?= Date: Wed, 14 Feb 2024 10:09:37 -0300 Subject: [PATCH 01/14] feat: future balance detail --- src/components/DisbursementDetails/index.tsx | 12 +++++++++++- src/pages/DisbursementDraftDetails.tsx | 13 +++++++++++++ src/pages/DisbursementsNew.tsx | 19 ++++++++++++++++++- 3 files changed, 42 insertions(+), 2 deletions(-) diff --git a/src/components/DisbursementDetails/index.tsx b/src/components/DisbursementDetails/index.tsx index bba2c0f..29651ed 100644 --- a/src/components/DisbursementDetails/index.tsx +++ b/src/components/DisbursementDetails/index.tsx @@ -25,6 +25,7 @@ import "./styles.scss"; interface DisbursementDetailsProps { variant: DisbursementStep; + assetBalance?: string; details?: Disbursement; csvFile?: File; onChange?: (state: Disbursement) => void; @@ -50,12 +51,14 @@ const initDetails: Disbursement = { createdAt: "", status: "DRAFT", statusHistory: [], - smsRegistrationMessageTemplate: "" + smsRegistrationMessageTemplate: "", + stats: undefined, }; export const DisbursementDetails: React.FC = ({ variant, details = initDetails, + assetBalance, csvFile, onChange, onValidate, @@ -244,6 +247,13 @@ export const DisbursementDetails: React.FC = ({ +
+ +
+ {Number(assetBalance) - Number(details.stats?.totalAmount)} +
+
+ {variant === "confirmation" ? (
diff --git a/src/pages/DisbursementDraftDetails.tsx b/src/pages/DisbursementDraftDetails.tsx index c919ccf..87d5563 100644 --- a/src/pages/DisbursementDraftDetails.tsx +++ b/src/pages/DisbursementDraftDetails.tsx @@ -57,6 +57,9 @@ export const DisbursementDraftDetails = () => { const [isDraftInProgress, setIsDraftInProgress] = useState(false); const [isResponseSuccess, setIsResponseSuccess] = useState(false); + const allBalances = organization.data.assetBalances?.[0].balances; + + console.log(organization.data.assetBalances); const dispatch: AppDispatch = useDispatch(); const navigate = useNavigate(); const { isLoading: csvDownloadIsLoading } = useDownloadCsvFile( @@ -286,6 +289,11 @@ export const DisbursementDraftDetails = () => { a.assetCode === draftDetails?.details.asset.code, + )?.balance + } csvFile={csvFile} /> { a.assetCode === draftDetails?.details.asset.code, + )?.balance + } /> { "organization", ); const { assetBalances, distributionAccountPublicKey } = organization.data; + const allBalances = assetBalances?.[0].balances; const [draftDetails, setDraftDetails] = useState(); const [customMessage, setCustomMessage] = useState(""); @@ -198,7 +199,14 @@ export const DisbursementsNew = () => { if (currentStep === "preview") { return (
- + a.assetCode === draftDetails?.asset.code) + ?.balance + } + /> { a.assetCode === draftDetails?.asset.code, + )?.balance + } csvFile={csvFile} /> { a.assetCode === draftDetails?.asset.code) + ?.balance + } onChange={(updatedState) => { if (apiError) { dispatch(clearDisbursementDraftsErrorAction()); From 59d244670b96a84a1d7eb82d5d3fc1b78c8afe4c Mon Sep 17 00:00:00 2001 From: Caio Teixeira Date: Fri, 16 Feb 2024 18:21:02 -0300 Subject: [PATCH 02/14] fix: useOrgAccount info to update organization balance state --- src/pages/DisbursementDraftDetails.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/pages/DisbursementDraftDetails.tsx b/src/pages/DisbursementDraftDetails.tsx index 87d5563..d63e1f6 100644 --- a/src/pages/DisbursementDraftDetails.tsx +++ b/src/pages/DisbursementDraftDetails.tsx @@ -3,6 +3,7 @@ import { useNavigate, useParams } from "react-router-dom"; import { Badge, Heading, Link, Notification } from "@stellar/design-system"; import { useDispatch } from "react-redux"; import { useRedux } from "hooks/useRedux"; +import { useOrgAccountInfo } from "hooks/useOrgAccountInfo"; import { useDownloadCsvFile } from "hooks/useDownloadCsvFile"; import { AppDispatch } from "store"; @@ -59,7 +60,6 @@ export const DisbursementDraftDetails = () => { const allBalances = organization.data.assetBalances?.[0].balances; - console.log(organization.data.assetBalances); const dispatch: AppDispatch = useDispatch(); const navigate = useNavigate(); const { isLoading: csvDownloadIsLoading } = useDownloadCsvFile( @@ -110,6 +110,8 @@ export const DisbursementDraftDetails = () => { disbursementDetails.status, ]); + useOrgAccountInfo(organization.data.distributionAccountPublicKey); + useEffect(() => { setDraftDetails(disbursementDetails); dispatch(setDraftIdAction(disbursementDetails.details.id)); From 38899cd68e31a5503500e12ec11ef4553740b246 Mon Sep 17 00:00:00 2001 From: Caio Teixeira Date: Fri, 16 Feb 2024 18:21:57 -0300 Subject: [PATCH 03/14] fix: calculate total amount from file when disbursement is not saved yet --- src/pages/DisbursementsNew.tsx | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/pages/DisbursementsNew.tsx b/src/pages/DisbursementsNew.tsx index 415075d..db3c19b 100644 --- a/src/pages/DisbursementsNew.tsx +++ b/src/pages/DisbursementsNew.tsx @@ -156,9 +156,37 @@ export const DisbursementsNew = () => { if (apiError) { dispatch(clearDisbursementDraftsErrorAction()); } + calculateDisbursementTotalAmountFromFile(file); setCsvFile(file); }; + const calculateDisbursementTotalAmountFromFile = (file?: File) => { + if (file) { + const reader = new FileReader(); + reader.readAsText(file); + const handleLoadFile = () => { + const totalAmount = reader.result + ?.toString() + .split("\n") + .slice(1) + .reduce( + (accumulator, line) => + !line ? accumulator : accumulator + Number(line.split(",")[2]), + 0, + ); + + setDraftDetails({ + ...draftDetails, + stats: { + ...draftDetails?.stats, + totalAmount: totalAmount?.toString() ?? "0", + }, + } as Disbursement); + }; + reader.addEventListener("load", handleLoadFile, false); + } + }; + const handleViewDetails = () => { navigate(`${Routes.DISBURSEMENTS}/${disbursementDrafts.newDraftId}`); resetState(); From bf5c0c1b6046b8a3257f0939b8d3ee1167c38a3d Mon Sep 17 00:00:00 2001 From: Caio Teixeira Date: Mon, 19 Feb 2024 15:46:32 -0300 Subject: [PATCH 04/14] refactor: add negative balances in red and block disbursement confirmation --- src/components/DisbursementDetails/index.tsx | 14 ++++++---- .../DisbursementDetails/styles.scss | 4 +++ src/pages/DisbursementDraftDetails.tsx | 23 ++++++++-------- src/pages/DisbursementsNew.tsx | 27 +++++++++---------- 4 files changed, 38 insertions(+), 30 deletions(-) diff --git a/src/components/DisbursementDetails/index.tsx b/src/components/DisbursementDetails/index.tsx index 29651ed..3bb30cf 100644 --- a/src/components/DisbursementDetails/index.tsx +++ b/src/components/DisbursementDetails/index.tsx @@ -25,8 +25,8 @@ import "./styles.scss"; interface DisbursementDetailsProps { variant: DisbursementStep; - assetBalance?: string; details?: Disbursement; + futureBalance?: number; csvFile?: File; onChange?: (state: Disbursement) => void; onValidate?: (isValid: boolean) => void; @@ -58,7 +58,7 @@ const initDetails: Disbursement = { export const DisbursementDetails: React.FC = ({ variant, details = initDetails, - assetBalance, + futureBalance = 0, csvFile, onChange, onValidate, @@ -248,9 +248,13 @@ export const DisbursementDetails: React.FC = ({
- -
- {Number(assetBalance) - Number(details.stats?.totalAmount)} + +
= 0 ? "" : "DisbursementDetailsFields__negative" + }`} + > + {futureBalance}
diff --git a/src/components/DisbursementDetails/styles.scss b/src/components/DisbursementDetails/styles.scss index f186abb..98e95b7 100644 --- a/src/components/DisbursementDetails/styles.scss +++ b/src/components/DisbursementDetails/styles.scss @@ -19,4 +19,8 @@ font-weight: var(--font-weight-medium); margin-top: pxToRem(4px); } + + &__negative { + color: var(--color-red-60); + } } diff --git a/src/pages/DisbursementDraftDetails.tsx b/src/pages/DisbursementDraftDetails.tsx index d63e1f6..42a6da6 100644 --- a/src/pages/DisbursementDraftDetails.tsx +++ b/src/pages/DisbursementDraftDetails.tsx @@ -181,6 +181,14 @@ export const DisbursementDraftDetails = () => { resetState(); }; + const handleCalculateFutureBalance = (): number => { + const assetBalance = Number( + allBalances?.find((a) => a.assetCode === draftDetails?.details.asset.code) + ?.balance, + ); + return assetBalance - Number(draftDetails?.details.stats?.totalAmount); + }; + const handleSubmitDisbursement = ( event: React.FormEvent, ) => { @@ -236,7 +244,8 @@ export const DisbursementDraftDetails = () => { }} isDraftDisabled={!isCsvFileUpdated} isSubmitDisabled={ - !(Boolean(draftDetails) && Boolean(csvFile) && canUserSubmit) + !(Boolean(draftDetails) && Boolean(csvFile) && canUserSubmit) || + handleCalculateFutureBalance() < 0 } isDraftPending={disbursementDrafts.status === "PENDING"} actionType={disbursementDrafts.actionType} @@ -291,11 +300,7 @@ export const DisbursementDraftDetails = () => { a.assetCode === draftDetails?.details.asset.code, - )?.balance - } + futureBalance={handleCalculateFutureBalance()} csvFile={csvFile} /> { a.assetCode === draftDetails?.details.asset.code, - )?.balance - } + futureBalance={handleCalculateFutureBalance()} /> { const [customMessage, setCustomMessage] = useState(""); const [isDetailsValid, setIsDetailsValid] = useState(false); const [csvFile, setCsvFile] = useState(); + const [futureBalance, setFutureBalance] = useState(0); const [currentStep, setCurrentStep] = useState("edit"); const [isDraftInProgress, setIsDraftInProgress] = useState(false); @@ -182,6 +183,12 @@ export const DisbursementsNew = () => { totalAmount: totalAmount?.toString() ?? "0", }, } as Disbursement); + + // update future balance + const assetBalance = allBalances?.find( + (a) => a.assetCode === draftDetails?.asset.code, + )?.balance; + setFutureBalance(Number(assetBalance) - totalAmount!); }; reader.addEventListener("load", handleLoadFile, false); } @@ -208,7 +215,9 @@ export const DisbursementsNew = () => { Boolean(disbursementDrafts.newDraftId && currentStep === "preview") } isSubmitDisabled={ - organization.data.isApprovalRequired || !(draftDetails && csvFile) + organization.data.isApprovalRequired || + !(draftDetails && csvFile) || + futureBalance < 0 } isReviewDisabled={!isReviewEnabled} isDraftPending={disbursementDrafts.status === "PENDING"} @@ -230,10 +239,7 @@ export const DisbursementsNew = () => { a.assetCode === draftDetails?.asset.code) - ?.balance - } + futureBalance={futureBalance} /> { a.assetCode === draftDetails?.asset.code, - )?.balance - } + futureBalance={futureBalance} csvFile={csvFile} /> { a.assetCode === draftDetails?.asset.code) - ?.balance - } + futureBalance={futureBalance} onChange={(updatedState) => { if (apiError) { dispatch(clearDisbursementDraftsErrorAction()); From b2d0f15e8a2f57eccd171b7caa0f303f86af996a Mon Sep 17 00:00:00 2001 From: Caio Teixeira Date: Tue, 20 Feb 2024 14:24:46 -0300 Subject: [PATCH 05/14] refactor: address PR comments --- src/pages/DisbursementDraftDetails.tsx | 10 ++++++---- src/pages/DisbursementsNew.tsx | 5 ++++- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/pages/DisbursementDraftDetails.tsx b/src/pages/DisbursementDraftDetails.tsx index 42a6da6..ed5e0c6 100644 --- a/src/pages/DisbursementDraftDetails.tsx +++ b/src/pages/DisbursementDraftDetails.tsx @@ -186,9 +186,11 @@ export const DisbursementDraftDetails = () => { allBalances?.find((a) => a.assetCode === draftDetails?.details.asset.code) ?.balance, ); - return assetBalance - Number(draftDetails?.details.stats?.totalAmount); + return assetBalance - Number(draftDetails?.details.stats?.totalAmount || 0); }; + const futureBalance = handleCalculateFutureBalance(); + const handleSubmitDisbursement = ( event: React.FormEvent, ) => { @@ -245,7 +247,7 @@ export const DisbursementDraftDetails = () => { isDraftDisabled={!isCsvFileUpdated} isSubmitDisabled={ !(Boolean(draftDetails) && Boolean(csvFile) && canUserSubmit) || - handleCalculateFutureBalance() < 0 + futureBalance < 0 } isDraftPending={disbursementDrafts.status === "PENDING"} actionType={disbursementDrafts.actionType} @@ -300,7 +302,7 @@ export const DisbursementDraftDetails = () => { { { const assetBalance = allBalances?.find( (a) => a.assetCode === draftDetails?.asset.code, )?.balance; - setFutureBalance(Number(assetBalance) - totalAmount!); + + if (totalAmount) { + setFutureBalance(Number(assetBalance) - totalAmount); + } }; reader.addEventListener("load", handleLoadFile, false); } From 6a925909d12df2cbcfbef4edd96a8219d290d515 Mon Sep 17 00:00:00 2001 From: Marwen Abid Date: Thu, 22 Feb 2024 16:12:19 -0800 Subject: [PATCH 06/14] [SDP-1099] add external payment id to disbursement instructions (#77) --- public/resources/disbursement-template.csv | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/public/resources/disbursement-template.csv b/public/resources/disbursement-template.csv index cbabd97..519a263 100644 --- a/public/resources/disbursement-template.csv +++ b/public/resources/disbursement-template.csv @@ -1 +1,5 @@ -phone,id,amount,verification +phone,id,amount,verification,paymentID +"--- Below are EXAMPLE rows for each Verification.","Please DELETE these rows before uploading your actual data ---",,, ++1234567890,RECEIVER_01,0.2,1980-01-01,PAY_01 ++1234567891,RECEIVER_11,0.35,55066,PAY_06 ++1234567892,RECEIVER_33,1.15,9D0000024, \ No newline at end of file From 9d871ed7709096acc5835d4a5db72ef99d8db523 Mon Sep 17 00:00:00 2001 From: Caio Teixeira Date: Fri, 23 Feb 2024 12:15:24 -0300 Subject: [PATCH 07/14] refactor: use BigNumber.js to avoid overflow --- src/components/DisbursementDetails/index.tsx | 5 ++++- src/pages/DisbursementDraftDetails.tsx | 9 ++++++--- src/pages/DisbursementsNew.tsx | 9 +++++++-- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/components/DisbursementDetails/index.tsx b/src/components/DisbursementDetails/index.tsx index 3bb30cf..5851da7 100644 --- a/src/components/DisbursementDetails/index.tsx +++ b/src/components/DisbursementDetails/index.tsx @@ -22,6 +22,7 @@ import { } from "types"; import "./styles.scss"; +import BigNumber from "bignumber.js"; interface DisbursementDetailsProps { variant: DisbursementStep; @@ -251,7 +252,9 @@ export const DisbursementDetails: React.FC = ({
= 0 ? "" : "DisbursementDetailsFields__negative" + BigNumber(futureBalance).gte(0) + ? "" + : "DisbursementDetailsFields__negative" }`} > {futureBalance} diff --git a/src/pages/DisbursementDraftDetails.tsx b/src/pages/DisbursementDraftDetails.tsx index ed5e0c6..acb1d16 100644 --- a/src/pages/DisbursementDraftDetails.tsx +++ b/src/pages/DisbursementDraftDetails.tsx @@ -31,6 +31,7 @@ import { DisbursementButtons } from "components/DisbursementButtons"; import { ErrorWithExtras } from "components/ErrorWithExtras"; import { DisbursementDraft, DisbursementStep } from "types"; +import BigNumber from "bignumber.js"; export const DisbursementDraftDetails = () => { const { id: draftId } = useParams(); @@ -182,11 +183,13 @@ export const DisbursementDraftDetails = () => { }; const handleCalculateFutureBalance = (): number => { - const assetBalance = Number( + const assetBalance = BigNumber( allBalances?.find((a) => a.assetCode === draftDetails?.details.asset.code) - ?.balance, + ?.balance || 0, ); - return assetBalance - Number(draftDetails?.details.stats?.totalAmount || 0); + return assetBalance + .minus(BigNumber(draftDetails?.details.stats?.totalAmount || 0)) + .toNumber(); }; const futureBalance = handleCalculateFutureBalance(); diff --git a/src/pages/DisbursementsNew.tsx b/src/pages/DisbursementsNew.tsx index 76593ea..24260a0 100644 --- a/src/pages/DisbursementsNew.tsx +++ b/src/pages/DisbursementsNew.tsx @@ -33,6 +33,7 @@ import { AccountBalances } from "components/AccountBalances"; import { ErrorWithExtras } from "components/ErrorWithExtras"; import { Disbursement, DisbursementStep } from "types"; +import BigNumber from "bignumber.js"; export const DisbursementsNew = () => { const { disbursementDrafts, organization } = useRedux( @@ -172,7 +173,11 @@ export const DisbursementsNew = () => { .slice(1) .reduce( (accumulator, line) => - !line ? accumulator : accumulator + Number(line.split(",")[2]), + !line + ? accumulator + : BigNumber(accumulator) + .plus(BigNumber(line.split(",")[2])) + .toNumber(), 0, ); @@ -220,7 +225,7 @@ export const DisbursementsNew = () => { isSubmitDisabled={ organization.data.isApprovalRequired || !(draftDetails && csvFile) || - futureBalance < 0 + BigNumber(futureBalance).lt(0) } isReviewDisabled={!isReviewEnabled} isDraftPending={disbursementDrafts.status === "PENDING"} From e0af409b9018b9a95327b157ebe0d29b848bb444 Mon Sep 17 00:00:00 2001 From: Caio Teixeira Date: Fri, 23 Feb 2024 12:20:22 -0300 Subject: [PATCH 08/14] fix: format asset amount --- src/components/DisbursementDetails/index.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/DisbursementDetails/index.tsx b/src/components/DisbursementDetails/index.tsx index 5851da7..8914478 100644 --- a/src/components/DisbursementDetails/index.tsx +++ b/src/components/DisbursementDetails/index.tsx @@ -23,6 +23,7 @@ import { import "./styles.scss"; import BigNumber from "bignumber.js"; +import { AssetAmount } from "components/AssetAmount"; interface DisbursementDetailsProps { variant: DisbursementStep; @@ -257,7 +258,10 @@ export const DisbursementDetails: React.FC = ({ : "DisbursementDetailsFields__negative" }`} > - {futureBalance} +
From 1cd28e0ec097a35e14b7774ba0322b5fe78a9dea Mon Sep 17 00:00:00 2001 From: Caio Teixeira Date: Fri, 23 Feb 2024 17:03:11 -0300 Subject: [PATCH 09/14] lint: fix import order --- src/components/DisbursementDetails/index.tsx | 4 ++-- src/pages/DisbursementDraftDetails.tsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/DisbursementDetails/index.tsx b/src/components/DisbursementDetails/index.tsx index 8914478..ef54ea7 100644 --- a/src/components/DisbursementDetails/index.tsx +++ b/src/components/DisbursementDetails/index.tsx @@ -5,11 +5,13 @@ import { Title, Notification, } from "@stellar/design-system"; +import BigNumber from "bignumber.js"; import { useWallets } from "apiQueries/useWallets"; import { useAssetsByWallet } from "apiQueries/useAssetsByWallet"; import { useCountries } from "apiQueries/useCountries"; import { useVerificationTypes } from "apiQueries/useVerificationTypes"; +import { AssetAmount } from "components/AssetAmount"; import { InfoTooltip } from "components/InfoTooltip"; import { formatUploadedFileDisplayName } from "helpers/formatUploadedFileDisplayName"; import { @@ -22,8 +24,6 @@ import { } from "types"; import "./styles.scss"; -import BigNumber from "bignumber.js"; -import { AssetAmount } from "components/AssetAmount"; interface DisbursementDetailsProps { variant: DisbursementStep; diff --git a/src/pages/DisbursementDraftDetails.tsx b/src/pages/DisbursementDraftDetails.tsx index acb1d16..7d1075c 100644 --- a/src/pages/DisbursementDraftDetails.tsx +++ b/src/pages/DisbursementDraftDetails.tsx @@ -5,6 +5,7 @@ import { useDispatch } from "react-redux"; import { useRedux } from "hooks/useRedux"; import { useOrgAccountInfo } from "hooks/useOrgAccountInfo"; import { useDownloadCsvFile } from "hooks/useDownloadCsvFile"; +import BigNumber from "bignumber.js"; import { AppDispatch } from "store"; import { @@ -31,7 +32,6 @@ import { DisbursementButtons } from "components/DisbursementButtons"; import { ErrorWithExtras } from "components/ErrorWithExtras"; import { DisbursementDraft, DisbursementStep } from "types"; -import BigNumber from "bignumber.js"; export const DisbursementDraftDetails = () => { const { id: draftId } = useParams(); From 57695d811ed455d924af664067dd8ce79183d09e Mon Sep 17 00:00:00 2001 From: Caio Teixeira Date: Fri, 23 Feb 2024 17:07:57 -0300 Subject: [PATCH 10/14] lint: fix import order --- src/pages/DisbursementsNew.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/DisbursementsNew.tsx b/src/pages/DisbursementsNew.tsx index 24260a0..3b22278 100644 --- a/src/pages/DisbursementsNew.tsx +++ b/src/pages/DisbursementsNew.tsx @@ -8,6 +8,7 @@ import { } from "@stellar/design-system"; import { useDispatch } from "react-redux"; import { useNavigate } from "react-router-dom"; +import BigNumber from "bignumber.js"; import { AppDispatch } from "store"; import { @@ -33,7 +34,6 @@ import { AccountBalances } from "components/AccountBalances"; import { ErrorWithExtras } from "components/ErrorWithExtras"; import { Disbursement, DisbursementStep } from "types"; -import BigNumber from "bignumber.js"; export const DisbursementsNew = () => { const { disbursementDrafts, organization } = useRedux( From b4cc657488e33d6c0859fde1171e2b2913ae3db3 Mon Sep 17 00:00:00 2001 From: Caio Teixeira Date: Wed, 28 Feb 2024 11:41:03 -0300 Subject: [PATCH 11/14] feat: patch receivers' verification info --- src/apiQueries/useReceiversReceiverId.ts | 17 ++-- src/apiQueries/useUpdateReceiverDetails.ts | 13 ++- src/helpers/formatReceiver.ts | 5 +- src/pages/ReceiverDetailsEdit.tsx | 102 +++++++++++++++------ src/types/index.ts | 9 +- 5 files changed, 102 insertions(+), 44 deletions(-) diff --git a/src/apiQueries/useReceiversReceiverId.ts b/src/apiQueries/useReceiversReceiverId.ts index 875d0c5..e8b4efe 100644 --- a/src/apiQueries/useReceiversReceiverId.ts +++ b/src/apiQueries/useReceiversReceiverId.ts @@ -3,7 +3,7 @@ import { API_URL } from "constants/envVariables"; import { fetchApi } from "helpers/fetchApi"; import { formatPaymentReceiver } from "helpers/formatPaymentReceiver"; import { formatReceiver } from "helpers/formatReceiver"; -import { ApiReceiver, AppError } from "types"; +import { AppError, PaymentDetailsReceiver, ReceiverDetails } from "types"; export const useReceiversReceiverId = ({ receiverId, @@ -14,22 +14,19 @@ export const useReceiversReceiverId = ({ dataFormat: "receiver" | "paymentReceiver"; receiverWalletId?: string; }) => { - const query = useQuery({ + const query = useQuery({ queryKey: ["receivers", dataFormat, receiverId, { receiverWalletId }], queryFn: async () => { - return await fetchApi(`${API_URL}/receivers/${receiverId}`); + const response = await fetchApi(`${API_URL}/receivers/${receiverId}`); + return dataFormat === "receiver" + ? formatReceiver(response) + : formatPaymentReceiver(response, receiverWalletId); }, enabled: !!receiverId, }); - const formatData = (data: ApiReceiver) => { - return dataFormat === "receiver" - ? formatReceiver(data) - : formatPaymentReceiver(data, receiverWalletId); - }; - return { ...query, - data: query.data ? (formatData(query.data) as T) : undefined, + data: query.data as T, }; }; diff --git a/src/apiQueries/useUpdateReceiverDetails.ts b/src/apiQueries/useUpdateReceiverDetails.ts index 1fe8ee2..d0fa1d4 100644 --- a/src/apiQueries/useUpdateReceiverDetails.ts +++ b/src/apiQueries/useUpdateReceiverDetails.ts @@ -4,12 +4,23 @@ import { fetchApi } from "helpers/fetchApi"; import { sanitizeObject } from "helpers/sanitizeObject"; import { AppError } from "types"; +interface ReceiverDetailsUpdate { + email: string; + externalId: string; + dataOfBirth: string; + pin: string; + nationalId: string; +} + export const useUpdateReceiverDetails = (receiverId: string | undefined) => { const mutation = useMutation({ - mutationFn: (fields: { email: string; externalId: string }) => { + mutationFn: (fields: ReceiverDetailsUpdate) => { const fieldsToSubmit = sanitizeObject({ email: fields.email, external_id: fields.externalId, + date_of_birth: fields.dataOfBirth, + pin: fields.pin, + national_id: fields.nationalId, }); if (Object.keys(fieldsToSubmit).length === 0) { diff --git a/src/helpers/formatReceiver.ts b/src/helpers/formatReceiver.ts index 05445a4..de5c98e 100644 --- a/src/helpers/formatReceiver.ts +++ b/src/helpers/formatReceiver.ts @@ -30,7 +30,8 @@ export const formatReceiver = (receiver: ApiReceiver): ReceiverDetails => ({ withdrawnAmount: "", })), verifications: receiver.verifications.map((v) => ({ - verificationField: v.VerificationField, - value: v.HashedValue, + verificationField: v.verification_field, + value: v.hashed_value, + confirmedAt: v.confirmed_at, })), }); diff --git a/src/pages/ReceiverDetailsEdit.tsx b/src/pages/ReceiverDetailsEdit.tsx index b0c616e..7954de2 100644 --- a/src/pages/ReceiverDetailsEdit.tsx +++ b/src/pages/ReceiverDetailsEdit.tsx @@ -1,4 +1,4 @@ -import { useEffect, useState } from "react"; +import { useCallback, useEffect, useState } from "react"; import { useParams, useNavigate } from "react-router-dom"; import { Card, @@ -19,9 +19,15 @@ import { InfoTooltip } from "components/InfoTooltip"; import { LoadingContent } from "components/LoadingContent"; import { ErrorWithExtras } from "components/ErrorWithExtras"; -import { ReceiverDetails, ReceiverEditFields } from "types"; +import { + ReceiverDetails, + ReceiverEditFields, + ReceiverVerification, +} from "types"; import { useUpdateReceiverDetails } from "apiQueries/useUpdateReceiverDetails"; +type VerificationFieldType = "DATE_OF_BIRTH" | "PIN" | "NATIONAL_ID_NUMBER"; + export const ReceiverDetailsEdit = () => { const { id: receiverId } = useParams(); @@ -31,6 +37,9 @@ export const ReceiverDetailsEdit = () => { useState({ email: "", externalId: "", + dateOfBirth: "", + pin: "", + nationalId: "", }); const { @@ -52,17 +61,40 @@ export const ReceiverDetailsEdit = () => { reset, } = useUpdateReceiverDetails(receiverId); + const getReadyOnlyValue = useCallback( + (field: VerificationFieldType) => { + return ( + receiverDetails?.verifications.find( + (v) => v.verificationField === field, + )?.value ?? "" + ); + }, + [receiverDetails?.verifications], + ); + + const isVerificationFieldConfirmed = ( + field: VerificationFieldType, + ): boolean => { + const verification: ReceiverVerification | undefined = + receiverDetails?.verifications.find((v) => v.verificationField === field); + return !verification ? false : verification.confirmedAt !== null; + }; + useEffect(() => { if (isReceiverDetailsSuccess) { setReceiverEditFields({ - email: receiverDetails?.email || "", - externalId: receiverDetails?.orgId || "", + email: receiverDetails?.email ?? "", + externalId: receiverDetails?.orgId ?? "", + dateOfBirth: getReadyOnlyValue("DATE_OF_BIRTH"), + pin: getReadyOnlyValue("PIN"), + nationalId: getReadyOnlyValue("NATIONAL_ID_NUMBER"), }); } }, [ isReceiverDetailsSuccess, receiverDetails?.email, receiverDetails?.orgId, + getReadyOnlyValue, ]); useEffect(() => { @@ -81,15 +113,6 @@ export const ReceiverDetailsEdit = () => { }; }, [updateError, reset]); - const getReadyOnlyValue = ( - field: "DATE_OF_BIRTH" | "PIN" | "NATIONAL_ID_NUMBER", - ) => { - return ( - receiverDetails?.verifications.find((v) => v.verificationField === field) - ?.value || "" - ); - }; - const emptyValueIfNotChanged = (newValue: string, oldValue: string) => { return newValue === oldValue ? "" : newValue; }; @@ -99,15 +122,25 @@ export const ReceiverDetailsEdit = () => { ) => { e.preventDefault(); - const { email, externalId } = receiverEditFields; + const { email, externalId, dateOfBirth, pin, nationalId } = + receiverEditFields; if (receiverId) { try { await mutateAsync({ - email: emptyValueIfNotChanged(email, receiverDetails?.email || ""), + email: emptyValueIfNotChanged(email, receiverDetails?.email ?? ""), externalId: emptyValueIfNotChanged( externalId, - receiverDetails?.orgId || "", + receiverDetails?.orgId ?? "", + ), + dataOfBirth: emptyValueIfNotChanged( + dateOfBirth, + getReadyOnlyValue("DATE_OF_BIRTH"), + ), + pin: emptyValueIfNotChanged(pin, getReadyOnlyValue("PIN")), + nationalId: emptyValueIfNotChanged( + nationalId, + getReadyOnlyValue("NATIONAL_ID_NUMBER"), ), }); } catch (e) { @@ -119,8 +152,11 @@ export const ReceiverDetailsEdit = () => { const handleReceiverEditCancel = (e: React.FormEvent) => { e.preventDefault(); setReceiverEditFields({ - email: receiverDetails?.email || "", - externalId: receiverDetails?.orgId || "", + email: receiverDetails?.email ?? "", + externalId: receiverDetails?.orgId ?? "", + dateOfBirth: getReadyOnlyValue("DATE_OF_BIRTH"), + pin: getReadyOnlyValue("PIN"), + nationalId: getReadyOnlyValue("NATIONAL_ID_NUMBER"), }); navigate(`${Routes.RECEIVERS}/${receiverId}`); }; @@ -155,7 +191,10 @@ export const ReceiverDetailsEdit = () => { const isSubmitDisabled = receiverEditFields.email === receiverDetails?.email && - receiverEditFields.externalId === receiverDetails.orgId; + receiverEditFields.externalId === receiverDetails.orgId && + receiverEditFields.dateOfBirth === getReadyOnlyValue("DATE_OF_BIRTH") && + receiverEditFields.pin === getReadyOnlyValue("PIN") && + receiverEditFields.nationalId === getReadyOnlyValue("NATIONAL_ID_NUMBER"); return ( <> @@ -213,28 +252,33 @@ export const ReceiverDetailsEdit = () => { onChange={handleDetailsChange} /> diff --git a/src/types/index.ts b/src/types/index.ts index a0d69eb..1aead7e 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -364,6 +364,7 @@ export type ReceiverWallet = { export type ReceiverVerification = { verificationField: string; value: string; + confirmedAt?: string; }; export type ReceiverWalletBalance = { @@ -407,6 +408,9 @@ export type ReceiverDetails = { export type ReceiverEditFields = { email: string; externalId: string; + dateOfBirth: string; + pin: string; + nationalId: string; }; // ============================================================================= @@ -715,8 +719,9 @@ export type ApiReceiverWallet = { }; export type ApiReceiverVerification = { - VerificationField: string; - HashedValue: string; + verification_field: string; + hashed_value: string; + confirmed_at: string; }; export type ApiReceiver = { From 721f72956776d76969f51509f68b9350d7f6cd91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cec=C3=ADlia=20Rom=C3=A3o?= Date: Wed, 13 Mar 2024 16:52:43 -0300 Subject: [PATCH 12/14] fix: show entire public key (#80) --- src/api/getStellarAccountInfo.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/api/getStellarAccountInfo.ts b/src/api/getStellarAccountInfo.ts index 4b58294..5937df5 100644 --- a/src/api/getStellarAccountInfo.ts +++ b/src/api/getStellarAccountInfo.ts @@ -1,5 +1,4 @@ import { HORIZON_URL } from "constants/envVariables"; -import { shortenAccountKey } from "helpers/shortenAccountKey"; import { ApiStellarAccount } from "types"; export const getStellarAccountInfo = async ( @@ -10,7 +9,7 @@ export const getStellarAccountInfo = async ( }); if (response.status === 404) { - throw `${shortenAccountKey(stellarAddress)} address was not found.`; + throw `${stellarAddress} address was not found.`; } return await response.json(); From 4ad4daf6b979c67df60066146c18e21386d757a6 Mon Sep 17 00:00:00 2001 From: Marcelo Salloum Date: Mon, 15 Apr 2024 11:39:26 -0300 Subject: [PATCH 13/14] Bump version to 1.1.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 279280d..470c732 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "stellar-disbursement-platform-frontend", - "version": "1.1.1", + "version": "1.1.2", "license": "Apache-2.0", "engines": { "node": ">=18.x" From 8bb82fa97139c25e3c026b05a48a26944b4a8f92 Mon Sep 17 00:00:00 2001 From: Marcelo Salloum Date: Mon, 15 Apr 2024 11:53:55 -0300 Subject: [PATCH 14/14] Update changelog with 1.1.2 changes. --- CHANGELOG.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ab6393f..347b194 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,31 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). > Place unreleased changes here. +## [1.1.2](https://github.com/stellar/stellar-disbursement-platform-backend/compare/1.1.1...1.1.2) + +Attention, this version is compatible with the backend version +[1.1.6](https://github.com/stellar/stellar-disbursement-platform-backend/releases/tag/1.1.6). + +### Added + +- Add the "Future Balance" label in the disbursement detail component to display + what will be balance for the asset on the distribution account after the + disbursement is completed. + [#76](https://github.com/stellar/stellar-disbursement-platform-frontend/pull/76) +- Add option to update a receiver's verification info from the receiver's detail + page. + [#78](https://github.com/stellar/stellar-disbursement-platform-frontend/pull/78) + +### Changed + +- Update the CSV template by adding examples with and without the paymentID + (optional) column. + [#77](https://github.com/stellar/stellar-disbursement-platform-frontend/pull/77) +- Display the entire disbursement account address for the tenant when that + disbursement account does not exist in the network, making it easier to + identify the account that needs to be funded. + [#80](https://github.com/stellar/stellar-disbursement-platform-frontend/pull/80) + ## [1.1.1](https://github.com/stellar/stellar-disbursement-platform-backend/compare/1.1.0...1.1.1) ### Fixed