| @@ -1,71 +1,395 @@ | |||||
| "use client" | |||||
| import { MutableRefObject } from "react"; | |||||
| import StyledDataGrid from "../StyledDataGrid" | |||||
| import { GridApiCommunity } from "@mui/x-data-grid/internals"; | |||||
| import { GridColDef } from "@mui/x-data-grid"; | |||||
| "use client"; | |||||
| import { | |||||
| Dispatch, | |||||
| MutableRefObject, | |||||
| SetStateAction, | |||||
| useCallback, | |||||
| useEffect, | |||||
| useMemo, | |||||
| useState, | |||||
| } from "react"; | |||||
| import StyledDataGrid from "../StyledDataGrid"; | |||||
| import { | |||||
| FooterPropsOverrides, | |||||
| GridActionsCellItem, | |||||
| GridCellParams, | |||||
| GridColDef, | |||||
| GridEventListener, | |||||
| GridRowEditStopReasons, | |||||
| GridRowId, | |||||
| GridRowIdGetter, | |||||
| GridRowModel, | |||||
| GridRowModes, | |||||
| GridRowModesModel, | |||||
| GridRowSelectionModel, | |||||
| GridToolbarContainer, | |||||
| GridValidRowModel, | |||||
| useGridApiRef, | |||||
| } from "@mui/x-data-grid"; | |||||
| import { set, useFormContext } from "react-hook-form"; | |||||
| import SaveIcon from "@mui/icons-material/Save"; | |||||
| import DeleteIcon from "@mui/icons-material/Delete"; | |||||
| import CancelIcon from "@mui/icons-material/Cancel"; | |||||
| import { Add } from "@mui/icons-material"; | |||||
| import { Box, Button, Typography } from "@mui/material"; | |||||
| import { useTranslation } from "react-i18next"; | import { useTranslation } from "react-i18next"; | ||||
| import { dummyQCData, QcData } from "./dummyQcTemplate"; | |||||
| import { Checkbox } from "@mui/material"; | |||||
| import { | |||||
| GridApiCommunity, | |||||
| GridSlotsComponentsProps, | |||||
| } from "@mui/x-data-grid/internals"; | |||||
| import { dummyQCData } from "./dummyQcTemplate"; | |||||
| // T == CreatexxxInputs map of the form's fields | |||||
| // V == target field input inside CreatexxxInputs, e.g. qcChecks: ItemQc[], V = ItemQc | |||||
| // E == error | |||||
| interface ResultWithId { | |||||
| id: string | number; | |||||
| } | |||||
| // export type InputGridProps = { | |||||
| // [key: string]: any | |||||
| // } | |||||
| interface DefaultResult<E> { | |||||
| _isNew: boolean; | |||||
| _error: E; | |||||
| } | |||||
| interface Props { | |||||
| // apiRef: MutableRefObject<GridApiCommunity>; | |||||
| }; | |||||
| interface SelectionResult<E> { | |||||
| active: boolean; | |||||
| _isNew: boolean; | |||||
| _error: E; | |||||
| } | |||||
| type Result<E> = DefaultResult<E> | SelectionResult<E>; | |||||
| export type TableRow<V, E> = Partial< | |||||
| V & { | |||||
| isActive: boolean | undefined; | |||||
| _isNew: boolean; | |||||
| _error: E; | |||||
| } & ResultWithId | |||||
| >; | |||||
| export interface InputDataGridProps<T, V, E> { | |||||
| apiRef: MutableRefObject<GridApiCommunity>; | |||||
| // checkboxSelection: false | undefined; | |||||
| _formKey: keyof T; | |||||
| columns: GridColDef[]; | |||||
| validateRow: (newRow: GridRowModel<TableRow<V, E>>) => E; | |||||
| needAdd?: boolean; | |||||
| } | |||||
| export interface SelectionInputDataGridProps<T, V, E> { | |||||
| // thinking how do | |||||
| apiRef: MutableRefObject<GridApiCommunity>; | |||||
| // checkboxSelection: true; | |||||
| _formKey: keyof T; | |||||
| columns: GridColDef[]; | |||||
| validateRow: (newRow: GridRowModel<TableRow<V, E>>) => E; | |||||
| } | |||||
| export type Props<T, V, E> = | |||||
| | InputDataGridProps<T, V, E> | |||||
| | SelectionInputDataGridProps<T, V, E>; | |||||
| export class ProcessRowUpdateError<T, E> extends Error { | |||||
| public readonly row: T; | |||||
| public readonly errors: E | undefined; | |||||
| constructor(row: T, message?: string, errors?: E) { | |||||
| super(message); | |||||
| this.row = row; | |||||
| this.errors = errors; | |||||
| Object.setPrototypeOf(this, ProcessRowUpdateError.prototype); | |||||
| } | |||||
| } | |||||
| // T == CreatexxxInputs map of the form's fields | |||||
| // V == target field input inside CreatexxxInputs, e.g. qcChecks: ItemQc[], V = ItemQc | |||||
| // E == error | |||||
| function InputDataGrid<T, V, E>({ | |||||
| apiRef, | |||||
| // checkboxSelection = false, | |||||
| _formKey, | |||||
| columns, | |||||
| validateRow, | |||||
| }: Props<T, V, E>) { | |||||
| const { | |||||
| t, | |||||
| // i18n: { language }, | |||||
| } = useTranslation("purchaseOrder"); | |||||
| const formKey = _formKey.toString(); | |||||
| const { setValue, getValues } = useFormContext(); | |||||
| const [rowModesModel, setRowModesModel] = useState<GridRowModesModel>({}); | |||||
| // const apiRef = useGridApiRef(); | |||||
| const getRowId = useCallback<GridRowIdGetter<TableRow<V, E>>>( | |||||
| (row) => row.id! as number, | |||||
| [], | |||||
| ); | |||||
| const formValue = getValues(formKey) | |||||
| const list: TableRow<V, E>[] = !formValue || formValue.length == 0 ? dummyQCData : getValues(formKey); | |||||
| console.log(list) | |||||
| const [rows, setRows] = useState<TableRow<V, E>[]>(() => { | |||||
| // const list: TableRow<V, E>[] = getValues(formKey); | |||||
| console.log(list) | |||||
| return list && list.length > 0 ? list : []; | |||||
| }); | |||||
| console.log(rows) | |||||
| // const originalRows = list && list.length > 0 ? list : []; | |||||
| const originalRows = useMemo(() => ( | |||||
| list && list.length > 0 ? list : [] | |||||
| ), [list]) | |||||
| // const originalRowModel = originalRows.filter((li) => li.isActive).map(i => i.id) as GridRowSelectionModel | |||||
| const [rowSelectionModel, setRowSelectionModel] = | |||||
| useState<GridRowSelectionModel>(() => { | |||||
| // const rowModel = list.filter((li) => li.isActive).map(i => i.id) as GridRowSelectionModel | |||||
| const rowModel: GridRowSelectionModel = getValues( | |||||
| `${formKey}_active`, | |||||
| ) as GridRowSelectionModel; | |||||
| console.log(rowModel); | |||||
| return rowModel; | |||||
| }); | |||||
| useEffect(() => { | |||||
| for (let i = 0; i < rows.length; i++) { | |||||
| const currRow = rows[i] | |||||
| setRowModesModel((prevRowModesModel) => ({ | |||||
| ...prevRowModesModel, | |||||
| [currRow.id as number]: { mode: GridRowModes.View }, | |||||
| })); | |||||
| } | |||||
| }, [rows]) | |||||
| const handleSave = useCallback( | |||||
| (id: GridRowId) => () => { | |||||
| setRowModesModel((prevRowModesModel) => ({ | |||||
| ...prevRowModesModel, | |||||
| [id]: { mode: GridRowModes.View }, | |||||
| })); | |||||
| }, | |||||
| [], | |||||
| ); | |||||
| const onProcessRowUpdateError = useCallback( | |||||
| (updateError: ProcessRowUpdateError<T, E>) => { | |||||
| const errors = updateError.errors; | |||||
| const row = updateError.row; | |||||
| console.log(errors); | |||||
| apiRef.current.updateRows([{ ...row, _error: errors }]); | |||||
| }, | |||||
| [apiRef], | |||||
| ); | |||||
| const processRowUpdate = useCallback( | |||||
| ( | |||||
| newRow: GridRowModel<TableRow<V, E>>, | |||||
| originalRow: GridRowModel<TableRow<V, E>>, | |||||
| ) => { | |||||
| ///////////////// | |||||
| // validation here | |||||
| const errors = validateRow(newRow); | |||||
| console.log(newRow); | |||||
| if (errors) { | |||||
| throw new ProcessRowUpdateError( | |||||
| originalRow, | |||||
| "validation error", | |||||
| errors, | |||||
| ); | |||||
| } | |||||
| ///////////////// | |||||
| const { _isNew, _error, ...updatedRow } = newRow; | |||||
| const rowToSave = { | |||||
| ...updatedRow, | |||||
| } as TableRow<V, E>; /// test | |||||
| console.log(rowToSave); | |||||
| setRows((rw) => | |||||
| rw.map((r) => (getRowId(r) === getRowId(originalRow) ? rowToSave : r)), | |||||
| ); | |||||
| return rowToSave; | |||||
| }, | |||||
| [validateRow, getRowId], | |||||
| ); | |||||
| const addRow = useCallback(() => { | |||||
| const newEntry = { id: Date.now(), _isNew: true } as TableRow<V, E>; | |||||
| setRows((prev) => [...prev, newEntry]); | |||||
| setRowModesModel((model) => ({ | |||||
| ...model, | |||||
| [getRowId(newEntry)]: { | |||||
| mode: GridRowModes.Edit, | |||||
| // fieldToFocus: "team", /// test | |||||
| }, | |||||
| })); | |||||
| }, [getRowId]); | |||||
| const reset = useCallback(() => { | |||||
| setRowModesModel({}); | |||||
| setRows(originalRows); | |||||
| }, [originalRows]); | |||||
| const QcDataGrid: React.FC<Props> = ({ | |||||
| // apiRef | |||||
| }) => { | |||||
| const { t } = useTranslation("purchaseOrder"); | |||||
| const columns: GridColDef[] = [ | |||||
| { | |||||
| field: "qcItem", | |||||
| headerName: t("qcItem"), | |||||
| flex: 1, | |||||
| }, | |||||
| { | |||||
| field: "isPassed", | |||||
| headerName: t("passed"), | |||||
| flex: 1, | |||||
| renderCell: (params) => ( | |||||
| <Checkbox | |||||
| checked={params.value} | |||||
| // onChange={() => handleCheckboxChange(params.id)} | |||||
| /> | |||||
| ), | |||||
| }, | |||||
| { | |||||
| field: "isFailed", | |||||
| headerName: t("failed"), | |||||
| flex: 1, | |||||
| renderCell: (params) => ( | |||||
| <Checkbox | |||||
| checked={params.value} | |||||
| // onChange={() => handleCheckboxChange(params.id)} | |||||
| /> | |||||
| ), | |||||
| }, | |||||
| { | |||||
| field: "failedQty", | |||||
| headerName: t("failedQty"), | |||||
| flex: 1, | |||||
| editable: true, | |||||
| }, | |||||
| { | |||||
| field: "remarks", | |||||
| headerName: t("remarks"), | |||||
| flex: 1, | |||||
| editable: true, | |||||
| }, | |||||
| ] | |||||
| return ( | |||||
| <StyledDataGrid | |||||
| // apiRef={apiRef} | |||||
| autoHeight | |||||
| editMode="row" | |||||
| rows={dummyQCData} | |||||
| columns={columns} | |||||
| /> | |||||
| ) | |||||
| const handleCancel = useCallback( | |||||
| (id: GridRowId) => () => { | |||||
| setRowModesModel((model) => ({ | |||||
| ...model, | |||||
| [id]: { mode: GridRowModes.View, ignoreModifications: true }, | |||||
| })); | |||||
| const editedRow = rows.find((row) => getRowId(row) === id); | |||||
| if (editedRow?._isNew) { | |||||
| setRows((rw) => rw.filter((r) => getRowId(r) !== id)); | |||||
| } else { | |||||
| setRows((rw) => | |||||
| rw.map((r) => (getRowId(r) === id ? { ...r, _error: undefined } : r)), | |||||
| ); | |||||
| } | |||||
| }, | |||||
| [rows, getRowId], | |||||
| ); | |||||
| const handleDelete = useCallback( | |||||
| (id: GridRowId) => () => { | |||||
| setRows((prevRows) => prevRows.filter((row) => getRowId(row) !== id)); | |||||
| }, | |||||
| [getRowId], | |||||
| ); | |||||
| const _columns = useMemo<GridColDef[]>( | |||||
| () => [ | |||||
| ...columns, | |||||
| { | |||||
| field: "actions", | |||||
| type: "actions", | |||||
| headerName: "", | |||||
| flex: 0.5, | |||||
| cellClassName: "actions", | |||||
| getActions: ({ id }: { id: GridRowId }) => { | |||||
| const isInEditMode = rowModesModel[id]?.mode === GridRowModes.Edit; | |||||
| if (isInEditMode) { | |||||
| return [ | |||||
| <GridActionsCellItem | |||||
| icon={<SaveIcon />} | |||||
| label="Save" | |||||
| key="edit" | |||||
| sx={{ | |||||
| color: "primary.main", | |||||
| }} | |||||
| onClick={handleSave(id)} | |||||
| />, | |||||
| <GridActionsCellItem | |||||
| icon={<CancelIcon />} | |||||
| label="Cancel" | |||||
| key="edit" | |||||
| onClick={handleCancel(id)} | |||||
| />, | |||||
| ]; | |||||
| } | |||||
| return [ | |||||
| <GridActionsCellItem | |||||
| icon={<DeleteIcon />} | |||||
| label="Delete" | |||||
| sx={{ | |||||
| color: "error.main", | |||||
| }} | |||||
| onClick={handleDelete(id)} | |||||
| color="inherit" | |||||
| key="edit" | |||||
| />, | |||||
| ]; | |||||
| }, | |||||
| }, | |||||
| ], | |||||
| [columns, rowModesModel, handleSave, handleCancel, handleDelete], | |||||
| ); | |||||
| // sync useForm | |||||
| useEffect(() => { | |||||
| // console.log(formKey) | |||||
| // console.log(rows) | |||||
| setValue(formKey, rows); | |||||
| }, [formKey, rows, setValue]); | |||||
| const footer = ( | |||||
| <Box display="flex" gap={2} alignItems="center"> | |||||
| <Button | |||||
| disableRipple | |||||
| variant="outlined" | |||||
| startIcon={<Add />} | |||||
| onClick={addRow} | |||||
| size="small" | |||||
| > | |||||
| 新增 | |||||
| {/* {t("Add Record")} */} | |||||
| </Button> | |||||
| <Button | |||||
| disableRipple | |||||
| variant="outlined" | |||||
| startIcon={<Add />} | |||||
| onClick={reset} | |||||
| size="small" | |||||
| > | |||||
| {/* {t("Clean Record")} */} | |||||
| 清除 | |||||
| </Button> | |||||
| </Box> | |||||
| ); | |||||
| // const handleRowEditStop: GridEventListener<'rowEditStop'> = (params, event) => { | |||||
| // if (params.reason === GridRowEditStopReasons.rowFocusOut) { | |||||
| // event.defaultMuiPrevented = true; | |||||
| // } | |||||
| // }; | |||||
| return ( | |||||
| <StyledDataGrid | |||||
| // {...props} | |||||
| // getRowId={getRowId as GridRowIdGetter<GridValidRowModel>} | |||||
| rowSelectionModel={rowSelectionModel} | |||||
| apiRef={apiRef} | |||||
| rows={rows} | |||||
| columns={columns} | |||||
| editMode="row" | |||||
| autoHeight | |||||
| sx={{ | |||||
| "--DataGrid-overlayHeight": "100px", | |||||
| ".MuiDataGrid-row .MuiDataGrid-cell.hasError": { | |||||
| border: "1px solid", | |||||
| borderColor: "error.main", | |||||
| }, | |||||
| ".MuiDataGrid-row .MuiDataGrid-cell.hasWarning": { | |||||
| border: "1px solid", | |||||
| borderColor: "warning.main", | |||||
| }, | |||||
| }} | |||||
| disableColumnMenu | |||||
| processRowUpdate={processRowUpdate as any} | |||||
| // onRowEditStop={handleRowEditStop} | |||||
| rowModesModel={rowModesModel} | |||||
| onRowModesModelChange={setRowModesModel} | |||||
| onProcessRowUpdateError={onProcessRowUpdateError} | |||||
| getCellClassName={(params: GridCellParams<TableRow<T, E>>) => { | |||||
| let classname = ""; | |||||
| if (params.row._error) { | |||||
| classname = "hasError"; | |||||
| } | |||||
| return classname; | |||||
| }} | |||||
| slots={{ | |||||
| // footer: FooterToolbar, | |||||
| noRowsOverlay: NoRowsOverlay, | |||||
| }} | |||||
| // slotProps={{ | |||||
| // footer: { child: footer }, | |||||
| // } | |||||
| // } | |||||
| /> | |||||
| ); | |||||
| } | } | ||||
| export default QcDataGrid | |||||
| const FooterToolbar: React.FC<FooterPropsOverrides> = ({ child }) => { | |||||
| return <GridToolbarContainer sx={{ p: 2 }}>{child}</GridToolbarContainer>; | |||||
| }; | |||||
| const NoRowsOverlay: React.FC = () => { | |||||
| const { t } = useTranslation("home"); | |||||
| return ( | |||||
| <Box | |||||
| display="flex" | |||||
| justifyContent="center" | |||||
| alignItems="center" | |||||
| height="100%" | |||||
| > | |||||
| <Typography variant="caption">{t("Add some entries!")}</Typography> | |||||
| </Box> | |||||
| ); | |||||
| }; | |||||
| export default InputDataGrid; | |||||
| @@ -5,6 +5,7 @@ import { | |||||
| Box, | Box, | ||||
| Card, | Card, | ||||
| CardContent, | CardContent, | ||||
| Checkbox, | |||||
| Grid, | Grid, | ||||
| Stack, | Stack, | ||||
| Tab, | Tab, | ||||
| @@ -17,7 +18,7 @@ import { | |||||
| import { useFormContext } from "react-hook-form"; | import { useFormContext } from "react-hook-form"; | ||||
| import { useTranslation } from "react-i18next"; | import { useTranslation } from "react-i18next"; | ||||
| import StyledDataGrid from "../StyledDataGrid"; | import StyledDataGrid from "../StyledDataGrid"; | ||||
| import { useCallback, useEffect, useMemo, useState } from "react"; | |||||
| import { Dispatch, SetStateAction, useCallback, useEffect, useMemo, useState } from "react"; | |||||
| import { | import { | ||||
| GridColDef, | GridColDef, | ||||
| GridRowIdGetter, | GridRowIdGetter, | ||||
| @@ -43,22 +44,25 @@ import axiosInstance from "@/app/(main)/axios/axiosInstance"; | |||||
| import EscalationComponent from "./EscalationComponent"; | import EscalationComponent from "./EscalationComponent"; | ||||
| import QcDataGrid from "./QCDatagrid"; | import QcDataGrid from "./QCDatagrid"; | ||||
| import StockInFormVer2 from "./StockInFormVer2"; | import StockInFormVer2 from "./StockInFormVer2"; | ||||
| import { dummyEscalationHistory } from "./dummyQcTemplate"; | |||||
| import { dummyEscalationHistory, dummyQCData, QcData } from "./dummyQcTemplate"; | |||||
| import { ModalFormInput } from "@/app/api/dashboard/actions"; | |||||
| interface Props { | interface Props { | ||||
| itemDetail: StockInLine; | itemDetail: StockInLine; | ||||
| qc: QcItemWithChecks[]; | qc: QcItemWithChecks[]; | ||||
| disabled: boolean; | disabled: boolean; | ||||
| qcItems: QcData[] | |||||
| setQcItems: Dispatch<SetStateAction<QcData[]>> | |||||
| } | } | ||||
| type EntryError = | type EntryError = | ||||
| | { | | { | ||||
| [field in keyof PurchaseQcResult]?: string; | |||||
| [field in keyof QcData]?: string; | |||||
| } | } | ||||
| | undefined; | | undefined; | ||||
| type PoQcRow = TableRow<Partial<PurchaseQcResult>, EntryError>; | |||||
| type QcRow = TableRow<Partial<QcData>, EntryError>; | |||||
| // fetchQcItemCheck | // fetchQcItemCheck | ||||
| const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled }) => { | |||||
| const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled, qcItems, setQcItems }) => { | |||||
| const { t } = useTranslation("purchaseOrder"); | const { t } = useTranslation("purchaseOrder"); | ||||
| const apiRef = useGridApiRef(); | const apiRef = useGridApiRef(); | ||||
| const { | const { | ||||
| @@ -76,6 +80,8 @@ const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled }) => { | |||||
| const [tabIndex, setTabIndex] = useState(0); | const [tabIndex, setTabIndex] = useState(0); | ||||
| const [rowSelectionModel, setRowSelectionModel] = useState<GridRowSelectionModel>() | const [rowSelectionModel, setRowSelectionModel] = useState<GridRowSelectionModel>() | ||||
| const [qcResult, setQcResult] = useState(dummyEscalationHistory) | |||||
| // const [qcItems, setQcItems] = useState(dummyQCData) | |||||
| const column = useMemo<GridColDef[]>( | const column = useMemo<GridColDef[]>( | ||||
| () => [ | () => [ | ||||
| @@ -137,18 +143,18 @@ const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled }) => { | |||||
| // flex: 1, | // flex: 1, | ||||
| // editable: !disabled, | // editable: !disabled, | ||||
| // valueFormatter(params) { | // valueFormatter(params) { | ||||
| // const row = params.id ? params.api.getRow<PoQcRow>(params.id) : null; | |||||
| // const row = params.id ? params.api.getRow<QcRow>(params.id) : null; | |||||
| // if (!row) { | // if (!row) { | ||||
| // return null; | // return null; | ||||
| // } | // } | ||||
| // const Qc = qc.find((q) => q.id === row.qcItemId); | // const Qc = qc.find((q) => q.id === row.qcItemId); | ||||
| // return Qc ? `${Qc.code} - ${Qc.name}` : t("Please select QC"); | // return Qc ? `${Qc.code} - ${Qc.name}` : t("Please select QC"); | ||||
| // }, | // }, | ||||
| // renderCell(params: GridRenderCellParams<PoQcRow, number>) { | |||||
| // renderCell(params: GridRenderCellParams<QcRow, number>) { | |||||
| // console.log(params.value); | // console.log(params.value); | ||||
| // return <TwoLineCell>{params.formattedValue}</TwoLineCell>; | // return <TwoLineCell>{params.formattedValue}</TwoLineCell>; | ||||
| // }, | // }, | ||||
| // renderEditCell(params: GridRenderEditCellParams<PoQcRow, number>) { | |||||
| // renderEditCell(params: GridRenderEditCellParams<QcRow, number>) { | |||||
| // const errorMessage = | // const errorMessage = | ||||
| // params.row._error?.[params.field as keyof PurchaseQcResult]; | // params.row._error?.[params.field as keyof PurchaseQcResult]; | ||||
| // console.log(errorMessage); | // console.log(errorMessage); | ||||
| @@ -185,7 +191,7 @@ const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled }) => { | |||||
| // flex: 1, | // flex: 1, | ||||
| // editable: !disabled, | // editable: !disabled, | ||||
| // type: "number", | // type: "number", | ||||
| // renderEditCell(params: GridRenderEditCellParams<PoQcRow>) { | |||||
| // renderEditCell(params: GridRenderEditCellParams<QcRow>) { | |||||
| // // const recordQty = params.row.qty | // // const recordQty = params.row.qty | ||||
| // // if (recordQty !== undefined) { | // // if (recordQty !== undefined) { | ||||
| // // setUnrecordQty((prev) => prev - recordQty) | // // setUnrecordQty((prev) => prev - recordQty) | ||||
| @@ -217,23 +223,124 @@ const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled }) => { | |||||
| ); | ); | ||||
| /// validate datagrid | /// validate datagrid | ||||
| const validation = useCallback( | const validation = useCallback( | ||||
| (newRow: GridRowModel<PoQcRow>): EntryError => { | |||||
| (newRow: GridRowModel<QcRow>): EntryError => { | |||||
| const error: EntryError = {}; | const error: EntryError = {}; | ||||
| const { qcItemId, failQty } = newRow; | |||||
| if (!qcItemId || qcItemId <= 0) { | |||||
| error["qcItemId"] = t("select qc"); | |||||
| } | |||||
| if (!failQty || failQty <= 0) { | |||||
| error["failQty"] = t("enter a failQty"); | |||||
| } | |||||
| if (failQty && failQty > itemDetail.acceptedQty) { | |||||
| error["failQty"] = t("qty too big"); | |||||
| } | |||||
| // const { qcItemId, failQty } = newRow; | |||||
| return Object.keys(error).length > 0 ? error : undefined; | return Object.keys(error).length > 0 ? error : undefined; | ||||
| }, | }, | ||||
| [], | [], | ||||
| ); | ); | ||||
| function BooleanEditCell(params: GridRenderEditCellParams) { | |||||
| const apiRef = useGridApiContext(); | |||||
| const { id, field, value } = params; | |||||
| const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => { | |||||
| apiRef.current.setEditCellValue({ id, field, value: e.target.checked }); | |||||
| apiRef.current.stopCellEditMode({ id, field }); // commit immediately | |||||
| }; | |||||
| return <Checkbox checked={!!value} onChange={handleChange} sx={{ p: 0 }} />; | |||||
| } | |||||
| const qcColumns: GridColDef[] = [ | |||||
| { | |||||
| field: "qcItem", | |||||
| headerName: t("qcItem"), | |||||
| flex: 1, | |||||
| }, | |||||
| { | |||||
| field: 'isPassed', | |||||
| headerName: t("passed"), | |||||
| flex: 1, | |||||
| renderCell: (params) => ( | |||||
| <Checkbox | |||||
| checked={!!params.value} | |||||
| onClick={(e) => e.stopPropagation()} // avoid row selection | |||||
| onMouseDown={(e) => e.stopPropagation()} // extra guard | |||||
| onChange={(e) => { | |||||
| const checked = e.target.checked; | |||||
| setQcItems((prev) => | |||||
| prev.map((r) => (r.id === params.id ? { ...r, isPassed: checked } : r)) | |||||
| ); | |||||
| }} | |||||
| size="small" | |||||
| /> | |||||
| ), | |||||
| }, | |||||
| { | |||||
| field: "isFailed", | |||||
| headerName: t("failed"), | |||||
| flex: 1, | |||||
| editable: true, | |||||
| type: "boolean", | |||||
| renderCell: (params) => ( | |||||
| <Checkbox | |||||
| checked={!!params.value} | |||||
| onClick={(e) => e.stopPropagation()} // avoid row selection | |||||
| onMouseDown={(e) => e.stopPropagation()} // extra guard | |||||
| onChange={(e) => { | |||||
| const checked = e.target.checked; | |||||
| setQcItems((prev) => | |||||
| prev.map((r) => (r.id === params.id ? { ...r, isFailed: checked } : r)) | |||||
| ); | |||||
| }} | |||||
| size="small" | |||||
| /> | |||||
| ), | |||||
| }, | |||||
| { | |||||
| field: "failedQty", | |||||
| headerName: t("failedQty"), | |||||
| flex: 1, | |||||
| // editable: true, | |||||
| renderCell: (params) => ( | |||||
| <TextField | |||||
| type="number" | |||||
| size="small" | |||||
| value={params.value ?? ''} | |||||
| onChange={(e) => { | |||||
| const v = e.target.value; | |||||
| const next = v === '' ? undefined : Number(v); | |||||
| if (Number.isNaN(next)) return; | |||||
| setQcItems((prev) => | |||||
| prev.map((r) => (r.id === params.id ? { ...r, failedQty: next } : r)) | |||||
| ); | |||||
| }} | |||||
| onClick={(e) => e.stopPropagation()} | |||||
| onMouseDown={(e) => e.stopPropagation()} | |||||
| onKeyDown={(e) => e.stopPropagation()} | |||||
| inputProps={{ min: 0 }} | |||||
| sx={{ width: '100%' }} | |||||
| /> | |||||
| ), | |||||
| }, | |||||
| { | |||||
| field: "remarks", | |||||
| headerName: t("remarks"), | |||||
| flex: 1, | |||||
| renderCell: (params) => ( | |||||
| <TextField | |||||
| size="small" | |||||
| value={params.value ?? ''} | |||||
| onChange={(e) => { | |||||
| const remarks = e.target.value; | |||||
| // const next = v === '' ? undefined : Number(v); | |||||
| // if (Number.isNaN(next)) return; | |||||
| setQcItems((prev) => | |||||
| prev.map((r) => (r.id === params.id ? { ...r, remarks: remarks } : r)) | |||||
| ); | |||||
| }} | |||||
| onClick={(e) => e.stopPropagation()} | |||||
| onMouseDown={(e) => e.stopPropagation()} | |||||
| onKeyDown={(e) => e.stopPropagation()} | |||||
| inputProps={{ min: 0 }} | |||||
| sx={{ width: '100%' }} | |||||
| /> | |||||
| ), | |||||
| }, | |||||
| ] | |||||
| useEffect(() => { | useEffect(() => { | ||||
| console.log(itemDetail); | console.log(itemDetail); | ||||
| const status = "receiving"; | const status = "receiving"; | ||||
| @@ -268,10 +375,21 @@ const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled }) => { | |||||
| {tabIndex == 0 && ( | {tabIndex == 0 && ( | ||||
| <> | <> | ||||
| <Grid item xs={12}> | <Grid item xs={12}> | ||||
| <QcDataGrid/> | |||||
| {/* <QcDataGrid<ModalFormInput, QcData, EntryError> | |||||
| apiRef={apiRef} | |||||
| columns={qcColumns} | |||||
| _formKey="qcResult" | |||||
| validateRow={validation} | |||||
| /> */} | |||||
| <StyledDataGrid | |||||
| columns={qcColumns} | |||||
| rows={qcItems} | |||||
| autoHeight | |||||
| /> | |||||
| </Grid> | </Grid> | ||||
| <Grid item xs={4}> | <Grid item xs={4}> | ||||
| <TextField | <TextField | ||||
| type="number" | |||||
| label={t("acceptedQty")} | label={t("acceptedQty")} | ||||
| fullWidth | fullWidth | ||||
| /> | /> | ||||
| @@ -296,7 +414,7 @@ const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled }) => { | |||||
| </Grid> | </Grid> | ||||
| <Grid item xs={12}> | <Grid item xs={12}> | ||||
| <StyledDataGrid | <StyledDataGrid | ||||
| rows={dummyEscalationHistory} | |||||
| rows={qcResult} | |||||
| columns={columns} | columns={columns} | ||||
| onRowSelectionModelChange={(newRowSelectionModel) => { | onRowSelectionModelChange={(newRowSelectionModel) => { | ||||
| setRowSelectionModel(newRowSelectionModel); | setRowSelectionModel(newRowSelectionModel); | ||||
| @@ -1,9 +1,16 @@ | |||||
| "use client"; | "use client"; | ||||
| import { StockInLine } from "@/app/api/po"; | import { StockInLine } from "@/app/api/po"; | ||||
| import { ModalFormInput, PurchaseQcResult } from "@/app/api/po/actions"; | import { ModalFormInput, PurchaseQcResult } from "@/app/api/po/actions"; | ||||
| import { QcItemWithChecks } from "@/app/api/qc"; | import { QcItemWithChecks } from "@/app/api/qc"; | ||||
| import { Box, Button, Grid, Modal, ModalProps, Stack, Typography } from "@mui/material"; | |||||
| import { | |||||
| Box, | |||||
| Button, | |||||
| Grid, | |||||
| Modal, | |||||
| ModalProps, | |||||
| Stack, | |||||
| Typography, | |||||
| } from "@mui/material"; | |||||
| import { Dispatch, SetStateAction, useCallback, useState } from "react"; | import { Dispatch, SetStateAction, useCallback, useState } from "react"; | ||||
| import { FormProvider, SubmitHandler, useForm } from "react-hook-form"; | import { FormProvider, SubmitHandler, useForm } from "react-hook-form"; | ||||
| import { StockInLineRow } from "./PoInputGrid"; | import { StockInLineRow } from "./PoInputGrid"; | ||||
| @@ -12,8 +19,8 @@ import StockInForm from "./StockInForm"; | |||||
| import StockInFormVer2 from "./StockInFormVer2"; | import StockInFormVer2 from "./StockInFormVer2"; | ||||
| import QcFormVer2 from "./QcFormVer2"; | import QcFormVer2 from "./QcFormVer2"; | ||||
| import PutawayForm from "./PutawayForm"; | import PutawayForm from "./PutawayForm"; | ||||
| import { dummyPutawayLine } from "./dummyQcTemplate"; | |||||
| import { dummyPutawayLine, dummyQCData } from "./dummyQcTemplate"; | |||||
| import { useGridApiRef } from "@mui/x-data-grid"; | |||||
| const style = { | const style = { | ||||
| position: "absolute", | position: "absolute", | ||||
| top: "50%", | top: "50%", | ||||
| @@ -25,9 +32,8 @@ const style = { | |||||
| pb: 10, | pb: 10, | ||||
| display: "block", | display: "block", | ||||
| width: { xs: "60%", sm: "60%", md: "60%" }, | width: { xs: "60%", sm: "60%", md: "60%" }, | ||||
| // height: { xs: "60%", sm: "60%", md: "60%" }, | |||||
| // height: { xs: "60%", sm: "60%", md: "60%" }, | |||||
| }; | }; | ||||
| interface CommonProps extends Omit<ModalProps, "children"> { | interface CommonProps extends Omit<ModalProps, "children"> { | ||||
| // setRows: Dispatch<SetStateAction<PurchaseOrderLine[]>>; | // setRows: Dispatch<SetStateAction<PurchaseOrderLine[]>>; | ||||
| setEntries?: Dispatch<SetStateAction<StockInLineRow[]>>; | setEntries?: Dispatch<SetStateAction<StockInLineRow[]>>; | ||||
| @@ -43,12 +49,10 @@ interface CommonProps extends Omit<ModalProps, "children"> { | |||||
| >; | >; | ||||
| qc?: QcItemWithChecks[]; | qc?: QcItemWithChecks[]; | ||||
| warehouse?: any[]; | warehouse?: any[]; | ||||
| // type: "qc" | "stockIn" | "escalation" | "putaway" | "reject"; | |||||
| // type: "qc" | "stockIn" | "escalation" | "putaway" | "reject"; | |||||
| } | } | ||||
| interface Props extends CommonProps{ | |||||
| interface Props extends CommonProps { | |||||
| itemDetail: StockInLine & { qcResult?: PurchaseQcResult[] }; | itemDetail: StockInLine & { qcResult?: PurchaseQcResult[] }; | ||||
| } | } | ||||
| const PoQcStockInModalVer2: React.FC<Props> = ({ | const PoQcStockInModalVer2: React.FC<Props> = ({ | ||||
| // type, | // type, | ||||
| @@ -62,165 +66,243 @@ const PoQcStockInModalVer2: React.FC<Props> = ({ | |||||
| qc, | qc, | ||||
| warehouse, | warehouse, | ||||
| }) => { | }) => { | ||||
| console.log(warehouse) | |||||
| const { | |||||
| t, | |||||
| i18n: { language }, | |||||
| } = useTranslation("purchaseOrder"); | |||||
| console.log(warehouse); | |||||
| const { | |||||
| t, | |||||
| i18n: { language }, | |||||
| } = useTranslation("purchaseOrder"); | |||||
| const [qcItems, setQcItems] = useState(dummyQCData) | |||||
| const formProps = useForm<ModalFormInput>({ | const formProps = useForm<ModalFormInput>({ | ||||
| defaultValues: { | defaultValues: { | ||||
| ...itemDetail, | ...itemDetail, | ||||
| putawayLine: dummyPutawayLine | |||||
| putawayLine: dummyPutawayLine, | |||||
| // receiptDate: itemDetail.receiptDate || dayjs().add(-1, "month").format(INPUT_DATE_FORMAT), | // receiptDate: itemDetail.receiptDate || dayjs().add(-1, "month").format(INPUT_DATE_FORMAT), | ||||
| // warehouseId: itemDetail.defaultWarehouseId || 0 | // warehouseId: itemDetail.defaultWarehouseId || 0 | ||||
| }, | }, | ||||
| }); | }); | ||||
| const closeHandler = useCallback<NonNullable<ModalProps["onClose"]>>( | const closeHandler = useCallback<NonNullable<ModalProps["onClose"]>>( | ||||
| (...args) => { | |||||
| onClose?.(...args); | |||||
| // reset(); | |||||
| }, | |||||
| [onClose], | |||||
| ); | |||||
| const [openPutaway, setOpenPutaway] = useState(false) | |||||
| (...args) => { | |||||
| onClose?.(...args); | |||||
| // reset(); | |||||
| }, | |||||
| [onClose], | |||||
| ); | |||||
| const [openPutaway, setOpenPutaway] = useState(false); | |||||
| const onOpenPutaway = useCallback(() => { | const onOpenPutaway = useCallback(() => { | ||||
| setOpenPutaway(true); | setOpenPutaway(true); | ||||
| }, []); | }, []); | ||||
| const onClosePutaway = useCallback(() => { | const onClosePutaway = useCallback(() => { | ||||
| setOpenPutaway(false); | setOpenPutaway(false); | ||||
| }, []); | }, []); | ||||
| const [submissionType, setSubmissionType] = useState<"stockIn" | "qc" | "escalate" | undefined>(undefined) | |||||
| const onSubmit = useCallback<SubmitHandler<ModalFormInput>>( | |||||
| // Stock In submission handler | |||||
| const onSubmitStockIn = useCallback<SubmitHandler<ModalFormInput>>( | |||||
| async (data, event) => { | async (data, event) => { | ||||
| console.log(event!.nativeEvent) | |||||
| // closeHandler({}, "backdropClick"); | |||||
| // for now go to putaway form | |||||
| onOpenPutaway() | |||||
| // divide 3 section for this submition | |||||
| // switch (submissionType) { | |||||
| // submit stock in data | |||||
| // submit qc data | |||||
| // submit putaway | |||||
| // } | |||||
| console.log("Stock In Submission:", event!.nativeEvent); | |||||
| // Extract only stock-in related fields | |||||
| const stockInData = { | |||||
| // quantity: data.quantity, | |||||
| // receiptDate: data.receiptDate, | |||||
| // batchNumber: data.batchNumber, | |||||
| // expiryDate: data.expiryDate, | |||||
| // warehouseId: data.warehouseId, | |||||
| // location: data.location, | |||||
| // unitCost: data.unitCost, | |||||
| data: data, | |||||
| // Add other stock-in specific fields from your form | |||||
| }; | |||||
| console.log("Stock In Data:", stockInData); | |||||
| // Handle stock-in submission logic here | |||||
| // e.g., call API, update state, etc. | |||||
| }, | |||||
| [], | |||||
| ); | |||||
| // QC submission handler | |||||
| const onSubmitQc = useCallback<SubmitHandler<ModalFormInput>>( | |||||
| async (data, event) => { | |||||
| console.log("QC Submission:", event!.nativeEvent); | |||||
| // Extract only QC related fields | |||||
| const qcData = { | |||||
| // qcStatus: data.qcStatus, | |||||
| // qcComments: data.qcComments, | |||||
| // qcResult: data.qcResult, | |||||
| // approvedBy: data.approvedBy, | |||||
| // qualityGrade: data.qualityGrade, | |||||
| // defectNotes: data.defectNotes, | |||||
| data: data, | |||||
| // Add other QC specific fields from your form | |||||
| }; | |||||
| console.log(qcItems) | |||||
| console.log("QC Data:", qcData); | |||||
| // Handle QC submission logic here | |||||
| // After QC approval, open putaway form | |||||
| // onOpenPutaway(); | |||||
| }, | |||||
| [onOpenPutaway, qcItems], | |||||
| ); | |||||
| // Email supplier handler | |||||
| const onSubmitEmailSupplier = useCallback<SubmitHandler<ModalFormInput>>( | |||||
| async (data, event) => { | |||||
| console.log("Email Supplier Submission:", event!.nativeEvent); | |||||
| // Extract only email supplier related fields | |||||
| const emailData = { | |||||
| // supplierEmail: data.supplierEmail, | |||||
| // issueDescription: data.issueDescription, | |||||
| // qcComments: data.qcComments, | |||||
| // defectNotes: data.defectNotes, | |||||
| // attachments: data.attachments, | |||||
| // escalationReason: data.escalationReason, | |||||
| data: data, | |||||
| }, [submissionType]) | |||||
| // Add other email-specific fields | |||||
| }; | |||||
| console.log("Email Supplier Data:", emailData); | |||||
| // Handle email supplier logic here | |||||
| // e.g., send email to supplier, log escalation, etc. | |||||
| }, | |||||
| [], | |||||
| ); | |||||
| // Putaway submission handler | |||||
| const onSubmitPutaway = useCallback<SubmitHandler<ModalFormInput>>( | |||||
| async (data, event) => { | |||||
| console.log("Putaway Submission:", event!.nativeEvent); | |||||
| // Extract only putaway related fields | |||||
| const putawayData = { | |||||
| // putawayLine: data.putawayLine, | |||||
| // putawayLocation: data.putawayLocation, | |||||
| // binLocation: data.binLocation, | |||||
| // putawayQuantity: data.putawayQuantity, | |||||
| // putawayNotes: data.putawayNotes, | |||||
| data: data, | |||||
| return ( | |||||
| <> | |||||
| {/* {itemDetail !== undefined && ( | |||||
| <PutawayForm | |||||
| itemDetail={itemDetail} | |||||
| warehouse={warehouse!} | |||||
| disabled={false} | |||||
| /> | |||||
| )} */} | |||||
| <FormProvider {...formProps}> | |||||
| <Modal open={open} onClose={closeHandler}> | |||||
| <Box | |||||
| sx={{ | |||||
| ...style, | |||||
| padding: 2, // Add padding to the Box | |||||
| maxHeight: '90vh', // Limit the height of the modal | |||||
| overflowY: 'auto', // Enable scrolling if content overflows | |||||
| marginLeft: 3, | |||||
| marginRight: 3, | |||||
| }} | |||||
| component="form" | |||||
| onSubmit={formProps.handleSubmit(onSubmit)} | |||||
| > | |||||
| {openPutaway ? ( | |||||
| <> | |||||
| <PutawayForm | |||||
| itemDetail={itemDetail} | |||||
| warehouse={warehouse!} | |||||
| disabled={false} | |||||
| /> | |||||
| <Stack direction="row" justifyContent="flex-end" gap={1}> | |||||
| <Button | |||||
| id="qc" | |||||
| type="button" | |||||
| variant="contained" | |||||
| color="secondary" | |||||
| sx={{ mt: 1 }} | |||||
| > | |||||
| {t("print")} | |||||
| </Button> | |||||
| <Button | |||||
| id="qc" | |||||
| type="submit" | |||||
| variant="contained" | |||||
| color="secondary" | |||||
| sx={{ mt: 1 }} | |||||
| > | |||||
| {t("confirm putaway")} | |||||
| </Button> | |||||
| </Stack> | |||||
| </> | |||||
| // Add other putaway specific fields | |||||
| }; | |||||
| console.log("Putaway Data:", putawayData); | |||||
| // Handle putaway submission logic here | |||||
| // Close modal after successful putaway | |||||
| closeHandler({}, "backdropClick"); | |||||
| }, | |||||
| [closeHandler], | |||||
| ); | |||||
| // Print handler | |||||
| const onPrint = useCallback(() => { | |||||
| console.log("Print putaway documents"); | |||||
| // Handle print logic here | |||||
| window.print(); | |||||
| }, []); | |||||
| return ( | |||||
| <> | |||||
| <FormProvider {...formProps}> | |||||
| <Modal open={open} onClose={closeHandler}> | |||||
| <Box | |||||
| sx={{ | |||||
| ...style, | |||||
| padding: 2, | |||||
| maxHeight: "90vh", | |||||
| overflowY: "auto", | |||||
| marginLeft: 3, | |||||
| marginRight: 3, | |||||
| }} | |||||
| > | |||||
| {openPutaway ? ( | |||||
| <Box | |||||
| component="form" | |||||
| onSubmit={formProps.handleSubmit(onSubmitPutaway)} | |||||
| > | |||||
| <PutawayForm | |||||
| itemDetail={itemDetail} | |||||
| warehouse={warehouse!} | |||||
| disabled={false} | |||||
| /> | |||||
| <Stack direction="row" justifyContent="flex-end" gap={1}> | |||||
| <Button | |||||
| id="printButton" | |||||
| type="button" | |||||
| variant="contained" | |||||
| color="secondary" | |||||
| sx={{ mt: 1 }} | |||||
| onClick={onPrint} | |||||
| > | |||||
| {t("print")} | |||||
| </Button> | |||||
| <Button | |||||
| id="putawaySubmit" | |||||
| type="submit" | |||||
| variant="contained" | |||||
| color="secondary" | |||||
| sx={{ mt: 1 }} | |||||
| > | |||||
| {t("confirm putaway")} | |||||
| </Button> | |||||
| </Stack> | |||||
| </Box> | |||||
| ) : ( | ) : ( | ||||
| <> | |||||
| <Grid container justifyContent="flex-start" alignItems="flex-start"> | |||||
| <Grid item xs={12}> | |||||
| <Typography variant="h6" display="block" marginBlockEnd={1}> | |||||
| {t("qc processing")} | |||||
| </Typography> | |||||
| </Grid> | |||||
| <Grid item xs={12}> | |||||
| <StockInFormVer2 | |||||
| itemDetail={itemDetail} | |||||
| disabled={false} | |||||
| /> | |||||
| </Grid> | |||||
| </Grid> | |||||
| <Stack direction="row" justifyContent="flex-end" gap={1}> | |||||
| <Button | |||||
| id="stockIn" | |||||
| type="button" | |||||
| variant="contained" | |||||
| color="primary" | |||||
| > | |||||
| {t("submitStockIn")} | |||||
| </Button> | |||||
| </Stack> | |||||
| <Grid container justifyContent="flex-start" alignItems="flex-start"> | |||||
| <QcFormVer2 | |||||
| qc={qc!} | |||||
| itemDetail={itemDetail} | |||||
| disabled={false} | |||||
| /> | |||||
| </Grid> | |||||
| <Stack direction="row" justifyContent="flex-end" gap={1}> | |||||
| <Button | |||||
| id="qc" | |||||
| type="button" | |||||
| variant="contained" | |||||
| color="secondary" | |||||
| sx={{ mt: 1 }} | |||||
| > | |||||
| {t("email supplier")} | |||||
| </Button> | |||||
| <Button | |||||
| id="qc" | |||||
| type="submit" | |||||
| variant="contained" | |||||
| color="secondary" | |||||
| sx={{ mt: 1 }} | |||||
| > | |||||
| {t("confirm putaway")} | |||||
| </Button> | |||||
| </Stack> | |||||
| </> | |||||
| ) | |||||
| } | |||||
| </Box> | |||||
| </Modal> | |||||
| </FormProvider> | |||||
| </> | |||||
| ) | |||||
| } | |||||
| export default PoQcStockInModalVer2 | |||||
| <> | |||||
| <Grid | |||||
| container | |||||
| justifyContent="flex-start" | |||||
| alignItems="flex-start" | |||||
| > | |||||
| <Grid item xs={12}> | |||||
| <Typography variant="h6" display="block" marginBlockEnd={1}> | |||||
| {t("qc processing")} | |||||
| </Typography> | |||||
| </Grid> | |||||
| <Grid item xs={12}> | |||||
| <StockInFormVer2 itemDetail={itemDetail} disabled={false} /> | |||||
| </Grid> | |||||
| </Grid> | |||||
| <Stack direction="row" justifyContent="flex-end" gap={1}> | |||||
| <Button | |||||
| id="stockInSubmit" | |||||
| type="button" | |||||
| variant="contained" | |||||
| color="primary" | |||||
| onClick={formProps.handleSubmit(onSubmitStockIn)} | |||||
| > | |||||
| {t("submitStockIn")} | |||||
| </Button> | |||||
| </Stack> | |||||
| <Grid | |||||
| container | |||||
| justifyContent="flex-start" | |||||
| alignItems="flex-start" | |||||
| > | |||||
| <QcFormVer2 | |||||
| qc={qc!} | |||||
| itemDetail={itemDetail} | |||||
| disabled={false} | |||||
| qcItems={qcItems} | |||||
| setQcItems={setQcItems} | |||||
| /> | |||||
| </Grid> | |||||
| <Stack direction="row" justifyContent="flex-end" gap={1}> | |||||
| <Button | |||||
| id="emailSupplier" | |||||
| type="button" | |||||
| variant="contained" | |||||
| color="secondary" | |||||
| sx={{ mt: 1 }} | |||||
| onClick={formProps.handleSubmit(onSubmitEmailSupplier)} | |||||
| > | |||||
| {t("email supplier")} | |||||
| </Button> | |||||
| <Button | |||||
| id="qcSubmit" | |||||
| type="button" | |||||
| variant="contained" | |||||
| color="secondary" | |||||
| sx={{ mt: 1 }} | |||||
| onClick={formProps.handleSubmit(onSubmitQc)} | |||||
| > | |||||
| {t("confirm putaway")} | |||||
| </Button> | |||||
| </Stack> | |||||
| </> | |||||
| )} | |||||
| </Box> | |||||
| </Modal> | |||||
| </FormProvider> | |||||
| </> | |||||
| ); | |||||
| }; | |||||
| export default PoQcStockInModalVer2; | |||||
| @@ -82,7 +82,7 @@ const StockInForm: React.FC<Props> = ({ | |||||
| // receiptDate default tdy | // receiptDate default tdy | ||||
| setValue("receiptDate", dayjs().add(0, "month").format(INPUT_DATE_FORMAT)); | setValue("receiptDate", dayjs().add(0, "month").format(INPUT_DATE_FORMAT)); | ||||
| setValue("status", "received"); | setValue("status", "received"); | ||||
| }, []); | |||||
| }, [setValue]); | |||||
| useEffect(() => { | useEffect(() => { | ||||
| console.log(errors); | console.log(errors); | ||||
| @@ -97,7 +97,7 @@ const StockInForm: React.FC<Props> = ({ | |||||
| if (expiryDate) clearErrors(); | if (expiryDate) clearErrors(); | ||||
| if (productionDate) clearErrors(); | if (productionDate) clearErrors(); | ||||
| }, [productionDate, expiryDate, clearErrors]); | }, [productionDate, expiryDate, clearErrors]); | ||||
| return ( | return ( | ||||
| <Grid container justifyContent="flex-start" alignItems="flex-start"> | <Grid container justifyContent="flex-start" alignItems="flex-start"> | ||||
| <Grid item xs={12}> | <Grid item xs={12}> | ||||