| @@ -9,6 +9,12 @@ export interface QcCategoryResult { | |||||
| name: string; | name: string; | ||||
| } | } | ||||
| export interface QcCategoryCombo { | |||||
| id: number; | |||||
| value: number; | |||||
| label: string; | |||||
| } | |||||
| export const preloadQcCategory = () => { | export const preloadQcCategory = () => { | ||||
| fetchQcCategories(); | fetchQcCategories(); | ||||
| }; | }; | ||||
| @@ -18,3 +24,9 @@ export const fetchQcCategories = cache(async () => { | |||||
| next: { tags: ["qcCategories"] }, | next: { tags: ["qcCategories"] }, | ||||
| }); | }); | ||||
| }); | }); | ||||
| export const fetchQcCategoryCombo = cache(async () => { | |||||
| return serverFetchJson<QcCategoryCombo[]>(`${BASE_API_URL}/qcCategories/combo`, { | |||||
| next: { tags: ["qcCategoryCombo"] }, | |||||
| }); | |||||
| }); | |||||
| @@ -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<ShopCombo[]>(`${BASE_API_URL}/shop/combo/supplier`, { | |||||
| method: "GET", | |||||
| headers: { "Content-Type": "application/json"}, | |||||
| next: { | |||||
| tags: ["supplierCombo"] | |||||
| } | |||||
| }) | |||||
| }) | |||||
| @@ -29,12 +29,14 @@ import QcDetails from "./QcDetails"; | |||||
| import { ItemQc } from "@/app/api/settings/item"; | import { ItemQc } from "@/app/api/settings/item"; | ||||
| import { saveItemQcChecks } from "@/app/api/settings/qcCheck/actions"; | import { saveItemQcChecks } from "@/app/api/settings/qcCheck/actions"; | ||||
| import { useGridApiRef } from "@mui/x-data-grid"; | import { useGridApiRef } from "@mui/x-data-grid"; | ||||
| import { QcCategoryCombo } from "@/app/api/settings/qcCategory"; | |||||
| type Props = { | type Props = { | ||||
| isEditMode: boolean; | isEditMode: boolean; | ||||
| // type: TypeEnum; | // type: TypeEnum; | ||||
| defaultValues: Partial<CreateItemInputs> | undefined; | defaultValues: Partial<CreateItemInputs> | undefined; | ||||
| qcChecks: ItemQc[]; | qcChecks: ItemQc[]; | ||||
| qcCategoryCombo: QcCategoryCombo[] | |||||
| }; | }; | ||||
| const CreateItem: React.FC<Props> = ({ | const CreateItem: React.FC<Props> = ({ | ||||
| @@ -42,6 +44,7 @@ const CreateItem: React.FC<Props> = ({ | |||||
| // type, | // type, | ||||
| defaultValues, | defaultValues, | ||||
| qcChecks, | qcChecks, | ||||
| qcCategoryCombo, | |||||
| }) => { | }) => { | ||||
| // console.log(type) | // console.log(type) | ||||
| const apiRef = useGridApiRef(); | const apiRef = useGridApiRef(); | ||||
| @@ -192,7 +195,7 @@ const CreateItem: React.FC<Props> = ({ | |||||
| {serverError} | {serverError} | ||||
| </Typography> | </Typography> | ||||
| )} | )} | ||||
| {tabIndex === 0 && <ProductDetails isEditMode={isEditMode} />} | |||||
| {tabIndex === 0 && <ProductDetails isEditMode={isEditMode} qcCategoryCombo={qcCategoryCombo}/>} | |||||
| {tabIndex === 1 && <QcDetails apiRef={apiRef} />} | {tabIndex === 1 && <QcDetails apiRef={apiRef} />} | ||||
| {/* {type === TypeEnum.MATERIAL && <MaterialDetails />} */} | {/* {type === TypeEnum.MATERIAL && <MaterialDetails />} */} | ||||
| {/* {type === TypeEnum.BYPRODUCT && <ByProductDetails />} */} | {/* {type === TypeEnum.BYPRODUCT && <ByProductDetails />} */} | ||||
| @@ -5,6 +5,7 @@ import { CreateItemInputs } from "@/app/api/settings/item/actions"; | |||||
| import { notFound } from "next/navigation"; | import { notFound } from "next/navigation"; | ||||
| import { fetchItem } from "@/app/api/settings/item"; | import { fetchItem } from "@/app/api/settings/item"; | ||||
| import { fetchQcItems } from "@/app/api/settings/qcItem"; | import { fetchQcItems } from "@/app/api/settings/qcItem"; | ||||
| import { fetchQcCategoryCombo } from "@/app/api/settings/qcCategory"; | |||||
| interface SubComponents { | interface SubComponents { | ||||
| Loading: typeof CreateItemLoading; | Loading: typeof CreateItemLoading; | ||||
| } | } | ||||
| @@ -37,14 +38,18 @@ const CreateItemWrapper: React.FC<Props> & SubComponents = async ({ id }) => { | |||||
| maxQty: item?.maxQty, | maxQty: item?.maxQty, | ||||
| qcChecks: qcChecks, | qcChecks: qcChecks, | ||||
| qcChecks_active: activeRows, | qcChecks_active: activeRows, | ||||
| qcCategoryId: item.qcCategory?.id | |||||
| }; | }; | ||||
| } | } | ||||
| const qcCategoryCombo = await fetchQcCategoryCombo(); | |||||
| return ( | return ( | ||||
| <CreateItem | <CreateItem | ||||
| isEditMode={Boolean(id)} | isEditMode={Boolean(id)} | ||||
| defaultValues={defaultValues} | defaultValues={defaultValues} | ||||
| qcChecks={qcChecks || []} | qcChecks={qcChecks || []} | ||||
| qcCategoryCombo={qcCategoryCombo} | |||||
| /> | /> | ||||
| ); | ); | ||||
| }; | }; | ||||
| @@ -1,5 +1,6 @@ | |||||
| "use client"; | "use client"; | ||||
| import { | import { | ||||
| Autocomplete, | |||||
| Box, | Box, | ||||
| Button, | Button, | ||||
| Card, | Card, | ||||
| @@ -10,11 +11,11 @@ import { | |||||
| Typography, | Typography, | ||||
| } from "@mui/material"; | } from "@mui/material"; | ||||
| import { Check, Close, EditNote } from "@mui/icons-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 { useTranslation } from "react-i18next"; | ||||
| import InputDataGrid from "../InputDataGrid"; | 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 { GridColDef, GridRowModel } from "@mui/x-data-grid"; | ||||
| import { InputDataGridProps, TableRow } from "../InputDataGrid/InputDataGrid"; | import { InputDataGridProps, TableRow } from "../InputDataGrid/InputDataGrid"; | ||||
| import { TypeEnum } from "@/app/utils/typeEnum"; | import { TypeEnum } from "@/app/utils/typeEnum"; | ||||
| @@ -22,6 +23,7 @@ import { NumberInputProps } from "./NumberInputProps"; | |||||
| import { CreateItemInputs } from "@/app/api/settings/item/actions"; | import { CreateItemInputs } from "@/app/api/settings/item/actions"; | ||||
| import { RestartAlt } from "@mui/icons-material"; | import { RestartAlt } from "@mui/icons-material"; | ||||
| import { ItemQc } from "@/app/api/settings/item"; | import { ItemQc } from "@/app/api/settings/item"; | ||||
| import { QcCategoryCombo } from "@/app/api/settings/qcCategory"; | |||||
| type Props = { | type Props = { | ||||
| // isEditMode: boolean; | // isEditMode: boolean; | ||||
| // type: TypeEnum; | // type: TypeEnum; | ||||
| @@ -29,9 +31,10 @@ type Props = { | |||||
| // type: TypeEnum; | // type: TypeEnum; | ||||
| defaultValues?: Partial<CreateItemInputs> | undefined; | defaultValues?: Partial<CreateItemInputs> | undefined; | ||||
| qcChecks?: ItemQc[]; | qcChecks?: ItemQc[]; | ||||
| qcCategoryCombo: QcCategoryCombo[]; | |||||
| }; | }; | ||||
| const ProductDetails: React.FC<Props> = ({ isEditMode }) => { | |||||
| const ProductDetails: React.FC<Props> = ({ isEditMode, qcCategoryCombo }) => { | |||||
| const { | const { | ||||
| t, | t, | ||||
| i18n: { language }, | i18n: { language }, | ||||
| @@ -104,6 +107,11 @@ const ProductDetails: React.FC<Props> = ({ isEditMode }) => { | |||||
| // router.replace(`/settings/product`); | // router.replace(`/settings/product`); | ||||
| console.log("cancel"); | console.log("cancel"); | ||||
| }; | }; | ||||
| const handleAutoCompleteChange = useCallback((event: SyntheticEvent<Element, Event>, value: QcCategoryCombo, onChange: (...event: any[]) => void) => { | |||||
| onChange(value.id) | |||||
| }, []) | |||||
| return ( | return ( | ||||
| <Card sx={{ display: "block" }}> | <Card sx={{ display: "block" }}> | ||||
| <CardContent component={Stack} spacing={4}> | <CardContent component={Stack} spacing={4}> | ||||
| @@ -202,7 +210,31 @@ const ProductDetails: React.FC<Props> = ({ isEditMode }) => { | |||||
| helperText={errors.maxQty?.message} | helperText={errors.maxQty?.message} | ||||
| /> | /> | ||||
| </Grid> | </Grid> | ||||
| <Grid item xs={0}> | |||||
| <Grid item xs={6}> | |||||
| <Controller | |||||
| control={control} | |||||
| name="qcCategoryId" | |||||
| render={({ field }) => ( | |||||
| <Autocomplete | |||||
| disableClearable | |||||
| options={qcCategoryCombo} | |||||
| defaultValue={qcCategoryCombo.find(qc => qc.id === field.value)} | |||||
| onChange={(event, value) => { | |||||
| handleAutoCompleteChange(event, value, field.onChange) | |||||
| }} | |||||
| onBlur={field.onBlur} | |||||
| renderInput={(params) => ( | |||||
| <TextField | |||||
| {...params} | |||||
| variant="outlined" | |||||
| label={t("Qc Category")} | |||||
| /> | |||||
| )} | |||||
| /> | |||||
| )} | |||||
| /> | |||||
| </Grid> | |||||
| <Grid item xs={12}> | |||||
| <Stack | <Stack | ||||
| direction="row" | direction="row" | ||||
| justifyContent="flex-end" | justifyContent="flex-end" | ||||
| @@ -82,23 +82,23 @@ const InventoryLotLineTable: React.FC<Props> = ({ inventoryLotLines, pagingContr | |||||
| }, | }, | ||||
| { | { | ||||
| name: "uom", | 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", | align: "left", | ||||
| headerAlign: "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", | name: "expiryDate", | ||||
| label: t("Expiry Date"), | label: t("Expiry Date"), | ||||
| @@ -41,25 +41,25 @@ const InventoryTable: React.FC<Props> = ({ inventories, pagingController, setPag | |||||
| }, | }, | ||||
| { | { | ||||
| name: "uomUdfudesc", | 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", | align: "left", | ||||
| headerAlign: "left", | headerAlign: "left", | ||||
| }, | }, | ||||
| // { | // { | ||||
| // name: "qtyPerSmallestUnit", | // 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"), | // label: t("Qty Per Smallest Unit"), | ||||
| // align: "right", | // align: "right", | ||||
| // headerAlign: "right", | // headerAlign: "right", | ||||
| @@ -8,12 +8,13 @@ | |||||
| "UoM": "單位", | "UoM": "單位", | ||||
| "mat": "物料", | "mat": "物料", | ||||
| "fg": "成品", | "fg": "成品", | ||||
| "Available Qty": "可用數量 (銷售單位)", | |||||
| "Available Qty": "可用數量 (倉存單位)", | |||||
| "Sales UoM": "銷售單位", | "Sales UoM": "銷售單位", | ||||
| "Stock UoM": "倉存單位", | |||||
| "Available Qty Per Smallest Unit": "可用數量 (基本單位)", | "Available Qty Per Smallest Unit": "可用數量 (基本單位)", | ||||
| "Base UoM": "基本單位", | "Base UoM": "基本單位", | ||||
| "Lot No": "批號", | "Lot No": "批號", | ||||
| "Expiry Date": "到期日", | "Expiry Date": "到期日", | ||||
| "No items are selected yet.": "未選擇項目", | "No items are selected yet.": "未選擇項目", | ||||
| "Item selected": "已選擇項目" | "Item selected": "已選擇項目" | ||||
| } | |||||
| } | |||||
| @@ -10,6 +10,7 @@ | |||||
| "Product / Material": "產品 / 材料", | "Product / Material": "產品 / 材料", | ||||
| "Product / Material Details": "產品 / 材料詳情", | "Product / Material Details": "產品 / 材料詳情", | ||||
| "Qc items": "QC 項目", | "Qc items": "QC 項目", | ||||
| "Qc Category": "質檢模板", | |||||
| "Name": "名稱", | "Name": "名稱", | ||||
| "name": "名稱", | "name": "名稱", | ||||
| "description": "描述", | "description": "描述", | ||||