Skip to content

Commit

Permalink
Merge branch 'develop' into sdp-multitenant
Browse files Browse the repository at this point in the history
  • Loading branch information
marwen-abid committed Mar 29, 2024
2 parents 6450c38 + 721f729 commit e473a09
Show file tree
Hide file tree
Showing 11 changed files with 209 additions and 50 deletions.
6 changes: 5 additions & 1 deletion public/resources/disbursement-template.csv
Original file line number Diff line number Diff line change
@@ -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,
3 changes: 1 addition & 2 deletions src/api/getStellarAccountInfo.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { HORIZON_URL } from "constants/envVariables";
import { shortenAccountKey } from "helpers/shortenAccountKey";
import { ApiStellarAccount } from "types";

export const getStellarAccountInfo = async (
Expand All @@ -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();
Expand Down
17 changes: 7 additions & 10 deletions src/apiQueries/useReceiversReceiverId.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 = <T>({
receiverId,
Expand All @@ -14,22 +14,19 @@ export const useReceiversReceiverId = <T>({
dataFormat: "receiver" | "paymentReceiver";
receiverWalletId?: string;
}) => {
const query = useQuery<ApiReceiver, AppError>({
const query = useQuery<ReceiverDetails | PaymentDetailsReceiver, AppError>({
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,
};
};
13 changes: 12 additions & 1 deletion src/apiQueries/useUpdateReceiverDetails.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
21 changes: 21 additions & 0 deletions src/components/DisbursementDetails/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -26,6 +28,7 @@ import "./styles.scss";
interface DisbursementDetailsProps {
variant: DisbursementStep;
details?: Disbursement;
futureBalance?: number;
csvFile?: File;
onChange?: (state: Disbursement) => void;
onValidate?: (isValid: boolean) => void;
Expand All @@ -51,11 +54,13 @@ const initDetails: Disbursement = {
status: "DRAFT",
statusHistory: [],
smsRegistrationMessageTemplate: "",
stats: undefined,
};

export const DisbursementDetails: React.FC<DisbursementDetailsProps> = ({
variant,
details = initDetails,
futureBalance = 0,
csvFile,
onChange,
onValidate,
Expand Down Expand Up @@ -244,6 +249,22 @@ export const DisbursementDetails: React.FC<DisbursementDetailsProps> = ({
</div>
</div>

<div>
<label className="Label Label--sm">Future balance</label>
<div
className={`DisbursementDetailsFields__value ${
BigNumber(futureBalance).gte(0)
? ""
: "DisbursementDetailsFields__negative"
}`}
>
<AssetAmount
amount={futureBalance.toString()}
assetCode={details.asset.code}
/>
</div>
</div>

{variant === "confirmation" ? (
<div>
<label className="Label Label--sm">CSV</label>
Expand Down
4 changes: 4 additions & 0 deletions src/components/DisbursementDetails/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,8 @@
font-weight: var(--font-weight-medium);
margin-top: pxToRem(4px);
}

&__negative {
color: var(--color-red-60);
}
}
5 changes: 3 additions & 2 deletions src/helpers/formatReceiver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
})),
});
23 changes: 22 additions & 1 deletion src/pages/DisbursementDraftDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ 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 BigNumber from "bignumber.js";

import { AppDispatch } from "store";
import {
Expand Down Expand Up @@ -57,6 +59,8 @@ export const DisbursementDraftDetails = () => {
const [isDraftInProgress, setIsDraftInProgress] = useState(false);
const [isResponseSuccess, setIsResponseSuccess] = useState<boolean>(false);

const allBalances = organization.data.assetBalances?.[0].balances;

const dispatch: AppDispatch = useDispatch();
const navigate = useNavigate();
const { isLoading: csvDownloadIsLoading } = useDownloadCsvFile(
Expand Down Expand Up @@ -107,6 +111,8 @@ export const DisbursementDraftDetails = () => {
disbursementDetails.status,
]);

useOrgAccountInfo(organization.data.distributionAccountPublicKey);

useEffect(() => {
setDraftDetails(disbursementDetails);
dispatch(setDraftIdAction(disbursementDetails.details.id));
Expand Down Expand Up @@ -176,6 +182,18 @@ export const DisbursementDraftDetails = () => {
resetState();
};

const handleCalculateFutureBalance = (): number => {
const assetBalance = BigNumber(
allBalances?.find((a) => a.assetCode === draftDetails?.details.asset.code)
?.balance || 0,
);
return assetBalance
.minus(BigNumber(draftDetails?.details.stats?.totalAmount || 0))
.toNumber();
};

const futureBalance = handleCalculateFutureBalance();

const handleSubmitDisbursement = (
event: React.FormEvent<HTMLFormElement>,
) => {
Expand Down Expand Up @@ -231,7 +249,8 @@ export const DisbursementDraftDetails = () => {
}}
isDraftDisabled={!isCsvFileUpdated}
isSubmitDisabled={
!(Boolean(draftDetails) && Boolean(csvFile) && canUserSubmit)
!(Boolean(draftDetails) && Boolean(csvFile) && canUserSubmit) ||
futureBalance < 0
}
isDraftPending={disbursementDrafts.status === "PENDING"}
actionType={disbursementDrafts.actionType}
Expand Down Expand Up @@ -286,6 +305,7 @@ export const DisbursementDraftDetails = () => {
<DisbursementDetails
variant="confirmation"
details={draftDetails?.details}
futureBalance={futureBalance}
csvFile={csvFile}
/>
<DisbursementInviteMessage
Expand Down Expand Up @@ -326,6 +346,7 @@ export const DisbursementDraftDetails = () => {
<DisbursementDetails
variant="preview"
details={draftDetails?.details}
futureBalance={futureBalance}
/>
<DisbursementInviteMessage
isEditMessage={false}
Expand Down
56 changes: 54 additions & 2 deletions src/pages/DisbursementsNew.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -40,11 +41,13 @@ export const DisbursementsNew = () => {
"organization",
);
const { assetBalances, distributionAccountPublicKey } = organization.data;
const allBalances = assetBalances?.[0].balances;

const [draftDetails, setDraftDetails] = useState<Disbursement>();
const [customMessage, setCustomMessage] = useState("");
const [isDetailsValid, setIsDetailsValid] = useState(false);
const [csvFile, setCsvFile] = useState<File | undefined>();
const [futureBalance, setFutureBalance] = useState(0);

const [currentStep, setCurrentStep] = useState<DisbursementStep>("edit");
const [isDraftInProgress, setIsDraftInProgress] = useState(false);
Expand Down Expand Up @@ -155,9 +158,50 @@ 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
: BigNumber(accumulator)
.plus(BigNumber(line.split(",")[2]))
.toNumber(),
0,
);

setDraftDetails({
...draftDetails,
stats: {
...draftDetails?.stats,
totalAmount: totalAmount?.toString() ?? "0",
},
} as Disbursement);

// update future balance
const assetBalance = allBalances?.find(
(a) => a.assetCode === draftDetails?.asset.code,
)?.balance;

if (totalAmount) {
setFutureBalance(Number(assetBalance) - totalAmount);
}
};
reader.addEventListener("load", handleLoadFile, false);
}
};

const handleViewDetails = () => {
navigate(`${Routes.DISBURSEMENTS}/${disbursementDrafts.newDraftId}`);
resetState();
Expand All @@ -179,7 +223,9 @@ export const DisbursementsNew = () => {
Boolean(disbursementDrafts.newDraftId && currentStep === "preview")
}
isSubmitDisabled={
organization.data.isApprovalRequired || !(draftDetails && csvFile)
organization.data.isApprovalRequired ||
!(draftDetails && csvFile) ||
BigNumber(futureBalance).lt(0)
}
isReviewDisabled={!isReviewEnabled}
isDraftPending={disbursementDrafts.status === "PENDING"}
Expand All @@ -198,7 +244,11 @@ export const DisbursementsNew = () => {
if (currentStep === "preview") {
return (
<form onSubmit={handleSubmitDisbursement} className="DisbursementForm">
<DisbursementDetails variant="preview" details={draftDetails} />
<DisbursementDetails
variant="preview"
details={draftDetails}
futureBalance={futureBalance}
/>
<DisbursementInviteMessage
isEditMessage={false}
draftMessage={customMessage}
Expand Down Expand Up @@ -244,6 +294,7 @@ export const DisbursementsNew = () => {
<DisbursementDetails
variant="confirmation"
details={draftDetails}
futureBalance={futureBalance}
csvFile={csvFile}
/>
<DisbursementInviteMessage
Expand Down Expand Up @@ -273,6 +324,7 @@ export const DisbursementsNew = () => {
<DisbursementDetails
variant="edit"
details={draftDetails}
futureBalance={futureBalance}
onChange={(updatedState) => {
if (apiError) {
dispatch(clearDisbursementDraftsErrorAction());
Expand Down
Loading

0 comments on commit e473a09

Please sign in to comment.