diff --git a/src/app/(main)/settings/warehouse/create/page.tsx b/src/app/(main)/settings/warehouse/create/page.tsx new file mode 100644 index 0000000..0ee2965 --- /dev/null +++ b/src/app/(main)/settings/warehouse/create/page.tsx @@ -0,0 +1,21 @@ +import { I18nProvider, getServerI18n } from "@/i18n"; +import React, { Suspense } from "react"; +import { Typography } from "@mui/material"; +import CreateWarehouse from "@/components/CreateWarehouse"; + +const CreateWarehousePage: React.FC = async () => { + const { t } = await getServerI18n("warehouse"); + + return ( + <> + {t("Create Warehouse")} + + }> + + + + + ); +}; + +export default CreateWarehousePage; diff --git a/src/app/(main)/settings/warehouse/page.tsx b/src/app/(main)/settings/warehouse/page.tsx new file mode 100644 index 0000000..e008d12 --- /dev/null +++ b/src/app/(main)/settings/warehouse/page.tsx @@ -0,0 +1,45 @@ +import { Metadata } from "next"; +import { getServerI18n, I18nProvider } from "@/i18n"; +import Typography from "@mui/material/Typography"; +import { Suspense } from "react"; +import { Stack } from "@mui/material"; +import { Button } from "@mui/material"; +import Link from "next/link"; +import WarehouseHandle from "@/components/WarehouseHandle"; +import Add from "@mui/icons-material/Add"; + +export const metadata: Metadata = { + title: "Warehouse Management", +}; + +const Warehouse: React.FC = async () => { + const { t } = await getServerI18n("warehouse"); + return ( + <> + + + {t("Warehouse")} + + + + + }> + + + + + ); +}; +export default Warehouse; diff --git a/src/app/api/warehouse/actions.ts b/src/app/api/warehouse/actions.ts index d1ea95a..2d7e6ba 100644 --- a/src/app/api/warehouse/actions.ts +++ b/src/app/api/warehouse/actions.ts @@ -1,7 +1,63 @@ "use server"; -import { serverFetchString } from "@/app/utils/fetchUtil"; +import { serverFetchString, serverFetchWithNoContent, serverFetchJson } from "@/app/utils/fetchUtil"; import { BASE_API_URL } from "@/config/api"; +import { revalidateTag } from "next/cache"; +import { WarehouseResult } from "./index"; +import { cache } from "react"; + +export interface WarehouseInputs { + code?: string; + name?: string; + description?: string; + capacity?: number; + store_id?: string; + warehouse?: string; + area?: string; + slot?: string; + stockTakeSection?: string; +} + +export const fetchWarehouseDetail = cache(async (id: number) => { + return serverFetchJson(`${BASE_API_URL}/warehouse/${id}`, { + next: { tags: ["warehouse"] }, + }); +}); + +export const createWarehouse = async (data: WarehouseInputs) => { + const newWarehouse = await serverFetchWithNoContent(`${BASE_API_URL}/warehouse/save`, { + method: "POST", + body: JSON.stringify(data), + headers: { "Content-Type": "application/json" }, + }); + revalidateTag("warehouse"); + return newWarehouse; +}; + +export const editWarehouse = async (id: number, data: WarehouseInputs) => { + const updatedWarehouse = await serverFetchWithNoContent(`${BASE_API_URL}/warehouse/${id}`, { + method: "PUT", + body: JSON.stringify(data), + headers: { "Content-Type": "application/json" }, + }); + revalidateTag("warehouse"); + return updatedWarehouse; +}; + +export const deleteWarehouse = async (id: number) => { + try { + const result = await serverFetchJson(`${BASE_API_URL}/warehouse/${id}`, { + method: "DELETE", + headers: { "Content-Type": "application/json" }, + }); + revalidateTag("warehouse"); + return result; + } catch (error) { + console.error("Error deleting warehouse:", error); + revalidateTag("warehouse"); + throw error; + } +}; export const importWarehouse = async (data: FormData) => { const importWarehouse = await serverFetchString( diff --git a/src/app/api/warehouse/index.ts b/src/app/api/warehouse/index.ts index ce560fe..871693b 100644 --- a/src/app/api/warehouse/index.ts +++ b/src/app/api/warehouse/index.ts @@ -4,10 +4,17 @@ import { serverFetchJson } from "@/app/utils/fetchUtil"; import { BASE_API_URL } from "@/config/api"; export interface WarehouseResult { + action: any; id: number; code: string; name: string; description: string; + store_id?: string; + warehouse?: string; + area?: string; + slot?: string; + order?: number; + stockTakeSection?: string; } export interface WarehouseCombo { diff --git a/src/components/CreateWarehouse/CreateWarehouse.tsx b/src/components/CreateWarehouse/CreateWarehouse.tsx new file mode 100644 index 0000000..bbdd858 --- /dev/null +++ b/src/components/CreateWarehouse/CreateWarehouse.tsx @@ -0,0 +1,148 @@ +"use client"; +import { useRouter } from "next/navigation"; +import React, { + useCallback, + useEffect, + useState, +} from "react"; +import { useTranslation } from "react-i18next"; +import { + Button, + Stack, + Typography, +} from "@mui/material"; +import { + FormProvider, + SubmitErrorHandler, + SubmitHandler, + useForm, +} from "react-hook-form"; +import { Check, Close, RestartAlt } from "@mui/icons-material"; +import { + WarehouseInputs, + createWarehouse, +} from "@/app/api/warehouse/actions"; +import WarehouseDetail from "./WarehouseDetail"; + +const CreateWarehouse: React.FC = () => { + const { t } = useTranslation(["warehouse", "common"]); + const formProps = useForm(); + const router = useRouter(); + const [serverError, setServerError] = useState(""); + + const resetForm = React.useCallback((e?: React.MouseEvent) => { + e?.preventDefault(); + e?.stopPropagation(); + try { + formProps.reset({ + store_id: "", + warehouse: "", + area: "", + slot: "", + stockTakeSection: "", + }); + } catch (error) { + console.log(error); + setServerError(t("An error has occurred. Please try again later.")); + } + }, [formProps, t]); + + useEffect(() => { + resetForm(); + }, []); + + const handleCancel = () => { + router.back(); + }; + + const onSubmit = useCallback>( + async (data) => { + try { + // Automatically append "F" to store_id if not already present + // Remove any existing "F" to avoid duplication, then append it + const cleanStoreId = (data.store_id || "").replace(/F$/i, "").trim(); + const storeIdWithF = cleanStoreId ? `${cleanStoreId}F` : ""; + + // Generate code, name, description from the input fields + // Format: store_idF-warehouse-area-slot (F is automatically appended) + const code = storeIdWithF + ? `${storeIdWithF}-${data.warehouse || ""}-${data.area || ""}-${data.slot || ""}` + : `${data.warehouse || ""}-${data.area || ""}-${data.slot || ""}`; + const name = storeIdWithF + ? `${storeIdWithF}-${data.warehouse || ""}` + : `${data.warehouse || ""}`; + const description = storeIdWithF + ? `${storeIdWithF}-${data.warehouse || ""}` + : `${data.warehouse || ""}`; + + const warehouseData: WarehouseInputs = { + ...data, + store_id: storeIdWithF, // Save with F (F is automatically appended) + code: code.trim(), + name: name.trim(), + description: description.trim(), + capacity: 10000, // Default capacity + }; + + await createWarehouse(warehouseData); + router.replace("/settings/warehouse"); + } catch (e) { + console.log(e); + setServerError(t("An error has occurred. Please try again later.")); + } + }, + [router, t], + ); + + const onSubmitError = useCallback>( + (errors) => { + console.log(errors); + }, + [], + ); + + return ( + <> + {serverError && ( + + {serverError} + + )} + + + + + + + + + + + + ); +}; +export default CreateWarehouse; diff --git a/src/components/CreateWarehouse/CreateWarehouseLoading.tsx b/src/components/CreateWarehouse/CreateWarehouseLoading.tsx new file mode 100644 index 0000000..4568c4a --- /dev/null +++ b/src/components/CreateWarehouse/CreateWarehouseLoading.tsx @@ -0,0 +1,29 @@ +import Card from "@mui/material/Card"; +import CardContent from "@mui/material/CardContent"; +import Skeleton from "@mui/material/Skeleton"; +import Stack from "@mui/material/Stack"; +import React from "react"; + +export const CreateWarehouseLoading: React.FC = () => { + return ( + <> + + + + + + + + + + + + ); +}; + +export default CreateWarehouseLoading; diff --git a/src/components/CreateWarehouse/CreateWarehouseWrapper.tsx b/src/components/CreateWarehouse/CreateWarehouseWrapper.tsx new file mode 100644 index 0000000..935b6f8 --- /dev/null +++ b/src/components/CreateWarehouse/CreateWarehouseWrapper.tsx @@ -0,0 +1,15 @@ +import React from "react"; +import CreateWarehouse from "./CreateWarehouse"; +import CreateWarehouseLoading from "./CreateWarehouseLoading"; + +interface SubComponents { + Loading: typeof CreateWarehouseLoading; +} + +const CreateWarehouseWrapper: React.FC & SubComponents = async () => { + return ; +}; + +CreateWarehouseWrapper.Loading = CreateWarehouseLoading; + +export default CreateWarehouseWrapper; diff --git a/src/components/CreateWarehouse/WarehouseDetail.tsx b/src/components/CreateWarehouse/WarehouseDetail.tsx new file mode 100644 index 0000000..d8ba4f8 --- /dev/null +++ b/src/components/CreateWarehouse/WarehouseDetail.tsx @@ -0,0 +1,139 @@ +"use client"; + +import { + Card, + CardContent, + Stack, + TextField, + Typography, + Box, + InputAdornment, +} from "@mui/material"; +import { useFormContext, Controller } from "react-hook-form"; +import { useTranslation } from "react-i18next"; +import { WarehouseInputs } from "@/app/api/warehouse/actions"; + +const WarehouseDetail: React.FC = () => { + const { t } = useTranslation("warehouse"); + const { + register, + control, + formState: { errors }, + } = useFormContext(); + + return ( + + + + {t("Warehouse Detail")} + + + {/* 樓層 field with F inside on the right - F is automatically generated */} + ( + F + ), + }} + onChange={(e) => { + // Automatically remove "F" if user tries to type it (F is auto-generated) + const value = e.target.value.replace(/F/gi, "").trim(); + field.onChange(value); + }} + error={Boolean(errors.store_id)} + helperText={errors.store_id?.message} + /> + )} + /> + + - + + {/* 倉庫 field */} + ( + + )} + /> + + - + + {/* 區域 field */} + ( + + )} + /> + + - + + {/* 儲位 field */} + ( + + )} + /> + {/* stockTakeSection field in the same row */} + + + + + + + ); +}; + +export default WarehouseDetail; diff --git a/src/components/CreateWarehouse/index.ts b/src/components/CreateWarehouse/index.ts new file mode 100644 index 0000000..1f233cb --- /dev/null +++ b/src/components/CreateWarehouse/index.ts @@ -0,0 +1 @@ +export { default } from "./CreateWarehouseWrapper"; diff --git a/src/components/NavigationContent/NavigationContent.tsx b/src/components/NavigationContent/NavigationContent.tsx index 7e33068..6f2a516 100644 --- a/src/components/NavigationContent/NavigationContent.tsx +++ b/src/components/NavigationContent/NavigationContent.tsx @@ -299,7 +299,7 @@ const NavigationContent: React.FC = () => { { icon: , label: "Warehouse", - path: "/settings/user", + path: "/settings/warehouse", }, { icon: , diff --git a/src/components/SearchBox/SearchBox.tsx b/src/components/SearchBox/SearchBox.tsx index 0387d8a..4227e18 100644 --- a/src/components/SearchBox/SearchBox.tsx +++ b/src/components/SearchBox/SearchBox.tsx @@ -52,6 +52,7 @@ interface OptionWithLabel { interface TextCriterion extends BaseCriterion { type: "text"; + placeholder?: string; } interface SelectCriterion extends BaseCriterion { @@ -286,6 +287,7 @@ function SearchBox({ diff --git a/src/components/WarehouseHandle/WarehouseHandle.tsx b/src/components/WarehouseHandle/WarehouseHandle.tsx new file mode 100644 index 0000000..453de68 --- /dev/null +++ b/src/components/WarehouseHandle/WarehouseHandle.tsx @@ -0,0 +1,364 @@ +"use client"; + +import { useCallback, useMemo, useState } from "react"; +import { useTranslation } from "react-i18next"; +import SearchResults, { Column } from "../SearchResults/index"; +import DeleteIcon from "@mui/icons-material/Delete"; +import { useRouter } from "next/navigation"; +import { deleteDialog, successDialog } from "../Swal/CustomAlerts"; +import { WarehouseResult } from "@/app/api/warehouse"; +import { deleteWarehouse } from "@/app/api/warehouse/actions"; +import Card from "@mui/material/Card"; +import CardContent from "@mui/material/CardContent"; +import CardActions from "@mui/material/CardActions"; +import Typography from "@mui/material/Typography"; +import TextField from "@mui/material/TextField"; +import Button from "@mui/material/Button"; +import Box from "@mui/material/Box"; +import RestartAlt from "@mui/icons-material/RestartAlt"; +import Search from "@mui/icons-material/Search"; +import InputAdornment from "@mui/material/InputAdornment"; + +interface Props { + warehouses: WarehouseResult[]; +} + +type SearchQuery = Partial>; +type SearchParamNames = keyof SearchQuery; + +const WarehouseHandle: React.FC = ({ warehouses }) => { + const { t } = useTranslation(["warehouse", "common"]); + const [filteredWarehouse, setFilteredWarehouse] = useState(warehouses); + const [pagingController, setPagingController] = useState({ + pageNum: 1, + pageSize: 10, + }); + const router = useRouter(); + const [isSearching, setIsSearching] = useState(false); + + const [searchInputs, setSearchInputs] = useState({ + store_id: "", + warehouse: "", + area: "", + slot: "", + stockTakeSection: "", + }); + + const onDeleteClick = useCallback((warehouse: WarehouseResult) => { + deleteDialog(async () => { + try { + await deleteWarehouse(warehouse.id); + setFilteredWarehouse(prev => prev.filter(w => w.id !== warehouse.id)); + router.refresh(); + successDialog(t("Delete Success"), t); + } catch (error) { + console.error("Failed to delete warehouse:", error); + // Don't redirect on error, just show error message + // The error will be logged but user stays on the page + } + }, t); + }, [t, router]); + + const handleReset = useCallback(() => { + setSearchInputs({ + store_id: "", + warehouse: "", + area: "", + slot: "", + stockTakeSection: "", + }); + setFilteredWarehouse(warehouses); + setPagingController({ pageNum: 1, pageSize: pagingController.pageSize }); + }, [warehouses, pagingController.pageSize]); + + const handleSearch = useCallback(() => { + setIsSearching(true); + try { + let results: WarehouseResult[] = warehouses; + + // Build search pattern from the four fields: store_idF-warehouse-area-slot + // Only search by code field - match the code that follows this pattern + const storeId = searchInputs.store_id?.trim() || ""; + const warehouse = searchInputs.warehouse?.trim() || ""; + const area = searchInputs.area?.trim() || ""; + const slot = searchInputs.slot?.trim() || ""; + const stockTakeSection = searchInputs.stockTakeSection?.trim() || ""; + + // If any field has a value, filter by code pattern and stockTakeSection + if (storeId || warehouse || area || slot || stockTakeSection) { + results = warehouses.filter((warehouseItem) => { + // Filter by stockTakeSection if provided + if (stockTakeSection) { + const itemStockTakeSection = String(warehouseItem.stockTakeSection || "").toLowerCase(); + if (!itemStockTakeSection.includes(stockTakeSection.toLowerCase())) { + return false; + } + } + + // Filter by code pattern if any code-related field is provided + if (storeId || warehouse || area || slot) { + if (!warehouseItem.code) { + return false; + } + + const codeValue = String(warehouseItem.code).toLowerCase(); + + // Check if code matches the pattern: store_id-warehouse-area-slot + // Match each part if provided + const codeParts = codeValue.split("-"); + + if (codeParts.length >= 4) { + const codeStoreId = codeParts[0] || ""; + const codeWarehouse = codeParts[1] || ""; + const codeArea = codeParts[2] || ""; + const codeSlot = codeParts[3] || ""; + + const storeIdMatch = !storeId || codeStoreId.includes(storeId.toLowerCase()); + const warehouseMatch = !warehouse || codeWarehouse.includes(warehouse.toLowerCase()); + const areaMatch = !area || codeArea.includes(area.toLowerCase()); + const slotMatch = !slot || codeSlot.includes(slot.toLowerCase()); + + return storeIdMatch && warehouseMatch && areaMatch && slotMatch; + } + + // Fallback: if code doesn't follow the pattern, check if it contains any of the search terms + const storeIdMatch = !storeId || codeValue.includes(storeId.toLowerCase()); + const warehouseMatch = !warehouse || codeValue.includes(warehouse.toLowerCase()); + const areaMatch = !area || codeValue.includes(area.toLowerCase()); + const slotMatch = !slot || codeValue.includes(slot.toLowerCase()); + + return storeIdMatch && warehouseMatch && areaMatch && slotMatch; + } + + // If only stockTakeSection is provided, return true (already filtered above) + return true; + }); + } else { + // If no search terms, show all warehouses + results = warehouses; + } + + setFilteredWarehouse(results); + setPagingController({ pageNum: 1, pageSize: pagingController.pageSize }); + } catch (error) { + console.error("Error searching warehouses:", error); + // Fallback: filter by code pattern and stockTakeSection + const storeId = searchInputs.store_id?.trim().toLowerCase() || ""; + const warehouse = searchInputs.warehouse?.trim().toLowerCase() || ""; + const area = searchInputs.area?.trim().toLowerCase() || ""; + const slot = searchInputs.slot?.trim().toLowerCase() || ""; + const stockTakeSection = searchInputs.stockTakeSection?.trim().toLowerCase() || ""; + + setFilteredWarehouse( + warehouses.filter((warehouseItem) => { + // Filter by stockTakeSection if provided + if (stockTakeSection) { + const itemStockTakeSection = String(warehouseItem.stockTakeSection || "").toLowerCase(); + if (!itemStockTakeSection.includes(stockTakeSection)) { + return false; + } + } + + // Filter by code if any code-related field is provided + if (storeId || warehouse || area || slot) { + if (!warehouseItem.code) { + return false; + } + + const codeValue = String(warehouseItem.code).toLowerCase(); + const codeParts = codeValue.split("-"); + + if (codeParts.length >= 4) { + const storeIdMatch = !storeId || codeParts[0].includes(storeId); + const warehouseMatch = !warehouse || codeParts[1].includes(warehouse); + const areaMatch = !area || codeParts[2].includes(area); + const slotMatch = !slot || codeParts[3].includes(slot); + return storeIdMatch && warehouseMatch && areaMatch && slotMatch; + } + + return (!storeId || codeValue.includes(storeId)) && + (!warehouse || codeValue.includes(warehouse)) && + (!area || codeValue.includes(area)) && + (!slot || codeValue.includes(slot)); + } + + return true; + }) + ); + } finally { + setIsSearching(false); + } + }, [searchInputs, warehouses, pagingController.pageSize]); + + const columns = useMemo[]>( + () => [ + { + name: "code", + label: t("code"), + align: "left", + headerAlign: "left", + sx: { width: "15%", minWidth: "120px" }, + }, + { + name: "store_id", + label: t("store_id"), + align: "left", + headerAlign: "left", + sx: { width: "15%", minWidth: "120px" }, + }, + { + name: "warehouse", + label: t("warehouse"), + align: "left", + headerAlign: "left", + sx: { width: "15%", minWidth: "120px" }, + }, + { + name: "area", + label: t("area"), + align: "left", + headerAlign: "left", + sx: { width: "15%", minWidth: "120px" }, + }, + { + name: "slot", + label: t("slot"), + align: "left", + headerAlign: "left", + sx: { width: "15%", minWidth: "120px" }, + }, + { + name: "order", + label: t("order"), + align: "left", + headerAlign: "left", + sx: { width: "15%", minWidth: "120px" }, + }, + { + name: "stockTakeSection", + label: t("stockTakeSection"), + align: "left", + headerAlign: "left", + sx: { width: "15%", minWidth: "120px" }, + }, + { + name: "action", + label: t("Delete"), + onClick: onDeleteClick, + buttonIcon: , + color: "error", + sx: { width: "10%", minWidth: "80px" }, + }, + ], + [t, onDeleteClick], + ); + + return ( + <> + + + {t("Search Criteria")} + + {/* 樓層 field with F inside on the right */} + + setSearchInputs((prev) => ({ ...prev, store_id: e.target.value })) + } + size="small" + sx={{ width: "150px", minWidth: "120px" }} + InputProps={{ + endAdornment: ( + F + ), + }} + /> + + - + + {/* 倉庫 field */} + + setSearchInputs((prev) => ({ ...prev, warehouse: e.target.value })) + } + size="small" + sx={{ width: "150px", minWidth: "120px" }} + /> + + - + + {/* 區域 field */} + + setSearchInputs((prev) => ({ ...prev, area: e.target.value })) + } + size="small" + sx={{ width: "150px", minWidth: "120px" }} + /> + + - + + {/* 儲位 field */} + + setSearchInputs((prev) => ({ ...prev, slot: e.target.value })) + } + size="small" + sx={{ width: "150px", minWidth: "120px" }} + /> + {/* 盤點區域 field */} + + + setSearchInputs((prev) => ({ ...prev, stockTakeSection: e.target.value })) + } + size="small" + fullWidth + /> + + + + + + + + + + items={filteredWarehouse} + columns={columns} + pagingController={pagingController} + setPagingController={setPagingController} + /> + + ); +}; +export default WarehouseHandle; diff --git a/src/components/WarehouseHandle/WarehouseHandleLoading.tsx b/src/components/WarehouseHandle/WarehouseHandleLoading.tsx new file mode 100644 index 0000000..7111407 --- /dev/null +++ b/src/components/WarehouseHandle/WarehouseHandleLoading.tsx @@ -0,0 +1,40 @@ +import Card from "@mui/material/Card"; +import CardContent from "@mui/material/CardContent"; +import Skeleton from "@mui/material/Skeleton"; +import Stack from "@mui/material/Stack"; +import React from "react"; + +// Can make this nicer +export const WarehouseHandleLoading: React.FC = () => { + return ( + <> + + + + + + + + + + + + + + + + + + + + + + ); +}; + +export default WarehouseHandleLoading; diff --git a/src/components/WarehouseHandle/WarehouseHandleWrapper.tsx b/src/components/WarehouseHandle/WarehouseHandleWrapper.tsx new file mode 100644 index 0000000..e33d47e --- /dev/null +++ b/src/components/WarehouseHandle/WarehouseHandleWrapper.tsx @@ -0,0 +1,19 @@ +import React from "react"; +import WarehouseHandle from "./WarehouseHandle"; +import WarehouseHandleLoading from "./WarehouseHandleLoading"; +import { WarehouseResult, fetchWarehouseList } from "@/app/api/warehouse"; + +interface SubComponents { + Loading: typeof WarehouseHandleLoading; +} + +const WarehouseHandleWrapper: React.FC & SubComponents = async () => { + const warehouses = await fetchWarehouseList(); + console.log(warehouses); + + return ; +}; + +WarehouseHandleWrapper.Loading = WarehouseHandleLoading; + +export default WarehouseHandleWrapper; diff --git a/src/components/WarehouseHandle/index.ts b/src/components/WarehouseHandle/index.ts new file mode 100644 index 0000000..ac4bf97 --- /dev/null +++ b/src/components/WarehouseHandle/index.ts @@ -0,0 +1 @@ +export { default } from "./WarehouseHandleWrapper"; diff --git a/src/i18n/en/common.json b/src/i18n/en/common.json index eae518c..08e0da5 100644 --- a/src/i18n/en/common.json +++ b/src/i18n/en/common.json @@ -12,6 +12,7 @@ "Equipment not found": "Equipment not found", "Error saving data": "Error saving data", "Cancel": "Cancel", + "Do you want to delete?": "Do you want to delete?", "Save": "Save", "Yes": "Yes", "No": "No", diff --git a/src/i18n/en/user.json b/src/i18n/en/user.json index 2548c90..a5ceb84 100644 --- a/src/i18n/en/user.json +++ b/src/i18n/en/user.json @@ -14,5 +14,7 @@ "User ID": "用戶ID", "User Name": "用戶名稱", "User Group": "用戶群組", - "Authority": "權限" + "Authority": "權限", + "Delete Success": "Delete Success", + "Do you want to delete?": "Do you want to delete?" } \ No newline at end of file diff --git a/src/i18n/en/warehouse.json b/src/i18n/en/warehouse.json new file mode 100644 index 0000000..45bb138 --- /dev/null +++ b/src/i18n/en/warehouse.json @@ -0,0 +1,27 @@ +{ + "Create Warehouse": "Create Warehouse", + "Edit Warehouse": "Edit Warehouse", + "Warehouse Detail": "Warehouse Detail", + "code": "Code", + "name": "Name", + "description": "Description", + "Edit": "Edit", + "Delete": "Delete", + "Delete Success": "Delete Success", + "Warehouse": "Warehouse", + "warehouse": "warehouse", + "Rows per page": "Rows per page", + "capacity": "Capacity", + "store_id": "Store ID", + "area": "Area", + "slot": "Slot", + "order": "Order", + "stockTakeSection": "Stock Take Section", + "Do you want to delete?": "Do you want to delete?", + "Cancel": "Cancel", + "Reset": "Reset", + "Confirm": "Confirm", + "is required": "is required", + "Search Criteria": "Search Criteria", + "Search": "Search" +} diff --git a/src/i18n/zh/common.json b/src/i18n/zh/common.json index e1eb9fb..6a87bab 100644 --- a/src/i18n/zh/common.json +++ b/src/i18n/zh/common.json @@ -68,6 +68,7 @@ "Setup Time": "生產前預備時間", "Changeover Time": "生產後轉換時間", "Warehouse": "倉庫", + "warehouse": "倉庫", "Supplier": "供應商", "Purchase Order": "採購單", "Demand Forecast": "需求預測", @@ -259,6 +260,7 @@ "Seq No Remark": "序號明細", "Stock Available": "庫存可用", "Confirm": "確認", + "Do you want to delete?": "您確定要刪除嗎?", "Stock Status": "庫存狀態", "Target Production Date": "目標生產日期", "id": "ID", diff --git a/src/i18n/zh/user.json b/src/i18n/zh/user.json index 796605b..63eac26 100644 --- a/src/i18n/zh/user.json +++ b/src/i18n/zh/user.json @@ -28,5 +28,7 @@ "user": "用戶", "qrcode": "二維碼", "staffNo": "員工編號", - "Rows per page": "每頁行數" + "Rows per page": "每頁行數", + "Delete Success": "刪除成功", + "Do you want to delete?": "您確定要刪除嗎?" } \ No newline at end of file diff --git a/src/i18n/zh/warehouse.json b/src/i18n/zh/warehouse.json new file mode 100644 index 0000000..763fed3 --- /dev/null +++ b/src/i18n/zh/warehouse.json @@ -0,0 +1,27 @@ +{ + "Create Warehouse": "新增倉庫", + "Edit Warehouse": "編輯倉庫資料", + "Warehouse Detail": "倉庫詳細資料", + "code": "編號", + "name": "名稱", + "description": "描述", + "Edit": "編輯", + "Delete": "刪除", + "Delete Success": "刪除成功", + "Warehouse": "倉庫", + "warehouse": "倉庫", + "Rows per page": "每頁行數", + "capacity": "容量", + "store_id": "樓層", + "area": "區域", + "slot": "位置", + "order": "提料單次序", + "stockTakeSection": "盤點區域", + "Do you want to delete?": "您確定要刪除嗎?", + "Cancel": "取消", + "Reset": "重置", + "Confirm": "確認", + "is required": "必填", + "Search Criteria": "搜尋條件", + "Search": "搜尋" +}