From 3ebdbaa3bbcbe0ea1e73d59e72da59bf99bf2318 Mon Sep 17 00:00:00 2001 From: "MSI\\derek" Date: Wed, 13 Aug 2025 17:29:23 +0800 Subject: [PATCH 1/7] 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": "收貨已上報列表" } From ef1079313116583dcc195cc18b548958775dd7be Mon Sep 17 00:00:00 2001 From: "MSI\\derek" Date: Wed, 13 Aug 2025 17:59:56 +0800 Subject: [PATCH 2/7] no message --- src/components/PoDetail/PoDetail.tsx | 29 +++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/components/PoDetail/PoDetail.tsx b/src/components/PoDetail/PoDetail.tsx index ed866a7..b7780cf 100644 --- a/src/components/PoDetail/PoDetail.tsx +++ b/src/components/PoDetail/PoDetail.tsx @@ -68,6 +68,7 @@ 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 { createStockInLine } from "@/app/api/dashboard/actions"; //import { useRouter } from "next/navigation"; @@ -340,6 +341,27 @@ const PoDetail: React.FC = ({ po, qc, warehouse }) => { [] ); + const handleStart = useCallback( + () => { + setTimeout(async () => { + // post stock in line + const oldId = row.id; + const postData = { + itemId: row.itemId, + itemNo: row.itemNo, + itemName: row.itemName, + purchaseOrderId: row.purchaseOrderId, + purchaseOrderLineId: row.id, + acceptedQty: secondReceiveQty || 0, + // acceptedQty: row.acceptedQty, + }; + if (postData.acceptedQty === 0) return + const res = await createStockInLine(postData); + console.log(res); + }, 200); + }, + [], + ); return ( <> = ({ po, qc, warehouse }) => { /> - From 5381ad80e8246b515bfe1bad287381d597cf38a4 Mon Sep 17 00:00:00 2001 From: "MSI\\derek" Date: Wed, 13 Aug 2025 18:04:32 +0800 Subject: [PATCH 3/7] update --- src/components/PoDetail/PoDetail.tsx | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/src/components/PoDetail/PoDetail.tsx b/src/components/PoDetail/PoDetail.tsx index b7780cf..a258c5a 100644 --- a/src/components/PoDetail/PoDetail.tsx +++ b/src/components/PoDetail/PoDetail.tsx @@ -355,13 +355,34 @@ const PoDetail: React.FC = ({ po, qc, warehouse }) => { acceptedQty: secondReceiveQty || 0, // acceptedQty: row.acceptedQty, }; - if (postData.acceptedQty === 0) return + if (secondReceiveQty === 0) return const res = await createStockInLine(postData); console.log(res); }, 200); }, [], ); + + const handleChange = (e: React.ChangeEvent) => { + const raw = e.target.value; + + // Allow empty input + if (raw.trim() === '') { + setSecondReceiveQty(undefined); + return; + } + + // Keep digits only + const cleaned = raw.replace(/[^\d]/g, ''); + if (cleaned === '') { + // If the user typed only non-digits, keep previous value + return; + } + + // Parse and clamp to non-negative integer + const next = Math.max(0, Math.floor(Number(cleaned))); + setSecondReceiveQty(next); +}; return ( <> = ({ po, qc, warehouse }) => { type="text" // Use type="text" to allow validation in the change handler variant="outlined" value={secondReceiveQty} - // onChange={handleChange} + onChange={handleChange} InputProps={{ inputProps: { min: 0, // Optional: set a minimum value From a9551005714c62bbe3eb2568c9dec632cfa524a9 Mon Sep 17 00:00:00 2001 From: "MSI\\derek" Date: Wed, 13 Aug 2025 18:05:42 +0800 Subject: [PATCH 4/7] update --- src/components/PoDetail/PoDetail.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/PoDetail/PoDetail.tsx b/src/components/PoDetail/PoDetail.tsx index a258c5a..e8d7bf7 100644 --- a/src/components/PoDetail/PoDetail.tsx +++ b/src/components/PoDetail/PoDetail.tsx @@ -609,12 +609,12 @@ const PoDetail: React.FC = ({ po, qc, warehouse }) => { type="text" variant="outlined" fullWidth - InputProps={{ - inputProps: { - min: 0, - step: 1 - } - }} + // InputProps={{ + // inputProps: { + // min: 0, + // step: 1 + // } + // }} /> Date: Thu, 14 Aug 2025 18:39:14 +0800 Subject: [PATCH 5/7] add qc category to item master, update inventory --- src/app/api/settings/qcCategory/index.ts | 12 ++++++ src/app/api/shop/index.ts | 20 ++++++++++ src/components/CreateItem/CreateItem.tsx | 5 ++- .../CreateItem/CreateItemWrapper.tsx | 5 +++ src/components/CreateItem/ProductDetails.tsx | 40 +++++++++++++++++-- .../InventorySearch/InventoryLotLineTable.tsx | 28 ++++++------- .../InventorySearch/InventoryTable.tsx | 28 ++++++------- src/i18n/zh/inventory.json | 5 ++- src/i18n/zh/items.json | 1 + 9 files changed, 109 insertions(+), 35 deletions(-) create mode 100644 src/app/api/shop/index.ts diff --git a/src/app/api/settings/qcCategory/index.ts b/src/app/api/settings/qcCategory/index.ts index 451572c..b96bb9c 100644 --- a/src/app/api/settings/qcCategory/index.ts +++ b/src/app/api/settings/qcCategory/index.ts @@ -9,6 +9,12 @@ export interface QcCategoryResult { name: string; } +export interface QcCategoryCombo { + id: number; + value: number; + label: string; +} + export const preloadQcCategory = () => { fetchQcCategories(); }; @@ -18,3 +24,9 @@ export const fetchQcCategories = cache(async () => { next: { tags: ["qcCategories"] }, }); }); + +export const fetchQcCategoryCombo = cache(async () => { + return serverFetchJson(`${BASE_API_URL}/qcCategories/combo`, { + next: { tags: ["qcCategoryCombo"] }, + }); +}); diff --git a/src/app/api/shop/index.ts b/src/app/api/shop/index.ts new file mode 100644 index 0000000..d74cc1b --- /dev/null +++ b/src/app/api/shop/index.ts @@ -0,0 +1,20 @@ +"server only" +import { BASE_API_URL } from '@/config/api'; +import { serverFetchJson } from '@/app/utils/fetchUtil'; +import { cache } from "react"; + +export interface ShopCombo { + id: number; + value: number; // id + label: string; +} + +export const fetchSupplierCombo = cache(async() => { + return serverFetchJson(`${BASE_API_URL}/shop/combo/supplier`, { + method: "GET", + headers: { "Content-Type": "application/json"}, + next: { + tags: ["supplierCombo"] + } + }) +}) \ No newline at end of file diff --git a/src/components/CreateItem/CreateItem.tsx b/src/components/CreateItem/CreateItem.tsx index 7b8f592..1572e3f 100644 --- a/src/components/CreateItem/CreateItem.tsx +++ b/src/components/CreateItem/CreateItem.tsx @@ -29,12 +29,14 @@ import QcDetails from "./QcDetails"; import { ItemQc } from "@/app/api/settings/item"; import { saveItemQcChecks } from "@/app/api/settings/qcCheck/actions"; import { useGridApiRef } from "@mui/x-data-grid"; +import { QcCategoryCombo } from "@/app/api/settings/qcCategory"; type Props = { isEditMode: boolean; // type: TypeEnum; defaultValues: Partial | undefined; qcChecks: ItemQc[]; + qcCategoryCombo: QcCategoryCombo[] }; const CreateItem: React.FC = ({ @@ -42,6 +44,7 @@ const CreateItem: React.FC = ({ // type, defaultValues, qcChecks, + qcCategoryCombo, }) => { // console.log(type) const apiRef = useGridApiRef(); @@ -192,7 +195,7 @@ const CreateItem: React.FC = ({ {serverError} )} - {tabIndex === 0 && } + {tabIndex === 0 && } {tabIndex === 1 && } {/* {type === TypeEnum.MATERIAL && } */} {/* {type === TypeEnum.BYPRODUCT && } */} diff --git a/src/components/CreateItem/CreateItemWrapper.tsx b/src/components/CreateItem/CreateItemWrapper.tsx index 23eb9b8..c8aeb35 100644 --- a/src/components/CreateItem/CreateItemWrapper.tsx +++ b/src/components/CreateItem/CreateItemWrapper.tsx @@ -5,6 +5,7 @@ import { CreateItemInputs } from "@/app/api/settings/item/actions"; import { notFound } from "next/navigation"; import { fetchItem } from "@/app/api/settings/item"; import { fetchQcItems } from "@/app/api/settings/qcItem"; +import { fetchQcCategoryCombo } from "@/app/api/settings/qcCategory"; interface SubComponents { Loading: typeof CreateItemLoading; } @@ -37,14 +38,18 @@ const CreateItemWrapper: React.FC & SubComponents = async ({ id }) => { maxQty: item?.maxQty, qcChecks: qcChecks, qcChecks_active: activeRows, + qcCategoryId: item.qcCategory?.id }; } + const qcCategoryCombo = await fetchQcCategoryCombo(); + return ( ); }; diff --git a/src/components/CreateItem/ProductDetails.tsx b/src/components/CreateItem/ProductDetails.tsx index 93dae61..12a05d7 100644 --- a/src/components/CreateItem/ProductDetails.tsx +++ b/src/components/CreateItem/ProductDetails.tsx @@ -1,5 +1,6 @@ "use client"; import { + Autocomplete, Box, Button, Card, @@ -10,11 +11,11 @@ import { Typography, } from "@mui/material"; import { Check, Close, EditNote } from "@mui/icons-material"; -import { useFormContext } from "react-hook-form"; +import { Controller, useFormContext } from "react-hook-form"; import { useTranslation } from "react-i18next"; import InputDataGrid from "../InputDataGrid"; -import { useCallback, useMemo, useState } from "react"; +import { SyntheticEvent, useCallback, useMemo, useState } from "react"; import { GridColDef, GridRowModel } from "@mui/x-data-grid"; import { InputDataGridProps, TableRow } from "../InputDataGrid/InputDataGrid"; import { TypeEnum } from "@/app/utils/typeEnum"; @@ -22,6 +23,7 @@ import { NumberInputProps } from "./NumberInputProps"; import { CreateItemInputs } from "@/app/api/settings/item/actions"; import { RestartAlt } from "@mui/icons-material"; import { ItemQc } from "@/app/api/settings/item"; +import { QcCategoryCombo } from "@/app/api/settings/qcCategory"; type Props = { // isEditMode: boolean; // type: TypeEnum; @@ -29,9 +31,10 @@ type Props = { // type: TypeEnum; defaultValues?: Partial | undefined; qcChecks?: ItemQc[]; + qcCategoryCombo: QcCategoryCombo[]; }; -const ProductDetails: React.FC = ({ isEditMode }) => { +const ProductDetails: React.FC = ({ isEditMode, qcCategoryCombo }) => { const { t, i18n: { language }, @@ -104,6 +107,11 @@ const ProductDetails: React.FC = ({ isEditMode }) => { // router.replace(`/settings/product`); console.log("cancel"); }; + + const handleAutoCompleteChange = useCallback((event: SyntheticEvent, value: QcCategoryCombo, onChange: (...event: any[]) => void) => { + onChange(value.id) +}, []) + return ( @@ -202,7 +210,31 @@ const ProductDetails: React.FC = ({ isEditMode }) => { helperText={errors.maxQty?.message} />
- + + ( + qc.id === field.value)} + onChange={(event, value) => { + handleAutoCompleteChange(event, value, field.onChange) + }} + onBlur={field.onBlur} + renderInput={(params) => ( + + )} + /> + )} + /> + + = ({ inventoryLotLines, pagingContr }, { name: "uom", - label: t("Sales UoM"), - align: "left", - headerAlign: "left", - }, - { - name: "qtyPerSmallestUnit", - label: t("Available Qty Per Smallest Unit"), - align: "right", - headerAlign: "right", - type: "integer", - }, - { - name: "baseUom", - label: t("Base UoM"), + label: t("Stock UoM"), align: "left", headerAlign: "left", }, + // { + // name: "qtyPerSmallestUnit", + // label: t("Available Qty Per Smallest Unit"), + // align: "right", + // headerAlign: "right", + // type: "integer", + // }, + // { + // name: "baseUom", + // label: t("Base UoM"), + // align: "left", + // headerAlign: "left", + // }, { name: "expiryDate", label: t("Expiry Date"), diff --git a/src/components/InventorySearch/InventoryTable.tsx b/src/components/InventorySearch/InventoryTable.tsx index 8bdc452..6f98767 100644 --- a/src/components/InventorySearch/InventoryTable.tsx +++ b/src/components/InventorySearch/InventoryTable.tsx @@ -41,25 +41,25 @@ const InventoryTable: React.FC = ({ inventories, pagingController, setPag }, { name: "uomUdfudesc", - label: t("Sales UoM"), - align: "left", - headerAlign: "left", - }, - { - name: "qtyPerSmallestUnit", - label: t("Available Qty Per Smallest Unit"), - align: "right", - headerAlign: "right", - type: "integer", - }, - { - name: "baseUom", - label: t("Base UoM"), + label: t("Stock UoM"), align: "left", headerAlign: "left", }, // { // name: "qtyPerSmallestUnit", + // label: t("Available Qty Per Smallest Unit"), + // align: "right", + // headerAlign: "right", + // type: "integer", + // }, + // { + // name: "baseUom", + // label: t("Base UoM"), + // align: "left", + // headerAlign: "left", + // }, + // { + // name: "qtyPerSmallestUnit", // label: t("Qty Per Smallest Unit"), // align: "right", // headerAlign: "right", diff --git a/src/i18n/zh/inventory.json b/src/i18n/zh/inventory.json index e776f16..34df3b9 100644 --- a/src/i18n/zh/inventory.json +++ b/src/i18n/zh/inventory.json @@ -8,12 +8,13 @@ "UoM": "單位", "mat": "物料", "fg": "成品", - "Available Qty": "可用數量 (銷售單位)", + "Available Qty": "可用數量 (倉存單位)", "Sales UoM": "銷售單位", + "Stock UoM": "倉存單位", "Available Qty Per Smallest Unit": "可用數量 (基本單位)", "Base UoM": "基本單位", "Lot No": "批號", "Expiry Date": "到期日", "No items are selected yet.": "未選擇項目", "Item selected": "已選擇項目" -} +} \ No newline at end of file diff --git a/src/i18n/zh/items.json b/src/i18n/zh/items.json index 34d1c0a..196fd52 100644 --- a/src/i18n/zh/items.json +++ b/src/i18n/zh/items.json @@ -10,6 +10,7 @@ "Product / Material": "產品 / 材料", "Product / Material Details": "產品 / 材料詳情", "Qc items": "QC 項目", +"Qc Category": "質檢模板", "Name": "名稱", "name": "名稱", "description": "描述", From 2b500868faf2235d7aa4bea6f143285722b76eef Mon Sep 17 00:00:00 2001 From: "cyril.tsui" Date: Thu, 14 Aug 2025 18:48:45 +0800 Subject: [PATCH 6/7] update item, po --- src/app/(main)/settings/items/edit/page.tsx | 4 +++ src/app/api/settings/item/actions.ts | 1 + src/app/api/settings/item/index.ts | 2 ++ src/components/PoDetail/PoDetail.tsx | 6 ++--- src/components/PoDetail/PoInputGrid.tsx | 28 ++++++++++----------- 5 files changed, 24 insertions(+), 17 deletions(-) diff --git a/src/app/(main)/settings/items/edit/page.tsx b/src/app/(main)/settings/items/edit/page.tsx index 9e2f936..ee32d7f 100644 --- a/src/app/(main)/settings/items/edit/page.tsx +++ b/src/app/(main)/settings/items/edit/page.tsx @@ -1,3 +1,4 @@ +import { fetchQcCategoryCombo } from "@/app/api/settings/qcCategory"; import { SearchParams } from "@/app/utils/fetchUtil"; import { TypeEnum } from "@/app/utils/typeEnum"; import CreateProductMaterial from "@/components/CreateItem"; @@ -17,6 +18,9 @@ const productSetting: React.FC = async ({ searchParams }) => { if (!id) { notFound(); } + + fetchQcCategoryCombo() + return ( <> {/* {t("Create Material")} */} diff --git a/src/app/api/settings/item/actions.ts b/src/app/api/settings/item/actions.ts index 522b376..8012d3f 100644 --- a/src/app/api/settings/item/actions.ts +++ b/src/app/api/settings/item/actions.ts @@ -35,6 +35,7 @@ export type CreateItemInputs = { type: string; qcChecks: QcChecksInputs[]; qcChecks_active: number[]; + qcCategoryId: number | undefined; }; export const saveItem = async (data: CreateItemInputs) => { diff --git a/src/app/api/settings/item/index.ts b/src/app/api/settings/item/index.ts index 5d224c2..bee66f1 100644 --- a/src/app/api/settings/item/index.ts +++ b/src/app/api/settings/item/index.ts @@ -4,6 +4,7 @@ import "server-only"; // import { BASE_API_URL } from "@/config/api"; import { serverFetchJson } from "../../../utils/fetchUtil"; import { BASE_API_URL } from "../../../../config/api"; +import { QcCategoryResult } from "../qcCategory"; // import { TypeInputs, UomInputs, WeightUnitInputs } from "./actions"; @@ -37,6 +38,7 @@ export type ItemsResult = { action?: any; fgName?: string; excludeDate?: string; + qcCategory?: QcCategoryResult; }; export type Result = { diff --git a/src/components/PoDetail/PoDetail.tsx b/src/components/PoDetail/PoDetail.tsx index e8d7bf7..b67d6e9 100644 --- a/src/components/PoDetail/PoDetail.tsx +++ b/src/components/PoDetail/PoDetail.tsx @@ -406,9 +406,9 @@ const PoDetail: React.FC = ({ po, qc, warehouse }) => { {integerFormatter.format(row.qty)} {integerFormatter.format(processedQty)} {row.uom?.code} - + {/* {decimalFormatter.format(totalWeight)} {weightUnit} - + */} {/* {weightUnit} */} {decimalFormatter.format(row.price)} {/* {row.expiryDate} */} @@ -651,7 +651,7 @@ const PoDetail: React.FC = ({ po, qc, warehouse }) => { {t("qty")} {t("processed")} {t("uom")} - {t("total weight")} + {/* {t("total weight")} */} {`${t("price")} (HKD)`} {t("status")} {renderFieldCondition(FIRST_IN_FIELD) ? {t("receivedQty")} : undefined} diff --git a/src/components/PoDetail/PoInputGrid.tsx b/src/components/PoDetail/PoInputGrid.tsx index e16affc..c278f97 100644 --- a/src/components/PoDetail/PoInputGrid.tsx +++ b/src/components/PoDetail/PoInputGrid.tsx @@ -480,20 +480,20 @@ const closeNewModal = useCallback(() => { return params.row.uom.code; }, }, - { - field: "weight", - headerName: t("weight"), - width: 120, - // flex: 0.5, - renderCell: (params) => { - const weight = calculateWeight( - params.row.acceptedQty, - params.row.uom, - ); - const weightUnit = returnWeightUnit(params.row.uom); - return `${decimalFormatter.format(weight)} ${weightUnit}`; - }, - }, + // { + // field: "weight", + // headerName: t("weight"), + // width: 120, + // // flex: 0.5, + // renderCell: (params) => { + // const weight = calculateWeight( + // params.row.acceptedQty, + // params.row.uom, + // ); + // const weightUnit = returnWeightUnit(params.row.uom); + // return `${decimalFormatter.format(weight)} ${weightUnit}`; + // }, + // }, { field: "status", headerName: t("status"), From da4f29f41ba5ddf7d5f65436b14f45f6387b6d97 Mon Sep 17 00:00:00 2001 From: "cyril.tsui" Date: Thu, 14 Aug 2025 19:21:02 +0800 Subject: [PATCH 7/7] quick update for po detail --- src/components/PoDetail/PoDetail.tsx | 13 +++++++------ src/components/PoDetail/PoInputGrid.tsx | 3 +++ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/components/PoDetail/PoDetail.tsx b/src/components/PoDetail/PoDetail.tsx index b67d6e9..f49256a 100644 --- a/src/components/PoDetail/PoDetail.tsx +++ b/src/components/PoDetail/PoDetail.tsx @@ -182,7 +182,7 @@ const PoDetail: React.FC = ({ po, qc, warehouse }) => { const searchParams = useSearchParams(); const [row, setRow] = useState(rows[0]); - const [stockInLine, setStockInLine] = useState([]); + const [stockInLine, setStockInLine] = useState(rows[0].stockInLine); const [processedQty, setProcessedQty] = useState(rows[0].processed); const router = useRouter(); @@ -288,9 +288,9 @@ const PoDetail: React.FC = ({ po, qc, warehouse }) => { setRows(purchaseOrder.pol || []); }, [purchaseOrder]); - useEffect(() => { - setStockInLine([]) - }, []); + // useEffect(() => { + // setStockInLine([]) + // }, []); function Row(props: { row: PurchaseOrderLine }) { const { row } = props; @@ -330,15 +330,16 @@ const PoDetail: React.FC = ({ po, qc, warehouse }) => { (id: number) => { console.log(id) //rows = purchaseOrderLine + console.log(rows) const target = rows.find((r) => r.id === id) const stockInLine = target!.stockInLine console.log(stockInLine) - console.log(stockInLine) setStockInLine(stockInLine) + setRow(target!) // console.log(pathname) // router.replace(`/po/edit?id=${item.poId}&polId=${item.polId}&stockInLineId=${item.stockInLineId}`); }, - [] + [rows] ); const handleStart = useCallback( diff --git a/src/components/PoDetail/PoInputGrid.tsx b/src/components/PoDetail/PoInputGrid.tsx index c278f97..008cebf 100644 --- a/src/components/PoDetail/PoInputGrid.tsx +++ b/src/components/PoDetail/PoInputGrid.tsx @@ -121,6 +121,9 @@ function PoInputGrid({ ); console.log(stockInLine); const [entries, setEntries] = useState(stockInLine || []); + useEffect(() => { + setEntries(stockInLine) + }, [stockInLine]) const [modalInfo, setModalInfo] = useState< StockInLine & { qcResult?: PurchaseQcResult[] } >();