From d20b65bd1225d763461ffc5af530fc0f1eaa97dd Mon Sep 17 00:00:00 2001 From: SeongHyun Kim Date: Wed, 1 Apr 2026 21:46:22 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EC=9E=85=EA=B3=A0=20=EC=9E=A5=EB=B0=94?= =?UTF-8?q?=EA=B5=AC=EB=8B=88=20=EB=8F=85=EB=A6=BD=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=20(/pop/inbound/cart)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - InboundCartPage: cart_items DB 영속 저장, 탭 이동해도 유지 - 상단: 입고일자(날짜피커) + 창고(터치선택) + 입고번호(자동채번) - 품목: 체크박스, 수량수정(키패드), 삭제, 포장정보 - 검사: 필수→완료필수, 비필수→패스버튼, 미완료시 확정차단 - 전량 입고 원칙: 불량 포함 전체 입고 (팀장 규칙) - PurchaseInbound: 장바구니 아이콘→독립페이지 이동 (모달 제거) --- frontend/app/(pop)/pop/inbound/cart/page.tsx | 12 + .../app/(pop)/pop/inbound/purchase/page.tsx | 7 +- .../pop/hardcoded/inbound/InboundCartPage.tsx | 1156 +++++++++++++++++ .../pop/hardcoded/inbound/PurchaseInbound.tsx | 35 +- .../components/pop/hardcoded/inbound/index.ts | 1 + 5 files changed, 1181 insertions(+), 30 deletions(-) create mode 100644 frontend/app/(pop)/pop/inbound/cart/page.tsx create mode 100644 frontend/components/pop/hardcoded/inbound/InboundCartPage.tsx diff --git a/frontend/app/(pop)/pop/inbound/cart/page.tsx b/frontend/app/(pop)/pop/inbound/cart/page.tsx new file mode 100644 index 00000000..b6dada4f --- /dev/null +++ b/frontend/app/(pop)/pop/inbound/cart/page.tsx @@ -0,0 +1,12 @@ +"use client"; + +import { PopShell } from "@/components/pop/hardcoded"; +import { InboundCartPage } from "@/components/pop/hardcoded/inbound/InboundCartPage"; + +export default function InboundCartRoute() { + return ( + + + + ); +} diff --git a/frontend/app/(pop)/pop/inbound/purchase/page.tsx b/frontend/app/(pop)/pop/inbound/purchase/page.tsx index de8295d6..6afb9891 100644 --- a/frontend/app/(pop)/pop/inbound/purchase/page.tsx +++ b/frontend/app/(pop)/pop/inbound/purchase/page.tsx @@ -1,12 +1,13 @@ "use client"; import { useState } from "react"; +import { useRouter } from "next/navigation"; import { PopShell } from "@/components/pop/hardcoded"; import { PurchaseInbound } from "@/components/pop/hardcoded/inbound"; export default function PurchaseInboundPage() { + const router = useRouter(); const [cartCount, setCartCount] = useState(0); - const [openCart, setOpenCart] = useState(false); return ( setOpenCart(true)} + onClick={() => router.push("/pop/inbound/cart")} className="relative w-11 h-11 rounded-xl bg-white/10 flex items-center justify-center text-white hover:bg-white/20 active:scale-95 transition-all" > @@ -30,8 +31,6 @@ export default function PurchaseInboundPage() { > setOpenCart(false)} /> ); diff --git a/frontend/components/pop/hardcoded/inbound/InboundCartPage.tsx b/frontend/components/pop/hardcoded/inbound/InboundCartPage.tsx new file mode 100644 index 00000000..66ab2737 --- /dev/null +++ b/frontend/components/pop/hardcoded/inbound/InboundCartPage.tsx @@ -0,0 +1,1156 @@ +"use client"; + +import React, { useState, useEffect, useCallback, useRef } from "react"; +import { useRouter } from "next/navigation"; +import { apiClient } from "@/lib/api/client"; +import { InspectionModal, type InspectionResult } from "./InspectionModal"; +import { NumberPadModal, type PackageEntry } from "./NumberPadModal"; + +/* ------------------------------------------------------------------ */ +/* Types */ +/* ------------------------------------------------------------------ */ + +interface Warehouse { + warehouse_code: string; + warehouse_name: string; + warehouse_type?: string; +} + +interface CartItemRow { + id: string; + screen_id: string; + source_table: string; + row_key: string; + row_data: string; + quantity: string; + unit: string; + package_entries?: string; + status?: string; + user_id?: string; +} + +interface CartItemParsed { + id: string; + dbId: string; // cart_items.id for DELETE + source_table: string; + source_id: string; + purchase_no: string; + item_code: string; + item_name: string; + spec: string; + material: string; + order_qty: number; + remain_qty: number; + inbound_qty: number; + unit_price: number; + supplier_code: string; + supplier_name: string; + order_date?: string; + inspection_required?: boolean; + inspection_type?: "self" | "request" | null; + packages?: PackageEntry[]; + inspectionResult?: InspectionResult | null; +} + +/* ------------------------------------------------------------------ */ +/* Component */ +/* ------------------------------------------------------------------ */ + +export function InboundCartPage() { + const router = useRouter(); + + /* Cart items */ + const [items, setItems] = useState([]); + const [loading, setLoading] = useState(true); + + /* Selection */ + const [selectedItems, setSelectedItems] = useState>(new Set()); + + /* Warehouse */ + const [warehouses, setWarehouses] = useState([]); + const [selectedWarehouse, setSelectedWarehouse] = useState(""); + const [warehousePickerOpen, setWarehousePickerOpen] = useState(false); + + /* Inbound number */ + const [inboundNumber, setInboundNumber] = useState(""); + + /* Inbound date */ + const [inboundDate, setInboundDate] = useState( + new Date().toISOString().slice(0, 10) + ); + + /* Confirm state */ + const [confirming, setConfirming] = useState(false); + const [resultMsg, setResultMsg] = useState(null); + + /* Inspection modal */ + const [inspectionModalOpen, setInspectionModalOpen] = useState(false); + const [inspectionTarget, setInspectionTarget] = useState(null); + + /* Numpad modal (for qty edit) */ + const [numpadOpen, setNumpadOpen] = useState(false); + const [numpadTarget, setNumpadTarget] = useState(null); + + /* Derived: supplier name (all items should be same supplier) */ + const supplierName = items.length > 0 ? items[0].supplier_name : ""; + + /* Ref for fetch-once guard */ + const fetchedRef = useRef(false); + + /* ------------------------------------------------------------------ */ + /* Fetch cart_items from DB */ + /* ------------------------------------------------------------------ */ + const fetchCartItems = useCallback(async () => { + setLoading(true); + try { + const res = await apiClient.get("/data/cart_items", { + params: { + screen_id: "pop-purchase-inbound", + status: "pending", + pageSize: 100, + sortColumn: "created_date", + sortDirection: "desc", + }, + }); + + const rows: CartItemRow[] = res.data?.data ?? res.data?.rows ?? []; + + if (Array.isArray(rows) && rows.length > 0) { + const parsed: CartItemParsed[] = rows.map((row) => { + let data: Record = {}; + try { + data = JSON.parse(row.row_data || "{}"); + } catch { + /* ignore parse error */ + } + + let packages: PackageEntry[] | undefined; + if (row.package_entries) { + try { + packages = JSON.parse(row.package_entries); + } catch { + /* ignore */ + } + } + + return { + id: row.row_key || row.id, + dbId: row.id, + source_table: row.source_table || "purchase_detail", + source_id: row.row_key || "", + purchase_no: String(data.purchase_no ?? ""), + item_code: String(data.item_code ?? ""), + item_name: String(data.item_name ?? ""), + spec: String(data.spec ?? ""), + material: String(data.material ?? ""), + order_qty: Number(data.order_qty ?? 0), + remain_qty: Number(data.remain_qty ?? 0), + inbound_qty: Number(row.quantity ?? 0), + unit_price: Number(data.unit_price ?? 0), + supplier_code: String(data.supplier_code ?? ""), + supplier_name: String(data.supplier_name ?? ""), + order_date: data.order_date ? String(data.order_date) : undefined, + inspection_type: + data.inspection_type === "self" + ? "self" + : data.inspection_type === "request" + ? "request" + : null, + inspection_required: data.inspection_type === "self", + packages, + }; + }); + setItems(parsed); + // Auto select all + setSelectedItems(new Set(parsed.map((p) => p.id))); + } else { + setItems([]); + } + } catch { + setItems([]); + } finally { + setLoading(false); + } + }, []); + + /* ------------------------------------------------------------------ */ + /* Fetch warehouses */ + /* ------------------------------------------------------------------ */ + const fetchWarehouses = useCallback(async () => { + try { + const res = await apiClient.get("/receiving/warehouses"); + const data: Warehouse[] = res.data?.data ?? []; + setWarehouses(data); + if (data.length > 0) { + setSelectedWarehouse(data[0].warehouse_code); + } + } catch { + /* keep empty */ + } + }, []); + + /* ------------------------------------------------------------------ */ + /* Generate inbound number */ + /* ------------------------------------------------------------------ */ + const fetchInboundNumber = useCallback(async () => { + try { + const res = await apiClient.get("/receiving/generate-number"); + if (res.data?.success && res.data?.data) { + setInboundNumber(res.data.data); + } + } catch { + /* generate on confirm as fallback */ + } + }, []); + + /* ------------------------------------------------------------------ */ + /* Initial load */ + /* ------------------------------------------------------------------ */ + useEffect(() => { + if (fetchedRef.current) return; + fetchedRef.current = true; + fetchCartItems(); + fetchWarehouses(); + fetchInboundNumber(); + }, [fetchCartItems, fetchWarehouses, fetchInboundNumber]); + + /* ------------------------------------------------------------------ */ + /* Selection */ + /* ------------------------------------------------------------------ */ + const toggleSelect = (id: string) => { + setSelectedItems((prev) => { + const next = new Set(prev); + if (next.has(id)) next.delete(id); + else next.add(id); + return next; + }); + }; + + const toggleSelectAll = () => { + if (selectedItems.size === items.length) { + setSelectedItems(new Set()); + } else { + setSelectedItems(new Set(items.map((i) => i.id))); + } + }; + + /* ------------------------------------------------------------------ */ + /* Qty edit via numpad */ + /* ------------------------------------------------------------------ */ + const openNumpad = (item: CartItemParsed) => { + setNumpadTarget(item); + setNumpadOpen(true); + }; + + const handleNumpadConfirm = (qty: number, packages: PackageEntry[]) => { + if (!numpadTarget) return; + setItems((prev) => + prev.map((item) => + item.id === numpadTarget.id + ? { + ...item, + inbound_qty: Math.min(qty, item.remain_qty), + packages: packages.length > 0 ? packages : item.packages, + } + : item + ) + ); + setNumpadTarget(null); + + // Update cart_items in DB (background) + const targetItem = items.find((i) => i.id === numpadTarget.id); + if (targetItem) { + apiClient + .post("/pop/execute-action", { + tasks: [{ type: "cart-save" }], + cartChanges: { + toUpdate: [ + { + id: targetItem.dbId, + quantity: String(Math.min(qty, targetItem.remain_qty)), + ...(packages.length > 0 + ? { package_entries: JSON.stringify(packages) } + : {}), + }, + ], + }, + }) + .catch(() => {}); + } + }; + + /* ------------------------------------------------------------------ */ + /* Remove item */ + /* ------------------------------------------------------------------ */ + const handleRemove = (id: string) => { + const item = items.find((i) => i.id === id); + setItems((prev) => prev.filter((i) => i.id !== id)); + setSelectedItems((prev) => { + const next = new Set(prev); + next.delete(id); + return next; + }); + + // Delete from cart_items DB (background) + if (item) { + apiClient + .post("/pop/execute-action", { + tasks: [{ type: "cart-save" }], + cartChanges: { + toDelete: [item.dbId], + }, + }) + .catch(() => {}); + } + }; + + /* ------------------------------------------------------------------ */ + /* Inspection */ + /* ------------------------------------------------------------------ */ + const openInspection = (item: CartItemParsed) => { + setInspectionTarget(item); + setInspectionModalOpen(true); + }; + + const handleInspectionComplete = (result: InspectionResult) => { + if (!inspectionTarget) return; + setItems((prev) => + prev.map((item) => + item.id === inspectionTarget.id + ? { ...item, inspectionResult: result } + : item + ) + ); + setInspectionTarget(null); + }; + + /* Pass inspection (non-required only) */ + const handlePassInspection = (id: string) => { + setItems((prev) => + prev.map((item) => + item.id === id + ? { + ...item, + inspectionResult: { + items: [], + goodQty: item.inbound_qty, + badQty: 0, + remark: "pass", + completed: true, + }, + } + : item + ) + ); + }; + + /* ------------------------------------------------------------------ */ + /* Validation: required inspections */ + /* ------------------------------------------------------------------ */ + const selectedItemsList = items.filter((i) => selectedItems.has(i.id)); + + const hasUnfinishedRequiredInspection = selectedItemsList.some( + (item) => + item.inspection_required && + item.inspection_type === "self" && + !item.inspectionResult?.completed + ); + + /* ------------------------------------------------------------------ */ + /* Confirm inbound */ + /* ------------------------------------------------------------------ */ + const handleConfirm = async () => { + if (selectedItemsList.length === 0) return; + + if (!selectedWarehouse) { + setResultMsg("오류: 입고 창고를 선택해주세요."); + return; + } + + if (hasUnfinishedRequiredInspection) { + setResultMsg("오류: 필수 검사를 완료해주세요."); + return; + } + + setConfirming(true); + setResultMsg(null); + + try { + // Use existing inbound number or generate new one + let finalNumber = inboundNumber; + if (!finalNumber) { + try { + const numRes = await apiClient.get("/receiving/generate-number"); + if (numRes.data?.success && numRes.data?.data) { + finalNumber = numRes.data.data; + } + } catch { + /* backend will handle */ + } + } + + // POST /api/receiving -- same payload structure as PC + const payload = { + inbound_number: finalNumber, + inbound_date: inboundDate, + warehouse_code: selectedWarehouse, + inbound_type: "구매입고", + items: selectedItemsList.map((item, idx) => ({ + inbound_type: "구매입고", + item_number: item.item_code, + item_name: item.item_name, + spec: item.spec || "", + material: item.material || "", + unit: "EA", + inbound_qty: String(item.inbound_qty), + unit_price: String(item.unit_price || 0), + total_amount: String( + (item.inbound_qty || 0) * (item.unit_price || 0) + ), + reference_number: item.purchase_no, + supplier_code: item.supplier_code, + supplier_name: item.supplier_name, + inspection_status: item.inspectionResult?.completed + ? "검사완료" + : item.inspection_required + ? "검사대기" + : "합격", + source_table: item.source_table, + source_id: item.source_id || item.id, + seq_no: idx + 1, + })), + }; + + const res = await apiClient.post("/receiving", payload); + + if (res.data?.success) { + // Delete confirmed items from cart_items + const dbIds = selectedItemsList + .map((item) => item.dbId) + .filter(Boolean); + if (dbIds.length > 0) { + apiClient + .post("/pop/execute-action", { + tasks: [{ type: "cart-save" }], + cartChanges: { + toDelete: dbIds, + }, + }) + .catch(() => {}); + } + + const inboundNo = + res.data?.data?.header?.inbound_number || finalNumber || ""; + setResultMsg( + `${selectedItemsList.length}건 입고 등록 완료! (${inboundNo})` + ); + setTimeout(() => { + router.push("/pop/inbound"); + }, 1500); + } else { + setResultMsg( + `오류: ${res.data?.message || "입고 등록에 실패했습니다."}` + ); + } + } catch (err: unknown) { + const msg = + err instanceof Error ? err.message : "입고 등록에 실패했습니다."; + setResultMsg(`오류: ${msg}`); + } finally { + setConfirming(false); + } + }; + + /* ------------------------------------------------------------------ */ + /* Helpers */ + /* ------------------------------------------------------------------ */ + const selectedWarehouseName = + warehouses.find((w) => w.warehouse_code === selectedWarehouse) + ?.warehouse_name || selectedWarehouse; + + const totalQty = selectedItemsList.reduce((s, i) => s + i.inbound_qty, 0); + + /* ------------------------------------------------------------------ */ + /* Render */ + /* ------------------------------------------------------------------ */ + return ( +
+ {/* ===== Header ===== */} +
+
+ +
+

+ 입고 장바구니 +

+ {supplierName && ( +

{supplierName}

+ )} +
+
+ + {/* Confirm button (header) */} + +
+ + {/* ===== Info banner ===== */} +
+
+ {supplierName && ( + + {supplierName} + + )} + + {inboundDate} + + {selectedWarehouseName && ( + + | {selectedWarehouseName} + + )} + {inboundNumber && ( + + {inboundNumber} + + )} +
+ + {/* Info fields: 3 columns */} +
+ {/* Inbound date */} +
+ + setInboundDate(e.target.value)} + className="w-full px-3 py-2.5 border border-gray-200 rounded-lg text-sm outline-none focus:border-blue-400 focus:ring-1 focus:ring-blue-100 bg-white" + /> +
+ + {/* Warehouse selector - card-style touch button */} +
+ + +
+ + {/* Inbound number (readonly) */} +
+ +
+ {inboundNumber || "자동 채번"} +
+
+
+
+ + {/* ===== Select all bar ===== */} + {items.length > 0 && ( +
+
+ + + 담은 품목{" "} + {items.length} + +
+ + +
+ )} + + {/* ===== Items list ===== */} + {loading ? ( +
+ + + + + 불러오는 중... +
+ ) : items.length === 0 ? ( +
+ + + +

+ 담은 품목이 없습니다 +

+

+ 구매입고 화면에서 품목을 담아주세요 +

+ +
+ ) : ( +
+ {items.map((item) => ( +
+ {/* Top row: checkbox + item info + delete */} +
+ {/* Checkbox */} + + + {/* Product image placeholder */} +
+ {"\uD83D\uDCE6"} +
+ +
+
+ + {item.item_code} + +
+

+ {item.item_name} +

+ {(item.spec || item.material) && ( +

+ {[item.spec, item.material].filter(Boolean).join(" | ")} +

+ )} + {item.purchase_no && ( +

+ {item.purchase_no} + {item.order_date ? ` | ${item.order_date}` : ""} +

+ )} +
+ + {/* Delete button */} + +
+ + {/* Qty row: remain info + qty edit button */} +
+
+ + 미입고{" "} + + {item.remain_qty.toLocaleString()} + + + {item.unit_price > 0 && ( + + | @{item.unit_price.toLocaleString()}원 + + )} +
+ + {/* Qty button (opens numpad) */} + +
+ + {/* Package info */} + {item.packages && item.packages.length > 0 && ( +
+
+ + 포장완료 + + + {"\uD83D\uDCE6"}{" "} + {item.packages + .map( + (p) => + `${p.count}${p.unit.label} x ${p.qtyPerUnit.toLocaleString()} = ${( + p.count * p.qtyPerUnit + ).toLocaleString()}EA` + ) + .join(", ")} + +
+
+ )} + + {/* Inspection row */} + {(item.inspection_type === "self" || + item.inspection_type === "request") && ( +
+
+ + + {/* Pass button for non-required */} + {!item.inspection_required && + !item.inspectionResult?.completed && ( + + )} +
+
+ )} +
+ ))} +
+ )} + + {/* ===== Footer summary + confirm ===== */} + {items.length > 0 && ( +
+ {/* Result message */} + {resultMsg && ( +
+ {resultMsg} +
+ )} + + {/* Required inspection warning */} + {hasUnfinishedRequiredInspection && ( +
+ 필수 검사를 완료해주세요. 검사 미완료 품목이 있어 확정할 수 없습니다. +
+ )} + + {/* Summary */} +
+ + 선택{" "} + + {selectedItemsList.length} + + /{items.length}건 + + + 합계 수량:{" "} + + {totalQty.toLocaleString()} + {" "} + EA + +
+ + {/* Confirm button (footer) */} + +
+ )} + + {/* ===== Warehouse picker modal ===== */} + {warehousePickerOpen && ( +
+
setWarehousePickerOpen(false)} + /> +
+ {/* Header */} +
+

창고 선택

+ +
+ + {/* Warehouse list */} +
+ {warehouses.length === 0 ? ( +

+ 등록된 창고가 없습니다 +

+ ) : ( +
+ {warehouses.map((wh) => ( + + ))} +
+ )} +
+
+
+ )} + + {/* ===== Inspection Modal ===== */} + {inspectionTarget && ( + { + setInspectionModalOpen(false); + setInspectionTarget(null); + }} + onComplete={handleInspectionComplete} + itemCode={inspectionTarget.item_code} + itemName={inspectionTarget.item_name} + totalQty={inspectionTarget.inbound_qty} + initialResult={inspectionTarget.inspectionResult} + /> + )} + + {/* ===== NumberPad Modal (qty edit) ===== */} + {numpadTarget && ( + { + setNumpadOpen(false); + setNumpadTarget(null); + }} + onConfirm={handleNumpadConfirm} + maxQty={numpadTarget.remain_qty} + itemName={numpadTarget.item_name} + initialQty={numpadTarget.inbound_qty} + initialPackages={numpadTarget.packages} + /> + )} +
+ ); +} diff --git a/frontend/components/pop/hardcoded/inbound/PurchaseInbound.tsx b/frontend/components/pop/hardcoded/inbound/PurchaseInbound.tsx index 6cf00c13..ec39f9cd 100644 --- a/frontend/components/pop/hardcoded/inbound/PurchaseInbound.tsx +++ b/frontend/components/pop/hardcoded/inbound/PurchaseInbound.tsx @@ -4,7 +4,7 @@ import React, { useState, useCallback, useEffect, useMemo, useRef } from "react" import { useRouter } from "next/navigation"; import { apiClient } from "@/lib/api/client"; import { SupplierModal, type Supplier, matchChosung } from "./SupplierModal"; -import { InboundCart, type CartItem } from "./InboundCart"; +import type { CartItem } from "./InboundCart"; import { NumberPadModal, type PackageEntry } from "./NumberPadModal"; import { BarcodeScanModal } from "../common/BarcodeScanModal"; @@ -86,19 +86,14 @@ const DUMMY_ORDERS: PurchaseOrder[] = [ interface PurchaseInboundProps { onCartCountChange?: (count: number) => void; - externalCartOpen?: boolean; - onExternalCartClose?: () => void; } -export function PurchaseInbound({ onCartCountChange, externalCartOpen, onExternalCartClose }: PurchaseInboundProps = {}) { +export function PurchaseInbound({ onCartCountChange }: PurchaseInboundProps = {}) { const router = useRouter(); /* State */ const [selectedSupplier, setSelectedSupplier] = useState(null); const [supplierModalOpen, setSupplierModalOpen] = useState(false); - const [_cartOpen, _setCartOpen] = useState(false); - const cartOpen = externalCartOpen || _cartOpen; - const setCartOpen = (v: boolean) => { _setCartOpen(v); if (!v && onExternalCartClose) onExternalCartClose(); }; const [cartItems, _setCartItems] = useState([]); const setCartItems: typeof _setCartItems = (val) => { _setCartItems(val); @@ -300,14 +295,13 @@ export function PurchaseInbound({ onCartCountChange, externalCartOpen, onExterna /* Remove from cart (cancel) */ const handleRemoveFromCart = (id: string) => { setCartItems((prev) => prev.filter((c) => c.id !== id)); - }; - - /* Cart operations */ - const handleUpdateCartQty = (id: string, qty: number) => { - setCartItems((prev) => prev.map((c) => c.id === id ? { ...c, inbound_qty: qty } : c)); - }; - const handleClearCart = () => { - setCartItems([]); + // Delete from cart_items DB (background) + apiClient.post("/pop/execute-action", { + tasks: [{ type: "cart-save" }], + cartChanges: { + toDelete: [id], + }, + }).catch(() => {}); }; /* Search */ @@ -617,17 +611,6 @@ export function PurchaseInbound({ onCartCountChange, externalCartOpen, onExterna onSelect={(s) => setSelectedSupplier(s)} /> - setCartOpen(false)} - items={cartItems} - onUpdateQty={handleUpdateCartQty} - onRemove={handleRemoveFromCart} - onClear={handleClearCart} - supplierName={selectedSupplier?.customer_name} - onUpdateItems={setCartItems} - /> - { setNumpadOpen(false); setNumpadTarget(null); }} diff --git a/frontend/components/pop/hardcoded/inbound/index.ts b/frontend/components/pop/hardcoded/inbound/index.ts index 88b57e34..b4272e78 100644 --- a/frontend/components/pop/hardcoded/inbound/index.ts +++ b/frontend/components/pop/hardcoded/inbound/index.ts @@ -5,3 +5,4 @@ export { InboundCart } from "./InboundCart"; export { NumberPadModal } from "./NumberPadModal"; export { PackagingModal } from "./PackagingModal"; export { InspectionModal } from "./InspectionModal"; +export { InboundCartPage } from "./InboundCartPage";