From 3ebdbaa3bbcbe0ea1e73d59e72da59bf99bf2318 Mon Sep 17 00:00:00 2001 From: "MSI\\derek" Date: Wed, 13 Aug 2025 17:29:23 +0800 Subject: [PATCH] update supervisor approval log --- src/app/api/dashboard/index.ts | 7 ++ .../DashboardPage/DashboardPage.tsx | 16 +++- .../DashboardPage/DashboardWrapper.tsx | 8 +- .../DashboardPage/QC/SupervisorQcApproval.tsx | 93 +++++++++++++++++++ src/components/PoDetail/PoDetail.tsx | 62 +++++++++---- src/components/PoDetail/PoInputGrid.tsx | 22 +++-- .../SearchResults/SearchResults.tsx | 2 +- src/i18n/zh/dashboard.json | 4 +- 8 files changed, 183 insertions(+), 31 deletions(-) create mode 100644 src/components/DashboardPage/QC/SupervisorQcApproval.tsx diff --git a/src/app/api/dashboard/index.ts b/src/app/api/dashboard/index.ts index 9989faf..2bbb799 100644 --- a/src/app/api/dashboard/index.ts +++ b/src/app/api/dashboard/index.ts @@ -6,6 +6,7 @@ import { serverFetchJson } from "../../utils/fetchUtil"; import { BASE_API_URL } from "../../../config/api"; import { Uom } from "../settings/uom"; import { RecordsRes } from "../utils"; +import { IQCItems } from "@/components/DashboardPage/QC/SupervisorQcApproval"; export interface PoResult { id: number; @@ -81,3 +82,9 @@ export const fetchPoWithStockInLines = cache(async (id: number) => { next: { tags: ["po"] }, }); }); + +export const fetchIqcLogByUser = cache(async () => { + return serverFetchJson(`${BASE_API_URL}/supervisionApprovalLog/stock-in`, { + next: { tags: ["qcLog"] }, + }); +}); diff --git a/src/components/DashboardPage/DashboardPage.tsx b/src/components/DashboardPage/DashboardPage.tsx index 6be3a93..23c7a15 100644 --- a/src/components/DashboardPage/DashboardPage.tsx +++ b/src/components/DashboardPage/DashboardPage.tsx @@ -14,15 +14,27 @@ import ApplicationCompletionChart from "./chart/ApplicationCompletionChart"; import OrderCompletionChart from "./chart/OrderCompletionChart"; import DashboardBox from "./Dashboardbox"; import CollapsibleCard from "./CollapsibleCard"; -type Props = {}; +import SupervisorQcApproval, { IQCItems } from "./QC/SupervisorQcApproval"; +type Props = { + iqc: IQCItems[] +}; -const DashboardPage: React.FC = ({}) => { +const DashboardPage: React.FC = ({ + iqc +}) => { const { t } = useTranslation("dashboard"); const router = useRouter(); return ( + + + + + + + diff --git a/src/components/DashboardPage/DashboardWrapper.tsx b/src/components/DashboardPage/DashboardWrapper.tsx index 87bf240..4b35ed1 100644 --- a/src/components/DashboardPage/DashboardWrapper.tsx +++ b/src/components/DashboardPage/DashboardWrapper.tsx @@ -4,6 +4,7 @@ import DashboardPage from "./DashboardPage"; import { Typography } from "@mui/material"; import { I18nProvider, getServerI18n } from "@/i18n"; import DashboardLoading from "./DashboardLoading"; +import { fetchIqcLogByUser } from "@/app/api/dashboard"; // export type SessionWithAbilities = { // abilities: string[] @@ -22,11 +23,16 @@ const DashboardWrapper: React.FC & SubComponents = async ({ }) => { const { t } = await getServerI18n("dashboard"); // const session: SessionWithAbilities = await getServerSession(authOptions) - + const [iqcLog] = await Promise.all([ + fetchIqcLogByUser() + ]) + + console.log(iqcLog) return ( <> {t("Dashboard")} diff --git a/src/components/DashboardPage/QC/SupervisorQcApproval.tsx b/src/components/DashboardPage/QC/SupervisorQcApproval.tsx new file mode 100644 index 0000000..35a4136 --- /dev/null +++ b/src/components/DashboardPage/QC/SupervisorQcApproval.tsx @@ -0,0 +1,93 @@ +"use client" + +import { Box, Card, CardActionArea, CardContent, CardHeader, Grid, Paper, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Typography } from "@mui/material"; +import { useRouter } from "next/navigation"; +import { useCallback, useState } from "react"; +import { usePathname } from "next/navigation"; +import { useTranslation } from "react-i18next"; + +export type IQCItems = { + id: number; + poId: number; + polId: number; + stockInLineId: number; + poCode: string + itemName: string + escalationLevel: string + reason: string +}; + +type Props = { + items: IQCItems[]; +}; + +const SupervisorQcApproval: React.FC = ({ + items +}) => { + const { t } = useTranslation("dashboard"); + const CARD_HEADER = t("stock in escalation list") + + const pathname = usePathname(); + const router = useRouter(); + const [selectedId, setSelectedId] = useState(null); + +const navigateTo = useCallback( + (item: IQCItems) => { + setSelectedId(item.id); + console.log(pathname) + router.replace(`/po/edit?id=${item.poId}&polId=${item.polId}&stockInLineId=${item.stockInLineId}`); + }, + [router, pathname] + ); + + const handleKeyDown = useCallback( + (e: React.KeyboardEvent, item: IQCItems) => { + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); + navigateTo(item); + } + }, + [navigateTo] + ); + + return ( + + + + + {t("purchase order code")} + {t("item name")} + {t("escalation level")} + {t("reason")} + + + + {items.map((item) => { + const selected = selectedId === item.id; + return ( + navigateTo(item)} + // onKeyDown={(e) => handleKeyDown(e, item)} + tabIndex={0} + sx={{ cursor: 'pointer' }} + // aria-label={`${item.name}, ${item.detail}`} + > + + {item.poCode} + + {item.itemName} + {item.escalationLevel} + {item.reason} + + ); + })} + +
+
+ ); + }; + +export default SupervisorQcApproval; diff --git a/src/components/PoDetail/PoDetail.tsx b/src/components/PoDetail/PoDetail.tsx index 4fba6f3..ed866a7 100644 --- a/src/components/PoDetail/PoDetail.tsx +++ b/src/components/PoDetail/PoDetail.tsx @@ -66,9 +66,6 @@ import DoneIcon from "@mui/icons-material/Done"; import { getCustomWidth } from "@/app/utils/commonUtil"; import PoInfoCard from "./PoInfoCard"; import { decimalFormatter, integerFormatter } from "@/app/utils/formatUtil"; - - - import { fetchPoListClient } from "@/app/api/po/actions"; import { List, ListItem, ListItemButton, ListItemText, Divider } from "@mui/material"; //import { useRouter } from "next/navigation"; @@ -181,20 +178,13 @@ const PoDetail: React.FC = ({ po, qc, warehouse }) => { purchaseOrder.pol || [], ); const pathname = usePathname() - const router = useRouter(); const searchParams = useSearchParams(); const [row, setRow] = useState(rows[0]); - const [stockInLine, setStockInLine] = useState(rows[0].stockInLine); + const [stockInLine, setStockInLine] = useState([]); const [processedQty, setProcessedQty] = useState(rows[0].processed); - // const [currPoStatus, setCurrPoStatus] = useState(purchaseOrder.status); - - - - - - //const router = useRouter(); + const router = useRouter(); const [poList, setPoList] = useState([]); const [selectedPoId, setSelectedPoId] = useState(po.id); const currentPoId = searchParams.get('id'); @@ -297,14 +287,18 @@ const PoDetail: React.FC = ({ po, qc, warehouse }) => { setRows(purchaseOrder.pol || []); }, [purchaseOrder]); + useEffect(() => { + setStockInLine([]) + }, []); + function Row(props: { row: PurchaseOrderLine }) { const { row } = props; - const [firstReceiveQty, setFirstReceiveQty] = useState() + // const [firstReceiveQty, setFirstReceiveQty] = useState() const [secondReceiveQty, setSecondReceiveQty] = useState() - const [open, setOpen] = useState(false); + // const [open, setOpen] = useState(false); const [processedQty, setProcessedQty] = useState(row.processed); const [currStatus, setCurrStatus] = useState(row.status); - const [stockInLine, setStockInLine] = useState(row.stockInLine); + // const [stockInLine, setStockInLine] = useState(row.stockInLine); const totalWeight = useMemo( () => calculateWeight(row.qty, row.uom), [row.qty, row.uom], @@ -314,6 +308,13 @@ const PoDetail: React.FC = ({ po, qc, warehouse }) => { [row.uom], ); + useEffect(() => { + const polId = searchParams.get("polId") != null ? parseInt(searchParams.get("polId")!) : null + if (polId) { + setStockInLine(rows.find((r) => r.id == polId)!.stockInLine) + } + }, []); + useEffect(() => { if (processedQty === row.qty) { setCurrStatus("completed".toUpperCase()); @@ -324,9 +325,29 @@ const PoDetail: React.FC = ({ po, qc, warehouse }) => { } }, [processedQty, row.qty]); + const changeStockInLines = useCallback( + (id: number) => { + console.log(id) + //rows = purchaseOrderLine + const target = rows.find((r) => r.id === id) + const stockInLine = target!.stockInLine + console.log(stockInLine) + console.log(stockInLine) + setStockInLine(stockInLine) + // console.log(pathname) + // router.replace(`/po/edit?id=${item.poId}&polId=${item.polId}&stockInLineId=${item.stockInLineId}`); + }, + [] + ); + return ( <> - *": { borderBottom: "unset" }, color: "black" }}> + *": { borderBottom: "unset" }, + color: "black" + }} + onClick={() => changeStockInLines(row.id)} + > {/* = ({ po, qc, warehouse }) => { const renderFieldCondition = useCallback((field: "firstInQty" | "secondInQty"): boolean => { switch (field) { case FIRST_IN_FIELD: - return true; case SECOND_IN_FIELD: return true; @@ -499,6 +519,14 @@ const PoDetail: React.FC = ({ po, qc, warehouse }) => { } }, []); + useEffect(() => { + const params = searchParams.get("polId") + if (params) { + const polId = parseInt(params) + + } + }, [searchParams]) + return ( <> diff --git a/src/components/PoDetail/PoInputGrid.tsx b/src/components/PoDetail/PoInputGrid.tsx index 0dc4762..e16affc 100644 --- a/src/components/PoDetail/PoInputGrid.tsx +++ b/src/components/PoDetail/PoInputGrid.tsx @@ -278,16 +278,11 @@ const closeNewModal = useCallback(() => { setNewOpen(true); }, []); - // Open modal if `stockInLineId` exists in the URL - useEffect(() => { - if (stockInLineId && !newOpen) { - openNewModal(); - } - }, [stockInLineId, newOpen, openNewModal]); - // Button handler to update the URL and open the modal const handleNewQC = useCallback( (id: GridRowId, params: any) => async () => { + console.log(id) + console.log(params) setBtnIsLoading(true); setRowModesModel((prev) => ({ ...prev, @@ -304,12 +299,21 @@ const closeNewModal = useCallback(() => { const newParams = new URLSearchParams(searchParams.toString()); newParams.set("stockInLineId", id.toString()); // Ensure `set` to avoid duplicates router.replace(`${pathname}?${newParams.toString()}`); + console.log("hello") + openNewModal() setBtnIsLoading(false); }, 200); }, - [fetchQcDefaultValue, searchParams, router, pathname] + [fetchQcDefaultValue, openNewModal, pathname, router, searchParams] ); - + // Open modal if `stockInLineId` exists in the URL + useEffect(() => { + if (stockInLineId) { + console.log("heeloo") + console.log(stockInLineId) + handleNewQC(stockInLineId, apiRef.current.getRow(stockInLineId)); + } + }, [stockInLineId, newOpen, handleNewQC, apiRef]); const handleEscalation = useCallback( (id: GridRowId, params: any) => () => { // setBtnIsLoading(true); diff --git a/src/components/SearchResults/SearchResults.tsx b/src/components/SearchResults/SearchResults.tsx index bb082f0..c1dde62 100644 --- a/src/components/SearchResults/SearchResults.tsx +++ b/src/components/SearchResults/SearchResults.tsx @@ -281,7 +281,7 @@ function SearchResults({ setCheckboxIds(newSelected); } }, - [checkboxIds], + [checkboxIds, setCheckboxIds], ); const handleSelectAllClick = (event: React.ChangeEvent) => { diff --git a/src/i18n/zh/dashboard.json b/src/i18n/zh/dashboard.json index b678a19..bccb67c 100644 --- a/src/i18n/zh/dashboard.json +++ b/src/i18n/zh/dashboard.json @@ -29,5 +29,7 @@ "Pending application": "待處理提料申請", "pending inspection material": "待品檢物料", "inspected material": "已品檢物料", - "total material": "物料總數" + "total material": "物料總數", + + "stock in escalation list": "收貨已上報列表" }