Skip to content

Commit

Permalink
feat: Basic hook logic
Browse files Browse the repository at this point in the history
  • Loading branch information
DafyddLlyr committed Nov 27, 2024
1 parent fc82d58 commit 50b7831
Show file tree
Hide file tree
Showing 5 changed files with 116 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import React from "react";
import { FONT_WEIGHT_SEMI_BOLD } from "theme";

import { formattedPriceWithCurrencySymbol } from "../../model";
import { useFeeBreakdown } from "./useFeeBreakdown";

const StyledTable = styled(Table)(() => ({
[`& .${tableCellClasses.root}`]: {
Expand All @@ -26,7 +27,7 @@ const BoldTableRow = styled(TableRow)(() => ({
},
}));

const VAT_RATE = 20;
const VAT_RATE = 0.2;

const DESCRIPTION =
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.";
Expand All @@ -40,49 +41,56 @@ const Header = () => (
</TableHead>
);

const ApplicationFee = () => (
const ApplicationFee: React.FC<{ amount: number }> = ({ amount }) => (
<TableRow>
<TableCell>Application fee</TableCell>
<TableCell align="right">{formattedPriceWithCurrencySymbol(100)}</TableCell>
<TableCell align="right">
{formattedPriceWithCurrencySymbol(amount)}
</TableCell>
</TableRow>
);

const Exemptions = () => (
<TableRow>
<TableCell>Exemptions</TableCell>
<TableCell align="right">{formattedPriceWithCurrencySymbol(-20)}</TableCell>
</TableRow>
);
const Reductions: React.FC<{ amount?: number }> = ({ amount }) => {
if (!amount) return null;

const Reductions = () => (
<TableRow>
<TableCell>Reductions</TableCell>
<TableCell align="right">{formattedPriceWithCurrencySymbol(-30)}</TableCell>
</TableRow>
);
return (
<TableRow>
<TableCell>Reductions</TableCell>
<TableCell align="right">
{formattedPriceWithCurrencySymbol(-amount)}
</TableCell>
</TableRow>
);
};

const ServiceCharge = () => (
<TableRow>
<TableCell>Service charge</TableCell>
<TableCell align="right">{formattedPriceWithCurrencySymbol(30)}</TableCell>
</TableRow>
);
const VAT: React.FC<{ amount?: number }> = ({ amount }) => {
if (!amount) return null;

const VAT = () => (
<TableRow>
<TableCell>{`VAT (${VAT_RATE}%)`}</TableCell>
<TableCell align="right">-</TableCell>
</TableRow>
);
return (
<TableRow>
<TableCell variant="footer">{`Includes VAT (${
VAT_RATE * 100
}%)`}</TableCell>
<TableCell variant="footer" align="right">
{formattedPriceWithCurrencySymbol(amount)}
</TableCell>
</TableRow>
);
};

const Total = () => (
const Total: React.FC<{ amount: number }> = ({ amount }) => (
<BoldTableRow>
<TableCell>Total</TableCell>
<TableCell align="right">{formattedPriceWithCurrencySymbol(80)}</TableCell>
<TableCell align="right">
{formattedPriceWithCurrencySymbol(amount)}
</TableCell>
</BoldTableRow>
);

export const FeeBreakdown: React.FC = () => {
const breakdown = useFeeBreakdown();
if (!breakdown) return null;

if (!hasFeatureFlag("FEE_BREAKDOWN")) return null;

return (
Expand All @@ -97,12 +105,10 @@ export const FeeBreakdown: React.FC = () => {
<StyledTable data-testid="fee-breakdown-table">
<Header />
<TableBody>
<ApplicationFee />
<ServiceCharge />
<Exemptions />
<Reductions />
<VAT />
<Total />
<ApplicationFee amount={breakdown.applicationFee} />
<Reductions amount={breakdown.reduction} />
<Total amount={breakdown.total} />
<VAT amount={breakdown.vat} />
</TableBody>
</StyledTable>
</TableContainer>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export interface FeeBreakdown {
applicationFee: number;
total: number;
reduction: number;
vat: number | undefined;
}

export interface PassportFeeFields {
"application.fee.calculated": number;
"application.fee.payable": number;
"application.fee.payable.vat": number;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { logger } from "airbrake";
import { useStore } from "pages/FlowEditor/lib/store";

import { FeeBreakdown } from "./types";
import { createPassportSchema } from "./utils";

export const useFeeBreakdown = (): FeeBreakdown | undefined => {
const [passportData, sessionId] = useStore((state) => [
state.computePassport().data,
state.sessionId,
]);
const schema = createPassportSchema();
const result = schema.safeParse(passportData);

// Unable to parse fee data from passport, do not show FeeBreakdown component
if (!result.success) {
logger.notify(
`Failed to parse fee breakdown data from passport for session ${sessionId}. Error: ${result.error}`,
);
return;
}

return result.data;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { z } from "zod";

import { FeeBreakdown, PassportFeeFields } from "./types";

const toNumber = (input: number | number[]) =>
Array.isArray(input) ? Number(input[0]) : input;

const calculateReduction = (data: PassportFeeFields) =>
data["application.fee.calculated"]
? data["application.fee.calculated"] - data["application.fee.payable"]
: 0;

const toFeeBreakdown = (data: PassportFeeFields): FeeBreakdown => ({
applicationFee:
data["application.fee.calculated"] || data["application.fee.payable"],
total: data["application.fee.payable"],
vat: data["application.fee.payable.vat"],
reduction: calculateReduction(data),
});

export const createPassportSchema = () => {
const questionSchema = z.number();
const setValueSchema = z.array(z.coerce.number());
const feeSchema = z
.union([questionSchema, setValueSchema])
.transform(toNumber);

const schema = z
.object({
"application.fee.calculated": feeSchema.optional().default(0),
"application.fee.payable": feeSchema,
"application.fee.payable.vat": feeSchema.optional().default(0),
})
.transform(toFeeBreakdown);

return schema;
};
2 changes: 2 additions & 0 deletions editor.planx.uk/src/pages/Pay/MakePayment.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,8 @@ export default function MakePayment({
showInviteToPay={false}
hideFeeBanner={true}
paymentStatus={payment?.state.status}
// TODO: Handle fee breakdown for ITP scenarios
showFeeBreakdown={false}
/>
)}
</>
Expand Down

0 comments on commit 50b7831

Please sign in to comment.