| @@ -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<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 { 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<CreateItemInputs> | undefined; | |||
| qcChecks: ItemQc[]; | |||
| qcCategoryCombo: QcCategoryCombo[] | |||
| }; | |||
| const CreateItem: React.FC<Props> = ({ | |||
| @@ -42,6 +44,7 @@ const CreateItem: React.FC<Props> = ({ | |||
| // type, | |||
| defaultValues, | |||
| qcChecks, | |||
| qcCategoryCombo, | |||
| }) => { | |||
| // console.log(type) | |||
| const apiRef = useGridApiRef(); | |||
| @@ -192,7 +195,7 @@ const CreateItem: React.FC<Props> = ({ | |||
| {serverError} | |||
| </Typography> | |||
| )} | |||
| {tabIndex === 0 && <ProductDetails isEditMode={isEditMode} />} | |||
| {tabIndex === 0 && <ProductDetails isEditMode={isEditMode} qcCategoryCombo={qcCategoryCombo}/>} | |||
| {tabIndex === 1 && <QcDetails apiRef={apiRef} />} | |||
| {/* {type === TypeEnum.MATERIAL && <MaterialDetails />} */} | |||
| {/* {type === TypeEnum.BYPRODUCT && <ByProductDetails />} */} | |||
| @@ -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<Props> & SubComponents = async ({ id }) => { | |||
| maxQty: item?.maxQty, | |||
| qcChecks: qcChecks, | |||
| qcChecks_active: activeRows, | |||
| qcCategoryId: item.qcCategory?.id | |||
| }; | |||
| } | |||
| const qcCategoryCombo = await fetchQcCategoryCombo(); | |||
| return ( | |||
| <CreateItem | |||
| isEditMode={Boolean(id)} | |||
| defaultValues={defaultValues} | |||
| qcChecks={qcChecks || []} | |||
| qcCategoryCombo={qcCategoryCombo} | |||
| /> | |||
| ); | |||
| }; | |||
| @@ -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<CreateItemInputs> | undefined; | |||
| qcChecks?: ItemQc[]; | |||
| qcCategoryCombo: QcCategoryCombo[]; | |||
| }; | |||
| const ProductDetails: React.FC<Props> = ({ isEditMode }) => { | |||
| const ProductDetails: React.FC<Props> = ({ isEditMode, qcCategoryCombo }) => { | |||
| const { | |||
| t, | |||
| i18n: { language }, | |||
| @@ -104,6 +107,11 @@ const ProductDetails: React.FC<Props> = ({ isEditMode }) => { | |||
| // router.replace(`/settings/product`); | |||
| console.log("cancel"); | |||
| }; | |||
| const handleAutoCompleteChange = useCallback((event: SyntheticEvent<Element, Event>, value: QcCategoryCombo, onChange: (...event: any[]) => void) => { | |||
| onChange(value.id) | |||
| }, []) | |||
| return ( | |||
| <Card sx={{ display: "block" }}> | |||
| <CardContent component={Stack} spacing={4}> | |||
| @@ -202,7 +210,31 @@ const ProductDetails: React.FC<Props> = ({ isEditMode }) => { | |||
| helperText={errors.maxQty?.message} | |||
| /> | |||
| </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 | |||
| direction="row" | |||
| justifyContent="flex-end" | |||
| @@ -82,23 +82,23 @@ const InventoryLotLineTable: React.FC<Props> = ({ 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"), | |||
| @@ -41,25 +41,25 @@ const InventoryTable: React.FC<Props> = ({ 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", | |||
| @@ -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": "已選擇項目" | |||
| } | |||
| } | |||
| @@ -10,6 +10,7 @@ | |||
| "Product / Material": "產品 / 材料", | |||
| "Product / Material Details": "產品 / 材料詳情", | |||
| "Qc items": "QC 項目", | |||
| "Qc Category": "質檢模板", | |||
| "Name": "名稱", | |||
| "name": "名稱", | |||
| "description": "描述", | |||