| @@ -65,12 +65,18 @@ export interface EscalationInput { | |||
| acceptedQty: number; // this is the qty to be escalated | |||
| // escalationQty: number | |||
| } | |||
| export interface PutawayLine { | |||
| id?: number | |||
| qty: number | |||
| warehouseId: number; | |||
| warehouse: string; | |||
| printQty: number | |||
| } | |||
| export interface PutawayInput { | |||
| status: string; | |||
| acceptedQty: number; | |||
| warehouseId: number; | |||
| // handler: string | |||
| // stockInLine: StockInLineEntry[] | |||
| putawayLine: PutawayLine[] | |||
| } | |||
| export type ModalFormInput = Partial< | |||
| @@ -14,7 +14,7 @@ export interface PoResult { | |||
| supplier: string; | |||
| estimatedArrivalDate: string; | |||
| completedDate: string; | |||
| itemDetail?: String; | |||
| itemDetail?: string; | |||
| escalated: boolean; | |||
| status: string; | |||
| pol?: PurchaseOrderLine[]; | |||
| @@ -123,11 +123,13 @@ function InputDataGrid<T, V, E>({ | |||
| [], | |||
| ); | |||
| const list: TableRow<V, E>[] = getValues(formKey); | |||
| // console.log(list) | |||
| 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 : [] | |||
| @@ -298,7 +300,8 @@ function InputDataGrid<T, V, E>({ | |||
| onClick={addRow} | |||
| size="small" | |||
| > | |||
| {t("Add Record")} | |||
| 新增 | |||
| {/* {t("Add Record")} */} | |||
| </Button> | |||
| <Button | |||
| disableRipple | |||
| @@ -307,7 +310,8 @@ function InputDataGrid<T, V, E>({ | |||
| onClick={reset} | |||
| size="small" | |||
| > | |||
| {t("Clean Record")} | |||
| {/* {t("Clean Record")} */} | |||
| 清除 | |||
| </Button> | |||
| </Box> | |||
| ); | |||
| @@ -14,10 +14,13 @@ import { | |||
| Typography, | |||
| RadioGroup, | |||
| Radio, | |||
| Stack, | |||
| Autocomplete, | |||
| } from '@mui/material'; | |||
| import { SelectChangeEvent } from '@mui/material/Select'; | |||
| import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; | |||
| import ExpandLessIcon from '@mui/icons-material/ExpandLess'; | |||
| import { useTranslation } from 'react-i18next'; | |||
| interface NameOption { | |||
| value: string; | |||
| @@ -30,7 +33,15 @@ interface FormData { | |||
| 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 [formData, setFormData] = useState<FormData>({ | |||
| name: '', | |||
| @@ -48,9 +59,9 @@ function EscalationComponent(): JSX.Element { | |||
| ]; | |||
| const handleInputChange = ( | |||
| e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement> | SelectChangeEvent<string> | |||
| event: ChangeEvent<HTMLInputElement | HTMLTextAreaElement> | SelectChangeEvent<string> | |||
| ): void => { | |||
| const { name, value } = e.target; | |||
| const { name, value } = event.target; | |||
| setFormData((prev) => ({ | |||
| ...prev, | |||
| [name]: value, | |||
| @@ -69,8 +80,10 @@ function EscalationComponent(): JSX.Element { | |||
| return ( | |||
| // <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 | |||
| control={ | |||
| <Checkbox | |||
| @@ -91,38 +104,36 @@ function EscalationComponent(): JSX.Element { | |||
| } | |||
| /> | |||
| </Box> | |||
| <Collapse in={!isCollapsed}> | |||
| <Collapse in={isCollapsed}> | |||
| <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> | |||
| <InputLabel id="name-label">姓名</InputLabel> | |||
| <Select | |||
| labelId="name-label" | |||
| <select | |||
| id="name" | |||
| name="name" | |||
| value={formData.name} | |||
| label="姓名" | |||
| 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) => ( | |||
| <MenuItem key={option.value} value={option.value}> | |||
| <option key={option.value} value={option.value}> | |||
| {option.label} | |||
| </MenuItem> | |||
| </option> | |||
| ))} | |||
| </Select> | |||
| </select> | |||
| </FormControl> | |||
| <TextField | |||
| fullWidth | |||
| id="quantity" | |||
| @@ -147,18 +158,19 @@ function EscalationComponent(): JSX.Element { | |||
| 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> | |||
| </Collapse> | |||
| </Paper> | |||
| </> | |||
| ); | |||
| } | |||
| @@ -812,6 +812,7 @@ function PoInputGrid({ | |||
| setStockInLine={setStockInLine} | |||
| setItemDetail={setModalInfo} | |||
| qc={qc} | |||
| warehouse={warehouse} | |||
| open={newOpen} | |||
| onClose={closeNewModal} | |||
| itemDetail={modalInfo} | |||
| @@ -1,6 +1,6 @@ | |||
| "use client"; | |||
| import { PurchaseQcResult, PutawayInput } from "@/app/api/po/actions"; | |||
| import { PurchaseQcResult, PutawayInput, PutawayLine } from "@/app/api/po/actions"; | |||
| import { | |||
| Autocomplete, | |||
| Box, | |||
| @@ -50,6 +50,7 @@ import { QrCodeInfo } from "@/app/api/qrcode"; | |||
| import { useQrCodeScannerContext } from "../QrCodeScannerProvider/QrCodeScannerProvider"; | |||
| import dayjs from "dayjs"; | |||
| import arraySupport from "dayjs/plugin/arraySupport"; | |||
| import { dummyPutawayLine } from "./dummyQcTemplate"; | |||
| dayjs.extend(arraySupport); | |||
| interface Props { | |||
| @@ -60,11 +61,11 @@ interface Props { | |||
| } | |||
| type EntryError = | |||
| | { | |||
| [field in keyof PurchaseQcResult]?: string; | |||
| [field in keyof PutawayLine]?: string; | |||
| } | |||
| | undefined; | |||
| // type PoQcRow = TableRow<Partial<PurchaseQcResult>, EntryError>; | |||
| type PutawayRow = TableRow<Partial<PutawayLine>, EntryError>; | |||
| const style = { | |||
| position: "absolute", | |||
| @@ -100,6 +101,7 @@ const PutawayForm: React.FC<Props> = ({ itemDetail, warehouse, disabled }) => { | |||
| // do filtering here if any | |||
| return warehouse; | |||
| }, []); | |||
| const defaultOption = { | |||
| value: 0, // think think sin | |||
| 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 validateForm = useCallback(() => { | |||
| // console.log(accQty); | |||
| @@ -265,6 +267,44 @@ const PutawayForm: React.FC<Props> = ({ itemDetail, warehouse, disabled }) => { | |||
| return undefined; | |||
| }, [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 ( | |||
| <Grid container justifyContent="flex-start" alignItems="flex-start"> | |||
| <Grid item xs={12}> | |||
| @@ -331,8 +371,10 @@ const PutawayForm: React.FC<Props> = ({ itemDetail, warehouse, disabled }) => { | |||
| <TextField | |||
| label={t("productionDate")} | |||
| fullWidth | |||
| value={dayjs(itemDetail.productionDate) | |||
| .add(-1, "month") | |||
| value={ | |||
| // dayjs(itemDetail.productionDate) | |||
| dayjs() | |||
| // .add(-1, "month") | |||
| .format(OUTPUT_DATE_FORMAT)} | |||
| disabled | |||
| /> | |||
| @@ -341,8 +383,10 @@ const PutawayForm: React.FC<Props> = ({ itemDetail, warehouse, disabled }) => { | |||
| <TextField | |||
| label={t("expiryDate")} | |||
| fullWidth | |||
| value={dayjs(itemDetail.expiryDate) | |||
| .add(-1, "month") | |||
| value={ | |||
| // dayjs(itemDetail.expiryDate) | |||
| dayjs() | |||
| .add(20, "day") | |||
| .format(OUTPUT_DATE_FORMAT)} | |||
| disabled | |||
| /> | |||
| @@ -364,7 +408,7 @@ const PutawayForm: React.FC<Props> = ({ itemDetail, warehouse, disabled }) => { | |||
| /> | |||
| </FormControl> | |||
| </Grid> | |||
| <Grid item xs={5.5}> | |||
| {/* <Grid item xs={5.5}> | |||
| <TextField | |||
| label={t("acceptedQty")} | |||
| fullWidth | |||
| @@ -384,9 +428,9 @@ const PutawayForm: React.FC<Props> = ({ itemDetail, warehouse, disabled }) => { | |||
| <Button disabled={disabled} onClick={onOpenScanner}> | |||
| {t("bind")} | |||
| </Button> | |||
| </Grid> | |||
| <Grid item xs={5.5}> | |||
| {/* <Controller | |||
| </Grid> */} | |||
| {/* <Grid item xs={5.5}> | |||
| <Controller | |||
| control={control} | |||
| name="warehouseId" | |||
| render={({ field }) => { | |||
| @@ -412,7 +456,7 @@ const PutawayForm: React.FC<Props> = ({ itemDetail, warehouse, disabled }) => { | |||
| /> | |||
| ); | |||
| }} | |||
| /> */} | |||
| /> | |||
| <FormControl fullWidth> | |||
| <Autocomplete | |||
| noOptionsText={t("No Warehouse")} | |||
| @@ -441,13 +485,21 @@ const PutawayForm: React.FC<Props> = ({ itemDetail, warehouse, disabled }) => { | |||
| )} | |||
| /> | |||
| </FormControl> | |||
| </Grid> | |||
| </Grid> */} | |||
| <Grid | |||
| item | |||
| xs={12} | |||
| 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 | |||
| @@ -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, | |||
| GridRenderEditCellParams, | |||
| useGridApiRef, | |||
| GridRowSelectionModel, | |||
| } from "@mui/x-data-grid"; | |||
| import InputDataGrid from "../InputDataGrid"; | |||
| import { TableRow } from "../InputDataGrid/InputDataGrid"; | |||
| @@ -39,6 +40,10 @@ import { QcItemWithChecks } from "@/app/api/qc"; | |||
| import axios from "@/app/(main)/axios/axiosInstance"; | |||
| import { NEXT_PUBLIC_API_URL } from "@/config/api"; | |||
| 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 { | |||
| itemDetail: StockInLine; | |||
| @@ -68,10 +73,24 @@ const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled }) => { | |||
| setError, | |||
| clearErrors, | |||
| } = useFormContext<PurchaseQCInput>(); | |||
| console.log(itemDetail); | |||
| console.log(defaultValues); | |||
| 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"]>>( | |||
| (_e, newValue) => { | |||
| setTabIndex(newValue); | |||
| @@ -112,79 +131,89 @@ const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled }) => { | |||
| 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, | |||
| 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, | |||
| 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 | |||
| const validation = useCallback( | |||
| @@ -226,14 +255,66 @@ const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled }) => { | |||
| spacing={2} | |||
| 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> | |||
| </> | |||
| @@ -11,19 +11,21 @@ import { useTranslation } from "react-i18next"; | |||
| import StockInForm from "./StockInForm"; | |||
| import StockInFormVer2 from "./StockInFormVer2"; | |||
| import QcFormVer2 from "./QcFormVer2"; | |||
| import PutawayForm from "./PutawayForm"; | |||
| import { dummyPutawayLine } from "./dummyQcTemplate"; | |||
| const style = { | |||
| position: "absolute", | |||
| top: "50%", | |||
| left: "50%", | |||
| transform: "translate(-50%, -50%)", | |||
| overflowY: "scroll", | |||
| bgcolor: "background.paper", | |||
| pt: 5, | |||
| px: 5, | |||
| pb: 10, | |||
| display: "block", | |||
| width: { xs: "60%", sm: "60%", md: "60%" }, | |||
| // height: { xs: "60%", sm: "60%", md: "60%" }, | |||
| }; | |||
| interface CommonProps extends Omit<ModalProps, "children"> { | |||
| @@ -60,6 +62,7 @@ const PoQcStockInModalVer2: React.FC<Props> = ({ | |||
| qc, | |||
| warehouse, | |||
| }) => { | |||
| console.log(warehouse) | |||
| const { | |||
| t, | |||
| i18n: { language }, | |||
| @@ -67,6 +70,7 @@ const PoQcStockInModalVer2: React.FC<Props> = ({ | |||
| const formProps = useForm<ModalFormInput>({ | |||
| defaultValues: { | |||
| ...itemDetail, | |||
| putawayLine: dummyPutawayLine | |||
| // receiptDate: itemDetail.receiptDate || dayjs().add(-1, "month").format(INPUT_DATE_FORMAT), | |||
| // warehouseId: itemDetail.defaultWarehouseId || 0 | |||
| }, | |||
| @@ -79,11 +83,23 @@ const PoQcStockInModalVer2: React.FC<Props> = ({ | |||
| }, | |||
| [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 onSubmit = useCallback<SubmitHandler<ModalFormInput>>( | |||
| 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 | |||
| @@ -95,52 +111,113 @@ const PoQcStockInModalVer2: React.FC<Props> = ({ | |||
| return ( | |||
| <> | |||
| {/* {itemDetail !== undefined && ( | |||
| <PutawayForm | |||
| itemDetail={itemDetail} | |||
| warehouse={warehouse!} | |||
| disabled={false} | |||
| /> | |||
| )} */} | |||
| <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> | |||
| </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, | |||
| qcItem: "目測", | |||
| isPassed: undefined, | |||
| isFailed: undefined, | |||
| failedQty: undefined, | |||
| remarks: undefined, | |||
| }, | |||
| @@ -10,6 +22,7 @@ const dummyQCData = [ | |||
| id: 2, | |||
| qcItem: "目測2", | |||
| isPassed: undefined, | |||
| isFailed: undefined, | |||
| failedQty: undefined, | |||
| remarks: undefined, | |||
| }, | |||
| @@ -17,7 +30,33 @@ const dummyQCData = [ | |||
| id: 3, | |||
| qcItem: "目測3", | |||
| isPassed: undefined, | |||
| isFailed: undefined, | |||
| failedQty: 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; | |||
| }, [t, po]); | |||
| }, [t]); | |||
| const onDetailClick = useCallback( | |||
| (po: PoResult) => { | |||
| router.push(`/po/edit?id=${po.id}`); | |||
| router.push(`/po/edit?id=${po.id}&start=true`); | |||
| }, | |||
| [router], | |||
| ); | |||
| @@ -111,7 +111,7 @@ const PoSearch: React.FC<Props> = ({ | |||
| return "N/A" | |||
| } | |||
| 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); | |||
| } | |||
| }, | |||
| [fetchPoListClient], | |||
| [], | |||
| ); | |||
| useEffect(() => { | |||
| console.log(filteredPo) | |||
| }, [filteredPo]) | |||
| useEffect(() => { | |||
| newPageFetch(pagingController, filterArgs); | |||
| }, [newPageFetch, pagingController, filterArgs]); | |||
| @@ -102,7 +102,20 @@ | |||
| "submitStockIn": "更新來貨資料", | |||
| "QC Info": "品檢資料", | |||
| "Escalation History": "品檢資料", | |||
| "Escalation History": "上報記錄", | |||
| "Escalation Info": "上報資料", | |||
| "Escalation Result": "上報結果", | |||
| "update qc info": "更新品檢資料", | |||
| "email supplier": "電郵供應商", | |||
| "confirm putaway": "確定及上架", | |||
| "warehouse": "倉庫", | |||
| "qcItem": "檢查項目", | |||
| "passed": "合格", | |||
| "failed": "不合格", | |||
| "failedQty": "不合格數", | |||
| "remarks": "備註", | |||
| "Reject": "拒絕", | |||
| "submit": "提交", | |||