| @@ -65,12 +65,18 @@ export interface EscalationInput { | |||||
| acceptedQty: number; // this is the qty to be escalated | acceptedQty: number; // this is the qty to be escalated | ||||
| // escalationQty: number | // escalationQty: number | ||||
| } | } | ||||
| export interface PutawayLine { | |||||
| id?: number | |||||
| qty: number | |||||
| warehouseId: number; | |||||
| warehouse: string; | |||||
| printQty: number | |||||
| } | |||||
| export interface PutawayInput { | export interface PutawayInput { | ||||
| status: string; | status: string; | ||||
| acceptedQty: number; | acceptedQty: number; | ||||
| warehouseId: number; | warehouseId: number; | ||||
| // handler: string | |||||
| // stockInLine: StockInLineEntry[] | |||||
| putawayLine: PutawayLine[] | |||||
| } | } | ||||
| export type ModalFormInput = Partial< | export type ModalFormInput = Partial< | ||||
| @@ -14,7 +14,7 @@ export interface PoResult { | |||||
| supplier: string; | supplier: string; | ||||
| estimatedArrivalDate: string; | estimatedArrivalDate: string; | ||||
| completedDate: string; | completedDate: string; | ||||
| itemDetail?: String; | |||||
| itemDetail?: string; | |||||
| escalated: boolean; | escalated: boolean; | ||||
| status: string; | status: string; | ||||
| pol?: PurchaseOrderLine[]; | pol?: PurchaseOrderLine[]; | ||||
| @@ -123,11 +123,13 @@ function InputDataGrid<T, V, E>({ | |||||
| [], | [], | ||||
| ); | ); | ||||
| const list: TableRow<V, E>[] = getValues(formKey); | const list: TableRow<V, E>[] = getValues(formKey); | ||||
| // console.log(list) | |||||
| console.log(list) | |||||
| const [rows, setRows] = useState<TableRow<V, E>[]>(() => { | const [rows, setRows] = useState<TableRow<V, E>[]>(() => { | ||||
| const list: TableRow<V, E>[] = getValues(formKey); | const list: TableRow<V, E>[] = getValues(formKey); | ||||
| console.log(list) | |||||
| return list && list.length > 0 ? list : []; | return list && list.length > 0 ? list : []; | ||||
| }); | }); | ||||
| console.log(rows) | |||||
| // const originalRows = list && list.length > 0 ? list : []; | // const originalRows = list && list.length > 0 ? list : []; | ||||
| const originalRows = useMemo(() => ( | const originalRows = useMemo(() => ( | ||||
| list && list.length > 0 ? list : [] | list && list.length > 0 ? list : [] | ||||
| @@ -298,7 +300,8 @@ function InputDataGrid<T, V, E>({ | |||||
| onClick={addRow} | onClick={addRow} | ||||
| size="small" | size="small" | ||||
| > | > | ||||
| {t("Add Record")} | |||||
| 新增 | |||||
| {/* {t("Add Record")} */} | |||||
| </Button> | </Button> | ||||
| <Button | <Button | ||||
| disableRipple | disableRipple | ||||
| @@ -307,7 +310,8 @@ function InputDataGrid<T, V, E>({ | |||||
| onClick={reset} | onClick={reset} | ||||
| size="small" | size="small" | ||||
| > | > | ||||
| {t("Clean Record")} | |||||
| {/* {t("Clean Record")} */} | |||||
| 清除 | |||||
| </Button> | </Button> | ||||
| </Box> | </Box> | ||||
| ); | ); | ||||
| @@ -14,10 +14,13 @@ import { | |||||
| Typography, | Typography, | ||||
| RadioGroup, | RadioGroup, | ||||
| Radio, | Radio, | ||||
| Stack, | |||||
| Autocomplete, | |||||
| } from '@mui/material'; | } from '@mui/material'; | ||||
| import { SelectChangeEvent } from '@mui/material/Select'; | import { SelectChangeEvent } from '@mui/material/Select'; | ||||
| import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; | import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; | ||||
| import ExpandLessIcon from '@mui/icons-material/ExpandLess'; | import ExpandLessIcon from '@mui/icons-material/ExpandLess'; | ||||
| import { useTranslation } from 'react-i18next'; | |||||
| interface NameOption { | interface NameOption { | ||||
| value: string; | value: string; | ||||
| @@ -30,7 +33,15 @@ interface FormData { | |||||
| message: string; | message: string; | ||||
| } | } | ||||
| function EscalationComponent(): JSX.Element { | |||||
| interface Props { | |||||
| forSupervisor: boolean | |||||
| } | |||||
| const EscalationComponent: React.FC<Props> = ({ | |||||
| forSupervisor | |||||
| }) => { | |||||
| const { t } = useTranslation("purchaseOrder"); | |||||
| const [isCollapsed, setIsCollapsed] = useState<boolean>(false); | const [isCollapsed, setIsCollapsed] = useState<boolean>(false); | ||||
| const [formData, setFormData] = useState<FormData>({ | const [formData, setFormData] = useState<FormData>({ | ||||
| name: '', | name: '', | ||||
| @@ -48,9 +59,9 @@ function EscalationComponent(): JSX.Element { | |||||
| ]; | ]; | ||||
| const handleInputChange = ( | const handleInputChange = ( | ||||
| e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement> | SelectChangeEvent<string> | |||||
| event: ChangeEvent<HTMLInputElement | HTMLTextAreaElement> | SelectChangeEvent<string> | |||||
| ): void => { | ): void => { | ||||
| const { name, value } = e.target; | |||||
| const { name, value } = event.target; | |||||
| setFormData((prev) => ({ | setFormData((prev) => ({ | ||||
| ...prev, | ...prev, | ||||
| [name]: value, | [name]: value, | ||||
| @@ -69,8 +80,10 @@ function EscalationComponent(): JSX.Element { | |||||
| return ( | return ( | ||||
| // <Paper elevation={3} sx={{ maxWidth: 400, mx: 'auto', p: 3 }}> | // <Paper elevation={3} sx={{ maxWidth: 400, mx: 'auto', p: 3 }}> | ||||
| <Paper elevation={3} sx={{ mx: 'auto', p: 3 }}> | |||||
| <Box sx={{ display: 'flex', alignItems: 'center', mb: 2 }}> | |||||
| <> | |||||
| <Paper> | |||||
| {/* <Paper elevation={3} sx={{ mx: 'auto', p: 3 }}> */} | |||||
| <Box sx={{ display: 'flex', alignItems: 'center', mb: 2 }}> | |||||
| <FormControlLabel | <FormControlLabel | ||||
| control={ | control={ | ||||
| <Checkbox | <Checkbox | ||||
| @@ -91,38 +104,36 @@ function EscalationComponent(): JSX.Element { | |||||
| } | } | ||||
| /> | /> | ||||
| </Box> | </Box> | ||||
| <Collapse in={!isCollapsed}> | |||||
| <Collapse in={isCollapsed}> | |||||
| <Box component="form" onSubmit={handleSubmit} sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}> | <Box component="form" onSubmit={handleSubmit} sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}> | ||||
| <FormControl> | |||||
| <RadioGroup | |||||
| row | |||||
| aria-labelledby="demo-radio-buttons-group-label" | |||||
| defaultValue="pass" | |||||
| name="radio-buttons-group" | |||||
| > | |||||
| <FormControlLabel value="pass" control={<Radio />} label="合格" /> | |||||
| <FormControlLabel value="fail" control={<Radio />} label="不合格" /> | |||||
| </RadioGroup> | |||||
| </FormControl> | |||||
| {forSupervisor ? ( | |||||
| <FormControl> | |||||
| <RadioGroup | |||||
| row | |||||
| aria-labelledby="demo-radio-buttons-group-label" | |||||
| defaultValue="pass" | |||||
| name="radio-buttons-group" | |||||
| > | |||||
| <FormControlLabel value="pass" control={<Radio />} label="合格" /> | |||||
| <FormControlLabel value="fail" control={<Radio />} label="不合格" /> | |||||
| </RadioGroup> | |||||
| </FormControl> | |||||
| ): undefined} | |||||
| <FormControl fullWidth> | <FormControl fullWidth> | ||||
| <InputLabel id="name-label">姓名</InputLabel> | |||||
| <Select | |||||
| labelId="name-label" | |||||
| <select | |||||
| id="name" | id="name" | ||||
| name="name" | name="name" | ||||
| value={formData.name} | value={formData.name} | ||||
| label="姓名" | |||||
| onChange={handleInputChange} | onChange={handleInputChange} | ||||
| className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent bg-white" | |||||
| > | > | ||||
| {nameOptions.map((option: NameOption) => ( | {nameOptions.map((option: NameOption) => ( | ||||
| <MenuItem key={option.value} value={option.value}> | |||||
| <option key={option.value} value={option.value}> | |||||
| {option.label} | {option.label} | ||||
| </MenuItem> | |||||
| </option> | |||||
| ))} | ))} | ||||
| </Select> | |||||
| </select> | |||||
| </FormControl> | </FormControl> | ||||
| <TextField | <TextField | ||||
| fullWidth | fullWidth | ||||
| id="quantity" | id="quantity" | ||||
| @@ -147,18 +158,19 @@ function EscalationComponent(): JSX.Element { | |||||
| placeholder="請輸入您的備註" | placeholder="請輸入您的備註" | ||||
| /> | /> | ||||
| <Button | |||||
| type="submit" | |||||
| variant="contained" | |||||
| color="primary" | |||||
| fullWidth | |||||
| sx={{ mt: 1 }} | |||||
| > | |||||
| 提交 | |||||
| </Button> | |||||
| <Stack direction="row" justifyContent="flex-end" gap={1}> | |||||
| <Button | |||||
| type="submit" | |||||
| variant="contained" | |||||
| color="primary" | |||||
| > | |||||
| {t("update qc info")} | |||||
| </Button> | |||||
| </Stack> | |||||
| </Box> | </Box> | ||||
| </Collapse> | </Collapse> | ||||
| </Paper> | </Paper> | ||||
| </> | |||||
| ); | ); | ||||
| } | } | ||||
| @@ -812,6 +812,7 @@ function PoInputGrid({ | |||||
| setStockInLine={setStockInLine} | setStockInLine={setStockInLine} | ||||
| setItemDetail={setModalInfo} | setItemDetail={setModalInfo} | ||||
| qc={qc} | qc={qc} | ||||
| warehouse={warehouse} | |||||
| open={newOpen} | open={newOpen} | ||||
| onClose={closeNewModal} | onClose={closeNewModal} | ||||
| itemDetail={modalInfo} | itemDetail={modalInfo} | ||||
| @@ -1,6 +1,6 @@ | |||||
| "use client"; | "use client"; | ||||
| import { PurchaseQcResult, PutawayInput } from "@/app/api/po/actions"; | |||||
| import { PurchaseQcResult, PutawayInput, PutawayLine } from "@/app/api/po/actions"; | |||||
| import { | import { | ||||
| Autocomplete, | Autocomplete, | ||||
| Box, | Box, | ||||
| @@ -50,6 +50,7 @@ import { QrCodeInfo } from "@/app/api/qrcode"; | |||||
| import { useQrCodeScannerContext } from "../QrCodeScannerProvider/QrCodeScannerProvider"; | import { useQrCodeScannerContext } from "../QrCodeScannerProvider/QrCodeScannerProvider"; | ||||
| import dayjs from "dayjs"; | import dayjs from "dayjs"; | ||||
| import arraySupport from "dayjs/plugin/arraySupport"; | import arraySupport from "dayjs/plugin/arraySupport"; | ||||
| import { dummyPutawayLine } from "./dummyQcTemplate"; | |||||
| dayjs.extend(arraySupport); | dayjs.extend(arraySupport); | ||||
| interface Props { | interface Props { | ||||
| @@ -60,11 +61,11 @@ interface Props { | |||||
| } | } | ||||
| type EntryError = | type EntryError = | ||||
| | { | | { | ||||
| [field in keyof PurchaseQcResult]?: string; | |||||
| [field in keyof PutawayLine]?: string; | |||||
| } | } | ||||
| | undefined; | | undefined; | ||||
| // type PoQcRow = TableRow<Partial<PurchaseQcResult>, EntryError>; | |||||
| type PutawayRow = TableRow<Partial<PutawayLine>, EntryError>; | |||||
| const style = { | const style = { | ||||
| position: "absolute", | position: "absolute", | ||||
| @@ -100,6 +101,7 @@ const PutawayForm: React.FC<Props> = ({ itemDetail, warehouse, disabled }) => { | |||||
| // do filtering here if any | // do filtering here if any | ||||
| return warehouse; | return warehouse; | ||||
| }, []); | }, []); | ||||
| const defaultOption = { | const defaultOption = { | ||||
| value: 0, // think think sin | value: 0, // think think sin | ||||
| label: t("Select warehouse"), | label: t("Select warehouse"), | ||||
| @@ -141,7 +143,7 @@ const PutawayForm: React.FC<Props> = ({ itemDetail, warehouse, disabled }) => { | |||||
| }, | }, | ||||
| [], | [], | ||||
| ); | ); | ||||
| console.log(watch("putawayLine")) | |||||
| // const accQty = watch("acceptedQty"); | // const accQty = watch("acceptedQty"); | ||||
| // const validateForm = useCallback(() => { | // const validateForm = useCallback(() => { | ||||
| // console.log(accQty); | // console.log(accQty); | ||||
| @@ -265,6 +267,44 @@ const PutawayForm: React.FC<Props> = ({ itemDetail, warehouse, disabled }) => { | |||||
| return undefined; | return undefined; | ||||
| }, [options]); | }, [options]); | ||||
| const columns = useMemo<GridColDef[]>( | |||||
| () => [ | |||||
| { | |||||
| field: "qty", | |||||
| headerName: t("qty"), | |||||
| flex: 1, | |||||
| // renderCell(params) { | |||||
| // return <>100</> | |||||
| // }, | |||||
| }, | |||||
| { | |||||
| field: "warehouse", | |||||
| headerName: t("warehouse"), | |||||
| flex: 1, | |||||
| // renderCell(params) { | |||||
| // return <>{filteredWarehouse[0].name}</> | |||||
| // }, | |||||
| }, | |||||
| { | |||||
| field: "printQty", | |||||
| headerName: t("printQty"), | |||||
| flex: 1, | |||||
| // renderCell(params) { | |||||
| // return <>100</> | |||||
| // }, | |||||
| }, | |||||
| ], []) | |||||
| const validation = useCallback( | |||||
| (newRow: GridRowModel<PutawayRow>): EntryError => { | |||||
| const error: EntryError = {}; | |||||
| const { qty, warehouseId, printQty } = newRow; | |||||
| return Object.keys(error).length > 0 ? error : undefined; | |||||
| }, | |||||
| [], | |||||
| ); | |||||
| 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}> | ||||
| @@ -331,8 +371,10 @@ const PutawayForm: React.FC<Props> = ({ itemDetail, warehouse, disabled }) => { | |||||
| <TextField | <TextField | ||||
| label={t("productionDate")} | label={t("productionDate")} | ||||
| fullWidth | fullWidth | ||||
| value={dayjs(itemDetail.productionDate) | |||||
| .add(-1, "month") | |||||
| value={ | |||||
| // dayjs(itemDetail.productionDate) | |||||
| dayjs() | |||||
| // .add(-1, "month") | |||||
| .format(OUTPUT_DATE_FORMAT)} | .format(OUTPUT_DATE_FORMAT)} | ||||
| disabled | disabled | ||||
| /> | /> | ||||
| @@ -341,8 +383,10 @@ const PutawayForm: React.FC<Props> = ({ itemDetail, warehouse, disabled }) => { | |||||
| <TextField | <TextField | ||||
| label={t("expiryDate")} | label={t("expiryDate")} | ||||
| fullWidth | fullWidth | ||||
| value={dayjs(itemDetail.expiryDate) | |||||
| .add(-1, "month") | |||||
| value={ | |||||
| // dayjs(itemDetail.expiryDate) | |||||
| dayjs() | |||||
| .add(20, "day") | |||||
| .format(OUTPUT_DATE_FORMAT)} | .format(OUTPUT_DATE_FORMAT)} | ||||
| disabled | disabled | ||||
| /> | /> | ||||
| @@ -364,7 +408,7 @@ const PutawayForm: React.FC<Props> = ({ itemDetail, warehouse, disabled }) => { | |||||
| /> | /> | ||||
| </FormControl> | </FormControl> | ||||
| </Grid> | </Grid> | ||||
| <Grid item xs={5.5}> | |||||
| {/* <Grid item xs={5.5}> | |||||
| <TextField | <TextField | ||||
| label={t("acceptedQty")} | label={t("acceptedQty")} | ||||
| fullWidth | fullWidth | ||||
| @@ -384,9 +428,9 @@ const PutawayForm: React.FC<Props> = ({ itemDetail, warehouse, disabled }) => { | |||||
| <Button disabled={disabled} onClick={onOpenScanner}> | <Button disabled={disabled} onClick={onOpenScanner}> | ||||
| {t("bind")} | {t("bind")} | ||||
| </Button> | </Button> | ||||
| </Grid> | |||||
| <Grid item xs={5.5}> | |||||
| {/* <Controller | |||||
| </Grid> */} | |||||
| {/* <Grid item xs={5.5}> | |||||
| <Controller | |||||
| control={control} | control={control} | ||||
| name="warehouseId" | name="warehouseId" | ||||
| render={({ field }) => { | render={({ field }) => { | ||||
| @@ -412,7 +456,7 @@ const PutawayForm: React.FC<Props> = ({ itemDetail, warehouse, disabled }) => { | |||||
| /> | /> | ||||
| ); | ); | ||||
| }} | }} | ||||
| /> */} | |||||
| /> | |||||
| <FormControl fullWidth> | <FormControl fullWidth> | ||||
| <Autocomplete | <Autocomplete | ||||
| noOptionsText={t("No Warehouse")} | noOptionsText={t("No Warehouse")} | ||||
| @@ -441,13 +485,21 @@ const PutawayForm: React.FC<Props> = ({ itemDetail, warehouse, disabled }) => { | |||||
| )} | )} | ||||
| /> | /> | ||||
| </FormControl> | </FormControl> | ||||
| </Grid> | |||||
| </Grid> */} | |||||
| <Grid | <Grid | ||||
| item | item | ||||
| xs={12} | xs={12} | ||||
| style={{ display: "flex", justifyContent: "center" }} | style={{ display: "flex", justifyContent: "center" }} | ||||
| > | > | ||||
| <QrCode content={qrContent} sx={{ width: 200, height: 200 }} /> | |||||
| {/* <QrCode content={qrContent} sx={{ width: 200, height: 200 }} /> */} | |||||
| <InputDataGrid<PutawayInput, PutawayLine, EntryError> | |||||
| apiRef={apiRef} | |||||
| checkboxSelection={false} | |||||
| _formKey={"putawayLine"} | |||||
| columns={columns} | |||||
| validateRow={validation} | |||||
| needAdd={true} | |||||
| /> | |||||
| </Grid> | </Grid> | ||||
| </Grid> | </Grid> | ||||
| {/* <Grid | {/* <Grid | ||||
| @@ -1,400 +1,71 @@ | |||||
| "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 { | |||||
| GridApiCommunity, | |||||
| GridSlotsComponentsProps, | |||||
| } from "@mui/x-data-grid/internals"; | |||||
| // 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 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; | |||||
| needAdd?: boolean; | |||||
| } | |||||
| 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; | |||||
| "use client" | |||||
| 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 QcDatagrid<T, V, E>({ | |||||
| apiRef, | |||||
| checkboxSelection = false, | |||||
| _formKey, | |||||
| columns, | |||||
| validateRow, | |||||
| needAdd, | |||||
| }: Props<T, V, E>) { | |||||
| const { | |||||
| t, | |||||
| // i18n: { language }, | |||||
| } = useTranslation("common"); | |||||
| 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 list: TableRow<V, E>[] = getValues(formKey); | |||||
| // console.log(list) | |||||
| const [rows, setRows] = useState<TableRow<V, E>[]>(() => { | |||||
| const list: TableRow<V, E>[] = getValues(formKey); | |||||
| return list && list.length > 0 ? list : []; | |||||
| }); | |||||
| // 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; | |||||
| }); | |||||
| 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 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], | |||||
| ); | |||||
| import { MutableRefObject } from "react"; | |||||
| import StyledDataGrid from "../StyledDataGrid" | |||||
| import { GridApiCommunity } from "@mui/x-data-grid/internals"; | |||||
| import { GridColDef } from "@mui/x-data-grid"; | |||||
| import { useTranslation } from "react-i18next"; | |||||
| import { dummyQCData, QcData } from "./dummyQcTemplate"; | |||||
| import { Checkbox } from "@mui/material"; | |||||
| 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]); | |||||
| interface Props { | |||||
| // apiRef: MutableRefObject<GridApiCommunity>; | |||||
| }; | |||||
| 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; | |||||
| // } | |||||
| // }; | |||||
| const QcDataGrid: React.FC<Props> = ({ | |||||
| // apiRef | |||||
| }) => { | |||||
| const { t } = useTranslation("purchaseOrder"); | |||||
| return ( | |||||
| <StyledDataGrid | |||||
| // {...props} | |||||
| // getRowId={getRowId as GridRowIdGetter<GridValidRowModel>} | |||||
| // checkbox selection | |||||
| checkboxSelection={checkboxSelection} | |||||
| disableRowSelectionOnClick={checkboxSelection} | |||||
| onRowSelectionModelChange={(newRowSelectionModel) => { | |||||
| if (checkboxSelection) { | |||||
| setRowSelectionModel(newRowSelectionModel); | |||||
| setValue("qcChecks_active", newRowSelectionModel); | |||||
| } | |||||
| }} | |||||
| rowSelectionModel={rowSelectionModel} | |||||
| apiRef={apiRef} | |||||
| rows={rows} | |||||
| columns={!checkboxSelection ? _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={ | |||||
| !checkboxSelection | |||||
| ? { | |||||
| footer: FooterToolbar, | |||||
| noRowsOverlay: NoRowsOverlay, | |||||
| } | |||||
| : undefined | |||||
| } | |||||
| slotProps={ | |||||
| !checkboxSelection && Boolean(needAdd) | |||||
| ? { | |||||
| footer: { child: footer }, | |||||
| } | |||||
| : undefined | |||||
| // slotProps={renderFooter ? { | |||||
| // footer: { child: footer }, | |||||
| // }: undefined | |||||
| } | |||||
| /> | |||||
| ); | |||||
| 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 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 QcDatagrid; | |||||
| export default QcDataGrid | |||||
| @@ -26,6 +26,7 @@ import { | |||||
| GridRenderCellParams, | GridRenderCellParams, | ||||
| GridRenderEditCellParams, | GridRenderEditCellParams, | ||||
| useGridApiRef, | useGridApiRef, | ||||
| GridRowSelectionModel, | |||||
| } from "@mui/x-data-grid"; | } from "@mui/x-data-grid"; | ||||
| import InputDataGrid from "../InputDataGrid"; | import InputDataGrid from "../InputDataGrid"; | ||||
| import { TableRow } from "../InputDataGrid/InputDataGrid"; | import { TableRow } from "../InputDataGrid/InputDataGrid"; | ||||
| @@ -39,6 +40,10 @@ import { QcItemWithChecks } from "@/app/api/qc"; | |||||
| import axios from "@/app/(main)/axios/axiosInstance"; | import axios from "@/app/(main)/axios/axiosInstance"; | ||||
| import { NEXT_PUBLIC_API_URL } from "@/config/api"; | import { NEXT_PUBLIC_API_URL } from "@/config/api"; | ||||
| import axiosInstance from "@/app/(main)/axios/axiosInstance"; | import axiosInstance from "@/app/(main)/axios/axiosInstance"; | ||||
| import EscalationComponent from "./EscalationComponent"; | |||||
| import QcDataGrid from "./QCDatagrid"; | |||||
| import StockInFormVer2 from "./StockInFormVer2"; | |||||
| import { dummyEscalationHistory } from "./dummyQcTemplate"; | |||||
| interface Props { | interface Props { | ||||
| itemDetail: StockInLine; | itemDetail: StockInLine; | ||||
| @@ -68,10 +73,24 @@ const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled }) => { | |||||
| setError, | setError, | ||||
| clearErrors, | clearErrors, | ||||
| } = useFormContext<PurchaseQCInput>(); | } = useFormContext<PurchaseQCInput>(); | ||||
| console.log(itemDetail); | |||||
| console.log(defaultValues); | |||||
| const [tabIndex, setTabIndex] = useState(0); | const [tabIndex, setTabIndex] = useState(0); | ||||
| const [rowSelectionModel, setRowSelectionModel] = useState<GridRowSelectionModel>() | |||||
| const column = useMemo<GridColDef[]>( | |||||
| () => [ | |||||
| { | |||||
| field: "escalation", | |||||
| headerName: t("escalation"), | |||||
| flex: 1, | |||||
| }, | |||||
| { | |||||
| field: "supervisor", | |||||
| headerName: t("supervisor"), | |||||
| flex: 1, | |||||
| }, | |||||
| ], [] | |||||
| ) | |||||
| const handleTabChange = useCallback<NonNullable<TabsProps["onChange"]>>( | const handleTabChange = useCallback<NonNullable<TabsProps["onChange"]>>( | ||||
| (_e, newValue) => { | (_e, newValue) => { | ||||
| setTabIndex(newValue); | setTabIndex(newValue); | ||||
| @@ -112,79 +131,89 @@ const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled }) => { | |||||
| const columns = useMemo<GridColDef[]>( | const columns = useMemo<GridColDef[]>( | ||||
| () => [ | () => [ | ||||
| // { | |||||
| // field: "qcItemId", | |||||
| // headerName: t("qc Check"), | |||||
| // flex: 1, | |||||
| // editable: !disabled, | |||||
| // valueFormatter(params) { | |||||
| // const row = params.id ? params.api.getRow<PoQcRow>(params.id) : null; | |||||
| // if (!row) { | |||||
| // return null; | |||||
| // } | |||||
| // const Qc = qc.find((q) => q.id === row.qcItemId); | |||||
| // return Qc ? `${Qc.code} - ${Qc.name}` : t("Please select QC"); | |||||
| // }, | |||||
| // renderCell(params: GridRenderCellParams<PoQcRow, number>) { | |||||
| // console.log(params.value); | |||||
| // return <TwoLineCell>{params.formattedValue}</TwoLineCell>; | |||||
| // }, | |||||
| // renderEditCell(params: GridRenderEditCellParams<PoQcRow, number>) { | |||||
| // const errorMessage = | |||||
| // params.row._error?.[params.field as keyof PurchaseQcResult]; | |||||
| // console.log(errorMessage); | |||||
| // const content = ( | |||||
| // <QcSelect | |||||
| // allQcs={qc} | |||||
| // value={params.row.qcItemId} | |||||
| // onQcSelect={async (qcItemId) => { | |||||
| // await params.api.setEditCellValue({ | |||||
| // id: params.id, | |||||
| // field: "qcItemId", | |||||
| // value: qcItemId, | |||||
| // }); | |||||
| // // await params.api.setEditCellValue({ | |||||
| // // id: params.id, | |||||
| // // field: "type", | |||||
| // // value: "determine1", | |||||
| // // }); | |||||
| // }} | |||||
| // /> | |||||
| // ); | |||||
| // return errorMessage ? ( | |||||
| // <Tooltip title={errorMessage}> | |||||
| // <Box width="100%">{content}</Box> | |||||
| // </Tooltip> | |||||
| // ) : ( | |||||
| // content | |||||
| // ); | |||||
| // }, | |||||
| // }, | |||||
| // { | |||||
| // field: "failQty", | |||||
| // headerName: t("failQty"), | |||||
| // flex: 1, | |||||
| // editable: !disabled, | |||||
| // type: "number", | |||||
| // renderEditCell(params: GridRenderEditCellParams<PoQcRow>) { | |||||
| // // const recordQty = params.row.qty | |||||
| // // if (recordQty !== undefined) { | |||||
| // // setUnrecordQty((prev) => prev - recordQty) | |||||
| // // } | |||||
| // const errorMessage = | |||||
| // params.row._error?.[params.field as keyof PurchaseQcResult]; | |||||
| // const content = <GridEditInputCell {...params} />; | |||||
| // return errorMessage ? ( | |||||
| // <Tooltip title={t(errorMessage)}> | |||||
| // <Box width="100%">{content}</Box> | |||||
| // </Tooltip> | |||||
| // ) : ( | |||||
| // content | |||||
| // ); | |||||
| // }, | |||||
| // }, | |||||
| { | { | ||||
| field: "qcItemId", | |||||
| headerName: t("qc Check"), | |||||
| field: "escalation", | |||||
| headerName: t("escalation"), | |||||
| flex: 1, | flex: 1, | ||||
| editable: !disabled, | |||||
| valueFormatter(params) { | |||||
| const row = params.id ? params.api.getRow<PoQcRow>(params.id) : null; | |||||
| if (!row) { | |||||
| return null; | |||||
| } | |||||
| const Qc = qc.find((q) => q.id === row.qcItemId); | |||||
| return Qc ? `${Qc.code} - ${Qc.name}` : t("Please select QC"); | |||||
| }, | |||||
| renderCell(params: GridRenderCellParams<PoQcRow, number>) { | |||||
| console.log(params.value); | |||||
| return <TwoLineCell>{params.formattedValue}</TwoLineCell>; | |||||
| }, | |||||
| renderEditCell(params: GridRenderEditCellParams<PoQcRow, number>) { | |||||
| const errorMessage = | |||||
| params.row._error?.[params.field as keyof PurchaseQcResult]; | |||||
| console.log(errorMessage); | |||||
| const content = ( | |||||
| <QcSelect | |||||
| allQcs={qc} | |||||
| value={params.row.qcItemId} | |||||
| onQcSelect={async (qcItemId) => { | |||||
| await params.api.setEditCellValue({ | |||||
| id: params.id, | |||||
| field: "qcItemId", | |||||
| value: qcItemId, | |||||
| }); | |||||
| // await params.api.setEditCellValue({ | |||||
| // id: params.id, | |||||
| // field: "type", | |||||
| // value: "determine1", | |||||
| // }); | |||||
| }} | |||||
| /> | |||||
| ); | |||||
| return errorMessage ? ( | |||||
| <Tooltip title={errorMessage}> | |||||
| <Box width="100%">{content}</Box> | |||||
| </Tooltip> | |||||
| ) : ( | |||||
| content | |||||
| ); | |||||
| }, | |||||
| }, | }, | ||||
| { | { | ||||
| field: "failQty", | |||||
| headerName: t("failQty"), | |||||
| field: "supervisor", | |||||
| headerName: t("supervisor"), | |||||
| flex: 1, | flex: 1, | ||||
| editable: !disabled, | |||||
| type: "number", | |||||
| renderEditCell(params: GridRenderEditCellParams<PoQcRow>) { | |||||
| // const recordQty = params.row.qty | |||||
| // if (recordQty !== undefined) { | |||||
| // setUnrecordQty((prev) => prev - recordQty) | |||||
| // } | |||||
| const errorMessage = | |||||
| params.row._error?.[params.field as keyof PurchaseQcResult]; | |||||
| const content = <GridEditInputCell {...params} />; | |||||
| return errorMessage ? ( | |||||
| <Tooltip title={t(errorMessage)}> | |||||
| <Box width="100%">{content}</Box> | |||||
| </Tooltip> | |||||
| ) : ( | |||||
| content | |||||
| ); | |||||
| }, | |||||
| }, | }, | ||||
| ], | ], | ||||
| [qc], | |||||
| [], | |||||
| ); | ); | ||||
| /// validate datagrid | /// validate datagrid | ||||
| const validation = useCallback( | const validation = useCallback( | ||||
| @@ -226,14 +255,66 @@ const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled }) => { | |||||
| spacing={2} | spacing={2} | ||||
| sx={{ mt: 0.5 }} | sx={{ mt: 0.5 }} | ||||
| > | > | ||||
| <Tabs | |||||
| value={tabIndex} | |||||
| onChange={handleTabChange} | |||||
| variant="scrollable" | |||||
| > | |||||
| <Tab label={t("QC Info")} iconPosition="end" /> | |||||
| <Tab label={t("Escalation History")} iconPosition="end" /> | |||||
| </Tabs> | |||||
| <Grid item xs={12}> | |||||
| <Tabs | |||||
| value={tabIndex} | |||||
| onChange={handleTabChange} | |||||
| variant="scrollable" | |||||
| > | |||||
| <Tab label={t("QC Info")} iconPosition="end" /> | |||||
| <Tab label={t("Escalation History")} iconPosition="end" /> | |||||
| </Tabs> | |||||
| </Grid> | |||||
| {tabIndex == 0 && ( | |||||
| <> | |||||
| <Grid item xs={12}> | |||||
| <QcDataGrid/> | |||||
| </Grid> | |||||
| <Grid item xs={4}> | |||||
| <TextField | |||||
| label={t("acceptedQty")} | |||||
| fullWidth | |||||
| /> | |||||
| </Grid> | |||||
| <Grid item xs={12}> | |||||
| <EscalationComponent forSupervisor={false}/> | |||||
| </Grid> | |||||
| </> | |||||
| )} | |||||
| {tabIndex == 1 && ( | |||||
| <> | |||||
| {/* <Grid item xs={12}> | |||||
| <StockInFormVer2 | |||||
| itemDetail={itemDetail} | |||||
| disabled={false} | |||||
| /> | |||||
| </Grid> */} | |||||
| <Grid item xs={12}> | |||||
| <Typography variant="h6" display="block" marginBlockEnd={1}> | |||||
| {t("Escalation Info")} | |||||
| </Typography> | |||||
| </Grid> | |||||
| <Grid item xs={12}> | |||||
| <StyledDataGrid | |||||
| rows={dummyEscalationHistory} | |||||
| columns={columns} | |||||
| onRowSelectionModelChange={(newRowSelectionModel) => { | |||||
| setRowSelectionModel(newRowSelectionModel); | |||||
| }} | |||||
| /> | |||||
| </Grid> | |||||
| <Grid item xs={12}> | |||||
| <Typography variant="h6" display="block" marginBlockEnd={1}> | |||||
| {t("Escalation Result")} | |||||
| </Typography> | |||||
| </Grid> | |||||
| <Grid item xs={12}> | |||||
| <EscalationComponent | |||||
| forSupervisor={true} | |||||
| /> | |||||
| </Grid> | |||||
| </> | |||||
| )} | |||||
| </Grid> | </Grid> | ||||
| </Grid> | </Grid> | ||||
| </> | </> | ||||
| @@ -11,19 +11,21 @@ import { useTranslation } from "react-i18next"; | |||||
| import StockInForm from "./StockInForm"; | 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 { dummyPutawayLine } from "./dummyQcTemplate"; | |||||
| const style = { | const style = { | ||||
| position: "absolute", | position: "absolute", | ||||
| top: "50%", | top: "50%", | ||||
| left: "50%", | left: "50%", | ||||
| transform: "translate(-50%, -50%)", | transform: "translate(-50%, -50%)", | ||||
| overflowY: "scroll", | |||||
| bgcolor: "background.paper", | bgcolor: "background.paper", | ||||
| pt: 5, | pt: 5, | ||||
| px: 5, | px: 5, | ||||
| 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%" }, | |||||
| }; | }; | ||||
| interface CommonProps extends Omit<ModalProps, "children"> { | interface CommonProps extends Omit<ModalProps, "children"> { | ||||
| @@ -60,6 +62,7 @@ const PoQcStockInModalVer2: React.FC<Props> = ({ | |||||
| qc, | qc, | ||||
| warehouse, | warehouse, | ||||
| }) => { | }) => { | ||||
| console.log(warehouse) | |||||
| const { | const { | ||||
| t, | t, | ||||
| i18n: { language }, | i18n: { language }, | ||||
| @@ -67,6 +70,7 @@ const PoQcStockInModalVer2: React.FC<Props> = ({ | |||||
| const formProps = useForm<ModalFormInput>({ | const formProps = useForm<ModalFormInput>({ | ||||
| defaultValues: { | defaultValues: { | ||||
| ...itemDetail, | ...itemDetail, | ||||
| 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 | ||||
| }, | }, | ||||
| @@ -79,11 +83,23 @@ const PoQcStockInModalVer2: React.FC<Props> = ({ | |||||
| }, | }, | ||||
| [onClose], | [onClose], | ||||
| ); | ); | ||||
| const [openPutaway, setOpenPutaway] = useState(false) | |||||
| const onOpenPutaway = useCallback(() => { | |||||
| setOpenPutaway(true); | |||||
| }, []); | |||||
| const onClosePutaway = useCallback(() => { | |||||
| setOpenPutaway(false); | |||||
| }, []); | |||||
| const [submissionType, setSubmissionType] = useState<"stockIn" | "qc" | "escalate" | undefined>(undefined) | const [submissionType, setSubmissionType] = useState<"stockIn" | "qc" | "escalate" | undefined>(undefined) | ||||
| const onSubmit = useCallback<SubmitHandler<ModalFormInput>>( | const onSubmit = useCallback<SubmitHandler<ModalFormInput>>( | ||||
| async (data, event) => { | async (data, event) => { | ||||
| console.log(event!.nativeEvent) | console.log(event!.nativeEvent) | ||||
| // closeHandler({}, "backdropClick"); | |||||
| // for now go to putaway form | |||||
| onOpenPutaway() | |||||
| // divide 3 section for this submition | // divide 3 section for this submition | ||||
| // switch (submissionType) { | // switch (submissionType) { | ||||
| // submit stock in data | // submit stock in data | ||||
| @@ -95,52 +111,113 @@ const PoQcStockInModalVer2: React.FC<Props> = ({ | |||||
| return ( | return ( | ||||
| <> | <> | ||||
| {/* {itemDetail !== undefined && ( | |||||
| <PutawayForm | |||||
| itemDetail={itemDetail} | |||||
| warehouse={warehouse!} | |||||
| disabled={false} | |||||
| /> | |||||
| )} */} | |||||
| <FormProvider {...formProps}> | <FormProvider {...formProps}> | ||||
| <Modal open={open} onClose={closeHandler} sx={{ overflowY: "scroll" }}> | |||||
| <Box | |||||
| sx={style} | |||||
| component="form" | |||||
| onSubmit={formProps.handleSubmit(onSubmit)} | |||||
| > | |||||
| <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> | |||||
| <Button | |||||
| id="qc" | |||||
| type="button" | |||||
| variant="contained" | |||||
| color="secondary" | |||||
| > | |||||
| Submit QC | |||||
| </Button> | |||||
| </Box> | |||||
| <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> | |||||
| </> | |||||
| ) : ( | |||||
| <> | |||||
| <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> | </Modal> | ||||
| </FormProvider> | </FormProvider> | ||||
| </> | </> | ||||
| @@ -1,8 +1,20 @@ | |||||
| const dummyQCData = [ | |||||
| import { PutawayLine } from "@/app/api/po/actions" | |||||
| export interface QcData { | |||||
| id: number, | |||||
| qcItem: string, | |||||
| isPassed: boolean | undefined | |||||
| isFailed: boolean | undefined | |||||
| failedQty: number | undefined | |||||
| remarks: string | undefined | |||||
| } | |||||
| export const dummyQCData: QcData[] = [ | |||||
| { | { | ||||
| id: 1, | id: 1, | ||||
| qcItem: "目測", | qcItem: "目測", | ||||
| isPassed: undefined, | isPassed: undefined, | ||||
| isFailed: undefined, | |||||
| failedQty: undefined, | failedQty: undefined, | ||||
| remarks: undefined, | remarks: undefined, | ||||
| }, | }, | ||||
| @@ -10,6 +22,7 @@ const dummyQCData = [ | |||||
| id: 2, | id: 2, | ||||
| qcItem: "目測2", | qcItem: "目測2", | ||||
| isPassed: undefined, | isPassed: undefined, | ||||
| isFailed: undefined, | |||||
| failedQty: undefined, | failedQty: undefined, | ||||
| remarks: undefined, | remarks: undefined, | ||||
| }, | }, | ||||
| @@ -17,7 +30,33 @@ const dummyQCData = [ | |||||
| id: 3, | id: 3, | ||||
| qcItem: "目測3", | qcItem: "目測3", | ||||
| isPassed: undefined, | isPassed: undefined, | ||||
| isFailed: undefined, | |||||
| failedQty: undefined, | failedQty: undefined, | ||||
| remarks: undefined, | remarks: undefined, | ||||
| }, | }, | ||||
| ] | |||||
| export interface EscalationData { | |||||
| id: number, | |||||
| escalation: string, | |||||
| supervisor: string, | |||||
| } | |||||
| export const dummyEscalationHistory: EscalationData[] = [ | |||||
| { | |||||
| id: 1, | |||||
| escalation: "上報1", | |||||
| supervisor: "陳大文" | |||||
| }, | |||||
| ] | |||||
| export const dummyPutawayLine: PutawayLine[] = [ | |||||
| { | |||||
| id: 1, | |||||
| qty: 100, | |||||
| warehouseId: 1, | |||||
| warehouse: "W001 - 憶兆 3樓A倉", | |||||
| printQty: 100 | |||||
| } | |||||
| ] | ] | ||||
| @@ -65,11 +65,11 @@ const PoSearch: React.FC<Props> = ({ | |||||
| }, | }, | ||||
| ]; | ]; | ||||
| return searchCriteria; | return searchCriteria; | ||||
| }, [t, po]); | |||||
| }, [t]); | |||||
| const onDetailClick = useCallback( | const onDetailClick = useCallback( | ||||
| (po: PoResult) => { | (po: PoResult) => { | ||||
| router.push(`/po/edit?id=${po.id}`); | |||||
| router.push(`/po/edit?id=${po.id}&start=true`); | |||||
| }, | }, | ||||
| [router], | [router], | ||||
| ); | ); | ||||
| @@ -111,7 +111,7 @@ const PoSearch: React.FC<Props> = ({ | |||||
| return "N/A" | return "N/A" | ||||
| } | } | ||||
| const items = params.itemDetail.split(",") | const items = params.itemDetail.split(",") | ||||
| return items.map((item) => <p>{item}</p>) | |||||
| return items.map((item) => <Grid key={item}>{item}</Grid>) | |||||
| }, | }, | ||||
| }, | }, | ||||
| { | { | ||||
| @@ -167,9 +167,13 @@ const PoSearch: React.FC<Props> = ({ | |||||
| setTotalCount(res.total); | setTotalCount(res.total); | ||||
| } | } | ||||
| }, | }, | ||||
| [fetchPoListClient], | |||||
| [], | |||||
| ); | ); | ||||
| useEffect(() => { | |||||
| console.log(filteredPo) | |||||
| }, [filteredPo]) | |||||
| useEffect(() => { | useEffect(() => { | ||||
| newPageFetch(pagingController, filterArgs); | newPageFetch(pagingController, filterArgs); | ||||
| }, [newPageFetch, pagingController, filterArgs]); | }, [newPageFetch, pagingController, filterArgs]); | ||||
| @@ -102,7 +102,20 @@ | |||||
| "submitStockIn": "更新來貨資料", | "submitStockIn": "更新來貨資料", | ||||
| "QC Info": "品檢資料", | "QC Info": "品檢資料", | ||||
| "Escalation History": "品檢資料", | |||||
| "Escalation History": "上報記錄", | |||||
| "Escalation Info": "上報資料", | |||||
| "Escalation Result": "上報結果", | |||||
| "update qc info": "更新品檢資料", | |||||
| "email supplier": "電郵供應商", | |||||
| "confirm putaway": "確定及上架", | |||||
| "warehouse": "倉庫", | |||||
| "qcItem": "檢查項目", | |||||
| "passed": "合格", | |||||
| "failed": "不合格", | |||||
| "failedQty": "不合格數", | |||||
| "remarks": "備註", | |||||
| "Reject": "拒絕", | "Reject": "拒絕", | ||||
| "submit": "提交", | "submit": "提交", | ||||