From 41919bb25bdc484a96ab3f78f430f2519106e359 Mon Sep 17 00:00:00 2001 From: Wayne Date: Mon, 12 Aug 2024 14:26:02 +0900 Subject: [PATCH 1/3] Adjust color and login page --- src/components/LoginPage/LoginForm.tsx | 5 +-- src/components/LoginPage/LoginPage.tsx | 40 +++++++++++++++++++++--- src/theme/devias-material-kit/colors.ts | 18 +++++++++++ src/theme/devias-material-kit/palette.ts | 11 ++++++- 4 files changed, 66 insertions(+), 8 deletions(-) diff --git a/src/components/LoginPage/LoginForm.tsx b/src/components/LoginPage/LoginForm.tsx index 38b1bab..8f28886 100644 --- a/src/components/LoginPage/LoginForm.tsx +++ b/src/components/LoginPage/LoginForm.tsx @@ -1,6 +1,6 @@ "use client"; -import { FormHelperText } from "@mui/material"; +import { FormHelperText, SxProps } from "@mui/material"; import Button from "@mui/material/Button"; import Stack from "@mui/material/Stack"; import TextField from "@mui/material/TextField"; @@ -31,7 +31,7 @@ const getHumanFriendlyErrorMessage = ( } }; -const LoginForm: React.FC = () => { +const LoginForm: React.FC<{ sx?: SxProps }> = ({ sx }) => { const { t } = useTranslation("login"); const { register, @@ -65,6 +65,7 @@ const LoginForm: React.FC = () => { margin={5} component="form" onSubmit={handleSubmit(onSubmit)} + sx={sx} > {t("Sign In")} { return ( - - + + - - - + + + diff --git a/src/theme/devias-material-kit/colors.ts b/src/theme/devias-material-kit/colors.ts index 8f82296..0ba01d8 100644 --- a/src/theme/devias-material-kit/colors.ts +++ b/src/theme/devias-material-kit/colors.ts @@ -11,7 +11,25 @@ export const neutral = { 900: "#111927", }; +// export const primary = { +// lightest: "#F5F7FF", +// light: "#EBEEFE", +// main: "#6366F1", +// dark: "#4338CA", +// darkest: "#312E81", +// contrastText: "#FFFFFF", +// }; + export const primary = { + lightest: "#f9fff5", + light: "#f9feeb", + main: "#8dba00", + dark: "#638a01", + darkest: "#4a5f14", + contrastText: "#FFFFFF", +}; + +export const secondary = { lightest: "#F5F7FF", light: "#EBEEFE", main: "#6366F1", diff --git a/src/theme/devias-material-kit/palette.ts b/src/theme/devias-material-kit/palette.ts index c24b6ec..33299eb 100644 --- a/src/theme/devias-material-kit/palette.ts +++ b/src/theme/devias-material-kit/palette.ts @@ -1,6 +1,14 @@ import { common } from "@mui/material/colors"; import { PaletteOptions } from "@mui/material/styles"; -import { error, primary, info, neutral, success, warning } from "./colors"; +import { + error, + primary, + secondary, + info, + neutral, + success, + warning, +} from "./colors"; const palette = { action: { @@ -20,6 +28,7 @@ const palette = { info, mode: "light", primary, + secondary, success, text: { primary: neutral[900], From c2e110fa8b08e31d4d52c5fc7deb0f0fa963c825 Mon Sep 17 00:00:00 2001 From: Wayne Date: Mon, 12 Aug 2024 15:10:00 +0900 Subject: [PATCH 2/3] Add payment summary break down --- .../CreateProject/PaymentSummary.tsx | 73 +++++++++++++++++++ .../CreateProject/ProjectTotalFee.tsx | 55 +++++++++++++- 2 files changed, 124 insertions(+), 4 deletions(-) create mode 100644 src/components/CreateProject/PaymentSummary.tsx diff --git a/src/components/CreateProject/PaymentSummary.tsx b/src/components/CreateProject/PaymentSummary.tsx new file mode 100644 index 0000000..7f7cda0 --- /dev/null +++ b/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 = ({ taskGroups }) => { + const { t } = useTranslation(); + + const { watch } = useFormContext(); + const milestones = watch("milestones"); + const rows = useMemo(() => { + 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[]>( + () => [ + { 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 ; +}; + +export default PaymentSummary; diff --git a/src/components/CreateProject/ProjectTotalFee.tsx b/src/components/CreateProject/ProjectTotalFee.tsx index 383c35b..0db48e5 100644 --- a/src/components/CreateProject/ProjectTotalFee.tsx +++ b/src/components/CreateProject/ProjectTotalFee.tsx @@ -1,21 +1,51 @@ import { CreateProjectInputs } from "@/app/api/projects/actions"; import { TaskGroup } from "@/app/api/tasks"; 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 { useTranslation } from "react-i18next"; +import PaymentSummary from "./PaymentSummary"; interface Props { 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 = ({ taskGroups }) => { const { t } = useTranslation(); const { watch } = useFormContext(); const milestones = watch("milestones"); const expectedTotalFee = watch("expectedProjectFee"); + const [paymentSummaryOpen, setPaymentSummaryOpen] = useState(false); + const closePaymentSummary = useCallback(() => { + setPaymentSummaryOpen(false); + }, []); + const openPaymentSummary = useCallback(() => { + setPaymentSummaryOpen(true); + }, []); + let projectTotal = 0; return ( @@ -37,9 +67,24 @@ const ProjectTotalFee: React.FC = ({ taskGroups }) => { ); })} + + + + + + - {t("Sum of Payment Milestone Fee")} + + {t("Sum of Payment Milestone Fee")} + {moneyFormatter.format(projectTotal)} @@ -49,7 +94,9 @@ const ProjectTotalFee: React.FC = ({ taskGroups }) => { {projectTotal !== expectedTotalFee && ( - {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!", + )} )} From ecb233789a3e3b3a7e5b40164355c18092e7b281 Mon Sep 17 00:00:00 2001 From: Wayne Date: Mon, 12 Aug 2024 15:48:45 +0900 Subject: [PATCH 3/3] Add more validation for bulk adding payments --- .../CreateProject/BulkAddPaymentModal.tsx | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/components/CreateProject/BulkAddPaymentModal.tsx b/src/components/CreateProject/BulkAddPaymentModal.tsx index 6555dc8..4e92d4d 100644 --- a/src/components/CreateProject/BulkAddPaymentModal.tsx +++ b/src/components/CreateProject/BulkAddPaymentModal.tsx @@ -73,6 +73,7 @@ const BulkAddPaymentModal: React.FC = ({ const { register, reset, trigger, formState, watch, control } = useForm({ + mode: "onTouched", defaultValues: { dateType: "monthly", dateReference: dayjs() }, }); @@ -87,7 +88,12 @@ const BulkAddPaymentModal: React.FC = ({ description, } = formValues; - if (numberOfEntries > 0 && amountToDivide && description) { + if ( + Number.isInteger(numberOfEntries) && + numberOfEntries > 0 && + amountToDivide && + description + ) { const dividedAmount = amountToDivide / numberOfEntries; return Array(numberOfEntries) .fill(undefined) @@ -157,6 +163,17 @@ const BulkAddPaymentModal: React.FC = ({ {...register("numberOfEntries", { valueAsNumber: true, required: t("Required"), + validate: (value) => { + if (!value) { + return t("Required"); + } else if (value < 0) { + return t("Number must be positive"); + } else if (!Number.isInteger(value)) { + return t("Number must be an integer"); + } else { + return true; + } + }, })} error={Boolean(formState.errors.numberOfEntries)} helperText={formState.errors.numberOfEntries?.message}