Преглед на файлове

Create Invoice

tags/Baseline_30082024_FRONTEND_UAT
MSI\2Fi преди 1 година
родител
ревизия
12f2cbd8c7
променени са 6 файла, в които са добавени 137 реда и са изтрити 25 реда
  1. +28
    -6
      src/app/api/invoices/actions.ts
  2. +3
    -3
      src/app/api/invoices/index.ts
  3. +21
    -3
      src/components/CreateInvoice/CreateInvoice.tsx
  4. +38
    -13
      src/components/CreateInvoice/InvoiceDetails.tsx
  5. +1
    -0
      src/components/CreateInvoice/ProjectDetails.tsx
  6. +46
    -0
      src/components/CreateInvoice/ProjectTotalFee.tsx

+ 28
- 6
src/app/api/invoices/actions.ts Целия файл

@@ -8,16 +8,36 @@ export interface InvoiceResult {
id: number;
projectCode: string;
projectName: string;
stage: String;
comingPaymentMileStone: String;
paymentMilestoneDate: String;
stage: string;
comingPaymentMileStone: string;
paymentMilestoneDate: string;
resourceUsage: number;
unbilledHours: number;
reminder: String;
reminder: string;
}

export interface CreateInvoiceInputs {
id: number;

// Project Details
projectCode: string;
projectName: string;
stage: string;
comingPaymentMileStone: string;
paymentMilestoneDate: string;
resourceUsage: number;
unbilledHours: number;

// Invoice Info
client: string;
address: string;
attention: string;
invoiceDate: string;
dueDate: string;
projectRefNo: string;

// Invoice related Info
reminder: string;
amount: number;
billHours: number;
}
@@ -30,16 +50,18 @@ export interface InvoiceInformation{
invoiceDate: string;
dueDate: string;
projectRefNo: string;
amount: number;
}

export const fetchProjectInvoiceById = cache(async (id: number) => {
return serverFetchJson<InvoiceResult[]>(`${BASE_API_URL}/invoices/getProjectDetailById?id=${id}`, {
return serverFetchJson<InvoiceResult[]>(`${BASE_API_URL}/invoices/getProjectDetail/${id}`, {
next: { tags: ["projectDetailById"] },
});
})

export const fetchInvoiceInfoById = cache(async (id: number) => {
return serverFetchJson<InvoiceInformation[]>(`${BASE_API_URL}/invoices/getInvoiceInfoById?id=${id}`, {
return serverFetchJson<InvoiceInformation[]>(`${BASE_API_URL}/invoices/getInvoiceInfo/${id}`, {
next: { tags: ["invoiceInfoById"] },
});
})

+ 3
- 3
src/app/api/invoices/index.ts Целия файл

@@ -8,11 +8,11 @@ export interface InvoiceResult {
projectCode: string;
projectName: string;
stage: String;
comingPaymentMileStone: String;
paymentMilestoneDate: String;
comingPaymentMileStone: string;
paymentMilestoneDate: string;
resourceUsage: number;
unbilledHours: number;
reminder: String;
reminder: string;
}

export interface InvoiceInformatio{


+ 21
- 3
src/components/CreateInvoice/CreateInvoice.tsx Целия файл

@@ -20,6 +20,9 @@ import { InvoiceResult } from "@/app/api/invoices";
import { InvoiceInformation, fetchInvoiceInfoById, fetchProjectInvoiceById } from "@/app/api/invoices/actions";
import InvoiceDetails from "./InvoiceDetails";
import ProjectDetails from "./ProjectDetails";
import ProjectTotalFee from "./ProjectTotalFee";
import { timestampToDateString } from "@/app/utils/formatUtil";
import dayjs from "dayjs";

const CreateInvoice: React.FC = ({
}) => {
@@ -31,13 +34,18 @@ const CreateInvoice: React.FC = ({
const [invoiceDetail, setInvoiceDetail] = useState<InvoiceInformation>()
const [serverError, setServerError] = useState("");

// const { getValues } = useForm();

const fetchProjectDetails = async () =>{
const projectId = searchParams.get("id")
try{
if (projectId !== null && parseInt(projectId) > 0) {
const projectDetail = await fetchProjectInvoiceById(parseInt(projectId))
console.log(projectDetail)
setProjectDetail(projectDetail[0])
// console.log(projectDetail)
const updatedPrijectDetail = projectDetail.map(detail => {
return { ...detail, paymentMilestoneDate: timestampToDateString(detail.paymentMilestoneDate) }; // Update the age of person with id 2
});
setProjectDetail(updatedPrijectDetail[0])
}
} catch (error){
console.log(error)
@@ -51,7 +59,13 @@ const CreateInvoice: React.FC = ({
if (projectId !== null && parseInt(projectId) > 0) {
const invoiceInfo = await fetchInvoiceInfoById(parseInt(projectId))
console.log(invoiceInfo)
setInvoiceDetail(invoiceInfo[0])
const updatedInvoiceInfo = invoiceInfo.map(info => {
return { ...info,
invoiceDate: dayjs.unix(parseFloat(info.invoiceDate)/1000).format('YYYY/MM/DD'),
dueDate: dayjs.unix(parseFloat(info.dueDate)/1000).format('YYYY/MM/DD')
};
});
setInvoiceDetail(updatedInvoiceInfo[0])
}
} catch (error){
console.log(error)
@@ -69,6 +83,7 @@ const CreateInvoice: React.FC = ({
};

const handlePrintout = () => {
// const formData = getValues();
console.log("Printing in Progress")
}

@@ -114,6 +129,9 @@ const CreateInvoice: React.FC = ({
{
invoiceDetail && <InvoiceDetails invoiceinfo={invoiceDetail}/>
}
{
<ProjectTotalFee />
}
<Stack direction="row" justifyContent="flex-end" gap={1}>
<Button
variant="outlined"


+ 38
- 13
src/components/CreateInvoice/InvoiceDetails.tsx Целия файл

@@ -17,9 +17,12 @@ import RestartAlt from "@mui/icons-material/RestartAlt";
import Button from "@mui/material/Button";
import { Controller, UseFormRegister, useFormContext } from "react-hook-form";
import { CreateInvoiceInputs } from "@/app/api/invoices/actions";
import { TimePicker } from "@mui/x-date-pickers";
import dayjs from 'dayjs';
import { LocalizationProvider, DatePicker } from "@mui/x-date-pickers";
import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";
import dayjs from "dayjs";
import { InvoiceInformation } from "@/app/api/invoices/actions";
import { INPUT_DATE_FORMAT } from "@/app/utils/formatUtil";
import React, { useCallback, useEffect, useMemo, useState } from "react";

interface Props {
invoiceinfo: InvoiceInformation
@@ -28,16 +31,24 @@ interface Props {
const InvoiceDetails: React.FC<Props> = ({
invoiceinfo,
}) => {
const { t } = useTranslation();
const {
t,
i18n: { language },
} = useTranslation();
const {
register,
formState: { errors },
control,
setValue,
getValues,
} = useFormContext<CreateInvoiceInputs>();
console.log(invoiceinfo)
} = useFormContext<InvoiceInformation>();

useEffect(() => {
// const invoiceDate = getValues("invoiceDate");
// const dueDate = getValues("dueDate");
setValue("invoiceDate", dayjs(invoiceinfo.invoiceDate).format(INPUT_DATE_FORMAT))
setValue("dueDate", dayjs(invoiceinfo.dueDate).format(INPUT_DATE_FORMAT))
}, [invoiceinfo,]);

return (
<Card>
@@ -46,6 +57,10 @@ const InvoiceDetails: React.FC<Props> = ({
<Typography variant="overline" display="block" marginBlockEnd={1}>
{t("Invoice Information")}
</Typography>
<LocalizationProvider
dateAdapter={AdapterDayjs}
adapterLocale={`${language}-hk`}
>
<Grid container spacing={2} columns={{ xs: 6, sm: 12 }}>
<Grid item xs={6}>
<TextField
@@ -72,18 +87,28 @@ const InvoiceDetails: React.FC<Props> = ({
/>
</Grid>
<Grid item xs={6}>
<TextField
<FormControl fullWidth>
<DatePicker
label={t("Invoice Date")}
fullWidth
defaultValue={invoiceinfo.invoiceDate}
value={dayjs(invoiceinfo.invoiceDate)}
onChange={(date) => {
if (!date) return;
setValue("invoiceDate", date.format(INPUT_DATE_FORMAT));
}}
/>
</FormControl>
</Grid>
<Grid item xs={6}>
<TextField
<FormControl fullWidth>
<DatePicker
label={t("Due Date")}
fullWidth
defaultValue={invoiceinfo.dueDate}
value={dayjs(invoiceinfo.dueDate)}
onChange={(date) => {
if (!date) return;
setValue("dueDate", date.format(INPUT_DATE_FORMAT));
}}
/>
</FormControl>
</Grid>
<Grid item xs={6}>
<TextField
@@ -103,8 +128,8 @@ const InvoiceDetails: React.FC<Props> = ({
error={Boolean(errors.amount)}
/>
</Grid>
</Grid>
</LocalizationProvider>
</Box>
{/* <CardActions sx={{ justifyContent: "flex-end" }}>
<Button variant="text" startIcon={<RestartAlt />}>


+ 1
- 0
src/components/CreateInvoice/ProjectDetails.tsx Целия файл

@@ -14,6 +14,7 @@ import Typography from "@mui/material/Typography";
import { useTranslation } from "react-i18next";
import { InvoiceResult } from "@/app/api/invoices";
import { useFormContext } from "react-hook-form";
import { timestampToDateString } from "@/app/utils/formatUtil";

interface Props {
projectDetails: InvoiceResult


+ 46
- 0
src/components/CreateInvoice/ProjectTotalFee.tsx Целия файл

@@ -0,0 +1,46 @@
import { CreateInvoiceInputs } from "@/app/api/invoices/actions";
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 { useFormContext } from "react-hook-form";
import { useTranslation } from "react-i18next";



const ProjectTotalFee: React.FC= ({}) => {
const { t } = useTranslation();
const { watch } = useFormContext<CreateInvoiceInputs>();
const amount = watch("amount");

let projectTotal = 0;

return (
<Stack spacing={1}>
{/* {taskGroups.map((group, index) => {
const payments = milestones[group.id]?.payments || [];
const paymentTotal = payments.reduce((acc, p) => acc + p.amount, 0);
projectTotal += paymentTotal;

return (
<Stack
key={`${group.id}-${index}`}
direction="row"
justifyContent="space-between"
>
<Typography variant="subtitle2">{group.name}</Typography>
<Typography>{moneyFormatter.format(paymentTotal)}</Typography>
</Stack>
);
})} */}
<Divider sx={{ paddingBlockStart: 2 }} />
<Stack direction="row" justifyContent="space-between">
<Typography variant="h6">{t("Project Total Fee")}</Typography>
<Typography>{moneyFormatter.format(projectTotal += amount)}</Typography>
</Stack>
</Stack>
);
};

export default ProjectTotalFee;

Зареждане…
Отказ
Запис