Skip to content

Commit

Permalink
Merge branch 'develop' into tsq-refactor-assets
Browse files Browse the repository at this point in the history
  • Loading branch information
quietbits authored Oct 30, 2023
2 parents c724937 + f9aad0d commit 0305dc0
Show file tree
Hide file tree
Showing 6 changed files with 229 additions and 1 deletion.
40 changes: 40 additions & 0 deletions src/apiQueries/useUpdateOrgPaymentCancellationPeriodDays.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { useMutation } from "@tanstack/react-query";
import { API_URL } from "constants/settings";
import { fetchApi } from "helpers/fetchApi";
import { AppError } from "types";

export const useUpdateOrgPaymentCancellationPeriodDays = () => {
const mutation = useMutation({
mutationFn: (cancellationPeriod: number) => {
const formData = new FormData();

formData.append(
"data",
`{"payment_cancellation_period_days": ${cancellationPeriod}}`,
);

return fetchApi(
`${API_URL}/organization`,
{
method: "PATCH",
body: formData,
},
{ omitContentType: true },
);
},
cacheTime: 0,
});

return {
...mutation,
error: mutation.error as AppError,
data: mutation.data as { message: string },
mutateAsync: async (cancellationPeriod: number) => {
try {
await mutation.mutateAsync(cancellationPeriod);
} catch (e) {
// do nothing
}
},
};
};
2 changes: 2 additions & 0 deletions src/components/PaymentStatus/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ export const PaymentStatus = ({ status }: { status: PaymentStatusType }) => {
return <span className="PaymentStatus">Draft</span>;
case "FAILED":
return <span className="PaymentStatus">Failed</span>;
case "CANCELED":
return <span className="PaymentStatus">Canceled</span>;
case "PAUSED":
return <span className="PaymentStatus">Paused</span>;
case "PENDING":
Expand Down
175 changes: 175 additions & 0 deletions src/components/SettingsEnablePaymentCancellation.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
import { useEffect, useState } from "react";
import {
Button,
Card,
Input,
Notification,
Toggle,
Loader,
} from "@stellar/design-system";
import { useDispatch } from "react-redux";

import { DropdownMenu } from "components/DropdownMenu";
import { MoreMenuButton } from "components/MoreMenuButton";

import { useUpdateOrgPaymentCancellationPeriodDays } from "apiQueries/useUpdateOrgPaymentCancellationPeriodDays";
import { useRedux } from "hooks/useRedux";
import { AppDispatch } from "store";
import { getOrgInfoAction } from "store/ducks/organization";

export const SettingsEnablePaymentCancellation = () => {
const { organization } = useRedux("organization");

const [paymentCancellationPeriodDays, setPaymentCancellationPeriodDays] =
useState(0);
const [isEditMode, setIsEditMode] = useState(false);

const dispatch: AppDispatch = useDispatch();

const { mutateAsync, isLoading, error, isSuccess } =
useUpdateOrgPaymentCancellationPeriodDays();

useEffect(() => {
setPaymentCancellationPeriodDays(
organization.data.paymentCancellationPeriodDays,
);
}, [organization.data.paymentCancellationPeriodDays]);

useEffect(() => {
if (isSuccess) {
dispatch(getOrgInfoAction());
setIsEditMode(false);
}
}, [dispatch, isSuccess]);

const handleToggleChange = () => {
// Default period is 5 days
mutateAsync(organization.data.paymentCancellationPeriodDays === 0 ? 5 : 0);
};

const handlePaymentCancellationSubmit = (
event: React.FormEvent<HTMLFormElement>,
) => {
event.preventDefault();
mutateAsync(paymentCancellationPeriodDays);
};

const handlePaymentCancellationReset = (
event: React.FormEvent<HTMLFormElement>,
) => {
event.preventDefault();
setIsEditMode(false);
setPaymentCancellationPeriodDays(
organization.data.paymentCancellationPeriodDays,
);
};

const renderContent = () => {
return (
<div className="SdpSettings">
<div className="SdpSettings__row">
<div className="SdpSettings__item">
<label
className="SdpSettings__label"
htmlFor="payment-cancellation"
>
Enable automatic payments cancellation
</label>
<div className="Toggle__wrapper">
{isLoading ? <Loader size="1rem" /> : null}
<Toggle
id="payment-cancellation"
checked={Boolean(
organization.data.paymentCancellationPeriodDays,
)}
onChange={handleToggleChange}
disabled={isLoading}
/>
</div>
</div>
<div className="Note">
Select this option to automatically cancel pending payments after a
certain time period. Uncompleted payments will not be made once they
are canceled, even if the receiver tries to claim funds. Completed
payments are always final.
</div>
</div>

{organization.data.paymentCancellationPeriodDays ? (
<div className="SdpSettings__row">
<form
className="SdpSettings__form"
onSubmit={handlePaymentCancellationSubmit}
onReset={handlePaymentCancellationReset}
>
<div className="SdpSettings__form__row">
<Input
fieldSize="sm"
id="payment-cancellation-period"
label="Payments Cancellation Period (days)"
type="number"
value={paymentCancellationPeriodDays}
onChange={(e) =>
setPaymentCancellationPeriodDays(Number(e.target.value))
}
disabled={!isEditMode}
error={
paymentCancellationPeriodDays === 0
? "Cancellation period cannot be 0"
: ""
}
/>
{!isEditMode ? (
<DropdownMenu triggerEl={<MoreMenuButton />}>
<DropdownMenu.Item onClick={() => setIsEditMode(true)}>
Edit
</DropdownMenu.Item>
</DropdownMenu>
) : null}
</div>
{isEditMode ? (
<div className="SdpSettings__form__buttons">
<Button
variant="secondary"
size="xs"
type="reset"
isLoading={isLoading}
>
Cancel
</Button>
<Button
variant="primary"
size="xs"
type="submit"
isLoading={isLoading}
disabled={
!paymentCancellationPeriodDays ||
paymentCancellationPeriodDays ===
organization.data.paymentCancellationPeriodDays
}
>
Update
</Button>
</div>
) : null}
</form>
</div>
) : null}
</div>
);
};

return (
<>
{error ? (
<Notification variant="error" title="Error">
{error.message}
</Notification>
) : null}

<Card>
<div className="CardStack__card">{renderContent()}</div>
</Card>
</>
);
};
4 changes: 4 additions & 0 deletions src/pages/Settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { SectionHeader } from "components/SectionHeader";
import { SettingsTeamMembers } from "components/SettingsTeamMembers";
import { ReceiverInviteMessage } from "components/ReceiverInviteMessage";
import { SettingsEnableSmsRetry } from "components/SettingsEnableSmsRetry";
import { SettingsEnablePaymentCancellation } from "components/SettingsEnablePaymentCancellation";

export const Settings = () => {
return (
Expand All @@ -19,6 +20,9 @@ export const Settings = () => {
</SectionHeader>

<div className="CardStack">
{/* Enable automatic ready payments cancellation */}
<SettingsEnablePaymentCancellation />

{/* Enable SMS retry */}
<SettingsEnableSmsRetry />

Expand Down
4 changes: 4 additions & 0 deletions src/store/ducks/organization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ const initialState: OrganizationInitialState = {
assetBalances: undefined,
isApprovalRequired: undefined,
smsResendInterval: 0,
paymentCancellationPeriodDays: 0,
},
updateMessage: undefined,
status: undefined,
Expand Down Expand Up @@ -163,6 +164,9 @@ const organizationSlice = createSlice({
smsRegistrationMessageTemplate:
action.payload.sms_registration_message_template,
smsResendInterval: Number(action.payload.sms_resend_interval || 0),
paymentCancellationPeriodDays: Number(
action.payload.payment_cancellation_period_days || 0,
),
};
state.status = "SUCCESS";
});
Expand Down
5 changes: 4 additions & 1 deletion src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ export type OrganizationInitialState = {
isApprovalRequired: boolean | undefined;
smsResendInterval: number;
smsRegistrationMessageTemplate?: string;
paymentCancellationPeriodDays: number;
};
updateMessage?: string;
status: ActionStatus | undefined;
Expand Down Expand Up @@ -303,7 +304,8 @@ export type PaymentStatus =
| "PENDING"
| "PAUSED"
| "SUCCESS"
| "FAILED";
| "FAILED"
| "CANCELED";

export type PaymentsSearchParams = CommonFilters &
SortParams &
Expand Down Expand Up @@ -763,6 +765,7 @@ export type ApiOrgInfo = {
is_approval_required: boolean;
sms_resend_interval: string;
sms_registration_message_template?: string;
payment_cancellation_period_days: string;
};

export type ApiStellarAccountBalance = {
Expand Down

0 comments on commit 0305dc0

Please sign in to comment.