Files
star-erp/resources/js/Pages/Warehouse/SafetyStockSettings.tsx

194 lines
7.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 安全庫存設定頁面
* Last Updated: 2025-12-29
*/
import { useState, useEffect } from "react";
import { ArrowLeft, Plus, Shield } from "lucide-react";
import { Button } from "@/Components/ui/button";
import AuthenticatedLayout from "@/Layouts/AuthenticatedLayout";
import { Head, Link, router } from "@inertiajs/react";
import { SafetyStockSetting, WarehouseInventory, Warehouse, Product } from "@/types/warehouse";
import SafetyStockList from "@/Components/Warehouse/SafetyStock/SafetyStockList";
import AddSafetyStockDialog from "@/Components/Warehouse/SafetyStock/AddSafetyStockDialog";
import EditSafetyStockDialog from "@/Components/Warehouse/SafetyStock/EditSafetyStockDialog";
import { toast } from "sonner";
import { getInventoryBreadcrumbs } from "@/utils/breadcrumb";
import { Can } from "@/Components/Permission/Can";
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
} from "@/Components/ui/alert-dialog";
interface Props {
warehouse: Warehouse;
safetyStockSettings: SafetyStockSetting[];
inventories: WarehouseInventory[];
availableProducts: Product[];
}
export default function SafetyStockPage({
warehouse,
safetyStockSettings: initialSettings = [],
inventories = [],
availableProducts = [],
}: Props) {
const [settings, setSettings] = useState<SafetyStockSetting[]>(initialSettings);
const [showAddDialog, setShowAddDialog] = useState(false);
const [editingSetting, setEditingSetting] = useState<SafetyStockSetting | null>(null);
const [deleteId, setDeleteId] = useState<string | null>(null);
// 當 Props 更新時同步本地 State
useEffect(() => {
setSettings(initialSettings);
}, [initialSettings]);
const handleAdd = (newSettings: SafetyStockSetting[]) => {
router.post(route('warehouses.safety-stock.store', warehouse.id), {
settings: newSettings.map(s => ({
productId: s.productId,
quantity: s.safetyStock
})),
}, {
onSuccess: () => {
setShowAddDialog(false);
toast.success(`成功新增 ${newSettings.length} 項安全庫存設定`);
},
onError: (errors) => {
const firstError = Object.values(errors)[0];
toast.error(typeof firstError === 'string' ? firstError : "新增失敗");
}
});
};
const handleEdit = (updatedSetting: SafetyStockSetting) => {
router.put(route('warehouses.safety-stock.update', [warehouse.id, updatedSetting.id]), {
safetyStock: updatedSetting.safetyStock,
}, {
onSuccess: () => {
setEditingSetting(null);
toast.success(`成功更新 ${updatedSetting.productName} 的安全庫存`);
},
onError: (errors) => {
const firstError = Object.values(errors)[0];
toast.error(typeof firstError === 'string' ? firstError : "更新失敗");
}
});
};
const handleDelete = () => {
if (!deleteId) return;
router.delete(route('warehouses.safety-stock.destroy', [warehouse.id, deleteId]), {
onSuccess: () => {
setDeleteId(null);
toast.success("已刪除安全庫存設定");
},
onError: () => {
toast.error("刪除失敗");
}
});
};
if (!warehouse) {
return <div className="p-8 text-center text-muted-foreground">...</div>;
}
return (
<AuthenticatedLayout breadcrumbs={getInventoryBreadcrumbs(warehouse.id, warehouse.name, "安全庫存設定")}>
<Head title={`安全庫存設定 - ${warehouse.name}`} />
<div className="container mx-auto p-6 max-w-7xl">
{/* 頁面標題與導航 */}
<div className="mb-6">
<Link href={route('warehouses.inventory.index', warehouse.id)}>
<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="text-2xl font-bold text-grey-0 flex items-center gap-2">
<Shield className="h-6 w-6 text-[#01ab83]" />
- {warehouse.name}
</h1>
<p className="text-gray-500 mt-1">
</p>
</div>
<Can permission="inventory.safety_stock">
<Button
onClick={() => setShowAddDialog(true)}
className="button-filled-primary"
>
<Plus className="mr-2 h-4 w-4" />
</Button>
</Can>
</div>
</div>
{/* 安全庫存列表 */}
<SafetyStockList
settings={settings}
inventories={inventories}
onEdit={setEditingSetting}
onDelete={setDeleteId}
/>
{/* 新增對話框 */}
<AddSafetyStockDialog
open={showAddDialog}
onOpenChange={setShowAddDialog}
warehouseId={warehouse.id}
existingSettings={settings}
availableProducts={availableProducts}
onAdd={handleAdd}
/>
{/* 編輯對話框 */}
{editingSetting && (
<EditSafetyStockDialog
open={!!editingSetting}
onOpenChange={(open) => !open && setEditingSetting(null)}
setting={editingSetting}
onSave={handleEdit}
/>
)}
{/* 刪除確認對話框 */}
<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>
);
}