|
@@ -1,16 +1,8 @@ |
|
|
import React, { useCallback, useMemo, useState, useEffect } from "react"; |
|
|
|
|
|
import SearchBox, { Criterion } from "../SearchBox"; |
|
|
|
|
|
|
|
|
import React, { useCallback, useMemo, useState, useEffect, SyntheticEvent } from "react"; |
|
|
import { useTranslation } from "react-i18next"; |
|
|
import { useTranslation } from "react-i18next"; |
|
|
import SearchResults, { Column } from "../SearchResults"; |
|
|
|
|
|
import EditNote from "@mui/icons-material/EditNote"; |
|
|
|
|
|
import { moneyFormatter } from "@/app/utils/formatUtil" |
|
|
|
|
|
import { Button, ButtonGroup, Stack, Tab, Tabs, TabsProps, Dialog, DialogTitle, DialogContent, DialogContentText, DialogActions, TextField, CardContent, Typography, Divider, Card, Box, Autocomplete, MenuItem } from "@mui/material"; |
|
|
|
|
|
import FileUploadIcon from '@mui/icons-material/FileUpload'; |
|
|
|
|
|
import { Add, Check, Close, Delete } from "@mui/icons-material"; |
|
|
|
|
|
import { deleteInvoice, importIssuedInovice, importReceivedInovice, updateInvoice } from "@/app/api/invoices/actions"; |
|
|
|
|
|
import { deleteDialog, errorDialogWithContent, successDialog } from "../Swal/CustomAlerts"; |
|
|
|
|
|
import { invoiceList, issuedInvoiceList, issuedInvoiceSearchForm, receivedInvoiceList, receivedInvoiceSearchForm } from "@/app/api/invoices"; |
|
|
|
|
|
import EditOutlinedIcon from '@mui/icons-material/EditOutlined'; |
|
|
|
|
|
|
|
|
import { Button, TextField, CardContent, Typography, Divider, Card, Box, Autocomplete, MenuItem, AutocompleteChangeReason, AutocompleteChangeDetails } from "@mui/material"; |
|
|
|
|
|
import { Add, } from "@mui/icons-material"; |
|
|
|
|
|
import { invoiceList } from "@/app/api/invoices"; |
|
|
import { |
|
|
import { |
|
|
GridCellParams, |
|
|
GridCellParams, |
|
|
GridColDef, |
|
|
GridColDef, |
|
@@ -19,19 +11,17 @@ import { |
|
|
GridRowModel, |
|
|
GridRowModel, |
|
|
GridRowModes, |
|
|
GridRowModes, |
|
|
GridRowModesModel, |
|
|
GridRowModesModel, |
|
|
GridRenderEditCellParams, |
|
|
|
|
|
|
|
|
GridRenderEditCellParams, |
|
|
|
|
|
GridRenderCellParams, |
|
|
|
|
|
useGridApiContext, |
|
|
} from "@mui/x-data-grid"; |
|
|
} from "@mui/x-data-grid"; |
|
|
import { useGridApiRef } from "@mui/x-data-grid"; |
|
|
import { useGridApiRef } from "@mui/x-data-grid"; |
|
|
import StyledDataGrid from "../StyledDataGrid"; |
|
|
import StyledDataGrid from "../StyledDataGrid"; |
|
|
|
|
|
|
|
|
import { uniq } from "lodash"; |
|
|
|
|
|
import CreateInvoiceModal from "./CreateInvoiceModal"; |
|
|
|
|
|
import { GridToolbarContainer } from "@mui/x-data-grid"; |
|
|
import { GridToolbarContainer } from "@mui/x-data-grid"; |
|
|
import { FooterPropsOverrides } from "@mui/x-data-grid"; |
|
|
import { FooterPropsOverrides } from "@mui/x-data-grid"; |
|
|
import { th } from "@faker-js/faker"; |
|
|
|
|
|
import { GridRowIdGetter } from "@mui/x-data-grid"; |
|
|
|
|
|
import { useFormContext } from "react-hook-form"; |
|
|
import { useFormContext } from "react-hook-form"; |
|
|
import { ProjectResult } from "@/app/api/projects"; |
|
|
import { ProjectResult } from "@/app/api/projects"; |
|
|
|
|
|
import useEnhancedEffect from "@mui/material/utils/useEnhancedEffect"; |
|
|
|
|
|
|
|
|
type InvoiceListError = { |
|
|
type InvoiceListError = { |
|
|
[field in keyof invoiceList]?: string; |
|
|
[field in keyof invoiceList]?: string; |
|
@@ -69,13 +59,17 @@ type project = { |
|
|
} |
|
|
} |
|
|
const InvoiceTable: React.FC<Props> = ({ projects }) => { |
|
|
const InvoiceTable: React.FC<Props> = ({ projects }) => { |
|
|
console.log(projects) |
|
|
console.log(projects) |
|
|
|
|
|
// const projectCombos: project[] = projects.map(item => ({ |
|
|
|
|
|
// value: item.id, |
|
|
|
|
|
// label: item.code |
|
|
|
|
|
// })) |
|
|
|
|
|
const projectCombos = projects.map(item => item.code) |
|
|
const { t } = useTranslation() |
|
|
const { t } = useTranslation() |
|
|
const [rowModesModel, setRowModesModel] = useState<GridRowModesModel>({}); |
|
|
const [rowModesModel, setRowModesModel] = useState<GridRowModesModel>({}); |
|
|
const [selectedRow, setSelectedRow] = useState<invoiceListRow[] | []>([]); |
|
|
const [selectedRow, setSelectedRow] = useState<invoiceListRow[] | []>([]); |
|
|
const { getValues, setValue, clearErrors, setError } = |
|
|
|
|
|
useFormContext<any>(); |
|
|
|
|
|
|
|
|
const { getValues, setValue, clearErrors, setError } = useFormContext<any>(); |
|
|
const apiRef = useGridApiRef(); |
|
|
const apiRef = useGridApiRef(); |
|
|
const [projectCode, setProjectCode] = useState<project>({label: "", value: 0}) |
|
|
|
|
|
|
|
|
// const [projectCode, setProjectCode] = useState<project>({label: "", value: 0}) |
|
|
const validateInvoiceEntry = ( |
|
|
const validateInvoiceEntry = ( |
|
|
entry: Partial<invoiceList>, |
|
|
entry: Partial<invoiceList>, |
|
|
): InvoiceListError | undefined => { |
|
|
): InvoiceListError | undefined => { |
|
@@ -121,6 +115,8 @@ const InvoiceTable: React.FC<Props> = ({ projects }) => { |
|
|
params.id, |
|
|
params.id, |
|
|
"", |
|
|
"", |
|
|
) |
|
|
) |
|
|
|
|
|
console.log(params) |
|
|
|
|
|
console.log(apiRef.current) |
|
|
console.log(validateRow(params.id) !== undefined) |
|
|
console.log(validateRow(params.id) !== undefined) |
|
|
console.log(!validateRow(params.id)) |
|
|
console.log(!validateRow(params.id)) |
|
|
if (validateRow(params.id) !== undefined && !validateRow(params.id)) { |
|
|
if (validateRow(params.id) !== undefined && !validateRow(params.id)) { |
|
@@ -133,7 +129,7 @@ const InvoiceTable: React.FC<Props> = ({ projects }) => { |
|
|
console.log(row) |
|
|
console.log(row) |
|
|
setSelectedRow((row) => [...row] as any[]) |
|
|
setSelectedRow((row) => [...row] as any[]) |
|
|
event.defaultMuiPrevented = true; |
|
|
event.defaultMuiPrevented = true; |
|
|
}else{ |
|
|
|
|
|
|
|
|
} else { |
|
|
console.log(row) |
|
|
console.log(row) |
|
|
const error = validateRow(params.id) |
|
|
const error = validateRow(params.id) |
|
|
setSelectedRow((row) => { |
|
|
setSelectedRow((row) => { |
|
@@ -162,6 +158,8 @@ const InvoiceTable: React.FC<Props> = ({ projects }) => { |
|
|
newRow: GridRowModel<invoiceListRow>, |
|
|
newRow: GridRowModel<invoiceListRow>, |
|
|
originalRow: GridRowModel<invoiceListRow>, |
|
|
originalRow: GridRowModel<invoiceListRow>, |
|
|
) => { |
|
|
) => { |
|
|
|
|
|
console.log(newRow) |
|
|
|
|
|
console.log(originalRow) |
|
|
const errors = validateRow(newRow.id!!); |
|
|
const errors = validateRow(newRow.id!!); |
|
|
if (errors) { |
|
|
if (errors) { |
|
|
// console.log(errors) |
|
|
// console.log(errors) |
|
@@ -172,7 +170,6 @@ const InvoiceTable: React.FC<Props> = ({ projects }) => { |
|
|
errors, |
|
|
errors, |
|
|
) |
|
|
) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars |
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars |
|
|
const { _isNew, _error, ...updatedRow } = newRow; |
|
|
const { _isNew, _error, ...updatedRow } = newRow; |
|
|
|
|
|
|
|
@@ -209,32 +206,70 @@ const InvoiceTable: React.FC<Props> = ({ projects }) => { |
|
|
setValue("data", selectedRow) |
|
|
setValue("data", selectedRow) |
|
|
}, [selectedRow, setValue]); |
|
|
}, [selectedRow, setValue]); |
|
|
|
|
|
|
|
|
|
|
|
function renderAutocomplete(params: GridRenderCellParams<any, number>) { |
|
|
|
|
|
return( |
|
|
|
|
|
<Box sx={{ display: 'flex', alignItems: 'center', pr: 2 }}> |
|
|
|
|
|
<Autocomplete |
|
|
|
|
|
readOnly |
|
|
|
|
|
sx={{ width: 300 }} |
|
|
|
|
|
value={params.row.projectCode} |
|
|
|
|
|
options={projectCombos} |
|
|
|
|
|
renderInput={(params) => <TextField {...params} />} |
|
|
|
|
|
/> |
|
|
|
|
|
</Box> |
|
|
|
|
|
) |
|
|
|
|
|
} |
|
|
|
|
|
function AutocompleteInput(props: GridRenderCellParams<any, number>) { |
|
|
|
|
|
const { id, value, field, hasFocus } = props; |
|
|
|
|
|
const apiRef = useGridApiContext(); |
|
|
|
|
|
const ref = React.useRef<HTMLElement>(null); |
|
|
|
|
|
|
|
|
|
|
|
// useEnhancedEffect(() => { |
|
|
|
|
|
// if (hasFocus && ref.current) { |
|
|
|
|
|
// const input = ref.current.querySelector<HTMLInputElement>( |
|
|
|
|
|
// `input[value="${value}"]`, |
|
|
|
|
|
// ); |
|
|
|
|
|
// input?.focus(); |
|
|
|
|
|
// } |
|
|
|
|
|
// }, [hasFocus, value]); |
|
|
|
|
|
|
|
|
|
|
|
const handleValueChange = useCallback((newValue: any) => { |
|
|
|
|
|
console.log(newValue) |
|
|
|
|
|
apiRef.current.setEditCellValue({ id, field, value: newValue }) |
|
|
|
|
|
}, []); |
|
|
|
|
|
|
|
|
|
|
|
return ( |
|
|
|
|
|
<Box sx={{ display: 'flex', alignItems: 'center', pr: 2 }}> |
|
|
|
|
|
<Autocomplete |
|
|
|
|
|
disablePortal |
|
|
|
|
|
options={projectCombos} |
|
|
|
|
|
sx={{ width: 300 }} |
|
|
|
|
|
onChange={(event: React.SyntheticEvent<Element, Event>, value: string | null, ) => handleValueChange(value)} |
|
|
|
|
|
renderInput={(params) => <TextField {...params} />} |
|
|
|
|
|
/> |
|
|
|
|
|
</Box> |
|
|
|
|
|
); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const renderAutocompleteInput: GridColDef['renderCell'] = (params) => { |
|
|
|
|
|
return <AutocompleteInput {...params} />; |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
const editCombinedColumns = useMemo<GridColDef[]>( |
|
|
const editCombinedColumns = useMemo<GridColDef[]>( |
|
|
() => [ |
|
|
() => [ |
|
|
{ field: "invoiceNo", headerName: t("Invoice No"), editable: true, flex: 0.5 }, |
|
|
|
|
|
|
|
|
{ field: "invoiceNo", headerName: t("Invoice No"), editable: true, flex: 0.7 }, |
|
|
{ field: "projectCode", |
|
|
{ field: "projectCode", |
|
|
headerName: t("Project Code"), |
|
|
headerName: t("Project Code"), |
|
|
editable: true, |
|
|
editable: true, |
|
|
flex: 0.3, |
|
|
|
|
|
renderEditCell(params: GridRenderEditCellParams<invoiceListRow, number>){ |
|
|
|
|
|
return( |
|
|
|
|
|
<Autocomplete |
|
|
|
|
|
disablePortal |
|
|
|
|
|
options={[]} |
|
|
|
|
|
sx={{width: '100%'}} |
|
|
|
|
|
renderInput={(params) => <TextField {...params} />} |
|
|
|
|
|
/> |
|
|
|
|
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
flex: 1.5, |
|
|
|
|
|
renderCell: renderAutocomplete, |
|
|
|
|
|
renderEditCell: renderAutocompleteInput |
|
|
}, |
|
|
}, |
|
|
{ field: "issuedDate", |
|
|
{ field: "issuedDate", |
|
|
headerName: t("Issue Date"), |
|
|
headerName: t("Issue Date"), |
|
|
editable: true, |
|
|
editable: true, |
|
|
flex: 0.4, |
|
|
|
|
|
// type: 'date', |
|
|
|
|
|
|
|
|
flex: 1, |
|
|
|
|
|
type: 'date', |
|
|
// valueGetter: (params) => { |
|
|
// valueGetter: (params) => { |
|
|
// // console.log(params.row.issuedDate) |
|
|
// // console.log(params.row.issuedDate) |
|
|
// return new Date(params.row.issuedDate) |
|
|
// return new Date(params.row.issuedDate) |
|
@@ -243,19 +278,19 @@ const editCombinedColumns = useMemo<GridColDef[]>( |
|
|
{ field: "issuedAmount", |
|
|
{ field: "issuedAmount", |
|
|
headerName: t("Amount (HKD)"), |
|
|
headerName: t("Amount (HKD)"), |
|
|
editable: true, |
|
|
editable: true, |
|
|
flex: 0.5, |
|
|
|
|
|
|
|
|
flex: 1, |
|
|
type: 'number' |
|
|
type: 'number' |
|
|
}, |
|
|
}, |
|
|
{ |
|
|
{ |
|
|
field: "receiptDate", |
|
|
field: "receiptDate", |
|
|
headerName: t("Settle Date"), |
|
|
headerName: t("Settle Date"), |
|
|
editable: true, |
|
|
editable: true, |
|
|
flex: 0.4, |
|
|
|
|
|
|
|
|
flex: 1, |
|
|
}, |
|
|
}, |
|
|
{ field: "receivedAmount", |
|
|
{ field: "receivedAmount", |
|
|
headerName: t("Actual Received Amount (HKD)"), |
|
|
headerName: t("Actual Received Amount (HKD)"), |
|
|
editable: true, |
|
|
editable: true, |
|
|
flex: 0.5, |
|
|
|
|
|
|
|
|
flex: 1, |
|
|
type: 'number' |
|
|
type: 'number' |
|
|
}, |
|
|
}, |
|
|
], |
|
|
], |
|
|