Skip to content

Commit

Permalink
Merge pull request #91: release/1.1.2 to main
Browse files Browse the repository at this point in the history
## [1.1.2](stellar/stellar-disbursement-platform-backend@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](#76)
- Add option to update a receiver's verification info from the receiver's detail
  page.
  [#78](#78)

### Changed

- Update the CSV template by adding examples with and without the paymentID
  (optional) column.
  [#77](#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](#80)
  • Loading branch information
marcelosalloum authored Apr 15, 2024
2 parents 17f0da4 + 8bb82fa commit 5248c97
Show file tree
Hide file tree
Showing 13 changed files with 236 additions and 52 deletions.
25 changes: 25 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -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"
Expand Down
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
23 changes: 22 additions & 1 deletion 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 @@ -50,12 +53,14 @@ const initDetails: Disbursement = {
createdAt: "",
status: "DRAFT",
statusHistory: [],
smsRegistrationMessageTemplate: ""
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 5248c97

Please sign in to comment.