| @@ -7,6 +7,7 @@ import { cache } from "react"; | |||||
| export interface EscalationResult { | export interface EscalationResult { | ||||
| id: number; | id: number; | ||||
| submitter?: string; | |||||
| handler?: string; | handler?: string; | ||||
| handlerDepartment?: string; | handlerDepartment?: string; | ||||
| handlerName?: string; | handlerName?: string; | ||||
| @@ -38,7 +38,8 @@ export interface StockInLineEntry { | |||||
| } | } | ||||
| export interface PurchaseQcResult{ | export interface PurchaseQcResult{ | ||||
| id: number; | |||||
| id?: number; | |||||
| qcItemId: number; | |||||
| qcPassed?: boolean; | qcPassed?: boolean; | ||||
| failQty?: number; | failQty?: number; | ||||
| remarks?: string; | remarks?: string; | ||||
| @@ -16,7 +16,8 @@ export interface QcItemWithChecks { | |||||
| } | } | ||||
| export interface QcData { | export interface QcData { | ||||
| id: number, | |||||
| id?: number, | |||||
| qcItemId: number, | |||||
| code: string, | code: string, | ||||
| name: string, | name: string, | ||||
| qcDescription: string, | qcDescription: string, | ||||
| @@ -104,7 +104,7 @@ export const adminChangePassword = async (data: any) => { | |||||
| }; | }; | ||||
| export const fetchEscalationCombo = async () => { | export const fetchEscalationCombo = async () => { | ||||
| return serverFetchJson<EscalationCombo>(`${BASE_API_URL}/user/escalation-combo`, { | |||||
| return serverFetchJson<EscalationCombo[]>(`${BASE_API_URL}/user/escalation-combo`, { | |||||
| next: { tags: ["escalationCombo"]} | next: { tags: ["escalationCombo"]} | ||||
| }) | }) | ||||
| }; | }; | ||||
| @@ -89,6 +89,13 @@ const EscalationLogTable: React.FC<Props> = ({ | |||||
| const columns_dashboard = useMemo<Column<EscalationResult>[]>( | const columns_dashboard = useMemo<Column<EscalationResult>[]>( | ||||
| () => [ | () => [ | ||||
| { | |||||
| name: "submitter", | |||||
| label: t("escalateFrom"), | |||||
| align: "left", | |||||
| headerAlign: "left", | |||||
| sx: { width: "15%", minWidth: 100 }, | |||||
| }, | |||||
| { | { | ||||
| name: "poCode", | name: "poCode", | ||||
| label: t("Po Code"), | label: t("Po Code"), | ||||
| @@ -138,6 +145,13 @@ const EscalationLogTable: React.FC<Props> = ({ | |||||
| const columns_qc = useMemo<Column<EscalationResult>[]>( | const columns_qc = useMemo<Column<EscalationResult>[]>( | ||||
| () => [ | () => [ | ||||
| { | |||||
| name: "submitter", | |||||
| label: t("escalateFrom"), | |||||
| align: "left", | |||||
| headerAlign: "left", | |||||
| sx: { width: "15%", minWidth: 100 }, | |||||
| }, | |||||
| { | { | ||||
| name: "handler", | name: "handler", | ||||
| label: t("Responsible for handling colleagues"), | label: t("Responsible for handling colleagues"), | ||||
| @@ -153,12 +167,12 @@ const EscalationLogTable: React.FC<Props> = ({ | |||||
| }, | }, | ||||
| { | { | ||||
| name: "recordDate", | name: "recordDate", | ||||
| label: t("escalated date"), | |||||
| label: t("escalated datetime"), | |||||
| align: "right", | align: "right", | ||||
| headerAlign: "right", | headerAlign: "right", | ||||
| sx: { width: "10%", minWidth: 100 }, | sx: { width: "10%", minWidth: 100 }, | ||||
| renderCell: (params) => { | renderCell: (params) => { | ||||
| return arrayToDateString(params.recordDate); | |||||
| return arrayToDateTimeString(params.recordDate); | |||||
| } | } | ||||
| }, | }, | ||||
| { | { | ||||
| @@ -24,6 +24,7 @@ import { useTranslation } from 'react-i18next'; | |||||
| import { Controller, useFormContext } from 'react-hook-form'; | import { Controller, useFormContext } from 'react-hook-form'; | ||||
| import { EscalationInput, ModalFormInput } from '@/app/api/po/actions'; | import { EscalationInput, ModalFormInput } from '@/app/api/po/actions'; | ||||
| import { EscalationCombo } from "@/app/api/user"; | import { EscalationCombo } from "@/app/api/user"; | ||||
| import { fetchEscalationCombo } from "@/app/api/user/actions"; | |||||
| import { FireExtinguisher } from '@mui/icons-material'; | import { FireExtinguisher } from '@mui/icons-material'; | ||||
| interface NameOption { | interface NameOption { | ||||
| @@ -41,14 +42,14 @@ interface Props { | |||||
| forSupervisor: boolean | forSupervisor: boolean | ||||
| isCollapsed: boolean | isCollapsed: boolean | ||||
| setIsCollapsed: Dispatch<React.SetStateAction<boolean>> | setIsCollapsed: Dispatch<React.SetStateAction<boolean>> | ||||
| escalationCombo: EscalationCombo[] | |||||
| // escalationCombo: EscalationCombo[] | |||||
| } | } | ||||
| const EscalationComponent: React.FC<Props> = ({ | const EscalationComponent: React.FC<Props> = ({ | ||||
| forSupervisor, | forSupervisor, | ||||
| isCollapsed, | isCollapsed, | ||||
| setIsCollapsed, | setIsCollapsed, | ||||
| escalationCombo | |||||
| // escalationCombo | |||||
| }) => { | }) => { | ||||
| const { t } = useTranslation("purchaseOrder"); | const { t } = useTranslation("purchaseOrder"); | ||||
| @@ -58,6 +59,18 @@ const EscalationComponent: React.FC<Props> = ({ | |||||
| message: '', | message: '', | ||||
| }); | }); | ||||
| const [escalationCombo, setEscalationCombo] = useState<EscalationCombo[]>([]); | |||||
| useEffect(() => { | |||||
| async function fetchData() { | |||||
| if (escalationCombo.length < 1) { | |||||
| const escCombo = await fetchEscalationCombo(); | |||||
| setEscalationCombo(escCombo); | |||||
| } | |||||
| } | |||||
| fetchData(); | |||||
| }, []) | |||||
| const nameOptions: NameOption[] = [ | const nameOptions: NameOption[] = [ | ||||
| { value: '', label: '請選擇姓名...' }, | { value: '', label: '請選擇姓名...' }, | ||||
| { value: 'john', label: '張大明' }, | { value: 'john', label: '張大明' }, | ||||
| @@ -89,7 +89,6 @@ type Props = { | |||||
| qc: QcItemWithChecks[]; | qc: QcItemWithChecks[]; | ||||
| warehouse: WarehouseResult[]; | warehouse: WarehouseResult[]; | ||||
| printerCombo: PrinterCombo[]; | printerCombo: PrinterCombo[]; | ||||
| escalationCombo: EscalationCombo[]; | |||||
| }; | }; | ||||
| type EntryError = | type EntryError = | ||||
| @@ -192,7 +191,7 @@ interface PolInputResult { | |||||
| dnQty: number, | dnQty: number, | ||||
| } | } | ||||
| const PoDetail: React.FC<Props> = ({ po, qc, warehouse, printerCombo, escalationCombo }) => { | |||||
| const PoDetail: React.FC<Props> = ({ po, qc, warehouse, printerCombo }) => { | |||||
| const cameras = useContext(CameraContext); | const cameras = useContext(CameraContext); | ||||
| // console.log(cameras); | // console.log(cameras); | ||||
| const { t } = useTranslation("purchaseOrder"); | const { t } = useTranslation("purchaseOrder"); | ||||
| @@ -865,7 +864,6 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse, printerCombo, escalation | |||||
| fetchPoDetail={fetchPoDetail} | fetchPoDetail={fetchPoDetail} | ||||
| handleMailTemplateForStockInLine={handleMailTemplateForStockInLine} | handleMailTemplateForStockInLine={handleMailTemplateForStockInLine} | ||||
| printerCombo={printerCombo} | printerCombo={printerCombo} | ||||
| escalationCombo={escalationCombo} | |||||
| /> | /> | ||||
| </Box> | </Box> | ||||
| </TableCell> | </TableCell> | ||||
| @@ -27,17 +27,15 @@ const PoDetailWrapper: React.FC<Props> & SubComponents = async ({ id }) => { | |||||
| warehouse, | warehouse, | ||||
| qc, | qc, | ||||
| printerCombo, | printerCombo, | ||||
| escalationCombo, | |||||
| ] = await Promise.all([ | ] = await Promise.all([ | ||||
| fetchPoWithStockInLines(id), | fetchPoWithStockInLines(id), | ||||
| fetchWarehouseList(), | fetchWarehouseList(), | ||||
| fetchQcItemCheck(), | fetchQcItemCheck(), | ||||
| fetchPrinterCombo(), | fetchPrinterCombo(), | ||||
| fetchEscalationCombo() | |||||
| ]); | ]); | ||||
| // const poWithStockInLine = await fetchPoWithStockInLines(id) | // const poWithStockInLine = await fetchPoWithStockInLines(id) | ||||
| console.log("%c pol:", "color:green", poWithStockInLine); | console.log("%c pol:", "color:green", poWithStockInLine); | ||||
| return <PoDetail po={poWithStockInLine} qc={qc} warehouse={warehouse} printerCombo={printerCombo} escalationCombo={escalationCombo}/>; | |||||
| return <PoDetail po={poWithStockInLine} qc={qc} warehouse={warehouse} printerCombo={printerCombo} />; | |||||
| }; | }; | ||||
| PoDetailWrapper.Loading = PoDetailLoading; | PoDetailWrapper.Loading = PoDetailLoading; | ||||
| @@ -83,7 +83,6 @@ interface Props { | |||||
| fetchPoDetail: (poId: string) => void; | fetchPoDetail: (poId: string) => void; | ||||
| handleMailTemplateForStockInLine: (stockInLineId: number) => void; | handleMailTemplateForStockInLine: (stockInLineId: number) => void; | ||||
| printerCombo: PrinterCombo[]; | printerCombo: PrinterCombo[]; | ||||
| escalationCombo: EscalationCombo[]; | |||||
| } | } | ||||
| export type StockInLineEntryError = { | export type StockInLineEntryError = { | ||||
| @@ -125,7 +124,6 @@ function PoInputGrid({ | |||||
| fetchPoDetail, | fetchPoDetail, | ||||
| handleMailTemplateForStockInLine, | handleMailTemplateForStockInLine, | ||||
| printerCombo, | printerCombo, | ||||
| escalationCombo | |||||
| }: Props) { | }: Props) { | ||||
| const { t } = useTranslation("purchaseOrder"); | const { t } = useTranslation("purchaseOrder"); | ||||
| @@ -307,7 +305,6 @@ const closeNewModal = useCallback(() => { | |||||
| const handleNewQC = useCallback( | const handleNewQC = useCallback( | ||||
| (id: GridRowId, params: any) => async() => { | (id: GridRowId, params: any) => async() => { | ||||
| // console.log(id) | // console.log(id) | ||||
| console.log("params", params.row) | |||||
| // setBtnIsLoading(true); | // setBtnIsLoading(true); | ||||
| setRowModesModel((prev) => ({ | setRowModesModel((prev) => ({ | ||||
| ...prev, | ...prev, | ||||
| @@ -955,7 +952,6 @@ const closeNewModal = useCallback(() => { | |||||
| itemDetail={modalInfo} | itemDetail={modalInfo} | ||||
| handleMailTemplateForStockInLine={handleMailTemplateForStockInLine} | handleMailTemplateForStockInLine={handleMailTemplateForStockInLine} | ||||
| printerCombo={printerCombo} | printerCombo={printerCombo} | ||||
| escalationCombo={escalationCombo} | |||||
| /> | /> | ||||
| </> | </> | ||||
| ) | ) | ||||
| @@ -60,9 +60,8 @@ import CollapsibleCard from "../CollapsibleCard/CollapsibleCard"; | |||||
| interface Props { | interface Props { | ||||
| itemDetail: StockInLine & { qcResult?: PurchaseQcResult[] } & { escResult?: EscalationResult[] }; | itemDetail: StockInLine & { qcResult?: PurchaseQcResult[] } & { escResult?: EscalationResult[] }; | ||||
| qc: QcItemWithChecks[]; | |||||
| // qc: QcItemWithChecks[]; | |||||
| disabled: boolean; | disabled: boolean; | ||||
| escalationCombo: EscalationCombo[]; | |||||
| // qcItems: QcData[] | // qcItems: QcData[] | ||||
| // setQcItems: Dispatch<SetStateAction<QcData[]>> | // setQcItems: Dispatch<SetStateAction<QcData[]>> | ||||
| } | } | ||||
| @@ -75,7 +74,7 @@ type EntryError = | |||||
| type QcRow = TableRow<Partial<QcData>, EntryError>; | type QcRow = TableRow<Partial<QcData>, EntryError>; | ||||
| // fetchQcItemCheck | // fetchQcItemCheck | ||||
| const QcComponent: React.FC<Props> = ({ qc, itemDetail, disabled = false, escalationCombo }) => { | |||||
| const QcComponent: React.FC<Props> = ({ itemDetail, disabled = false }) => { | |||||
| const { t } = useTranslation("purchaseOrder"); | const { t } = useTranslation("purchaseOrder"); | ||||
| const apiRef = useGridApiRef(); | const apiRef = useGridApiRef(); | ||||
| const { | const { | ||||
| @@ -94,12 +93,16 @@ const QcComponent: React.FC<Props> = ({ qc, itemDetail, disabled = false, escala | |||||
| const [tabIndex, setTabIndex] = useState(0); | const [tabIndex, setTabIndex] = useState(0); | ||||
| const [rowSelectionModel, setRowSelectionModel] = useState<GridRowSelectionModel>(); | const [rowSelectionModel, setRowSelectionModel] = useState<GridRowSelectionModel>(); | ||||
| const [escalationHistory, setEscalationHistory] = useState(dummyEscalationHistory); | const [escalationHistory, setEscalationHistory] = useState(dummyEscalationHistory); | ||||
| // const [qcResult, setQcResult] = useState(); | |||||
| const qcAccept = watch("qcAccept"); | const qcAccept = watch("qcAccept"); | ||||
| const qcDecision = watch("qcDecision"); //WIP | const qcDecision = watch("qcDecision"); //WIP | ||||
| // const qcResult = useMemo(() => [...watch("qcResult")], [watch("qcResult")]); | // const qcResult = useMemo(() => [...watch("qcResult")], [watch("qcResult")]); | ||||
| const qcResult = [...watch("qcResult")]; | |||||
| const qcRecord = useMemo(() => { // Need testing | |||||
| const value = watch('qcResult'); console.log("%c QC update!", "color:green", value); | |||||
| return Array.isArray(value) ? [...value] : []; | |||||
| }, [watch('qcResult')]); | |||||
| const [qcHistory, setQcHistory] = useState<PurchaseQcResult[]>([]); | const [qcHistory, setQcHistory] = useState<PurchaseQcResult[]>([]); | ||||
| const [qcResult, setQcResult] = useState<PurchaseQcResult[]>([]); | |||||
| // const [qcAccept, setQcAccept] = useState(true); | // const [qcAccept, setQcAccept] = useState(true); | ||||
| // const [qcItems, setQcItems] = useState(dummyQCData) | // const [qcItems, setQcItems] = useState(dummyQCData) | ||||
| @@ -118,12 +121,16 @@ const QcComponent: React.FC<Props> = ({ qc, itemDetail, disabled = false, escala | |||||
| }, | }, | ||||
| ], [] | ], [] | ||||
| ) | ) | ||||
| const handleTabChange = useCallback<NonNullable<TabsProps["onChange"]>>( | |||||
| (_e, newValue) => { | |||||
| setTabIndex(newValue); | |||||
| }, | |||||
| [], | |||||
| ); | |||||
| const handleTabChange = useCallback<NonNullable<TabsProps["onChange"]>>( | |||||
| (_e, newValue) => { | |||||
| setTabIndex(newValue); | |||||
| }, | |||||
| [], | |||||
| ); | |||||
| const isExist = (data : string | number | undefined) => { | |||||
| return (data !== null && data !== undefined); | |||||
| } | |||||
| // W I P // | // W I P // | ||||
| const validateFieldFail = (field : FieldPath<PurchaseQCInput>, condition: boolean, message: string) : boolean => { | const validateFieldFail = (field : FieldPath<PurchaseQCInput>, condition: boolean, message: string) : boolean => { | ||||
| @@ -225,6 +232,10 @@ const QcComponent: React.FC<Props> = ({ qc, itemDetail, disabled = false, escala | |||||
| return <Checkbox checked={!!value} onChange={handleChange} sx={{ p: 0 }} />; | return <Checkbox checked={!!value} onChange={handleChange} sx={{ p: 0 }} />; | ||||
| } | } | ||||
| const qcDisabled = (row : PurchaseQcResult) => { | |||||
| return disabled || isExist(row.escalationLogId); | |||||
| }; | |||||
| const qcColumns: GridColDef[] = useMemo(() => [ | const qcColumns: GridColDef[] = useMemo(() => [ | ||||
| { | { | ||||
| field: "code", | field: "code", | ||||
| @@ -245,7 +256,7 @@ const QcComponent: React.FC<Props> = ({ qc, itemDetail, disabled = false, escala | |||||
| flex: 1.5, | flex: 1.5, | ||||
| renderCell: (params) => { | renderCell: (params) => { | ||||
| const rowValue = params.row; | const rowValue = params.row; | ||||
| const index = params.api.getRowIndexRelativeToVisibleRows(params.id); | |||||
| const index = Number(params.id);//params.api.getRowIndexRelativeToVisibleRows(params.id); | |||||
| // console.log(rowValue.row); | // console.log(rowValue.row); | ||||
| return ( | return ( | ||||
| <FormControl> | <FormControl> | ||||
| @@ -267,7 +278,7 @@ const QcComponent: React.FC<Props> = ({ qc, itemDetail, disabled = false, escala | |||||
| value="true" | value="true" | ||||
| control={<Radio />} | control={<Radio />} | ||||
| label="合格" | label="合格" | ||||
| disabled={disabled || itemDetail.status == "escalated"} | |||||
| disabled={qcDisabled(rowValue)} | |||||
| sx={{ | sx={{ | ||||
| color: rowValue.qcPassed === true ? "green" : "inherit", | color: rowValue.qcPassed === true ? "green" : "inherit", | ||||
| "& .Mui-checked": {color: "green"} | "& .Mui-checked": {color: "green"} | ||||
| @@ -277,7 +288,7 @@ const QcComponent: React.FC<Props> = ({ qc, itemDetail, disabled = false, escala | |||||
| value="false" | value="false" | ||||
| control={<Radio />} | control={<Radio />} | ||||
| label="不合格" | label="不合格" | ||||
| disabled={disabled || itemDetail.status == "escalated"} | |||||
| disabled={qcDisabled(rowValue)} | |||||
| sx={{ | sx={{ | ||||
| color: rowValue.qcPassed === false ? "red" : "inherit", | color: rowValue.qcPassed === false ? "red" : "inherit", | ||||
| "& .Mui-checked": {color: "red"} | "& .Mui-checked": {color: "red"} | ||||
| @@ -294,22 +305,27 @@ const QcComponent: React.FC<Props> = ({ qc, itemDetail, disabled = false, escala | |||||
| flex: 1, | flex: 1, | ||||
| // editable: true, | // editable: true, | ||||
| renderCell: (params) => { | renderCell: (params) => { | ||||
| const index = params.api.getRowIndexRelativeToVisibleRows(params.id); | |||||
| const index = Number(params.id);//params.api.getRowIndexRelativeToVisibleRows(params.id); | |||||
| return ( | return ( | ||||
| <TextField | <TextField | ||||
| type="number" | type="number" | ||||
| size="small" | size="small" | ||||
| value={!params.row.qcPassed? params.value : '0'} | value={!params.row.qcPassed? params.value : '0'} | ||||
| disabled={params.row.qcPassed || disabled || itemDetail.status == "escalated"} | |||||
| onBlur={(e) => { | |||||
| disabled={params.row.qcPassed || qcDisabled(params.row)} | |||||
| /* TODO improve */ | |||||
| /* Reference: https://grok.com/share/c2hhcmQtNA%3D%3D_10787069-3eec-40af-a7cc-bacbdb86bf05 */ | |||||
| onChange={(e) => { | |||||
| const v = e.target.value; | const v = e.target.value; | ||||
| const next = v === '' ? undefined : Number(v); | const next = v === '' ? undefined : Number(v); | ||||
| if (Number.isNaN(next)) return; | if (Number.isNaN(next)) return; | ||||
| // setQcItems((prev) => | |||||
| // prev.map((r) => (r.id === params.id ? { ...r, failQty: next } : r)) | |||||
| // ); | |||||
| setValue(`qcResult.${index}.failQty`, next); | setValue(`qcResult.${index}.failQty`, next); | ||||
| }} | }} | ||||
| // onBlur={(e) => { | |||||
| // const v = e.target.value; | |||||
| // const next = v === '' ? undefined : Number(v); | |||||
| // if (Number.isNaN(next)) return; | |||||
| // setValue(`qcResult.${index}.failQty`, next); | |||||
| // }} | |||||
| onClick={(e) => e.stopPropagation()} | onClick={(e) => e.stopPropagation()} | ||||
| onMouseDown={(e) => e.stopPropagation()} | onMouseDown={(e) => e.stopPropagation()} | ||||
| onKeyDown={(e) => e.stopPropagation()} | onKeyDown={(e) => e.stopPropagation()} | ||||
| @@ -324,12 +340,12 @@ const QcComponent: React.FC<Props> = ({ qc, itemDetail, disabled = false, escala | |||||
| headerName: t("remarks"), | headerName: t("remarks"), | ||||
| flex: 2, | flex: 2, | ||||
| renderCell: (params) => { | renderCell: (params) => { | ||||
| const index = params.api.getRowIndexRelativeToVisibleRows(params.id); | |||||
| const index = Number(params.id);//params.api.getRowIndexRelativeToVisibleRows(params.id); | |||||
| return ( | return ( | ||||
| <TextField | <TextField | ||||
| size="small" | size="small" | ||||
| defaultValue={params.value} | defaultValue={params.value} | ||||
| disabled={disabled || itemDetail.status == "escalated"} | |||||
| disabled={qcDisabled(params.row)} | |||||
| onBlur={(e) => { | onBlur={(e) => { | ||||
| const value = e.target.value; | const value = e.target.value; | ||||
| setValue(`qcResult.${index}.remarks`, value); | setValue(`qcResult.${index}.remarks`, value); | ||||
| @@ -365,15 +381,31 @@ const QcComponent: React.FC<Props> = ({ qc, itemDetail, disabled = false, escala | |||||
| }, [itemDetail?.demandQty, itemDetail?.acceptedQty, setValue]); | }, [itemDetail?.demandQty, itemDetail?.acceptedQty, setValue]); | ||||
| useEffect(() => { | useEffect(() => { | ||||
| // console.log("Qc Result updated:", qcResult); | |||||
| if (qcResult.length < 1) { // New QC | |||||
| const mutableQcData = dummyQCData; | |||||
| // const mutableQcData = JSON.parse(JSON.stringify(dummyQCData)); | |||||
| // replace([mutableQcData]); | |||||
| setValue("qcResult", mutableQcData); | |||||
| // setValue("qcResult.0.qcPassed", false); | |||||
| console.log("%c Qc Record updated:", "color:red", qcRecord); | |||||
| if (qcRecord.length < 1) { // New QC | |||||
| const fetchedQcData = dummyQCData; //TODO fetch from DB | |||||
| setValue("qcResult", fetchedQcData); | |||||
| } else { | |||||
| if (itemDetail.status == "escalated") { // Copy the previous QC data for editing | |||||
| if (qcRecord.find((qc) => !isExist(qc.escalationLogId)) === undefined) { | |||||
| const copiedQcData = qcRecord.map(qc => ({ ...qc, escalationLogId: undefined })); | |||||
| const mutableQcData = [...qcRecord, ...copiedQcData]; | |||||
| setValue("qcResult", mutableQcData); | |||||
| } | |||||
| } | |||||
| if (qcRecord.length > 0) { | |||||
| if (qcResult.length < 1) { // Set QC Result | |||||
| const filteredQcResult = qcRecord.filter((qc) => !isExist(qc.escalationLogId)); | |||||
| setQcResult(filteredQcResult); | |||||
| } | |||||
| if (qcHistory.length < 1) { // Set QC History | |||||
| const filteredQcHistory = qcRecord.filter((qc) => isExist(qc.escalationLogId)); | |||||
| setQcHistory(filteredQcHistory); | |||||
| } | |||||
| } | |||||
| } | } | ||||
| }, [qcResult, setValue]) | |||||
| }, [qcRecord, setValue]) | |||||
| // const [openCollapse, setOpenCollapse] = useState(false) | // const [openCollapse, setOpenCollapse] = useState(false) | ||||
| const [isCollapsed, setIsCollapsed] = useState<boolean>(true); | const [isCollapsed, setIsCollapsed] = useState<boolean>(true); | ||||
| @@ -415,16 +447,16 @@ const QcComponent: React.FC<Props> = ({ qc, itemDetail, disabled = false, escala | |||||
| } | } | ||||
| } | } | ||||
| useEffect(() => { | |||||
| if (qcHistory.length < 1) { | |||||
| setQcHistory(qcResult.filter((qc) => {qc.escalationLogId != null})); | |||||
| console.log("QC History updated:", qcHistory); | |||||
| } | |||||
| }, [watch("qcResult")]); | |||||
| // }, [watch("qcResult")]); | |||||
| // useEffect(() => { | // useEffect(() => { | ||||
| // // onFailedOpenCollapse(qcItems) | // // onFailedOpenCollapse(qcItems) | ||||
| // }, [qcItems]); | // }, [qcItems]); | ||||
| const getRowId = (row :any) => { | |||||
| return qcRecord.findIndex(qc => qc == row); | |||||
| // return row.id || `${row.name}-${Math.random().toString(36).substr(2, 9)}`; | |||||
| }; | |||||
| return ( | return ( | ||||
| <> | <> | ||||
| <Grid container justifyContent="flex-start" alignItems="flex-start"> | <Grid container justifyContent="flex-start" alignItems="flex-start"> | ||||
| @@ -475,6 +507,7 @@ const QcComponent: React.FC<Props> = ({ qc, itemDetail, disabled = false, escala | |||||
| // rows={disabled? qcResult:qcItems} | // rows={disabled? qcResult:qcItems} | ||||
| autoHeight | autoHeight | ||||
| sortModel={[]} | sortModel={[]} | ||||
| getRowId={getRowId} | |||||
| /> | /> | ||||
| </Grid> | </Grid> | ||||
| </> | </> | ||||
| @@ -497,7 +530,7 @@ const QcComponent: React.FC<Props> = ({ qc, itemDetail, disabled = false, escala | |||||
| <CollapsibleCard title={t("QC Record")}> | <CollapsibleCard title={t("QC Record")}> | ||||
| <StyledDataGrid | <StyledDataGrid | ||||
| columns={qcColumns} | columns={qcColumns} | ||||
| rows={qcResult} | |||||
| rows={qcHistory} | |||||
| // rows={qcResult && qcResult.length > 0 ? qcResult : qcItems} | // rows={qcResult && qcResult.length > 0 ? qcResult : qcItems} | ||||
| // rows={disabled? qcResult:qcItems} | // rows={disabled? qcResult:qcItems} | ||||
| autoHeight | autoHeight | ||||
| @@ -536,8 +569,8 @@ const QcComponent: React.FC<Props> = ({ qc, itemDetail, disabled = false, escala | |||||
| onChange={(e) => { | onChange={(e) => { | ||||
| const value = e.target.value.toString();// === 'true'; | const value = e.target.value.toString();// === 'true'; | ||||
| const input = document.getElementById('accQty') as HTMLInputElement; | |||||
| console.log("%c Error", "color:pink", errors.acceptQty); | |||||
| const input = document.getElementById('accQty') as HTMLInputElement; //TODO improve | |||||
| console.log("%c AccQty Error", "color:pink", errors.acceptQty); | |||||
| if (input) { // Selected Reject in new flow with Error | if (input) { // Selected Reject in new flow with Error | ||||
| if (value == "1") { // Selected Accept | if (value == "1") { // Selected Accept | ||||
| input.value = Number(accQty).toString(); | input.value = Number(accQty).toString(); | ||||
| @@ -641,7 +674,6 @@ const QcComponent: React.FC<Props> = ({ qc, itemDetail, disabled = false, escala | |||||
| forSupervisor={false} | forSupervisor={false} | ||||
| isCollapsed={isCollapsed} | isCollapsed={isCollapsed} | ||||
| setIsCollapsed={setIsCollapsed} | setIsCollapsed={setIsCollapsed} | ||||
| escalationCombo={escalationCombo} | |||||
| /> | /> | ||||
| </Grid>)} | </Grid>)} | ||||
| {/* {qcAccept && <Grid item xs={12}> | {/* {qcAccept && <Grid item xs={12}> | ||||
| @@ -67,7 +67,6 @@ interface CommonProps extends Omit<ModalProps, "children"> { | |||||
| // type: "qc" | "stockIn" | "escalation" | "putaway" | "reject"; | // type: "qc" | "stockIn" | "escalation" | "putaway" | "reject"; | ||||
| handleMailTemplateForStockInLine: (stockInLineId: number) => void; | handleMailTemplateForStockInLine: (stockInLineId: number) => void; | ||||
| printerCombo: PrinterCombo[]; | printerCombo: PrinterCombo[]; | ||||
| escalationCombo: EscalationCombo[]; | |||||
| onClose: () => void; | onClose: () => void; | ||||
| } | } | ||||
| interface Props extends CommonProps { | interface Props extends CommonProps { | ||||
| @@ -87,7 +86,6 @@ const PoQcStockInModalVer2: React.FC<Props> = ({ | |||||
| warehouse, | warehouse, | ||||
| handleMailTemplateForStockInLine, | handleMailTemplateForStockInLine, | ||||
| printerCombo, | printerCombo, | ||||
| escalationCombo | |||||
| }) => { | }) => { | ||||
| const { | const { | ||||
| t, | t, | ||||
| @@ -110,9 +108,10 @@ const defaultNewValue = useMemo(() => { | |||||
| escResult: (itemDetail.escResult && itemDetail.escResult?.length > 0) ? itemDetail.escResult : [], | escResult: (itemDetail.escResult && itemDetail.escResult?.length > 0) ? itemDetail.escResult : [], | ||||
| productionDate: itemDetail.productionDate ? arrayToDateString(itemDetail.productionDate, "input") : undefined, | productionDate: itemDetail.productionDate ? arrayToDateString(itemDetail.productionDate, "input") : undefined, | ||||
| expiryDate: itemDetail.expiryDate ? arrayToDateString(itemDetail.expiryDate, "input") : undefined, | expiryDate: itemDetail.expiryDate ? arrayToDateString(itemDetail.expiryDate, "input") : undefined, | ||||
| receiptDate: itemDetail.receiptDate ?? dayjs().add(0, "month").format(INPUT_DATE_FORMAT), | |||||
| receiptDate: itemDetail.receiptDate ? arrayToDateString(itemDetail.receiptDate, "input") | |||||
| : dayjs().add(0, "month").format(INPUT_DATE_FORMAT), | |||||
| acceptQty: itemDetail.demandQty?? itemDetail.acceptedQty, | acceptQty: itemDetail.demandQty?? itemDetail.acceptedQty, | ||||
| warehouseId: itemDetail.defaultWarehouseId ?? 1 | |||||
| warehouseId: itemDetail.defaultWarehouseId ?? 1, | |||||
| } | } | ||||
| ) | ) | ||||
| },[itemDetail]) | },[itemDetail]) | ||||
| @@ -153,10 +152,6 @@ const [qcItems, setQcItems] = useState(dummyQCData) | |||||
| }, [itemDetail]); | }, [itemDetail]); | ||||
| useEffect(() => { | useEffect(() => { | ||||
| const qcRes = itemDetail?.qcResult; | |||||
| // if (!qcRes || qcRes?.length <= 0) { | |||||
| // itemDetail.qcResult = dummyQCData; | |||||
| // } | |||||
| formProps.reset({ | formProps.reset({ | ||||
| ...defaultNewValue | ...defaultNewValue | ||||
| }) | }) | ||||
| @@ -220,7 +215,7 @@ const [qcItems, setQcItems] = useState(dummyQCData) | |||||
| const qcAccept = data.qcDecision == 1; | const qcAccept = data.qcDecision == 1; | ||||
| // const qcAccept = data.qcAccept; | // const qcAccept = data.qcAccept; | ||||
| let acceptQty = Number(data.acceptQty); | let acceptQty = Number(data.acceptQty); | ||||
| const qcResults = data.qcResult || dummyQCData; // qcItems; | |||||
| const qcResults = data.qcResult?.filter((qc) => qc.escalationLogId === undefined) || []; // Remove old QC data | |||||
| // const qcResults = data.qcResult as PurchaseQcResult[]; // qcItems; | // const qcResults = data.qcResult as PurchaseQcResult[]; // qcItems; | ||||
| // const qcResults = viewOnly? data.qcResult as PurchaseQcResult[] : qcItems; | // const qcResults = viewOnly? data.qcResult as PurchaseQcResult[] : qcItems; | ||||
| @@ -290,16 +285,17 @@ const [qcItems, setQcItems] = useState(dummyQCData) | |||||
| qcAccept: qcAccept? qcAccept : false, | qcAccept: qcAccept? qcAccept : false, | ||||
| acceptQty: acceptQty? acceptQty : 0, | acceptQty: acceptQty? acceptQty : 0, | ||||
| qcResult: itemDetail.status != "escalated" ? qcResults.map(item => ({ | |||||
| id: item.id, | |||||
| qcItemId: item.id, | |||||
| // qcResult: itemDetail.status != "escalated" ? qcResults.map(item => ({ | |||||
| qcResult: qcResults.map(item => ({ | |||||
| // id: item.id, | |||||
| qcItemId: item.qcItemId, | |||||
| // code: item.code, | // code: item.code, | ||||
| // qcDescription: item.qcDescription, | // qcDescription: item.qcDescription, | ||||
| qcPassed: item.qcPassed? item.qcPassed : false, | qcPassed: item.qcPassed? item.qcPassed : false, | ||||
| failQty: (item.failQty && !item.qcPassed) ? item.failQty : 0, | failQty: (item.failQty && !item.qcPassed) ? item.failQty : 0, | ||||
| // failedQty: (typeof item.failedQty === "number" && !item.isPassed) ? item.failedQty : 0, | // failedQty: (typeof item.failedQty === "number" && !item.isPassed) ? item.failedQty : 0, | ||||
| remarks: item.remarks || '' | |||||
| })) : [], | |||||
| remarks: item.remarks || '', | |||||
| })), | |||||
| }; | }; | ||||
| // const qcData = data; | // const qcData = data; | ||||
| @@ -389,7 +385,7 @@ const [qcItems, setQcItems] = useState(dummyQCData) | |||||
| status: data.status, //TODO Fix it! | status: data.status, //TODO Fix it! | ||||
| // ...data, | // ...data, | ||||
| dnDate : data.dnDate? arrayToDateString(data.dnDate) : dayjsToInputDateString(dayjs()), | |||||
| dnDate : data.dnDate? arrayToDateString(data.dnDate, "input") : dayjsToInputDateString(dayjs()), | |||||
| productionDate : arrayToDateString(data.productionDate, "input"), | productionDate : arrayToDateString(data.productionDate, "input"), | ||||
| expiryDate : arrayToDateString(data.expiryDate, "input"), | expiryDate : arrayToDateString(data.expiryDate, "input"), | ||||
| receiptDate : arrayToDateString(data.receiptDate, "input"), | receiptDate : arrayToDateString(data.receiptDate, "input"), | ||||
| @@ -406,8 +402,6 @@ const [qcItems, setQcItems] = useState(dummyQCData) | |||||
| alert("請新增上架資料!"); | alert("請新增上架資料!"); | ||||
| return; | return; | ||||
| } | } | ||||
| console.log(typeof data.putAwayLines!![0].qty + " = 'number'"); | |||||
| console.log(typeof data.putAwayLines!![0].qty !== "number"); | |||||
| if (data.putAwayLines!!.filter((line) => /[^0-9]/.test(String(line.qty))).length > 0) { //TODO Improve | if (data.putAwayLines!!.filter((line) => /[^0-9]/.test(String(line.qty))).length > 0) { //TODO Improve | ||||
| alert("上架數量不正確!"); | alert("上架數量不正確!"); | ||||
| return; | return; | ||||
| @@ -471,11 +465,11 @@ const [qcItems, setQcItems] = useState(dummyQCData) | |||||
| return isPassed | return isPassed | ||||
| }, [acceptQty, formProps]) | }, [acceptQty, formProps]) | ||||
| useEffect(() => { | |||||
| // maybe check if submitted before | |||||
| console.log("Modal QC Items updated:", qcItems); | |||||
| // checkQcIsPassed(qcItems) | |||||
| }, [qcItems, checkQcIsPassed]) | |||||
| // useEffect(() => { | |||||
| // // maybe check if submitted before | |||||
| // console.log("Modal QC Items updated:", qcItems); | |||||
| // // checkQcIsPassed(qcItems) | |||||
| // }, [qcItems, checkQcIsPassed]) | |||||
| return ( | return ( | ||||
| <> | <> | ||||
| @@ -576,10 +570,9 @@ const [qcItems, setQcItems] = useState(dummyQCData) | |||||
| alignItems="flex-start" | alignItems="flex-start" | ||||
| > | > | ||||
| <QcComponent | <QcComponent | ||||
| qc={qc!} | |||||
| // qc={qc!} | |||||
| itemDetail={itemDetail} | itemDetail={itemDetail} | ||||
| disabled={viewOnly} | disabled={viewOnly} | ||||
| escalationCombo={escalationCombo} | |||||
| // qcItems={qcItems} | // qcItems={qcItems} | ||||
| // setQcItems={setQcItems} | // setQcItems={setQcItems} | ||||
| /> | /> | ||||
| @@ -12,7 +12,8 @@ import { QcData } from "@/app/api/qc" | |||||
| export const dummyQCData: QcData[] = [ | export const dummyQCData: QcData[] = [ | ||||
| { | { | ||||
| id: 4, | |||||
| id: 1, | |||||
| qcItemId: 4, | |||||
| code: "包裝", | code: "包裝", | ||||
| qcDescription: "有破爛、污糟、脹袋、積水、與實物不符等任何一種情況,則不合格", | qcDescription: "有破爛、污糟、脹袋、積水、與實物不符等任何一種情況,則不合格", | ||||
| name: "有破爛、污糟、脹袋、積水、與實物不符等任何一種情況,則不合格", | name: "有破爛、污糟、脹袋、積水、與實物不符等任何一種情況,則不合格", | ||||
| @@ -21,7 +22,8 @@ export const dummyQCData: QcData[] = [ | |||||
| remarks: undefined, | remarks: undefined, | ||||
| }, | }, | ||||
| { | { | ||||
| id: 5, | |||||
| id: 2, | |||||
| qcItemId: 5, | |||||
| code: "肉質", | code: "肉質", | ||||
| qcDescription: "肉質鬆散,則不合格", | qcDescription: "肉質鬆散,則不合格", | ||||
| name: "肉質鬆散,則不合格", | name: "肉質鬆散,則不合格", | ||||
| @@ -30,7 +32,8 @@ export const dummyQCData: QcData[] = [ | |||||
| remarks: undefined, | remarks: undefined, | ||||
| }, | }, | ||||
| { | { | ||||
| id: 6, | |||||
| id: 3, | |||||
| qcItemId: 6, | |||||
| code: "顔色", | code: "顔色", | ||||
| qcDescription: "不是食材應有的顔色、顔色不均匀、出現其他顔色、腌料/醬顔色不均匀,油脂部分變綠色、黃色,則不合格", | qcDescription: "不是食材應有的顔色、顔色不均匀、出現其他顔色、腌料/醬顔色不均匀,油脂部分變綠色、黃色,則不合格", | ||||
| name: "不是食材應有的顔色、顔色不均匀、出現其他顔色、腌料/醬顔色不均匀,油脂部分變綠色、黃色,則不合格", | name: "不是食材應有的顔色、顔色不均匀、出現其他顔色、腌料/醬顔色不均匀,油脂部分變綠色、黃色,則不合格", | ||||
| @@ -39,7 +42,8 @@ export const dummyQCData: QcData[] = [ | |||||
| remarks: undefined, | remarks: undefined, | ||||
| }, | }, | ||||
| { | { | ||||
| id: 7, | |||||
| id: 4, | |||||
| qcItemId: 7, | |||||
| code: "狀態", | code: "狀態", | ||||
| qcDescription: "有結晶、結霜、解凍跡象、發霉、散發異味等任何一種情況,則不合格", | qcDescription: "有結晶、結霜、解凍跡象、發霉、散發異味等任何一種情況,則不合格", | ||||
| name: "有結晶、結霜、解凍跡象、發霉、散發異味等任何一種情況,則不合格", | name: "有結晶、結霜、解凍跡象、發霉、散發異味等任何一種情況,則不合格", | ||||
| @@ -48,7 +52,8 @@ export const dummyQCData: QcData[] = [ | |||||
| remarks: undefined, | remarks: undefined, | ||||
| }, | }, | ||||
| { | { | ||||
| id: 8, | |||||
| id: 5, | |||||
| qcItemId: 8, | |||||
| code: "異物", | code: "異物", | ||||
| qcDescription: "有不屬於本食材的雜質,則不合格", | qcDescription: "有不屬於本食材的雜質,則不合格", | ||||
| name: "有不屬於本食材的雜質,則不合格", | name: "有不屬於本食材的雜質,則不合格", | ||||
| @@ -34,6 +34,7 @@ | |||||
| "Pending application": "待處理提料申請", | "Pending application": "待處理提料申請", | ||||
| "pending inspection material": "待品檢物料", | "pending inspection material": "待品檢物料", | ||||
| "rejected": "已拒絕", | "rejected": "已拒絕", | ||||
| "accepted": "已收貨", | |||||
| "escalated": "已上報", | "escalated": "已上報", | ||||
| "inspected material": "已品檢物料", | "inspected material": "已品檢物料", | ||||
| "total material": "物料總數", | "total material": "物料總數", | ||||
| @@ -50,5 +51,6 @@ | |||||
| "QC Completed Count": "品檢完成數量", | "QC Completed Count": "品檢完成數量", | ||||
| "QC Fail-Total Count": "品檢不合格/總數", | "QC Fail-Total Count": "品檢不合格/總數", | ||||
| "escalationStatus": "上報狀態", | "escalationStatus": "上報狀態", | ||||
| "escalated datetime": "上報時間" | |||||
| "escalated datetime": "上報時間", | |||||
| "escalateFrom": "上報同事" | |||||
| } | } | ||||