瀏覽代碼

Add payment summary break down

tags/Baseline_30082024_FRONTEND_UAT
Wayne 1 年之前
父節點
當前提交
c2e110fa8b
共有 2 個檔案被更改,包括 124 行新增4 行删除
  1. +73
    -0
      src/components/CreateProject/PaymentSummary.tsx
  2. +51
    -4
      src/components/CreateProject/ProjectTotalFee.tsx

+ 73
- 0
src/components/CreateProject/PaymentSummary.tsx 查看文件

@@ -0,0 +1,73 @@
import { useMemo } from "react";
import StyledDataGrid from "../StyledDataGrid";
import { GridColDef, GridValidRowModel } from "@mui/x-data-grid";
import { useTranslation } from "react-i18next";
import dayjs from "dayjs";
import { moneyFormatter, OUTPUT_DATE_FORMAT } from "@/app/utils/formatUtil";
import { CreateProjectInputs } from "@/app/api/projects/actions";
import { useFormContext } from "react-hook-form";
import { TaskGroup } from "@/app/api/tasks";

interface Props {
taskGroups: TaskGroup[];
}

interface Row extends GridValidRowModel {
taskStage: string;
description: string;
date: string;
amount: number;
}

const PaymentSummary: React.FC<Props> = ({ taskGroups }) => {
const { t } = useTranslation();

const { watch } = useFormContext<CreateProjectInputs>();
const milestones = watch("milestones");
const rows = useMemo<Row[]>(() => {
return taskGroups.flatMap((group) => {
const payments = milestones[group.id]?.payments || [];
return payments.map(({ description, date, amount, id }) => ({
id,
taskStage: group.name,
description,
date,
amount,
}));
});
}, [milestones, taskGroups]);

const columns = useMemo<GridColDef<GridValidRowModel>[]>(
() => [
{ field: "taskStage", headerName: t("Task Stage"), width: 300 },
{
field: "description",
headerName: t("Payment Milestone Description"),
width: 300,
},
{
field: "date",
headerName: t("Payment Milestone Date"),
width: 200,
type: "date",
valueFormatter(params) {
return dayjs(params.value).format(OUTPUT_DATE_FORMAT);
},
},
{
field: "amount",
headerName: t("Payment Milestone Amount"),
width: 300,
type: "number",
valueFormatter(params) {
return moneyFormatter.format(params.value);
},
},
],
[t],
);

return <StyledDataGrid columns={columns} rows={rows} />;
};

export default PaymentSummary;

+ 51
- 4
src/components/CreateProject/ProjectTotalFee.tsx 查看文件

@@ -1,21 +1,51 @@
import { CreateProjectInputs } from "@/app/api/projects/actions"; import { CreateProjectInputs } from "@/app/api/projects/actions";
import { TaskGroup } from "@/app/api/tasks"; import { TaskGroup } from "@/app/api/tasks";
import { moneyFormatter } from "@/app/utils/formatUtil"; import { moneyFormatter } from "@/app/utils/formatUtil";
import { Divider, Stack, Typography } from "@mui/material";
import React from "react";
import {
Button,
Divider,
Modal,
Paper,
Stack,
SxProps,
Typography,
} from "@mui/material";
import React, { useCallback, useState } from "react";
import { useFormContext } from "react-hook-form"; import { useFormContext } from "react-hook-form";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import PaymentSummary from "./PaymentSummary";


interface Props { interface Props {
taskGroups: TaskGroup[]; taskGroups: TaskGroup[];
} }


const modalSx: SxProps = {
position: "absolute",
top: "50%",
left: "50%",
transform: "translate(-50%, -50%)",
width: "90%",
maxHeight: "90%",
padding: 3,
display: "flex",
flexDirection: "column",
gap: 2,
};

const ProjectTotalFee: React.FC<Props> = ({ taskGroups }) => { const ProjectTotalFee: React.FC<Props> = ({ taskGroups }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { watch } = useFormContext<CreateProjectInputs>(); const { watch } = useFormContext<CreateProjectInputs>();
const milestones = watch("milestones"); const milestones = watch("milestones");
const expectedTotalFee = watch("expectedProjectFee"); const expectedTotalFee = watch("expectedProjectFee");


const [paymentSummaryOpen, setPaymentSummaryOpen] = useState(false);
const closePaymentSummary = useCallback(() => {
setPaymentSummaryOpen(false);
}, []);
const openPaymentSummary = useCallback(() => {
setPaymentSummaryOpen(true);
}, []);

let projectTotal = 0; let projectTotal = 0;


return ( return (
@@ -37,9 +67,24 @@ const ProjectTotalFee: React.FC<Props> = ({ taskGroups }) => {
</Stack> </Stack>
); );
})} })}
<Button
variant="outlined"
size="small"
sx={{ alignSelf: "flex-start" }}
onClick={openPaymentSummary}
>
{t("View payment breakdown")}
</Button>
<Modal open={paymentSummaryOpen} onClose={closePaymentSummary}>
<Paper sx={modalSx}>
<PaymentSummary taskGroups={taskGroups} />
</Paper>
</Modal>
<Divider sx={{ paddingBlockStart: 2 }} /> <Divider sx={{ paddingBlockStart: 2 }} />
<Stack direction="row" justifyContent="space-between"> <Stack direction="row" justifyContent="space-between">
<Typography variant="h6">{t("Sum of Payment Milestone Fee")}</Typography>
<Typography variant="h6">
{t("Sum of Payment Milestone Fee")}
</Typography>
<Typography>{moneyFormatter.format(projectTotal)}</Typography> <Typography>{moneyFormatter.format(projectTotal)}</Typography>
</Stack> </Stack>
<Divider sx={{ paddingBlockStart: 2 }} /> <Divider sx={{ paddingBlockStart: 2 }} />
@@ -49,7 +94,9 @@ const ProjectTotalFee: React.FC<Props> = ({ taskGroups }) => {
</Stack> </Stack>
{projectTotal !== expectedTotalFee && ( {projectTotal !== expectedTotalFee && (
<Typography variant="caption" color="warning.main" alignSelf="flex-end"> <Typography variant="caption" color="warning.main" alignSelf="flex-end">
{t("Sum of Payment Milestone Fee should be same as the expected total fee!")}
{t(
"Sum of Payment Milestone Fee should be same as the expected total fee!",
)}
</Typography> </Typography>
)} )}
</Stack> </Stack>


Loading…
取消
儲存