@@ -6,7 +6,7 @@ import { serverFetchJson } from "../../utils/fetchUtil"; | |||||
import { BASE_API_URL } from "../../../config/api"; | import { BASE_API_URL } from "../../../config/api"; | ||||
import { Uom } from "../settings/uom"; | import { Uom } from "../settings/uom"; | ||||
import { RecordsRes } from "../utils"; | import { RecordsRes } from "../utils"; | ||||
import { IQCItems } from "@/components/DashboardPage/QC/SupervisorQcApproval"; | |||||
// import { IQCItems } from "@/components/DashboardPage/QC/SupervisorQcApproval"; | |||||
export interface PoResult { | export interface PoResult { | ||||
id: number; | id: number; | ||||
@@ -7,6 +7,7 @@ import { cache } from "react"; | |||||
export interface EscalationResult { | export interface EscalationResult { | ||||
id: number; | id: number; | ||||
personInCharge?: string; | |||||
personInChargeDepartment?: string; | personInChargeDepartment?: string; | ||||
personInChargeName?: string; | personInChargeName?: string; | ||||
personInChargeTitle?: string; | personInChargeTitle?: string; | ||||
@@ -17,6 +18,10 @@ export interface EscalationResult { | |||||
itemName?: string; | itemName?: string; | ||||
demandQty?: number; | demandQty?: number; | ||||
acceptedQty?: number; | acceptedQty?: number; | ||||
purchaseUomCode?: string; | |||||
purchaseUomDesc?: string; | |||||
stockUomCode?: string; | |||||
stockUomDesc?: string; | |||||
stockInLineId?: number; | stockInLineId?: number; | ||||
stockOutLineId?: number; | stockOutLineId?: number; | ||||
qcFailCount?: number; | qcFailCount?: number; | ||||
@@ -147,7 +147,7 @@ export interface GetPickOrderLineInfo { | |||||
itemId: number; | itemId: number; | ||||
itemCode: string; | itemCode: string; | ||||
itemName: string; | itemName: string; | ||||
availableQty: number; | |||||
availableQty: number | null; | |||||
requiredQty: number; | requiredQty: number; | ||||
uomCode: string; | uomCode: string; | ||||
uomDesc: string; | uomDesc: string; | ||||
@@ -14,8 +14,9 @@ import ApplicationCompletionChart from "./chart/ApplicationCompletionChart"; | |||||
import OrderCompletionChart from "./chart/OrderCompletionChart"; | import OrderCompletionChart from "./chart/OrderCompletionChart"; | ||||
import DashboardBox from "./Dashboardbox"; | import DashboardBox from "./Dashboardbox"; | ||||
import CollapsibleCard from "./CollapsibleCard"; | import CollapsibleCard from "./CollapsibleCard"; | ||||
import SupervisorQcApproval, { IQCItems } from "./QC/SupervisorQcApproval"; | |||||
// import SupervisorQcApproval, { IQCItems } from "./QC/SupervisorQcApproval"; | |||||
import { EscalationResult } from "@/app/api/escalation"; | import { EscalationResult } from "@/app/api/escalation"; | ||||
import EscalationLogTable from "./escalation/EscalationLogTable"; | |||||
type Props = { | type Props = { | ||||
// iqc: IQCItems[] | undefined | // iqc: IQCItems[] | undefined | ||||
escalationLogs: EscalationResult[] | escalationLogs: EscalationResult[] | ||||
@@ -32,9 +33,9 @@ const DashboardPage: React.FC<Props> = ({ | |||||
<ThemeProvider theme={theme}> | <ThemeProvider theme={theme}> | ||||
<Grid container spacing={2}> | <Grid container spacing={2}> | ||||
<Grid item xs={12}> | <Grid item xs={12}> | ||||
<CollapsibleCard title={t("stock in escalation list")}> | |||||
<CollapsibleCard title={t("Escalation List")}> | |||||
<CardContent> | <CardContent> | ||||
<SupervisorQcApproval items={escalationLogs || []}/> | |||||
<EscalationLogTable items={escalationLogs || []}/> | |||||
</CardContent> | </CardContent> | ||||
</CollapsibleCard> | </CollapsibleCard> | ||||
</Grid> | </Grid> | ||||
@@ -2,9 +2,13 @@ | |||||
import { Box, Card, CardActionArea, CardContent, CardHeader, Grid, Paper, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Typography } from "@mui/material"; | import { Box, Card, CardActionArea, CardContent, CardHeader, Grid, Paper, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Typography } from "@mui/material"; | ||||
import { useRouter } from "next/navigation"; | import { useRouter } from "next/navigation"; | ||||
import { useCallback, useState } from "react"; | |||||
import { useCallback, useMemo, useState } from "react"; | |||||
import { usePathname } from "next/navigation"; | import { usePathname } from "next/navigation"; | ||||
import { useTranslation } from "react-i18next"; | 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"; | |||||
export type IQCItems = { | export type IQCItems = { | ||||
id: number; | id: number; | ||||
@@ -18,11 +22,11 @@ export type IQCItems = { | |||||
}; | }; | ||||
type Props = { | type Props = { | ||||
items: IQCItems[]; | |||||
items: EscalationResult[]; | |||||
}; | }; | ||||
const SupervisorQcApproval: React.FC<Props> = ({ | |||||
items | |||||
const EscalationLogTable: React.FC<Props> = ({ | |||||
items | |||||
}) => { | }) => { | ||||
const { t } = useTranslation("dashboard"); | const { t } = useTranslation("dashboard"); | ||||
const CARD_HEADER = t("stock in escalation list") | const CARD_HEADER = t("stock in escalation list") | ||||
@@ -31,8 +35,8 @@ const SupervisorQcApproval: React.FC<Props> = ({ | |||||
const router = useRouter(); | const router = useRouter(); | ||||
const [selectedId, setSelectedId] = useState<number | null>(null); | const [selectedId, setSelectedId] = useState<number | null>(null); | ||||
const navigateTo = useCallback( | |||||
(item: IQCItems) => { | |||||
const navigateTo = useCallback( | |||||
(item: EscalationResult) => { | |||||
setSelectedId(item.id); | setSelectedId(item.id); | ||||
console.log(pathname) | console.log(pathname) | ||||
router.replace(`/po/edit?id=${item.poId}&polId=${item.polId}&stockInLineId=${item.stockInLineId}`); | router.replace(`/po/edit?id=${item.poId}&polId=${item.polId}&stockInLineId=${item.stockInLineId}`); | ||||
@@ -40,17 +44,62 @@ const navigateTo = useCallback( | |||||
[router, pathname] | [router, pathname] | ||||
); | ); | ||||
const handleKeyDown = useCallback( | |||||
(e: React.KeyboardEvent, item: IQCItems) => { | |||||
if (e.key === 'Enter' || e.key === ' ') { | |||||
e.preventDefault(); | |||||
navigateTo(item); | |||||
} | |||||
}, | |||||
[navigateTo] | |||||
); | |||||
const onRowClick = useCallback((item: EscalationResult) => { | |||||
router.push(`/po/edit?id=${item.poId}&selectedIds=${item.poId}&polId=${item.polId}&stockInLineId=${item.stockInLineId}`); | |||||
}, [router]) | |||||
// const handleKeyDown = useCallback( | |||||
// (e: React.KeyboardEvent, item: EscalationResult) => { | |||||
// if (e.key === 'Enter' || e.key === ' ') { | |||||
// e.preventDefault(); | |||||
// navigateTo(item); | |||||
// } | |||||
// }, | |||||
// [navigateTo] | |||||
// ); | |||||
return ( | |||||
const columns = useMemo<Column<EscalationResult>[]>( | |||||
() => [ | |||||
{ | |||||
name: "personInCharge", | |||||
label: t("Responsible for handling colleagues") | |||||
}, | |||||
{ | |||||
name: "acceptedQty", | |||||
label: t("Received Qty"), | |||||
align: "right", | |||||
headerAlign: "right" | |||||
}, | |||||
{ | |||||
name: "purchaseUomDesc", | |||||
label: t("Purchase UoM") | |||||
}, | |||||
{ | |||||
name: "dnDate", | |||||
label: t("DN Date"), | |||||
renderCell: (params) => { | |||||
return params.dnDate ? arrayToDateString(params.dnDate) : "N/A" | |||||
} | |||||
}, | |||||
{ | |||||
name: "qcTotalCount", | |||||
label: t("QC Completed Count"), | |||||
align: "right", | |||||
headerAlign: "right" | |||||
}, | |||||
{ | |||||
name: "qcFailCount", | |||||
label: t("QC Fail Count"), | |||||
align: "right", | |||||
headerAlign: "right" | |||||
}, | |||||
{ | |||||
name: "reason", | |||||
label: t("Reason"), | |||||
}, | |||||
], []) | |||||
{/* return ( | |||||
<TableContainer component={Paper}> | <TableContainer component={Paper}> | ||||
<Table aria-label="Two column navigable table" size="small"> | <Table aria-label="Two column navigable table" size="small"> | ||||
<TableHead> | <TableHead> | ||||
@@ -87,7 +136,15 @@ const navigateTo = useCallback( | |||||
</TableBody> | </TableBody> | ||||
</Table> | </Table> | ||||
</TableContainer> | </TableContainer> | ||||
); | |||||
}; | |||||
);*/} | |||||
return ( | |||||
<SearchResults | |||||
onRowClick={onRowClick} | |||||
items={items} | |||||
columns={columns} | |||||
isAutoPaging={false} | |||||
/> | |||||
) | |||||
}; | |||||
export default SupervisorQcApproval; | |||||
export default EscalationLogTable; |
@@ -210,6 +210,12 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => { | |||||
const searchParams = useSearchParams(); | const searchParams = useSearchParams(); | ||||
const [selectedRow, setSelectedRow] = useState<PurchaseOrderLine | null>(null); | const [selectedRow, setSelectedRow] = useState<PurchaseOrderLine | null>(null); | ||||
const defaultPolId = searchParams.get("polId") | |||||
useEffect(() => { | |||||
if (defaultPolId) { | |||||
setSelectedRow(rows.find((r) => r.id.toString() === defaultPolId) ?? null) | |||||
} | |||||
}, []) | |||||
const [stockInLine, setStockInLine] = useState<StockInLine[]>([]); | const [stockInLine, setStockInLine] = useState<StockInLine[]>([]); | ||||
const [processedQty, setProcessedQty] = useState(0); | const [processedQty, setProcessedQty] = useState(0); | ||||
@@ -270,6 +270,7 @@ function PoInputGrid({ | |||||
const [newOpen, setNewOpen] = useState(false); | const [newOpen, setNewOpen] = useState(false); | ||||
const stockInLineId = searchParams.get("stockInLineId"); | const stockInLineId = searchParams.get("stockInLineId"); | ||||
const poLineId = searchParams.get("poLineId"); | |||||
const closeNewModal = useCallback(() => { | const closeNewModal = useCallback(() => { | ||||
const newParams = new URLSearchParams(searchParams.toString()); | const newParams = new URLSearchParams(searchParams.toString()); | ||||
newParams.delete("stockInLineId"); // Remove the parameter | newParams.delete("stockInLineId"); // Remove the parameter | ||||
@@ -282,46 +283,51 @@ const closeNewModal = useCallback(() => { | |||||
// Open modal | // Open modal | ||||
const openNewModal = useCallback(() => { | const openNewModal = useCallback(() => { | ||||
setNewOpen(true); | |||||
setNewOpen(() => true); | |||||
}, []); | }, []); | ||||
// Button handler to update the URL and open the modal | // Button handler to update the URL and open the modal | ||||
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) | // console.log(params) | ||||
setBtnIsLoading(true); | |||||
// setBtnIsLoading(true); | |||||
setRowModesModel((prev) => ({ | setRowModesModel((prev) => ({ | ||||
...prev, | ...prev, | ||||
[id]: { mode: GridRowModes.View }, | [id]: { mode: GridRowModes.View }, | ||||
})); | })); | ||||
const qcResult = await fetchQcDefaultValue(id); | const qcResult = await fetchQcDefaultValue(id); | ||||
setModalInfo({ | |||||
setModalInfo(() => ({ | |||||
...params.row, | ...params.row, | ||||
qcResult: qcResult, | qcResult: qcResult, | ||||
receivedQty: itemDetail.receivedQty, | receivedQty: itemDetail.receivedQty, | ||||
}); | |||||
})); | |||||
setTimeout(() => { | setTimeout(() => { | ||||
const newParams = new URLSearchParams(searchParams.toString()); | const newParams = new URLSearchParams(searchParams.toString()); | ||||
newParams.set("stockInLineId", id.toString()); // Ensure `set` to avoid duplicates | newParams.set("stockInLineId", id.toString()); // Ensure `set` to avoid duplicates | ||||
router.replace(`${pathname}?${newParams.toString()}`); | router.replace(`${pathname}?${newParams.toString()}`); | ||||
console.log("hello") | |||||
// console.log("hello") | |||||
openNewModal() | openNewModal() | ||||
setBtnIsLoading(false); | |||||
// setBtnIsLoading(false); | |||||
}, 200); | }, 200); | ||||
}, | }, | ||||
[fetchQcDefaultValue, openNewModal, pathname, router, searchParams] | [fetchQcDefaultValue, openNewModal, pathname, router, searchParams] | ||||
); | ); | ||||
// Open modal if `stockInLineId` exists in the URL | // Open modal if `stockInLineId` exists in the URL | ||||
const [firstCheckForSil, setFirstCheckForSil] = useState(false) | |||||
useEffect(() => { | useEffect(() => { | ||||
if (stockInLineId) { | |||||
console.log("heeloo") | |||||
console.log(stockInLineId) | |||||
handleNewQC(stockInLineId, apiRef.current.getRow(stockInLineId)); | |||||
if (stockInLineId && itemDetail && !firstCheckForSil) { | |||||
// console.log("heeloo") | |||||
// console.log(stockInLineId) | |||||
// console.log(apiRef.current.getRow(stockInLineId)) | |||||
setFirstCheckForSil(true) | |||||
const fn = handleNewQC(stockInLineId, {row: apiRef.current.getRow(stockInLineId)}); | |||||
fn(); | |||||
} | } | ||||
}, [stockInLineId, newOpen, handleNewQC, apiRef]); | |||||
}, [stockInLineId, poLineId, itemDetail]); | |||||
const handleEscalation = useCallback( | const handleEscalation = useCallback( | ||||
(id: GridRowId, params: any) => () => { | (id: GridRowId, params: any) => () => { | ||||
// setBtnIsLoading(true); | // setBtnIsLoading(true); | ||||
@@ -580,7 +586,7 @@ const closeNewModal = useCallback(() => { | |||||
}} | }} | ||||
onClick={handleNewQC(params.row.id, params)} | onClick={handleNewQC(params.row.id, params)} | ||||
color="inherit" | color="inherit" | ||||
key="edit" | |||||
key={`edit`} | |||||
/>, | />, | ||||
<GridActionsCellItem | <GridActionsCellItem | ||||
icon={<Button | icon={<Button | ||||
@@ -70,7 +70,6 @@ const PoQcStockInModalVer2: React.FC<Props> = ({ | |||||
qc, | qc, | ||||
warehouse, | warehouse, | ||||
}) => { | }) => { | ||||
console.log(warehouse); | |||||
const { | const { | ||||
t, | t, | ||||
i18n: { language }, | i18n: { language }, | ||||
@@ -116,8 +115,10 @@ const [qcItems, setQcItems] = useState(dummyQCData) | |||||
const [viewOnly, setViewOnly] = useState(false); | const [viewOnly, setViewOnly] = useState(false); | ||||
useEffect(() => { | useEffect(() => { | ||||
const isViewOnly = itemDetail.status.toLowerCase() == "completed" || itemDetail.status.toLowerCase() == "rejected" | |||||
setViewOnly(isViewOnly) | |||||
if (itemDetail && itemDetail.status) { | |||||
const isViewOnly = itemDetail.status.toLowerCase() == "completed" || itemDetail.status.toLowerCase() == "rejected" | |||||
setViewOnly(isViewOnly) | |||||
} | |||||
}, [itemDetail]); | }, [itemDetail]); | ||||
const [openPutaway, setOpenPutaway] = useState(false); | const [openPutaway, setOpenPutaway] = useState(false); | ||||
@@ -311,10 +311,10 @@ const StockInFormVer2: React.FC<Props> = ({ | |||||
<TextField | <TextField | ||||
label={t("uom")} | label={t("uom")} | ||||
fullWidth | fullWidth | ||||
{...register("uom", { | |||||
{...register("uom.code", { | |||||
required: "uom required!", | required: "uom required!", | ||||
})} | })} | ||||
value={uom.code} | |||||
// value={uom?.code} | |||||
disabled={true} | disabled={true} | ||||
/> | /> | ||||
</Grid> | </Grid> | ||||
@@ -1,39 +1,46 @@ | |||||
{ | { | ||||
"Dashboard": "資訊展示面板", | |||||
"Order status": "訂單狀態", | |||||
"pending": "未收貨", | |||||
"receiving": "收貨中", | |||||
"total": "未完成總數", | |||||
"Warehouse temperature record": "倉庫溫度記錄", | |||||
"Warehouse type": "倉庫類型", | |||||
"Last 6 hours": "過去6小時", | |||||
"Last 24 hours": "過去24小時", | |||||
"Cold storage": "冷藏倉", | |||||
"Normal temperature storage": "常溫倉", | |||||
"Temperature status": "溫度狀態", | |||||
"Humidity status": "濕度狀態", | |||||
"Warehouse status": "倉庫狀態", | |||||
"Progress chart": "進度圖表", | |||||
"Purchase Order Code": "採購單號", | |||||
"Item Name": "貨品名稱", | |||||
"Escalation Level": "上報等級", | |||||
"Reason": "原因", | |||||
"Order completion": "訂單完成度", | |||||
"Raw material": "原料", | |||||
"Consumable": "消耗品", | |||||
"Shipment": "出貨", | |||||
"Extracted order": "已提取提料單", | |||||
"Pending order": "待提取提料單", | |||||
"Temperature": "溫度", | |||||
"Humidity": "濕度", | |||||
"Pending storage": "待入倉物料", | |||||
"Total storage": "已入倉物料", | |||||
"Application completion": "提料申請完成度", | |||||
"Processed application": "已處理提料申請", | |||||
"Pending application": "待處理提料申請", | |||||
"pending inspection material": "待品檢物料", | |||||
"inspected material": "已品檢物料", | |||||
"total material": "物料總數", | |||||
"stock in escalation list": "收貨已上報列表" | |||||
} | |||||
"Dashboard": "資訊展示面板", | |||||
"Order status": "訂單狀態", | |||||
"pending": "未收貨", | |||||
"receiving": "收貨中", | |||||
"total": "未完成總數", | |||||
"Warehouse temperature record": "倉庫溫度記錄", | |||||
"Warehouse type": "倉庫類型", | |||||
"Last 6 hours": "過去6小時", | |||||
"Last 24 hours": "過去24小時", | |||||
"Cold storage": "冷藏倉", | |||||
"Normal temperature storage": "常溫倉", | |||||
"Temperature status": "溫度狀態", | |||||
"Humidity status": "濕度狀態", | |||||
"Warehouse status": "倉庫狀態", | |||||
"Progress chart": "進度圖表", | |||||
"Purchase Order Code": "採購單號", | |||||
"Item Name": "貨品名稱", | |||||
"Escalation Level": "上報等級", | |||||
"Reason": "原因", | |||||
"Order completion": "訂單完成度", | |||||
"Raw material": "原料", | |||||
"Consumable": "消耗品", | |||||
"Shipment": "出貨", | |||||
"Extracted order": "已提取提料單", | |||||
"Pending order": "待提取提料單", | |||||
"Temperature": "溫度", | |||||
"Humidity": "濕度", | |||||
"Pending storage": "待入倉物料", | |||||
"Total storage": "已入倉物料", | |||||
"Application completion": "提料申請完成度", | |||||
"Processed application": "已處理提料申請", | |||||
"Pending application": "待處理提料申請", | |||||
"pending inspection material": "待品檢物料", | |||||
"inspected material": "已品檢物料", | |||||
"total material": "物料總數", | |||||
"stock in escalation list": "收貨已上報列表", | |||||
"Responsible for handling colleagues": "負責處理同事", | |||||
"Completed QC Total": "品檢完成數量", | |||||
"QC Fail Count": "品檢不合格數量", | |||||
"DN Date": "送貨日期", | |||||
"Received Qty": "收貨數量", | |||||
"Escalation List": "上報列表", | |||||
"Purchase UoM": "計量單位", | |||||
"QC Completed Count": "品檢完成數量" | |||||
} |