From 7ddb918e4f15338de7301cf5a25fed8d74725bf0 Mon Sep 17 00:00:00 2001 From: "MSI\\derek" Date: Tue, 10 Sep 2024 18:30:54 +0800 Subject: [PATCH] update save invoice --- src/app/(main)/projects/edit/page.tsx | 2 +- src/app/api/invoices/actions.ts | 21 ++++ .../InvoiceSearch/CreateInvoiceModal.tsx | 22 +++- src/components/InvoiceSearch/InvoiceTable.tsx | 119 +++++++++++------- .../MailSetting/TimesheetMailDetails.tsx | 1 - 5 files changed, 117 insertions(+), 48 deletions(-) diff --git a/src/app/(main)/projects/edit/page.tsx b/src/app/(main)/projects/edit/page.tsx index 7b364ce..69117c9 100644 --- a/src/app/(main)/projects/edit/page.tsx +++ b/src/app/(main)/projects/edit/page.tsx @@ -57,6 +57,7 @@ const Projects: React.FC = async ({ searchParams }) => { preloadStaff(); try { + console.log(projectId) await fetchProjectDetails(projectId); } catch (e) { if (e instanceof ServerFetchError && e.response?.status === 404) { @@ -66,7 +67,6 @@ const Projects: React.FC = async ({ searchParams }) => { return ( <> - {t("Edit Project")} diff --git a/src/app/api/invoices/actions.ts b/src/app/api/invoices/actions.ts index d4d526b..eaa78c6 100644 --- a/src/app/api/invoices/actions.ts +++ b/src/app/api/invoices/actions.ts @@ -17,6 +17,27 @@ export interface InvoiceResult { reminder: string; } +export type NewInvoice = { + invoiceNo: string | undefined, + projectCode: string | undefined, + issuedDate: Date + issuedAmount: number, + receiptDate: Date + receivedAmount: number +} +export type InvoiceType = { + data: NewInvoice[] +} + +export type PostInvoiceData = { + invoiceNo: string + projectId: number + projectCode: string | undefined, + issuedAmount: number + issueDate: string + receiptDate?: string + receivedAmount?: number +} export interface CreateInvoiceInputs { id: number; diff --git a/src/components/InvoiceSearch/CreateInvoiceModal.tsx b/src/components/InvoiceSearch/CreateInvoiceModal.tsx index a206fd1..8b6c2f5 100644 --- a/src/components/InvoiceSearch/CreateInvoiceModal.tsx +++ b/src/components/InvoiceSearch/CreateInvoiceModal.tsx @@ -14,6 +14,9 @@ import { FormProvider, SubmitHandler, useForm } from 'react-hook-form'; import { Check, Close } from "@mui/icons-material"; import InvoiceTable from './InvoiceTable'; import { ProjectResult } from '@/app/api/projects'; +import { InvoiceType, NewInvoice, PostInvoiceData } from '@/app/api/invoices/actions'; +import dayjs from 'dayjs'; +import { INPUT_DATE_FORMAT } from '@/app/utils/formatUtil'; interface Props { isOpen: boolean, @@ -34,11 +37,22 @@ const modalSx: SxProps= { const CreateInvoiceModal: React.FC = ({isOpen, onClose, projects}) => { const { t } = useTranslation() - const formProps = useForm(); + const formProps = useForm(); - const onSubmit = useCallback>( - (data) => { - console.log(data) + const onSubmit = useCallback>( + async ( data ) => { + const _data = data.data + // const postData: PostInvoiceData = _data.map(item => ({ + const postData: any = _data.map(item => ({ + invoiceNo: item.invoiceNo || '', + projectId: projects.find(p => p.code === item.projectCode)!.id, + projectCode: item.projectCode || '', + issuedAmount: item.issuedAmount || 0, + issueDate: dayjs(item.issuedDate).format(INPUT_DATE_FORMAT), + receiptDate: item.receiptDate || null, + receivedAmount: item.receivedAmount || null, + })) + console.log(postData) } , []) diff --git a/src/components/InvoiceSearch/InvoiceTable.tsx b/src/components/InvoiceSearch/InvoiceTable.tsx index 7102cda..3f33f44 100644 --- a/src/components/InvoiceSearch/InvoiceTable.tsx +++ b/src/components/InvoiceSearch/InvoiceTable.tsx @@ -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 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 { GridCellParams, GridColDef, @@ -19,19 +11,17 @@ import { GridRowModel, GridRowModes, GridRowModesModel, - GridRenderEditCellParams, + GridRenderEditCellParams, + GridRenderCellParams, + useGridApiContext, } from "@mui/x-data-grid"; import { useGridApiRef } from "@mui/x-data-grid"; import StyledDataGrid from "../StyledDataGrid"; - -import { uniq } from "lodash"; -import CreateInvoiceModal from "./CreateInvoiceModal"; import { GridToolbarContainer } 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 { ProjectResult } from "@/app/api/projects"; +import useEnhancedEffect from "@mui/material/utils/useEnhancedEffect"; type InvoiceListError = { [field in keyof invoiceList]?: string; @@ -69,13 +59,17 @@ type project = { } const InvoiceTable: React.FC = ({ 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 [rowModesModel, setRowModesModel] = useState({}); const [selectedRow, setSelectedRow] = useState([]); - const { getValues, setValue, clearErrors, setError } = - useFormContext(); + const { getValues, setValue, clearErrors, setError } = useFormContext(); const apiRef = useGridApiRef(); - const [projectCode, setProjectCode] = useState({label: "", value: 0}) + // const [projectCode, setProjectCode] = useState({label: "", value: 0}) const validateInvoiceEntry = ( entry: Partial, ): InvoiceListError | undefined => { @@ -121,6 +115,8 @@ const InvoiceTable: React.FC = ({ projects }) => { params.id, "", ) + console.log(params) + console.log(apiRef.current) console.log(validateRow(params.id) !== undefined) console.log(!validateRow(params.id)) if (validateRow(params.id) !== undefined && !validateRow(params.id)) { @@ -133,7 +129,7 @@ const InvoiceTable: React.FC = ({ projects }) => { console.log(row) setSelectedRow((row) => [...row] as any[]) event.defaultMuiPrevented = true; - }else{ + } else { console.log(row) const error = validateRow(params.id) setSelectedRow((row) => { @@ -162,6 +158,8 @@ const InvoiceTable: React.FC = ({ projects }) => { newRow: GridRowModel, originalRow: GridRowModel, ) => { + console.log(newRow) + console.log(originalRow) const errors = validateRow(newRow.id!!); if (errors) { // console.log(errors) @@ -172,7 +170,6 @@ const InvoiceTable: React.FC = ({ projects }) => { errors, ) } - // eslint-disable-next-line @typescript-eslint/no-unused-vars const { _isNew, _error, ...updatedRow } = newRow; @@ -209,32 +206,70 @@ const InvoiceTable: React.FC = ({ projects }) => { setValue("data", selectedRow) }, [selectedRow, setValue]); + function renderAutocomplete(params: GridRenderCellParams) { + return( + + } + /> + + ) + } + function AutocompleteInput(props: GridRenderCellParams) { + const { id, value, field, hasFocus } = props; + const apiRef = useGridApiContext(); + const ref = React.useRef(null); + + // useEnhancedEffect(() => { + // if (hasFocus && ref.current) { + // const input = ref.current.querySelector( + // `input[value="${value}"]`, + // ); + // input?.focus(); + // } + // }, [hasFocus, value]); + + const handleValueChange = useCallback((newValue: any) => { + console.log(newValue) + apiRef.current.setEditCellValue({ id, field, value: newValue }) + }, []); + + return ( + + , value: string | null, ) => handleValueChange(value)} + renderInput={(params) => } + /> + + ); + } + + const renderAutocompleteInput: GridColDef['renderCell'] = (params) => { + return ; + }; const editCombinedColumns = useMemo( () => [ - { field: "invoiceNo", headerName: t("Invoice No"), editable: true, flex: 0.5 }, + { field: "invoiceNo", headerName: t("Invoice No"), editable: true, flex: 0.7 }, { field: "projectCode", headerName: t("Project Code"), editable: true, - flex: 0.3, - renderEditCell(params: GridRenderEditCellParams){ - return( - } - /> - - ) - - } + flex: 1.5, + renderCell: renderAutocomplete, + renderEditCell: renderAutocompleteInput }, { field: "issuedDate", headerName: t("Issue Date"), editable: true, - flex: 0.4, - // type: 'date', + flex: 1, + type: 'date', // valueGetter: (params) => { // // console.log(params.row.issuedDate) // return new Date(params.row.issuedDate) @@ -243,19 +278,19 @@ const editCombinedColumns = useMemo( { field: "issuedAmount", headerName: t("Amount (HKD)"), editable: true, - flex: 0.5, + flex: 1, type: 'number' }, { field: "receiptDate", headerName: t("Settle Date"), editable: true, - flex: 0.4, + flex: 1, }, { field: "receivedAmount", headerName: t("Actual Received Amount (HKD)"), editable: true, - flex: 0.5, + flex: 1, type: 'number' }, ], diff --git a/src/components/MailSetting/TimesheetMailDetails.tsx b/src/components/MailSetting/TimesheetMailDetails.tsx index 0fcd426..c38f8c7 100644 --- a/src/components/MailSetting/TimesheetMailDetails.tsx +++ b/src/components/MailSetting/TimesheetMailDetails.tsx @@ -84,7 +84,6 @@ const TimesheetMailDetails: React.FC = ({ isActive }) => { required: true, validate: value => value?.includes("${date}") }} - />