@@ -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": "提交", | ||||