diff --git a/src/app/(main)/invoice/new/page.tsx b/src/app/(main)/invoice/new/page.tsx index 10e741f..da4701b 100644 --- a/src/app/(main)/invoice/new/page.tsx +++ b/src/app/(main)/invoice/new/page.tsx @@ -5,7 +5,7 @@ import Button from "@mui/material/Button"; import Stack from "@mui/material/Stack"; import Typography from "@mui/material/Typography"; import Link from "next/link"; -import CreateInvoice from "@/components/CreateInvoice"; +import CreateInvoice from "@/components/CreateInvoice_forGen"; export const metadata: Metadata = { title: "Create Invoice", diff --git a/src/app/api/invoices/actions.ts b/src/app/api/invoices/actions.ts index 7194c2c..dc45104 100644 --- a/src/app/api/invoices/actions.ts +++ b/src/app/api/invoices/actions.ts @@ -1,6 +1,6 @@ "use server" -import { serverFetchJson } from "@/app/utils/fetchUtil"; +import { serverFetchJson, serverFetchString } from "@/app/utils/fetchUtil"; import { BASE_API_URL } from "@/config/api"; import { cache } from "react"; @@ -64,4 +64,32 @@ export const fetchInvoiceInfoById = cache(async (id: number) => { return serverFetchJson(`${BASE_API_URL}/invoices/getInvoiceInfo/${id}`, { next: { tags: ["invoiceInfoById"] }, }); -}) \ No newline at end of file +}) + +export const importIssuedInovice = async (data: FormData) => { + // console.log("----------------",data) + const importIssuedInovice = await serverFetchJson( + `${BASE_API_URL}/invoices/import/issued`, + { + method: "POST", + body: data, + // headers: { "Content-Type": "multipart/form-data" }, + }, + ); + + return importIssuedInovice; +}; + +export const importReceivedInovice = async (data: FormData) => { + // console.log("----------------",data) + const importReceivedInovice = await serverFetchJson( + `${BASE_API_URL}/invoices/import/received`, + { + method: "POST", + body: data, + // headers: { "Content-Type": "multipart/form-data" }, + }, + ); + + return importReceivedInovice; +}; \ No newline at end of file diff --git a/src/app/api/invoices/index.ts b/src/app/api/invoices/index.ts index d20e0cf..4b00adb 100644 --- a/src/app/api/invoices/index.ts +++ b/src/app/api/invoices/index.ts @@ -15,6 +15,107 @@ export interface InvoiceResult { reminder: string; } +export interface issuedInvoiceResult { + id: number; + invoiceNo: string; + projectCode: string; + projectName: string; + team: string; + stage: string; + paymentMilestone: string; + paymentMilestoneDate: string; + client: string; + address: string; + attention: string; + invoiceDate: number[]; + dueDate: number[]; + issuedAmount: number; +} + +export interface receivedInvoiceResult { + id: number; + invoiceNo: string; + projectCode: string; + projectName: string; + team: string; + receiptDate: number[]; + receivedAmount: number; +} + + + +export interface issuedInvoiceList { + id: number; + invoiceNo: string; + projectCode: string; + projectName: string; + // team: string; + stage: string; + paymentMilestone: string; + // paymentMilestoneDate: string; + // client: string; + // address: string; + // attention: string; + invoiceDate: string; + dueDate: string; + issuedAmount: string; +} + +export interface receivedInvoiceList { + id: number; + invoiceNo: string; + projectCode: string; + projectName: string; + team: string; + // stage: string; + // paymentMilestone: string; + // paymentMilestoneDate: string; + // client: string; + // address: string; + // attention: string; + receiptDate: string; + receivedAmount: string; +} + +export interface issuedInvoiceSearchForm { + id: number; + invoiceNo: string; + projectCode: string; + projectName: string; + // team: string; + // stage: string; + // paymentMilestone: string; + // paymentMilestoneDate: string; + // client: string; + // address: string; + // attention: string; + invoiceDate: string; + invoiceDateTo: string; + dueDate: string; + dueDateTo: string; + // issuedAmount: string; +} + +export interface receivedInvoiceSearchForm { + id: number; + invoiceNo: string; + projectCode: string; + projectName: string; + // team: string; + // stage: string; + // paymentMilestone: string; + // paymentMilestoneDate: string; + // client: string; + // address: string; + // attention: string; + receiptDate: string; + receiptDateTo: string; + // dueDate: string; + // dueDateTo: string; + // issuedAmount: string; +} + + export interface InvoiceInformatio{ id: number; address: string; @@ -32,4 +133,16 @@ export const fetchInvoices = cache(async () => { return serverFetchJson(`${BASE_API_URL}/invoices`, { next: { tags: ["invoices"] }, }); +}); + +export const fetchIssuedInvoices = cache(async () => { + return serverFetchJson(`${BASE_API_URL}/invoices/v2/allInvoices`, { + next: { tags: ["invoices"] }, + }); +}); + +export const fetchReceivedInvoices = cache(async () => { + return serverFetchJson(`${BASE_API_URL}/invoices/v2/allInvoices/received`, { + next: { tags: ["invoices"] }, + }); }); \ No newline at end of file diff --git a/src/components/CreateInvoice/CreateInvoice.tsx b/src/components/CreateInvoice_forGen/CreateInvoice.tsx similarity index 100% rename from src/components/CreateInvoice/CreateInvoice.tsx rename to src/components/CreateInvoice_forGen/CreateInvoice.tsx diff --git a/src/components/CreateInvoice/CreateInvoiceWrapper.tsx b/src/components/CreateInvoice_forGen/CreateInvoiceWrapper.tsx similarity index 100% rename from src/components/CreateInvoice/CreateInvoiceWrapper.tsx rename to src/components/CreateInvoice_forGen/CreateInvoiceWrapper.tsx diff --git a/src/components/CreateInvoice/InvoiceDetails.tsx b/src/components/CreateInvoice_forGen/InvoiceDetails.tsx similarity index 100% rename from src/components/CreateInvoice/InvoiceDetails.tsx rename to src/components/CreateInvoice_forGen/InvoiceDetails.tsx diff --git a/src/components/CreateInvoice/ProjectDetails.tsx b/src/components/CreateInvoice_forGen/ProjectDetails.tsx similarity index 100% rename from src/components/CreateInvoice/ProjectDetails.tsx rename to src/components/CreateInvoice_forGen/ProjectDetails.tsx diff --git a/src/components/CreateInvoice/ProjectTotalFee.tsx b/src/components/CreateInvoice_forGen/ProjectTotalFee.tsx similarity index 100% rename from src/components/CreateInvoice/ProjectTotalFee.tsx rename to src/components/CreateInvoice_forGen/ProjectTotalFee.tsx diff --git a/src/components/CreateInvoice/index.ts b/src/components/CreateInvoice_forGen/index.ts similarity index 100% rename from src/components/CreateInvoice/index.ts rename to src/components/CreateInvoice_forGen/index.ts diff --git a/src/components/CustomerSave/CustomerSave.tsx b/src/components/CustomerSave/CustomerSave.tsx index acb4ecf..5673956 100644 --- a/src/components/CustomerSave/CustomerSave.tsx +++ b/src/components/CustomerSave/CustomerSave.tsx @@ -212,7 +212,7 @@ const CustomerSave: React.FC = ({ return false }) } - }, t) + }, t, {}) } catch (e) { console.log(e) setServerError(t("An error has occurred. Please try again later.")); diff --git a/src/components/InvoiceSearch/InvoiceSearch.tsx b/src/components/InvoiceSearch/InvoiceSearch.tsx index 52dbdd5..adf1153 100644 --- a/src/components/InvoiceSearch/InvoiceSearch.tsx +++ b/src/components/InvoiceSearch/InvoiceSearch.tsx @@ -5,98 +5,257 @@ import SearchBox, { Criterion } from "../SearchBox"; import { useTranslation } from "react-i18next"; import SearchResults, { Column } from "../SearchResults"; import EditNote from "@mui/icons-material/EditNote"; -import { InvoiceResult } from "@/app/api/invoices"; -import { useRouter } from "next/navigation"; +import { convertLocaleStringToNumber } from "@/app/utils/formatUtil" +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 { errorDialogWithContent, successDialog } from "../Swal/CustomAlerts"; +import { issuedInvoiceList, issuedInvoiceResult, issuedInvoiceSearchForm, receivedInvoiceList, receivedInvoiceSearchForm } from "@/app/api/invoices"; interface Props { - invoices: InvoiceResult[]; + issuedInvoice: issuedInvoiceList[]; + receivedInvoice: receivedInvoiceList[]; } -type SearchQuery = Partial>; +type SearchQuery = Partial>; type SearchParamNames = keyof SearchQuery; -const InvoiceSearch: React.FC = ({ invoices }) => { +type SearchQuery2 = Partial>; +type SearchParamNames2 = keyof SearchQuery2; + +const InvoiceSearch: React.FC = ({ issuedInvoice, receivedInvoice }) => { const { t } = useTranslation("invoices"); - const router = useRouter(); + const [tabIndex, setTabIndex] = useState(0); - const [filteredInvoices, setFilteredInvoices] = useState(invoices); + const [filteredIssuedInvoices, setFilteredIssuedInvoices] = useState(issuedInvoice); + const [filteredReceivedInvoices, setFilteredReceivedInvoices] = useState(receivedInvoice); const searchCriteria: Criterion[] = useMemo( () => [ - { label: t("Project code"), paramName: "projectCode", type: "text" }, - { label: t("Project name"), paramName: "projectName", type: "text" }, - // { label: t("Stage"), paramName: "stage", type: "text" }, - { - label: t("Coming payment milestone from"), - label2: t("Coming payment milestone to"), - paramName: "comingPaymentMileStone", - type: "dateRange" - }, - { - label: t("Payment date from"), - label2: t("Payment date to"), - paramName: "paymentMilestoneDate", - type: "dateRange" - }, - // { label: t("Resource utilization %"), paramName: "resourceUsage", type: "text" }, - // { label: t("Unbilled hours"), paramName: "unbilledHours", type: "text" }, - // { label: t("Reminder to issue invoice"), paramName: "reminder", type: "text" }, + { 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" }, + ], + [t, issuedInvoice], + ); + + const searchCriteria2: Criterion[] = useMemo( + () => [ + { label: t("Invoice No"), paramName: "invoiceNo", type: "text" }, + { label: t("Project Code"), paramName: "projectCode", type: "text" }, + { label: t("Recipt Date"), label2: t("Recipt Date To"), paramName: "receiptDate", type: "dateRange" }, ], - [t, invoices], + [t, issuedInvoice], ); const onReset = useCallback(() => { - setFilteredInvoices(invoices); - }, [invoices]); + setFilteredIssuedInvoices(issuedInvoice); + }, [issuedInvoice]); + + const handleImportClick = useCallback(async (event:any) => { + // console.log(event) + try { + + const file = event.target.files[0]; + + if (!file) { + console.log('No file selected'); + return; + } + + if (file.type !== 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') { + console.log('Invalid file format. Only XLSX files are allowed.'); + return; + } + + const formData = new FormData(); + formData.append('multipartFileList', file); + + const response = await importIssuedInovice(formData); + // response: status, message, projectList, emptyRowList, invoiceList + + console.log(response) + if (response.status) { + successDialog(t("Import Success"), t).then(() => { + window.location.reload() + }) + }else{ + if (response.emptyRowList.length >= 1){ + errorDialogWithContent(t("Import Fail"), + t(`Please fill the mandatory field at Row
${response.emptyRowList.join(", ")}`), t) + .then(() => { + window.location.reload() + }) + } + else if (response.projectList.length >= 1){ + errorDialogWithContent(t("Import Fail"), + t(`Please check the corresponding project code
${response.projectList.join(", ")}`), t) + .then(() => { + // window.location.reload() + }) + } + else if (response.invoiceList.length >= 1){ + errorDialogWithContent(t("Import Fail"), + t(`Please check the corresponding Invoice No.
`)+ `${response.invoiceList.join(", ")}`, t) + .then(() => { + window.location.reload() + }) + } + } + + } catch (err) { + console.log(err) + return false + } + }, []); + + const handleExportClick = useCallback(async (event:any) => { + try { + + const file = event.target.files[0]; - const onProjectClick = useCallback((project: InvoiceResult) => { - console.log(project); - router.push(`/invoice/new?id=${project.id}`) - }, [router, t]); + if (!file) { + console.log('No file selected'); + return; + } - const columns = useMemo[]>( + if (file.type !== 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') { + console.log('Invalid file format. Only XLSX files are allowed.'); + return; + } + + const formData = new FormData(); + formData.append('multipartFileList', file); + + const response = await importReceivedInovice(formData) + + }catch(error){ + console.log(error) + } + + }, []); + + const columns = useMemo[]>( () => [ - { - name: "id", - label: t("Details"), - onClick: onProjectClick, - buttonIcon: , - }, - { name: "projectCode", label: t("Project code") }, - { name: "projectName", label: t("Project name") }, + { name: "invoiceNo", label: t("Invoice No") }, + { name: "projectCode", label: t("Project Code") }, { name: "stage", label: t("Stage") }, - { name: "comingPaymentMileStone", label: t("Coming payment milestone") }, - { name: "paymentMilestoneDate", label: t("Payment date") }, - { name: "resourceUsage", label: t("Resource utilization %") }, - { name: "unbilledHours", label: t("Unbilled hours") }, - { name: "reminder", label: t("Reminder to issue invoice") }, + { name: "paymentMilestone", label: t("Payment Milestone") }, + { name: "invoiceDate", label: t("Invocie Date") }, + { name: "dueDate", label: t("Due Date") }, + { name: "issuedAmount", label: t("Amount (HKD") }, ], - [t, onProjectClick], + [t], + ); + + function isDateInRange(dateToCheck: string, startDate: string, endDate: string): boolean { + + if (!startDate || !endDate) { + return false; + } + + // console.log(dateToCheck, startDate, endDate) + const dateToCheckObj = new Date(dateToCheck); + const startDateObj = new Date(startDate); + const endDateObj = new Date(endDate); + // console.log(dateToCheckObj >= startDateObj && dateToCheckObj <= endDateObj) + return dateToCheckObj >= startDateObj && dateToCheckObj <= endDateObj; + } + + const handleTabChange = useCallback>( + (_e, newValue) => { + setTabIndex(newValue); + }, + [], ); return ( <> - + {/* */} + + + {/* */} + + { + tabIndex == 0 && + { + console.log(query) + setFilteredIssuedInvoices( + issuedInvoice.filter( + (s) => + (isDateInRange(s.invoiceDate, query.invoiceDate ?? undefined, query.invoiceDateTo ?? undefined)) || + (isDateInRange(s.dueDate, query.dueDate ?? undefined, query.dueDateTo ?? undefined)) || + (s.invoiceNo === query.invoiceNo) || + (s.projectCode === query.projectCode) + ), + ); + }} + onReset={onReset} + /> + } + { + tabIndex == 1 && + { console.log(query) - setFilteredInvoices( - invoices.filter( - (d) => - d.projectCode.toLowerCase().includes(query.projectCode.toLowerCase()) && - d.projectName.toLowerCase().includes(query.projectName.toLowerCase()) && - {/*(query.client === "All" || p.client === query.client) && - (query.category === "All" || p.category === query.category) && - (query.team === "All" || p.team === query.team), **/} + setFilteredIssuedInvoices( + issuedInvoice.filter( + (s) => + (isDateInRange(s.invoiceDate, query.receiptDate ?? undefined, query.receiptDateTo ?? undefined)) || + (s.invoiceNo === query.invoiceNo) || + (s.projectCode === query.projectCode) ), ); }} onReset={onReset} /> - - items={filteredInvoices} - columns={columns} - /> + } + + + + + { + tabIndex == 0 && + + items={filteredIssuedInvoices} + columns={columns} + /> + } + { + tabIndex == 1 && +

Todo

+ } + ); }; diff --git a/src/components/InvoiceSearch/InvoiceSearchLoading.tsx b/src/components/InvoiceSearch/InvoiceSearchLoading.tsx index 927c6c6..e2cc58d 100644 --- a/src/components/InvoiceSearch/InvoiceSearchLoading.tsx +++ b/src/components/InvoiceSearch/InvoiceSearchLoading.tsx @@ -5,7 +5,7 @@ import Stack from "@mui/material/Stack"; import React from "react"; // Can make this nicer -export const InvoiceSearchLoading: React.FC = () => { +export const SalarySearchLoading: React.FC = () => { return ( <> @@ -23,7 +23,7 @@ export const InvoiceSearchLoading: React.FC = () => { - Invoice + Salary @@ -37,4 +37,4 @@ export const InvoiceSearchLoading: React.FC = () => { ); }; -export default InvoiceSearchLoading; +export default SalarySearchLoading; diff --git a/src/components/InvoiceSearch/InvoiceSearchWrapper.tsx b/src/components/InvoiceSearch/InvoiceSearchWrapper.tsx index ba05baf..7a4cfcf 100644 --- a/src/components/InvoiceSearch/InvoiceSearchWrapper.tsx +++ b/src/components/InvoiceSearch/InvoiceSearchWrapper.tsx @@ -1,24 +1,44 @@ - +// import { fetchInvoiceCategories, fetchInvoices } from "@/app/api/companys"; import React from "react"; import InvoiceSearch from "./InvoiceSearch"; import InvoiceSearchLoading from "./InvoiceSearchLoading"; -import { fetchInvoices } from "@/app/api/invoices"; -import { timestampToDateString } from "@/app/utils/formatUtil"; +import { fetchIssuedInvoices, fetchReceivedInvoices, issuedInvoiceList, issuedInvoiceResult } from "@/app/api/invoices"; +import { INPUT_DATE_FORMAT, convertDateArrayToString, moneyFormatter } from "@/app/utils/formatUtil"; interface SubComponents { Loading: typeof InvoiceSearchLoading; } -const InvoiceSearchWrapper: React.FC & SubComponents = async () => { - const Invoices = await fetchInvoices(); - - const temp = Invoices.map((invoice) => ({ - ...invoice, - paymentMilestoneDate: timestampToDateString(invoice.paymentMilestoneDate) - })) +// function calculateHourlyRate(loweLimit: number, upperLimit: number, numOfWorkingDay: number, workingHour: number){ +// const hourlyRate = (loweLimit + upperLimit)/2/numOfWorkingDay/workingHour +// return hourlyRate.toLocaleString() +// } - return ; +const InvoiceSearchWrapper: React.FC & SubComponents = async () => { + const issuedInvoices = await fetchIssuedInvoices() + // const receivedInvoices = await fetchReceivedInvoices() + + const convertedIssedInvoices = issuedInvoices.map((invoice)=>{ + return{ + id: invoice.id, + invoiceNo: invoice.invoiceNo, + projectCode: invoice.projectCode, + projectName: invoice.projectName, + stage: invoice.stage, + paymentMilestone: invoice.paymentMilestone, + invoiceDate: convertDateArrayToString(invoice.invoiceDate, INPUT_DATE_FORMAT, false)!!, + dueDate: convertDateArrayToString(invoice.dueDate, INPUT_DATE_FORMAT, false)!!, + issuedAmount: moneyFormatter.format(invoice.issuedAmount) + } + }) + + + return + }; InvoiceSearchWrapper.Loading = InvoiceSearchLoading; diff --git a/src/components/InvoiceSearch_forGen/InvoiceSearch.tsx b/src/components/InvoiceSearch_forGen/InvoiceSearch.tsx new file mode 100644 index 0000000..52dbdd5 --- /dev/null +++ b/src/components/InvoiceSearch_forGen/InvoiceSearch.tsx @@ -0,0 +1,104 @@ +"use client"; + +import React, { useCallback, useMemo, useState } from "react"; +import SearchBox, { Criterion } from "../SearchBox"; +import { useTranslation } from "react-i18next"; +import SearchResults, { Column } from "../SearchResults"; +import EditNote from "@mui/icons-material/EditNote"; +import { InvoiceResult } from "@/app/api/invoices"; +import { useRouter } from "next/navigation"; + +interface Props { + invoices: InvoiceResult[]; +} + +type SearchQuery = Partial>; +type SearchParamNames = keyof SearchQuery; + +const InvoiceSearch: React.FC = ({ invoices }) => { + const { t } = useTranslation("invoices"); + const router = useRouter(); + + const [filteredInvoices, setFilteredInvoices] = useState(invoices); + + const searchCriteria: Criterion[] = useMemo( + () => [ + { label: t("Project code"), paramName: "projectCode", type: "text" }, + { label: t("Project name"), paramName: "projectName", type: "text" }, + // { label: t("Stage"), paramName: "stage", type: "text" }, + { + label: t("Coming payment milestone from"), + label2: t("Coming payment milestone to"), + paramName: "comingPaymentMileStone", + type: "dateRange" + }, + { + label: t("Payment date from"), + label2: t("Payment date to"), + paramName: "paymentMilestoneDate", + type: "dateRange" + }, + // { label: t("Resource utilization %"), paramName: "resourceUsage", type: "text" }, + // { label: t("Unbilled hours"), paramName: "unbilledHours", type: "text" }, + // { label: t("Reminder to issue invoice"), paramName: "reminder", type: "text" }, + ], + [t, invoices], + ); + + const onReset = useCallback(() => { + setFilteredInvoices(invoices); + }, [invoices]); + + const onProjectClick = useCallback((project: InvoiceResult) => { + console.log(project); + router.push(`/invoice/new?id=${project.id}`) + }, [router, t]); + + const columns = useMemo[]>( + () => [ + { + name: "id", + label: t("Details"), + onClick: onProjectClick, + buttonIcon: , + }, + { name: "projectCode", label: t("Project code") }, + { name: "projectName", label: t("Project name") }, + { name: "stage", label: t("Stage") }, + { name: "comingPaymentMileStone", label: t("Coming payment milestone") }, + { name: "paymentMilestoneDate", label: t("Payment date") }, + { name: "resourceUsage", label: t("Resource utilization %") }, + { name: "unbilledHours", label: t("Unbilled hours") }, + { name: "reminder", label: t("Reminder to issue invoice") }, + ], + [t, onProjectClick], + ); + + return ( + <> + { + console.log(query) + setFilteredInvoices( + invoices.filter( + (d) => + d.projectCode.toLowerCase().includes(query.projectCode.toLowerCase()) && + d.projectName.toLowerCase().includes(query.projectName.toLowerCase()) && + {/*(query.client === "All" || p.client === query.client) && + (query.category === "All" || p.category === query.category) && + (query.team === "All" || p.team === query.team), **/} + ), + ); + }} + onReset={onReset} + /> + + items={filteredInvoices} + columns={columns} + /> + + ); +}; + +export default InvoiceSearch; diff --git a/src/components/InvoiceSearch_forGen/InvoiceSearchLoading.tsx b/src/components/InvoiceSearch_forGen/InvoiceSearchLoading.tsx new file mode 100644 index 0000000..927c6c6 --- /dev/null +++ b/src/components/InvoiceSearch_forGen/InvoiceSearchLoading.tsx @@ -0,0 +1,40 @@ +import Card from "@mui/material/Card"; +import CardContent from "@mui/material/CardContent"; +import Skeleton from "@mui/material/Skeleton"; +import Stack from "@mui/material/Stack"; +import React from "react"; + +// Can make this nicer +export const InvoiceSearchLoading: React.FC = () => { + return ( + <> + + + + + + + + + + + Invoice + + + + + + + + + + + ); +}; + +export default InvoiceSearchLoading; diff --git a/src/components/InvoiceSearch_forGen/InvoiceSearchWrapper.tsx b/src/components/InvoiceSearch_forGen/InvoiceSearchWrapper.tsx new file mode 100644 index 0000000..ba05baf --- /dev/null +++ b/src/components/InvoiceSearch_forGen/InvoiceSearchWrapper.tsx @@ -0,0 +1,26 @@ + +import React from "react"; +import InvoiceSearch from "./InvoiceSearch"; +import InvoiceSearchLoading from "./InvoiceSearchLoading"; +import { fetchInvoices } from "@/app/api/invoices"; +import { timestampToDateString } from "@/app/utils/formatUtil"; + + +interface SubComponents { + Loading: typeof InvoiceSearchLoading; +} + +const InvoiceSearchWrapper: React.FC & SubComponents = async () => { + const Invoices = await fetchInvoices(); + + const temp = Invoices.map((invoice) => ({ + ...invoice, + paymentMilestoneDate: timestampToDateString(invoice.paymentMilestoneDate) + })) + + return ; +}; + +InvoiceSearchWrapper.Loading = InvoiceSearchLoading; + +export default InvoiceSearchWrapper; diff --git a/src/components/InvoiceSearch_forGen/index.ts b/src/components/InvoiceSearch_forGen/index.ts new file mode 100644 index 0000000..14315af --- /dev/null +++ b/src/components/InvoiceSearch_forGen/index.ts @@ -0,0 +1 @@ +export { default } from "./InvoiceSearchWrapper"; diff --git a/src/components/SalarySearch/SalarySearch.tsx b/src/components/SalarySearch/SalarySearch.tsx index b21176f..5c3a49d 100644 --- a/src/components/SalarySearch/SalarySearch.tsx +++ b/src/components/SalarySearch/SalarySearch.tsx @@ -12,6 +12,7 @@ import FileDownloadIcon from '@mui/icons-material/FileDownload'; import FileUploadIcon from '@mui/icons-material/FileUpload'; import { exportSalary, importSalarys } from "@/app/api/salarys/actions"; import { downloadFile } from "@/app/utils/commonUtil"; +import { errorDialog, successDialog } from "../Swal/CustomAlerts"; interface Props { salarys: SalaryResult[]; @@ -63,7 +64,13 @@ const SalarySearch: React.FC = ({ salarys }) => { const response = await importSalarys(formData); if (response === "OK") { - window.location.reload() + successDialog(t("Import Success"), t).then(()=>{ + window.location.reload() + }) + }else{ + errorDialog(t("Import Fail")).then(()=>{ + window.location.reload() + }) } } catch (err) { @@ -74,20 +81,24 @@ const SalarySearch: React.FC = ({ salarys }) => { const handleExportClick = useCallback(async (event:any) => { // console.log(event); - const response = await exportSalary() - if (response) { - downloadFile(new Uint8Array(response.blobValue), response.filename!!) + try{ + const response = await exportSalary() + if (response) { + downloadFile(new Uint8Array(response.blobValue), response.filename!!) + } + }catch(error){ + console.log(error) } }, []); const columns = useMemo[]>( () => [ - { - name: "id", - label: t("Details"), - onClick: onSalaryClick, - buttonIcon: , - }, + // { + // name: "id", + // label: t("Details"), + // onClick: onSalaryClick, + // buttonIcon: , + // }, { name: "salaryPoint", label: t("Salary Point") }, { name: "lowerLimit", label: t("Lower Limit") }, { name: "upperLimit", label: t("Upper Limit") }, @@ -123,12 +134,19 @@ const SalarySearch: React.FC = ({ salarys }) => { { + // console.log(Number(query.salaryPoint)) setFilteredSalarys( salarys.filter( (s) => - ((convertLocaleStringToNumber(s.lowerLimit) <= Number(query.salary))&& - (convertLocaleStringToNumber(s.upperLimit) >= Number(query.salary)))|| - (s.salaryPoint === Number(query.salaryPoint)) + { + // console.log(s) + return ( + ((convertLocaleStringToNumber(s.lowerLimit) <= Number(query.salary))&& + (convertLocaleStringToNumber(s.upperLimit) >= Number(query.salary)))|| + (s.salaryPoint === Number(query.salaryPoint)) + ) + } + ), ); }} diff --git a/src/components/Swal/CustomAlerts.js b/src/components/Swal/CustomAlerts.js index ad1aec6..d09011c 100644 --- a/src/components/Swal/CustomAlerts.js +++ b/src/components/Swal/CustomAlerts.js @@ -42,6 +42,16 @@ export const errorDialog = (text, t) => { }) } +export const errorDialogWithContent = (title, text, t) => { + return Swal.fire({ + icon: "error", + title: title, + html: text, + confirmButtonText: t("Confirm"), + showConfirmButton: true, + }) +} + export const warningDialog = (text, t) => { return Swal.fire({ icon: "warning",