@@ -15,6 +15,8 @@ export interface EscalationResult { | |||
poId?: number; | |||
reason?: string; | |||
handlerId?: number; | |||
status: string; | |||
recordDate: string; | |||
itemName?: string; | |||
demandQty?: number; | |||
acceptedQty?: number; | |||
@@ -42,6 +42,7 @@ export interface PurchaseQcResult{ | |||
qcPassed?: boolean; | |||
failQty?: number; | |||
remarks?: string; | |||
escalationLogId?: number; | |||
} | |||
export interface StockInInput { | |||
status: string; | |||
@@ -8,7 +8,7 @@ import { | |||
} from "../../utils/fetchUtil"; | |||
import { BASE_API_URL } from "../../../config/api"; | |||
import { revalidateTag } from "next/cache"; | |||
import { UserDetail, UserResult } from "."; | |||
import { EscalationCombo, UserDetail, UserResult } from "."; | |||
import { cache } from "react"; | |||
export interface UserInputs { | |||
@@ -102,3 +102,9 @@ export const adminChangePassword = async (data: any) => { | |||
}, | |||
); | |||
}; | |||
export const fetchEscalationCombo = async () => { | |||
return serverFetchJson<EscalationCombo>(`${BASE_API_URL}/user/escalation-combo`, { | |||
next: { tags: ["escalationCombo"]} | |||
}) | |||
}; |
@@ -67,7 +67,7 @@ export const fetchPwRules = cache(async () => { | |||
}); | |||
export const fetchEscalationCombo = cache(async () => { | |||
return serverFetchJson<EscalationCombo>(`${BASE_API_URL}/user/escalation-combo`, { | |||
return serverFetchJson<EscalationCombo[]>(`${BASE_API_URL}/user/escalation-combo`, { | |||
next: { tags: ["escalationCombo"]} | |||
}) | |||
}) |
@@ -36,7 +36,7 @@ export const INPUT_TIME_FORMAT = "HH:mm:ss"; | |||
export const OUTPUT_TIME_FORMAT = "HH:mm:ss"; | |||
export const arrayToDayjs = (arr: ConfigType | (number | undefined)[]) => { | |||
export const arrayToDayjs = (arr: ConfigType | (number | undefined)[], showTime: boolean = false) => { | |||
const isValidNumber = ( | |||
item: ListIterateeCustom<number | undefined, boolean>, | |||
): boolean => typeof item === "number" && !isNaN(item) && isFinite(item); | |||
@@ -47,6 +47,10 @@ export const arrayToDayjs = (arr: ConfigType | (number | undefined)[]) => { | |||
// [year, month, day] | |||
// tempArr = take(arr, 3); | |||
tempArr = `${arr[0]?.toString().padStart(4, "0")}-${arr[1]?.toString().padStart(2, "0")}-${arr[2]?.toString().padStart(2, "0")}`; | |||
if (showTime) { | |||
// [year, month, day, hour, minute, second] | |||
tempArr += ` ${arr[3]?.toString().padStart(2, "0")}-${arr[4]?.toString().padStart(2, "0")}-${arr[5]?.toString().padStart(2, "0")}`; | |||
} | |||
} | |||
return dayjs(tempArr as ConfigType); | |||
@@ -56,6 +60,10 @@ export const arrayToDateString = (arr: ConfigType | (number | undefined)[]) => { | |||
return arrayToDayjs(arr).format(OUTPUT_DATE_FORMAT); | |||
}; | |||
export const arrayToDateTimeString = (arr: ConfigType | (number | undefined)[]) => { | |||
return arrayToDayjs(arr, true).format(`${OUTPUT_DATE_FORMAT} ${OUTPUT_TIME_FORMAT}`); | |||
}; | |||
export const arrayToInputDateString = (arr: ConfigType | (number | undefined)[]) => { | |||
return arrayToDayjs(arr).format(INPUT_DATE_FORMAT); | |||
}; | |||
@@ -33,7 +33,7 @@ const DashboardPage: React.FC<Props> = ({ | |||
<ThemeProvider theme={theme}> | |||
<Grid container spacing={2}> | |||
<Grid item xs={12}> | |||
<CollapsibleCard title={t("Escalation List")}> | |||
<CollapsibleCard title={t("My Escalation List")}> | |||
<CardContent> | |||
<EscalationLogTable items={escalationLogs || []}/> | |||
</CardContent> | |||
@@ -8,7 +8,7 @@ import { useTranslation } from "react-i18next"; | |||
import { EscalationResult } from "@/app/api/escalation"; | |||
import { Column } from "@/components/SearchResults"; | |||
import SearchResults from "@/components/SearchResults/SearchResults"; | |||
import { arrayToDateString } from "@/app/utils/formatUtil"; | |||
import { arrayToDateString, arrayToDateTimeString } from "@/app/utils/formatUtil"; | |||
export type IQCItems = { | |||
id: number; | |||
@@ -22,11 +22,12 @@ export type IQCItems = { | |||
}; | |||
type Props = { | |||
type?: "dashboard" | "qc"; | |||
items: EscalationResult[]; | |||
}; | |||
const EscalationLogTable: React.FC<Props> = ({ | |||
items | |||
type = "dashboard", items | |||
}) => { | |||
const { t } = useTranslation("dashboard"); | |||
const CARD_HEADER = t("stock in escalation list") | |||
@@ -58,19 +59,66 @@ const EscalationLogTable: React.FC<Props> = ({ | |||
// [navigateTo] | |||
// ); | |||
const columns = useMemo<Column<EscalationResult>[]>( | |||
const base_col = useMemo<Column<EscalationResult>[]>( | |||
() => [ | |||
{ | |||
name: "handler", | |||
label: t("Responsible for handling colleagues"), | |||
sx: { width: "20%", minWidth: 200, maxWidth: 500 }, | |||
name: "qcFailCount", | |||
label: t("QC Fail Count"), | |||
align: "right", | |||
headerAlign: "right", | |||
sx: { width: "10%", minWidth: 120 }, | |||
renderCell: (params) => { | |||
return `${params.qcFailCount} / ${params.qcTotalCount}` | |||
} | |||
}, | |||
{ | |||
name: "reason", | |||
label: t("Reason"), | |||
sx: { width: "30%", minWidth: 150 }, | |||
}, | |||
{ | |||
name: "status", | |||
label: t("escalationStatus"), | |||
sx: { width: "10%", minWidth: 150 }, | |||
renderCell: (params) => { | |||
return t(params.status); | |||
// return t(`${params.status}`); | |||
} | |||
}, | |||
], []) | |||
const columns_dashboard = useMemo<Column<EscalationResult>[]>( | |||
() => [ | |||
{ | |||
name: "poCode", | |||
label: t("Po Code"), | |||
align: "left", | |||
headerAlign: "left", | |||
sx: { width: "15%", minWidth: 100 }, | |||
}, | |||
{ | |||
name: "recordDate", | |||
label: t("escalated date"), | |||
align: "right", | |||
headerAlign: "right", | |||
sx: { width: "10%", minWidth: 100 }, | |||
renderCell: (params) => { | |||
return arrayToDateString(params.recordDate); | |||
} | |||
}, | |||
{ | |||
name: "itemName", | |||
label: t("Item Name"), | |||
align: "left", | |||
headerAlign: "left", | |||
sx: { width: "15%", minWidth: 100 }, | |||
}, | |||
{ | |||
name: "acceptedQty", | |||
label: t("Received Qty"), | |||
align: "right", | |||
headerAlign: "right", | |||
sx: { width: "10%", minWidth: 100 }, | |||
sx: { width: "5%", minWidth: 100 }, | |||
}, | |||
{ | |||
name: "purchaseUomDesc", | |||
@@ -85,37 +133,55 @@ const EscalationLogTable: React.FC<Props> = ({ | |||
return params.dnDate ? arrayToDateString(params.dnDate) : "N/A" | |||
} | |||
}, | |||
...base_col | |||
], []) | |||
const columns_qc = useMemo<Column<EscalationResult>[]>( | |||
() => [ | |||
{ | |||
name: "qcFailCount", | |||
label: t("QC Fail Count"), | |||
name: "handler", | |||
label: t("Responsible for handling colleagues"), | |||
sx: { width: "20%", minWidth: 200, maxWidth: 500 }, | |||
renderCell: (params) => { | |||
const department = params.handlerDepartment; | |||
const name = params.handlerName; | |||
const title = params.handlerTitle; | |||
return ( | |||
(department ? `${department} - ` : "") + name + (title ? ` (${title})` : "") | |||
); | |||
} | |||
}, | |||
{ | |||
name: "recordDate", | |||
label: t("escalated date"), | |||
align: "right", | |||
headerAlign: "right", | |||
sx: { width: "15%", minWidth: 120 }, | |||
sx: { width: "10%", minWidth: 100 }, | |||
renderCell: (params) => { | |||
return `${params.qcFailCount} / ${params.qcTotalCount}` | |||
return arrayToDateString(params.recordDate); | |||
} | |||
}, | |||
// { | |||
// name: "qcTotalCount", | |||
// label: t("QC Completed Count"), | |||
// align: "right", | |||
// headerAlign: "right" | |||
// flex: 1, | |||
// }, | |||
// { | |||
// name: "qcFailCount", | |||
// label: t("QC Fail Count"), | |||
// align: "right", | |||
// headerAlign: "right" | |||
// flex: 1, | |||
// }, | |||
{ | |||
name: "reason", | |||
label: t("Reason"), | |||
sx: { width: "30%", minWidth: 150 }, | |||
name: "acceptedQty", | |||
label: t("Received Qty"), | |||
align: "right", | |||
headerAlign: "right", | |||
sx: { width: "5%", minWidth: 100 }, | |||
}, | |||
{ | |||
name: "purchaseUomDesc", | |||
label: t("Purchase UoM"), | |||
sx: { width: "15%", minWidth: 120 }, | |||
}, | |||
...base_col | |||
], []) | |||
const getColumnByType = (type : Props['type']) => { | |||
switch (type) { | |||
case "qc": return columns_qc; | |||
default: return columns_dashboard; | |||
} | |||
} | |||
{/* return ( | |||
<TableContainer component={Paper}> | |||
<Table aria-label="Two column navigable table" size="small"> | |||
@@ -158,7 +224,7 @@ const EscalationLogTable: React.FC<Props> = ({ | |||
<SearchResults | |||
onRowClick={onRowClick} | |||
items={items} | |||
columns={columns} | |||
columns={getColumnByType(type)} | |||
isAutoPaging={false} | |||
/> | |||
) | |||
@@ -1,4 +1,4 @@ | |||
import React, { useState, ChangeEvent, FormEvent, Dispatch } from 'react'; | |||
import React, { useState, ChangeEvent, FormEvent, Dispatch, useEffect } from 'react'; | |||
import { | |||
Box, | |||
Button, | |||
@@ -21,8 +21,10 @@ 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'; | |||
import { useFormContext } from 'react-hook-form'; | |||
import { Controller, useFormContext } from 'react-hook-form'; | |||
import { EscalationInput, ModalFormInput } from '@/app/api/po/actions'; | |||
import { EscalationCombo } from "@/app/api/user"; | |||
import { FireExtinguisher } from '@mui/icons-material'; | |||
interface NameOption { | |||
value: string; | |||
@@ -39,15 +41,17 @@ interface Props { | |||
forSupervisor: boolean | |||
isCollapsed: boolean | |||
setIsCollapsed: Dispatch<React.SetStateAction<boolean>> | |||
escalationCombo: EscalationCombo[] | |||
} | |||
const EscalationComponent: React.FC<Props> = ({ | |||
const EscalationComponent: React.FC<Props> = ({ | |||
forSupervisor, | |||
isCollapsed, | |||
setIsCollapsed | |||
}) => { | |||
setIsCollapsed, | |||
escalationCombo | |||
}) => { | |||
const { t } = useTranslation("purchaseOrder"); | |||
const [formData, setFormData] = useState<FormData>({ | |||
name: '', | |||
quantity: '', | |||
@@ -74,7 +78,7 @@ const EscalationComponent: React.FC<Props> = ({ | |||
resetField, | |||
setError, | |||
clearErrors, | |||
} = useFormContext<ModalFormInput>(); | |||
} = useFormContext<ModalFormInput>(); | |||
const handleInputChange = ( | |||
event: ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement> | SelectChangeEvent<string> | |||
@@ -86,7 +90,8 @@ const EscalationComponent: React.FC<Props> = ({ | |||
})); | |||
}; | |||
const handleSubmit = (e: FormEvent<HTMLFormElement>): void => {console.log("called this?"); | |||
const handleSubmit = (e: FormEvent<HTMLFormElement>): void => { | |||
console.log("Handled Submit?"); | |||
e.preventDefault(); | |||
console.log('表單已提交:', formData); | |||
// 處理表單提交 | |||
@@ -99,9 +104,9 @@ const EscalationComponent: React.FC<Props> = ({ | |||
return ( | |||
// <Paper elevation={3} sx={{ maxWidth: 400, mx: 'auto', p: 3 }}> | |||
<> | |||
<Paper sx={{padding: 2}}> | |||
{/* <Paper elevation={3} sx={{ mx: 'auto', p: 3 }}> */} | |||
<Box sx={{ display: 'flex', alignItems: 'center', mb: 2 }}> | |||
<Paper sx={{ padding: 2 }}> | |||
{/* <Paper elevation={3} sx={{ mx: 'auto', p: 3 }}> */} | |||
<Box sx={{ display: 'flex', alignItems: 'center', mb: 2 }}> | |||
<Box sx={{ display: 'flex', alignItems: 'center' }}> | |||
<Typography variant="body1">上報結果</Typography> | |||
{/* {isCollapsed ? ( | |||
@@ -110,7 +115,7 @@ const EscalationComponent: React.FC<Props> = ({ | |||
<ExpandMoreIcon sx={{ ml: 1 }} /> | |||
)} */} | |||
</Box> | |||
{/* <FormControlLabel | |||
{/* <FormControlLabel | |||
control={ | |||
<Checkbox | |||
checked={isCollapsed} | |||
@@ -129,11 +134,34 @@ const EscalationComponent: React.FC<Props> = ({ | |||
</Box> | |||
} | |||
/> */} | |||
</Box> | |||
<Collapse in={isCollapsed}> | |||
<Box component="form" onSubmit={handleSubmit} sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}> | |||
<FormControl fullWidth> | |||
<select | |||
</Box> | |||
<Collapse in={isCollapsed}> | |||
<Box component="form" onSubmit={handleSubmit} sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}> | |||
<FormControl fullWidth> | |||
<Controller | |||
control={control} | |||
name="escalationLog.handlerId" | |||
render={({field, fieldState, formState}) => { | |||
return (<Autocomplete | |||
noOptionsText={t("No Option")} | |||
disableClearable | |||
fullWidth | |||
// value={} | |||
// {...register("escalationLog.handlerId", { | |||
// required: "handler required!", | |||
// })} | |||
// onChange={() => handleInputChange} | |||
onChange={(event, value) => { | |||
field.onChange(value.id); | |||
}} | |||
// getOptionLabel={(option) => option.label} | |||
options={escalationCombo} | |||
renderInput={(params) => <TextField {...params} error={false} label={"負責處理同事"}/>} | |||
/>) | |||
}} | |||
/> | |||
{/* <select | |||
id="handlerId" | |||
// name="name" | |||
// value={formData.name} | |||
@@ -148,33 +176,33 @@ const EscalationComponent: React.FC<Props> = ({ | |||
{option.label} | |||
</option> | |||
))} | |||
</select> | |||
</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> | |||
</select> */} | |||
</FormControl> | |||
): undefined} | |||
{forSupervisor && (<TextField | |||
fullWidth | |||
id="decision" | |||
name="decision" | |||
label="上報結果" | |||
type="radio" | |||
// value={formData.decision} | |||
onChange={handleInputChange} | |||
InputProps={{ inputProps: { min: 1 } }} | |||
placeholder="請決定上報結果" | |||
/>)} | |||
{/* <TextField | |||
{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} | |||
{forSupervisor && (<TextField | |||
fullWidth | |||
id="decision" | |||
name="decision" | |||
label="上報結果" | |||
type="radio" | |||
// value={formData.decision} | |||
onChange={handleInputChange} | |||
InputProps={{ inputProps: { min: 1 } }} | |||
placeholder="請決定上報結果" | |||
/>)} | |||
{/* <TextField | |||
fullWidth | |||
id="quantity" | |||
name="quantity" | |||
@@ -186,22 +214,22 @@ const EscalationComponent: React.FC<Props> = ({ | |||
placeholder="請輸入數量" | |||
/> */} | |||
<TextField | |||
fullWidth | |||
id="reason" | |||
// name="reason" | |||
{...register("escalationLog.reason", { | |||
required: "reason required!", | |||
})} | |||
label="上報原因" | |||
multiline | |||
rows={4} | |||
// value={formData.reason} | |||
onChange={handleInputChange} | |||
placeholder="請輸入上報原因" | |||
/> | |||
<TextField | |||
fullWidth | |||
id="reason" | |||
// name="reason" | |||
{...register("escalationLog.reason", { | |||
required: "reason required!", | |||
})} | |||
label="上報原因" | |||
multiline | |||
rows={4} | |||
// value={formData.reason} | |||
onChange={handleInputChange} | |||
placeholder="請輸入上報原因" | |||
/> | |||
{/* <Stack direction="row" justifyContent="flex-end" gap={1}> | |||
{/* <Stack direction="row" justifyContent="flex-end" gap={1}> | |||
<Button | |||
type="submit" | |||
variant="contained" | |||
@@ -210,9 +238,9 @@ const EscalationComponent: React.FC<Props> = ({ | |||
{t("update qc info")} | |||
</Button> | |||
</Stack> */} | |||
</Box> | |||
</Collapse> | |||
</Paper> | |||
</Box> | |||
</Collapse> | |||
</Paper> | |||
</> | |||
); | |||
} | |||
@@ -80,6 +80,7 @@ import { debounce } from "lodash"; | |||
import LoadingComponent from "../General/LoadingComponent"; | |||
import { getMailTemplateForStockInLine } from "@/app/api/mailTemplate/actions"; | |||
import { PrinterCombo } from "@/app/api/settings/printer"; | |||
import { EscalationCombo } from "@/app/api/user"; | |||
//import { useRouter } from "next/navigation"; | |||
@@ -88,6 +89,7 @@ type Props = { | |||
qc: QcItemWithChecks[]; | |||
warehouse: WarehouseResult[]; | |||
printerCombo: PrinterCombo[]; | |||
escalationCombo: EscalationCombo[]; | |||
}; | |||
type EntryError = | |||
@@ -190,7 +192,7 @@ interface PolInputResult { | |||
dnQty: number, | |||
} | |||
const PoDetail: React.FC<Props> = ({ po, qc, warehouse, printerCombo }) => { | |||
const PoDetail: React.FC<Props> = ({ po, qc, warehouse, printerCombo, escalationCombo }) => { | |||
const cameras = useContext(CameraContext); | |||
// console.log(cameras); | |||
const { t } = useTranslation("purchaseOrder"); | |||
@@ -217,6 +219,7 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse, printerCombo }) => { | |||
useEffect(() => { | |||
if (defaultPolId) { | |||
setSelectedRow(rows.find((r) => r.id.toString() === defaultPolId) ?? null) | |||
console.log("%c StockIn:", "color:green", selectedRow); | |||
} | |||
}, []) | |||
@@ -263,6 +266,7 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse, printerCombo }) => { | |||
const result = await fetchPoInClient(parseInt(poId)); | |||
console.log(result) | |||
if (result) { | |||
console.log("%c Fetched PO:", "color:orange", result); | |||
setPurchaseOrder(result); | |||
setRows(result.pol || []); | |||
if (result.pol && result.pol.length > 0) { | |||
@@ -506,7 +510,7 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse, printerCombo }) => { | |||
<TableCell align="left">{row.itemNo}</TableCell> | |||
<TableCell align="left">{row.itemName}</TableCell> | |||
<TableCell align="right">{integerFormatter.format(row.qty)}</TableCell> | |||
<TableCell align="right">{integerFormatter.format(processedQty)}</TableCell> | |||
<TableCell align="right">{integerFormatter.format(row.processed)}</TableCell> | |||
<TableCell align="left">{row.uom?.code}</TableCell> | |||
{/* <TableCell align="right">{decimalFormatter.format(row.stockUom.stockQty)}</TableCell> */} | |||
<TableCell align="right">{decimalFormatter.format(row.stockInLine.filter((sil) => sil.purchaseOrderLineId === row.id).reduce((acc, cur) => acc + (cur.acceptedQty ?? 0),0) * purchaseToStockRatio)}</TableCell> | |||
@@ -814,7 +818,7 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse, printerCombo }) => { | |||
<TableCell align="right">{t("qty")}</TableCell> | |||
<TableCell align="right">{t("processed")}</TableCell> | |||
<TableCell align="left">{t("uom")}</TableCell> | |||
<TableCell align="right">{t("Stock In Qty")}</TableCell> | |||
<TableCell align="right">{t("receivedTotal")}</TableCell> | |||
<TableCell align="left">{t("Stock UoM")}</TableCell> | |||
{/* <TableCell align="right">{t("total weight")}</TableCell> */} | |||
{/* <TableCell align="right">{`${t("price")} (HKD)`}</TableCell> */} | |||
@@ -861,6 +865,7 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse, printerCombo }) => { | |||
fetchPoDetail={fetchPoDetail} | |||
handleMailTemplateForStockInLine={handleMailTemplateForStockInLine} | |||
printerCombo={printerCombo} | |||
escalationCombo={escalationCombo} | |||
/> | |||
</Box> | |||
</TableCell> | |||
@@ -878,7 +883,7 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse, printerCombo }) => { | |||
/> */} | |||
</Grid> | |||
</Stack> | |||
{itemInfo !== undefined && ( | |||
{/* {itemInfo !== undefined && ( | |||
<> | |||
<PoQcStockInModal | |||
type={"putaway"} | |||
@@ -889,7 +894,7 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse, printerCombo }) => { | |||
itemDetail={itemInfo} | |||
/> | |||
</> | |||
)} | |||
)} */} | |||
</> | |||
); | |||
}; |
@@ -26,18 +26,18 @@ const PoDetailWrapper: React.FC<Props> & SubComponents = async ({ id }) => { | |||
poWithStockInLine, | |||
warehouse, | |||
qc, | |||
escalationCombo, | |||
printerCombo, | |||
escalationCombo, | |||
] = await Promise.all([ | |||
fetchPoWithStockInLines(id), | |||
fetchWarehouseList(), | |||
fetchQcItemCheck(), | |||
fetchEscalationCombo(), | |||
fetchPrinterCombo() | |||
fetchPrinterCombo(), | |||
fetchEscalationCombo() | |||
]); | |||
// const poWithStockInLine = await fetchPoWithStockInLines(id) | |||
return <PoDetail po={poWithStockInLine} qc={qc} warehouse={warehouse} printerCombo={printerCombo} />; | |||
console.log("%c pol:", "color:green", poWithStockInLine); | |||
return <PoDetail po={poWithStockInLine} qc={qc} warehouse={warehouse} printerCombo={printerCombo} escalationCombo={escalationCombo}/>; | |||
}; | |||
PoDetailWrapper.Loading = PoDetailLoading; | |||
@@ -66,6 +66,7 @@ import { PrinterCombo } from "@/app/api/settings/printer"; | |||
import { EscalationResult } from "@/app/api/escalation"; | |||
import { fetchEscalationLogsByStockInLines } from "@/app/api/escalation/actions"; | |||
import { SessionWithTokens } from "@/config/authConfig"; | |||
import { EscalationCombo } from "@/app/api/user"; | |||
interface ResultWithId { | |||
id: number; | |||
@@ -82,6 +83,7 @@ interface Props { | |||
fetchPoDetail: (poId: string) => void; | |||
handleMailTemplateForStockInLine: (stockInLineId: number) => void; | |||
printerCombo: PrinterCombo[]; | |||
escalationCombo: EscalationCombo[]; | |||
} | |||
export type StockInLineEntryError = { | |||
@@ -122,7 +124,8 @@ function PoInputGrid({ | |||
warehouse, | |||
fetchPoDetail, | |||
handleMailTemplateForStockInLine, | |||
printerCombo | |||
printerCombo, | |||
escalationCombo | |||
}: Props) { | |||
const { t } = useTranslation("purchaseOrder"); | |||
@@ -263,8 +266,8 @@ function PoInputGrid({ | |||
})); | |||
const qcResult = await fetchQcDefaultValue(id); | |||
const escResult = await fetchEscalationLogsByStockInLines([Number(id)]); | |||
console.log(params.row); | |||
console.log(qcResult); | |||
// console.log(params.row); | |||
console.log("Fetched QC Result:", qcResult); | |||
setModalInfo({ | |||
...params.row, | |||
@@ -580,8 +583,11 @@ const closeNewModal = useCallback(() => { | |||
const handlerId = params.row.handlerId | |||
const status = params.row.status | |||
return (<span style={{ | |||
color: (status == "escalated")? "red":"inherit"}}> | |||
{t(`${params.row.status}`)} | |||
color: | |||
(status == "escalated")? "red": | |||
(status == "rejected" || status == "partially_completed") ? "orange" : "inherit"}} | |||
> | |||
{t(`${params.row.status}`)} | |||
</span>); | |||
}, | |||
}, | |||
@@ -622,6 +628,7 @@ const closeNewModal = useCallback(() => { | |||
variant="contained" | |||
color="primary" | |||
sx={{ width: '150px' }} | |||
disabled={params.row.status != "rejected" && params.row.status != "partially_completed"} | |||
onClick={() => handleMailTemplateForStockInLine(params.row.id as number)} | |||
> | |||
{t("email supplier")} | |||
@@ -944,6 +951,7 @@ const closeNewModal = useCallback(() => { | |||
itemDetail={modalInfo} | |||
handleMailTemplateForStockInLine={handleMailTemplateForStockInLine} | |||
printerCombo={printerCombo} | |||
escalationCombo={escalationCombo} | |||
/> | |||
</> | |||
) | |||
@@ -6,6 +6,7 @@ import { | |||
Card, | |||
CardContent, | |||
Checkbox, | |||
Collapse, | |||
FormControl, | |||
FormControlLabel, | |||
Grid, | |||
@@ -54,11 +55,13 @@ import { escape } from "lodash"; | |||
import { PanoramaSharp } from "@mui/icons-material"; | |||
import EscalationLogTable from "../DashboardPage/escalation/EscalationLogTable"; | |||
import { EscalationResult } from "@/app/api/escalation"; | |||
import { EscalationCombo } from "@/app/api/user"; | |||
interface Props { | |||
itemDetail: StockInLine & { qcResult?: PurchaseQcResult[] } & { escResult?: EscalationResult[] }; | |||
qc: QcItemWithChecks[]; | |||
disabled: boolean; | |||
escalationCombo: EscalationCombo[]; | |||
// qcItems: QcData[] | |||
// setQcItems: Dispatch<SetStateAction<QcData[]>> | |||
} | |||
@@ -71,7 +74,7 @@ type EntryError = | |||
type QcRow = TableRow<Partial<QcData>, EntryError>; | |||
// fetchQcItemCheck | |||
const QcComponent: React.FC<Props> = ({ qc, itemDetail, disabled = false }) => { | |||
const QcComponent: React.FC<Props> = ({ qc, itemDetail, disabled = false, escalationCombo }) => { | |||
const { t } = useTranslation("purchaseOrder"); | |||
const apiRef = useGridApiRef(); | |||
const { | |||
@@ -95,6 +98,7 @@ const QcComponent: React.FC<Props> = ({ qc, itemDetail, disabled = false }) => { | |||
const qcDecision = watch("qcDecision"); //WIP | |||
// const qcResult = useMemo(() => [...watch("qcResult")], [watch("qcResult")]); | |||
const qcResult = [...watch("qcResult")]; | |||
const [qcHistory, setQcHistory] = useState<PurchaseQcResult[]>([]); | |||
// const [qcAccept, setQcAccept] = useState(true); | |||
// const [qcItems, setQcItems] = useState(dummyQCData) | |||
@@ -407,6 +411,12 @@ const QcComponent: React.FC<Props> = ({ qc, itemDetail, disabled = false }) => { | |||
} | |||
} | |||
useEffect(() => { | |||
if (qcHistory.length < 1) { | |||
setQcHistory(qcResult.filter((qc) => {qc.escalationLogId != null})); | |||
console.log("QC History updated:", qcHistory); | |||
} | |||
}, [watch("qcResult")]); | |||
// useEffect(() => { | |||
// // onFailedOpenCollapse(qcItems) | |||
// }, [qcItems]); | |||
@@ -428,7 +438,8 @@ const QcComponent: React.FC<Props> = ({ qc, itemDetail, disabled = false }) => { | |||
variant="scrollable" | |||
> | |||
<Tab label={t("QC Info")} iconPosition="end" /> | |||
<Tab label={t("Escalation History")} iconPosition="end" /> | |||
{(itemDetail.escResult && itemDetail.escResult?.length > 0) && | |||
(<Tab label={t("Escalation History")} iconPosition="end" />)} | |||
</Tabs> | |||
</Grid> | |||
{tabIndex == 0 && ( | |||
@@ -478,7 +489,17 @@ const QcComponent: React.FC<Props> = ({ qc, itemDetail, disabled = false }) => { | |||
</Typography> | |||
</Grid> */} | |||
<Grid item xs={12}> | |||
<EscalationLogTable items={itemDetail.escResult || []}/> | |||
<EscalationLogTable type="qc" items={itemDetail.escResult || []}/> | |||
{/* <Collapse in={true}> */} | |||
<StyledDataGrid | |||
columns={qcColumns} | |||
rows={qcResult} | |||
// rows={qcResult && qcResult.length > 0 ? qcResult : qcItems} | |||
// rows={disabled? qcResult:qcItems} | |||
autoHeight | |||
sortModel={[]} | |||
/> | |||
{/* </Collapse> */} | |||
{/* <StyledDataGrid | |||
rows={escalationHistory} | |||
columns={columns} | |||
@@ -569,6 +590,7 @@ const QcComponent: React.FC<Props> = ({ qc, itemDetail, disabled = false }) => { | |||
forSupervisor={false} | |||
isCollapsed={isCollapsed} | |||
setIsCollapsed={setIsCollapsed} | |||
escalationCombo={escalationCombo} | |||
/> | |||
</Grid>)} | |||
{/* {qcAccept && <Grid item xs={12}> | |||
@@ -21,7 +21,6 @@ import StockInForm from "./StockInForm"; | |||
import StockInFormVer2 from "./StockInFormVer2"; | |||
import QcComponent from "./QcComponent"; | |||
import { dummyPutAwayLine, dummyQCData } from "./dummyQcTemplate"; | |||
// import QcFormVer2 from "./QcFormVer2"; | |||
import PutAwayForm from "./PutAwayForm"; | |||
import { GridRowModes, useGridApiRef } from "@mui/x-data-grid"; | |||
import {submitDialogWithWarning} from "../Swal/CustomAlerts"; | |||
@@ -34,6 +33,7 @@ import { EscalationResult } from "@/app/api/escalation"; | |||
import { SessionWithTokens } from "@/config/authConfig"; | |||
import { GridRowModesModel } from "@mui/x-data-grid"; | |||
import { isEmpty } from "lodash"; | |||
import { EscalationCombo } from "@/app/api/user"; | |||
const style = { | |||
@@ -68,6 +68,7 @@ interface CommonProps extends Omit<ModalProps, "children"> { | |||
// type: "qc" | "stockIn" | "escalation" | "putaway" | "reject"; | |||
handleMailTemplateForStockInLine: (stockInLineId: number) => void; | |||
printerCombo: PrinterCombo[]; | |||
escalationCombo: EscalationCombo[]; | |||
onClose: () => void; | |||
} | |||
interface Props extends CommonProps { | |||
@@ -86,7 +87,8 @@ const PoQcStockInModalVer2: React.FC<Props> = ({ | |||
qc, | |||
warehouse, | |||
handleMailTemplateForStockInLine, | |||
printerCombo | |||
printerCombo, | |||
escalationCombo | |||
}) => { | |||
const { | |||
t, | |||
@@ -486,7 +488,6 @@ const [qcItems, setQcItems] = useState(dummyQCData) | |||
onSubmit={formProps.handleSubmit(onSubmitPutaway)} | |||
> | |||
<PutAwayForm | |||
printerCombo={printerCombo} | |||
itemDetail={itemDetail} | |||
warehouse={warehouse!} | |||
disabled={viewOnly} | |||
@@ -569,6 +570,7 @@ const [qcItems, setQcItems] = useState(dummyQCData) | |||
qc={qc!} | |||
itemDetail={itemDetail} | |||
disabled={viewOnly} | |||
escalationCombo={escalationCombo} | |||
// qcItems={qcItems} | |||
// setQcItems={setQcItems} | |||
/> | |||
@@ -17,7 +17,8 @@ | |||
"Purchase Order Code": "採購單號", | |||
"Item Name": "貨品名稱", | |||
"Escalation Level": "上報等級", | |||
"Reason": "原因", | |||
"Reason": "上報原因", | |||
"escalated date": "上報日期", | |||
"Order completion": "訂單完成度", | |||
"Raw material": "原料", | |||
"Consumable": "消耗品", | |||
@@ -32,6 +33,9 @@ | |||
"Processed application": "已處理提料申請", | |||
"Pending application": "待處理提料申請", | |||
"pending inspection material": "待品檢物料", | |||
"pending": "待處理", | |||
"rejected": "已拒絕", | |||
"escalated": "已上報", | |||
"inspected material": "已品檢物料", | |||
"total material": "物料總數", | |||
"stock in escalation list": "收貨已上報列表", | |||
@@ -40,8 +44,11 @@ | |||
"QC Fail Count": "品檢不合格數量", | |||
"DN Date": "送貨日期", | |||
"Received Qty": "收貨數量", | |||
"Po Code": "採購訂單編號", | |||
"My Escalation List": "我的上報列表", | |||
"Escalation List": "上報列表", | |||
"Purchase UoM": "計量單位", | |||
"QC Completed Count": "品檢完成數量", | |||
"QC Fail-Total Count": "品檢不合格/總數" | |||
"QC Fail-Total Count": "品檢不合格/總數", | |||
"escalationStatus": "上報狀態" | |||
} |
@@ -39,7 +39,7 @@ | |||
"qty": "訂單數量", | |||
"uom": "計量單位", | |||
"Stock UoM": "庫存單位", | |||
"Stock In Qty": "收貨數量", | |||
"Stock In Qty": "換算庫存數量", | |||
"total weight": "總重量", | |||
"weight unit": "重量單位", | |||
"price": "訂單貨值", | |||
@@ -105,7 +105,7 @@ | |||
"No Warehouse": "沒有倉庫", | |||
"Please scan warehouse qr code.": "請掃描倉庫 QR 碼。", | |||
"receivedQty": "已來貨數量", | |||
"dnQty": "送貨單數量", | |||
"dnQty": "本批來貨數量", | |||
"Accept submit": "接受來貨", | |||
"qc processing": "處理來貨及品檢", | |||
"putaway processing": "處理來貨及上架", | |||
@@ -138,5 +138,7 @@ | |||
"Printer": "列印機", | |||
"Printing": "列印中", | |||
"rejectQty": "拒絕數量", | |||
"QC decision is required": "請決定品檢結果" | |||
"QC decision is required": "請決定品檢結果", | |||
"No Option": "沒有選項", | |||
"receivedTotal": "已來貨總數" | |||
} |