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")}
+
+ }
+ LinkComponent={Link}
+ href="/settings/warehouse/create"
+ >
+ {t("Create 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}
+
+ )}
+
+
+
+
+ }
+ onClick={(e) => {
+ e.preventDefault();
+ e.stopPropagation();
+ resetForm(e);
+ }}
+ type="button"
+ >
+ {t("Reset")}
+
+ }
+ onClick={handleCancel}
+ type="button"
+ >
+ {t("Cancel")}
+
+ } type="submit">
+ {t("Confirm")}
+
+
+
+
+ >
+ );
+};
+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
+ />
+
+
+
+ }
+ onClick={handleReset}
+ >
+ {t("Reset")}
+
+ }
+ onClick={handleSearch}
+ >
+ {t("Search")}
+
+
+
+
+
+ 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": "搜尋"
+}