first commit
This commit is contained in:
187
resources/js/Pages/Warehouse/Inventory.tsx
Normal file
187
resources/js/Pages/Warehouse/Inventory.tsx
Normal file
@@ -0,0 +1,187 @@
|
||||
import { useState, useMemo } from "react";
|
||||
import { ArrowLeft, PackagePlus, AlertTriangle, Shield } from "lucide-react";
|
||||
import { Button } from "@/Components/ui/button";
|
||||
import AuthenticatedLayout from "@/Layouts/AuthenticatedLayout";
|
||||
import { Head, Link, router } from "@inertiajs/react";
|
||||
import { Warehouse, WarehouseInventory, SafetyStockSetting, Product } from "@/types/warehouse";
|
||||
import InventoryToolbar from "@/Components/Warehouse/Inventory/InventoryToolbar";
|
||||
import InventoryTable from "@/Components/Warehouse/Inventory/InventoryTable";
|
||||
import { calculateLowStockCount } from "@/utils/inventory";
|
||||
import { toast } from "sonner";
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogAction,
|
||||
AlertDialogCancel,
|
||||
AlertDialogContent,
|
||||
AlertDialogDescription,
|
||||
AlertDialogFooter,
|
||||
AlertDialogHeader,
|
||||
AlertDialogTitle,
|
||||
} from "@/Components/ui/alert-dialog";
|
||||
|
||||
// 庫存頁面 Props
|
||||
interface Props {
|
||||
warehouse: Warehouse;
|
||||
inventories: WarehouseInventory[];
|
||||
safetyStockSettings: SafetyStockSetting[];
|
||||
availableProducts: Product[];
|
||||
}
|
||||
|
||||
export default function WarehouseInventoryPage({
|
||||
warehouse,
|
||||
inventories,
|
||||
safetyStockSettings,
|
||||
availableProducts,
|
||||
}: Props) {
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [typeFilter, setTypeFilter] = useState<string>("all");
|
||||
const [deleteId, setDeleteId] = useState<string | null>(null);
|
||||
|
||||
// 篩選庫存列表
|
||||
const filteredInventories = useMemo(() => {
|
||||
return inventories.filter((item) => {
|
||||
// 搜尋條件:匹配商品名稱、編號或批號
|
||||
const matchesSearch = !searchTerm ||
|
||||
item.productName.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
(item.productCode && item.productCode.toLowerCase().includes(searchTerm.toLowerCase())) ||
|
||||
item.batchNumber.toLowerCase().includes(searchTerm.toLowerCase());
|
||||
|
||||
// 類型篩選 (需要比對 availableProducts 找到類型)
|
||||
let matchesType = true;
|
||||
if (typeFilter !== "all") {
|
||||
const product = availableProducts.find((p) => p.id === item.productId);
|
||||
matchesType = product?.type === typeFilter;
|
||||
}
|
||||
|
||||
return matchesSearch && matchesType;
|
||||
});
|
||||
}, [inventories, searchTerm, typeFilter, availableProducts]);
|
||||
|
||||
// 計算統計資訊
|
||||
const lowStockItems = calculateLowStockCount(inventories, warehouse.id, safetyStockSettings);
|
||||
|
||||
// 導航至流動紀錄頁
|
||||
const handleView = (inventoryId: string) => {
|
||||
router.visit(route('warehouses.inventory.history', { warehouse: warehouse.id, inventory: inventoryId }));
|
||||
};
|
||||
|
||||
|
||||
const confirmDelete = (inventoryId: string) => {
|
||||
setDeleteId(inventoryId);
|
||||
};
|
||||
|
||||
const handleDelete = () => {
|
||||
if (!deleteId) return;
|
||||
|
||||
router.delete(route("warehouses.inventory.destroy", { warehouse: warehouse.id, inventory: deleteId }), {
|
||||
onSuccess: () => {
|
||||
toast.success("庫存記錄已刪除");
|
||||
setDeleteId(null);
|
||||
},
|
||||
onError: () => {
|
||||
toast.error("刪除失敗");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<AuthenticatedLayout>
|
||||
<Head title={`庫存管理 - ${warehouse.name}`} />
|
||||
<div className="container mx-auto p-6 max-w-7xl">
|
||||
{/* 頁面標題與導航 */}
|
||||
<div className="mb-6">
|
||||
<Link href="/warehouses">
|
||||
<Button
|
||||
variant="outline"
|
||||
className="gap-2 button-outlined-primary mb-6"
|
||||
>
|
||||
<ArrowLeft className="h-4 w-4" />
|
||||
返回倉庫管理
|
||||
</Button>
|
||||
</Link>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h1 className="mb-2">庫存管理 - {warehouse.name}</h1>
|
||||
<p className="text-gray-600 font-medium">查看並管理此倉庫內的商品庫存數量與批號資訊</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 操作按鈕 (位於標題下方) */}
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
{/* 安全庫存設定按鈕 */}
|
||||
<Link href={`/warehouses/${warehouse.id}/safety-stock-settings`}>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="button-outlined-primary"
|
||||
>
|
||||
<Shield className="mr-2 h-4 w-4" />
|
||||
安全庫存設定
|
||||
</Button>
|
||||
</Link>
|
||||
|
||||
{/* 庫存警告顯示 */}
|
||||
<Button
|
||||
variant="outline"
|
||||
className={`button-outlined-primary cursor-default hover:bg-transparent ${lowStockItems > 0
|
||||
? "border-orange-500 text-orange-600"
|
||||
: "border-green-500 text-green-600"
|
||||
}`}
|
||||
>
|
||||
<AlertTriangle className="mr-2 h-4 w-4" />
|
||||
庫存警告:{lowStockItems} 項
|
||||
</Button>
|
||||
|
||||
{/* 新增庫存按鈕 */}
|
||||
<Link href={`/warehouses/${warehouse.id}/add-inventory`}>
|
||||
<Button
|
||||
className="button-filled-primary"
|
||||
>
|
||||
<PackagePlus className="mr-2 h-4 w-4" />
|
||||
新增庫存
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{/* 篩選工具列 */}
|
||||
<div className="mb-6 bg-white rounded-lg shadow-sm border p-4">
|
||||
<InventoryToolbar
|
||||
searchTerm={searchTerm}
|
||||
onSearchChange={setSearchTerm}
|
||||
typeFilter={typeFilter}
|
||||
onTypeFilterChange={setTypeFilter}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 庫存表格 */}
|
||||
<div className="bg-white rounded-lg shadow-sm border overflow-hidden">
|
||||
<InventoryTable
|
||||
inventories={filteredInventories}
|
||||
onView={handleView}
|
||||
onDelete={confirmDelete}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
{/* 刪除確認對話框 */}
|
||||
<AlertDialog open={!!deleteId} onOpenChange={(open) => !open && setDeleteId(null)}>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>確認刪除庫存項目</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
您確定要刪除此筆庫存項目嗎?此操作將會清空該項目的數量並保留刪除紀錄。此動作無法復原。
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel className="button-outlined-primary">取消</AlertDialogCancel>
|
||||
<AlertDialogAction onClick={handleDelete} className="bg-red-600 hover:bg-red-700 text-white">
|
||||
確認刪除
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
</div>
|
||||
</AuthenticatedLayout>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user