Quellcode durchsuchen

update save invoice

tags/Baseline_180220205_Frontend
MSI\derek vor 11 Monaten
Ursprung
Commit
7ddb918e4f
5 geänderte Dateien mit 117 neuen und 48 gelöschten Zeilen
  1. +1
    -1
      src/app/(main)/projects/edit/page.tsx
  2. +21
    -0
      src/app/api/invoices/actions.ts
  3. +18
    -4
      src/components/InvoiceSearch/CreateInvoiceModal.tsx
  4. +77
    -42
      src/components/InvoiceSearch/InvoiceTable.tsx
  5. +0
    -1
      src/components/MailSetting/TimesheetMailDetails.tsx

+ 1
- 1
src/app/(main)/projects/edit/page.tsx Datei anzeigen

@@ -57,6 +57,7 @@ const Projects: React.FC<Props> = 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<Props> = async ({ searchParams }) => {

return (
<>
<Typography variant="h4">{t("Edit Project")}</Typography>
<I18nProvider namespaces={["projects"]}>
<CreateProject isEditMode projectId={projectId} />
</I18nProvider>


+ 21
- 0
src/app/api/invoices/actions.ts Datei anzeigen

@@ -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;



+ 18
- 4
src/components/InvoiceSearch/CreateInvoiceModal.tsx Datei anzeigen

@@ -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<Props> = ({isOpen, onClose, projects}) => {
const { t } = useTranslation()
const formProps = useForm<any>();
const formProps = useForm<InvoiceType>();

const onSubmit = useCallback<SubmitHandler<any>>(
(data) => {
console.log(data)
const onSubmit = useCallback<SubmitHandler<InvoiceType>>(
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)
}
, [])



+ 77
- 42
src/components/InvoiceSearch/InvoiceTable.tsx Datei anzeigen

@@ -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<Props> = ({ 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<GridRowModesModel>({});
const [selectedRow, setSelectedRow] = useState<invoiceListRow[] | []>([]);
const { getValues, setValue, clearErrors, setError } =
useFormContext<any>();
const { getValues, setValue, clearErrors, setError } = useFormContext<any>();
const apiRef = useGridApiRef();
const [projectCode, setProjectCode] = useState<project>({label: "", value: 0})
// const [projectCode, setProjectCode] = useState<project>({label: "", value: 0})
const validateInvoiceEntry = (
entry: Partial<invoiceList>,
): InvoiceListError | undefined => {
@@ -121,6 +115,8 @@ const InvoiceTable: React.FC<Props> = ({ 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<Props> = ({ 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<Props> = ({ projects }) => {
newRow: GridRowModel<invoiceListRow>,
originalRow: GridRowModel<invoiceListRow>,
) => {
console.log(newRow)
console.log(originalRow)
const errors = validateRow(newRow.id!!);
if (errors) {
// console.log(errors)
@@ -172,7 +170,6 @@ const InvoiceTable: React.FC<Props> = ({ projects }) => {
errors,
)
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { _isNew, _error, ...updatedRow } = newRow;
@@ -209,32 +206,70 @@ const InvoiceTable: React.FC<Props> = ({ projects }) => {
setValue("data", selectedRow)
}, [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[]>(
() => [
{ 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<invoiceListRow, number>){
return(
<Autocomplete
disablePortal
options={[]}
sx={{width: '100%'}}
renderInput={(params) => <TextField {...params} />}
/>

)
}
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<GridColDef[]>(
{ 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'
},
],


+ 0
- 1
src/components/MailSetting/TimesheetMailDetails.tsx Datei anzeigen

@@ -84,7 +84,6 @@ const TimesheetMailDetails: React.FC<Props> = ({ isActive }) => {
required: true,
validate: value => value?.includes("${date}")
}}

/>
</Grid>
</Grid>


Laden…
Abbrechen
Speichern