Browse Source

Combined Invoice tab as 1

tags/Baseline_30082024_FRONTEND_UAT
MSI\2Fi 1 year ago
parent
commit
526cc84746
5 changed files with 129 additions and 29 deletions
  1. +12
    -0
      src/app/api/invoices/actions.ts
  2. +38
    -0
      src/app/api/invoices/index.ts
  3. +4
    -1
      src/app/utils/formatUtil.ts
  4. +55
    -26
      src/components/InvoiceSearch/InvoiceSearch.tsx
  5. +20
    -2
      src/components/InvoiceSearch/InvoiceSearchWrapper.tsx

+ 12
- 0
src/app/api/invoices/actions.ts View File

@@ -92,4 +92,16 @@ export const importReceivedInovice = async (data: FormData) => {
);

return importReceivedInovice;
};

export const importInvoices = async (data: FormData) => {
const importInvoices = await serverFetchJson<any>(
`${BASE_API_URL}/invoices/import/v2`,
{
method: "POST",
body: data,
},
);

return importInvoices;
};

+ 38
- 0
src/app/api/invoices/index.ts View File

@@ -15,6 +15,25 @@ export interface InvoiceResult {
reminder: string;
}

export interface InvoiceResultV2 {
id: number;
invoiceNo: string;
projectCode: string;
projectName: string;
team: string;
stage: string;
paymentMilestone: string;
paymentMilestoneDate: string;
client: string;
address: string;
attention: string;
invoiceDate: string;
receiptDate: string;
dueDate: number[];
issueAmount: number;
paidAmount: number;
}

export interface issuedInvoiceResult {
id: number;
invoiceNo: string;
@@ -42,6 +61,18 @@ export interface receivedInvoiceResult {
receivedAmount: number;
}

export interface invoiceList {
id: number;
invoiceNo: string;
projectCode: string;
projectName: string;
// stage: string;
issuedDate: string;
receiptDate: string;
issuedAmount: string;
receivedAmount: string;
team: string;
}


export interface issuedInvoiceList {
@@ -82,6 +113,7 @@ export interface issuedInvoiceSearchForm {
invoiceNo: string;
projectCode: string;
projectName: string;
team: string;
// team: string;
// stage: string;
// paymentMilestone: string;
@@ -145,4 +177,10 @@ export const fetchReceivedInvoices = cache(async () => {
return serverFetchJson<receivedInvoiceResult[]>(`${BASE_API_URL}/invoices/v2/allInvoices/paid`, {
next: { tags: ["invoices"] },
});
});

export const fetchInvoicesV3 = cache(async () => {
return serverFetchJson<InvoiceResultV2[]>(`${BASE_API_URL}/invoices/v3/allInvoices`, {
next: { tags: ["invoices"] },
});
});

+ 4
- 1
src/app/utils/formatUtil.ts View File

@@ -45,6 +45,9 @@ export const convertDateArrayToString = (
return dayjs(dateString).format(format);
}
}
if (dateArray.length === 0){
return "-"
}
};

export const convertTimeArrayToString = (
@@ -132,6 +135,6 @@ export function timestampToDateString(timestamp: string): string {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
console.log(`${year}-${month}-${day}`);
// console.log(`${year}-${month}-${day}`);
return `${year}-${month}-${day}`;
}

+ 55
- 26
src/components/InvoiceSearch/InvoiceSearch.tsx View File

@@ -10,13 +10,14 @@ import { Button, ButtonGroup, Stack, Tab, Tabs, TabsProps } from "@mui/material"
import FileDownloadIcon from '@mui/icons-material/FileDownload';
import FileUploadIcon from '@mui/icons-material/FileUpload';
import { dateInRange, downloadFile } from "@/app/utils/commonUtil";
import { importIssuedInovice, importReceivedInovice } from "@/app/api/invoices/actions";
import { importInvoices, importIssuedInovice, importReceivedInovice } from "@/app/api/invoices/actions";
import { errorDialogWithContent, successDialog } from "../Swal/CustomAlerts";
import { issuedInvoiceList, issuedInvoiceResult, issuedInvoiceSearchForm, receivedInvoiceList, receivedInvoiceSearchForm } from "@/app/api/invoices";
import { invoiceList, issuedInvoiceList, issuedInvoiceResult, issuedInvoiceSearchForm, receivedInvoiceList, receivedInvoiceSearchForm } from "@/app/api/invoices";

interface Props {
issuedInvoice: issuedInvoiceList[];
receivedInvoice: receivedInvoiceList[];
invoices: invoiceList[];
}

type SearchQuery = Partial<Omit<issuedInvoiceSearchForm, "id">>;
@@ -25,19 +26,21 @@ type SearchParamNames = keyof SearchQuery;
type SearchQuery2 = Partial<Omit<receivedInvoiceSearchForm, "id">>;
type SearchParamNames2 = keyof SearchQuery2;

const InvoiceSearch: React.FC<Props> = ({ issuedInvoice, receivedInvoice }) => {
const InvoiceSearch: React.FC<Props> = ({ issuedInvoice, receivedInvoice, invoices }) => {
const { t } = useTranslation("invoices");
const [tabIndex, setTabIndex] = useState(0);

const [filteredIssuedInvoices, setFilteredIssuedInvoices] = useState(issuedInvoice);
const [filteredReceivedInvoices, setFilteredReceivedInvoices] = useState(receivedInvoice);
const [filteredIvoices, setFilterInovices] = useState(invoices);

const searchCriteria: Criterion<SearchParamNames>[] = useMemo(
() => [
{ label: t("Invoice No"), paramName: "invoiceNo", type: "text" },
{ label: t("Project Code"), paramName: "projectCode", type: "text" },
{ label: t("Invoice Date"), label2: t("Invoice Date To"), paramName: "invoiceDate", type: "dateRange" },
{ label: t("Due Date"), label2: t("Due Date To"), paramName: "dueDate", type: "dateRange" },
{ label: t("Team"), paramName: "team", type: "text" },
{ label: t("Issue Date"), label2: t("Issue Date To"), paramName: "invoiceDate", type: "dateRange" },
{ label: t("Settle Date"), label2: t("Settle Date To"), paramName: "dueDate", type: "dateRange" },
],
[t, issuedInvoice],
);
@@ -52,8 +55,9 @@ const InvoiceSearch: React.FC<Props> = ({ issuedInvoice, receivedInvoice }) => {
);

const onReset = useCallback(() => {
setFilteredIssuedInvoices(issuedInvoice);
}, [issuedInvoice]);
// setFilteredIssuedInvoices(issuedInvoice);
setFilterInovices(invoices)
}, [invoices]);

function concatListOfObject(obj: any[]): string {
return obj.map(obj => `Cannot find "${obj.paymentMilestone}" in ${obj.invoiceNo}`).join(", ")
@@ -82,7 +86,7 @@ const InvoiceSearch: React.FC<Props> = ({ issuedInvoice, receivedInvoice }) => {
const formData = new FormData();
formData.append('multipartFileList', file);

const response = await importIssuedInovice(formData);
const response = await importInvoices(formData);
// response: status, message, projectList, emptyRowList, invoiceList

console.log(response)
@@ -225,6 +229,20 @@ const InvoiceSearch: React.FC<Props> = ({ issuedInvoice, receivedInvoice }) => {
[t],
);

const combinedColumns = useMemo<Column<invoiceList>[]>(
() => [
{ name: "invoiceNo", label: t("Invoice No") },
{ name: "projectCode", label: t("Project Code") },
{ name: "projectName", label: t("Project Name") },
{ name: "team", label: t("Team") },
{ name: "issuedDate", label: t("Issue Date") },
{ name: "receivedAmount", label: t("Amount (HKD)") },
{ name: "receiptDate", label: t("Settle Date") },
{ name: "receivedAmount", label: t("Actual Received Amount (HKD)") },
],
[t]
)

function isDateInRange(dateToCheck: string, startDate: string, endDate: string): boolean {

if ((!startDate || startDate === "Invalid Date") && (!endDate || endDate === "Invalid Date")) {
@@ -254,7 +272,7 @@ const InvoiceSearch: React.FC<Props> = ({ issuedInvoice, receivedInvoice }) => {
spacing={2}
>
{/* <ButtonGroup variant="contained"> */}
<Button startIcon={<FileUploadIcon />} variant="contained" component="label">
{/* <Button startIcon={<FileUploadIcon />} variant="contained" component="label">
<input
id='importExcel'
type='file'
@@ -273,29 +291,40 @@ const InvoiceSearch: React.FC<Props> = ({ issuedInvoice, receivedInvoice }) => {
onChange={(event) => {handleRecImportClick(event)}}
/>
{t("Import Invoice Amount Receive Summary")}
</Button>
</Button> */}
{/* </ButtonGroup> */}
<Button startIcon={<FileUploadIcon />} variant="contained" component="label">
<input
id='importExcel'
type='file'
accept='.xlsx, .csv'
hidden
onChange={(event) => {handleImportClick(event)}}
/>
{t("Import Invoice Summary")}
</Button>
</Stack>
{
tabIndex == 0 &&
// tabIndex == 0 &&
<SearchBox
criteria={searchCriteria}
onSearch={(query) => {
console.log(query)
setFilteredIssuedInvoices(
issuedInvoice.filter(
setFilterInovices(
invoices.filter(
(s) =>
(isDateInRange(s.invoiceDate, query.invoiceDate ?? undefined, query.invoiceDateTo ?? undefined)) &&
(isDateInRange(s.dueDate, query.dueDate ?? undefined, query.dueDateTo ?? undefined)) &&
(isDateInRange(s.issuedDate, query.invoiceDate ?? undefined, query.invoiceDateTo ?? undefined)) &&
(isDateInRange(s.receiptDate, query.dueDate ?? undefined, query.dueDateTo ?? undefined)) &&
(s.invoiceNo.toLowerCase().includes(query.invoiceNo.toLowerCase())) &&
(s.projectCode.toLowerCase().includes(query.projectCode.toLowerCase()))
(s.projectCode.toLowerCase().includes(query.projectCode.toLowerCase())) &&
(s.team.toLowerCase().includes(query.team.toLowerCase()))
),
);
}}
onReset={onReset}
/>
}
{
{/* {
tabIndex == 1 &&
<SearchBox
criteria={searchCriteria2}
@@ -312,25 +341,25 @@ const InvoiceSearch: React.FC<Props> = ({ issuedInvoice, receivedInvoice }) => {
}}
onReset={onReset}
/>
}
<Tabs value={tabIndex} onChange={handleTabChange} variant="scrollable">
} */}
{/* {<Tabs value={tabIndex} onChange={handleTabChange} variant="scrollable">
<Tab label={t("Issued Invoices")}/>
<Tab label={t("Received Invoices")}/>
</Tabs>
</Tabs>} */}
{
tabIndex == 0 &&
<SearchResults<issuedInvoiceList>
items={filteredIssuedInvoices}
columns={columns}
// tabIndex == 0 &&
<SearchResults<invoiceList>
items={filteredIvoices}
columns={combinedColumns}
/>
}
{
{/* {
tabIndex == 1 &&
<SearchResults<receivedInvoiceList>
items={filteredReceivedInvoices}
columns={columns2}
/>
}
} */}
</>
);


+ 20
- 2
src/components/InvoiceSearch/InvoiceSearchWrapper.tsx View File

@@ -2,8 +2,8 @@
import React from "react";
import InvoiceSearch from "./InvoiceSearch";
import InvoiceSearchLoading from "./InvoiceSearchLoading";
import { fetchIssuedInvoices, fetchReceivedInvoices, issuedInvoiceList, issuedInvoiceResult } from "@/app/api/invoices";
import { INPUT_DATE_FORMAT, convertDateArrayToString, moneyFormatter } from "@/app/utils/formatUtil";
import { fetchInvoicesV3, fetchIssuedInvoices, fetchReceivedInvoices, issuedInvoiceList, issuedInvoiceResult } from "@/app/api/invoices";
import { INPUT_DATE_FORMAT, convertDateArrayToString, convertDateToString, moneyFormatter, timestampToDateString } from "@/app/utils/formatUtil";


interface SubComponents {
@@ -18,7 +18,10 @@ interface SubComponents {
const InvoiceSearchWrapper: React.FC & SubComponents = async () => {
const issuedInvoices = await fetchIssuedInvoices()
const receivedInvoices = await fetchReceivedInvoices()
const invoices = await fetchInvoicesV3()
// console.log(invoices)

const convertedIssedInvoices = issuedInvoices.map((invoice)=>{
return{
id: invoice.id,
@@ -45,9 +48,24 @@ const InvoiceSearchWrapper: React.FC & SubComponents = async () => {
}
})

const convertedInvoices = invoices.map((invoice)=>{
return{
id: invoice.id,
invoiceNo: invoice.invoiceNo,
projectCode: invoice.projectCode,
projectName: invoice.projectName,
team: invoice.team,
issuedDate: timestampToDateString(invoice.invoiceDate)!!,
receiptDate: timestampToDateString(invoice.receiptDate??0)!!,
issuedAmount: moneyFormatter.format(invoice.issueAmount),
receivedAmount: moneyFormatter.format(invoice.paidAmount)
}
})

return <InvoiceSearch
issuedInvoice={convertedIssedInvoices}
receivedInvoice={convertedReceivedInvoices}
invoices={convertedInvoices}
/>
};


Loading…
Cancel
Save