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

194 lines
7.8 KiB
TypeScript
Raw Normal View History

2025-12-30 15:03:19 +08:00
/**
*
* Last Updated: 2025-12-29
*/
import { useState, useEffect } from "react";
import { ArrowLeft, Plus, Shield } from "lucide-react";
2025-12-30 15:03:19 +08:00
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";
2026-01-07 13:06:49 +08:00
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";
2025-12-30 15:03:19 +08:00
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);
2025-12-30 15:03:19 +08:00
// 當 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]), {
2025-12-30 15:03:19 +08:00
onSuccess: () => {
setDeleteId(null);
2025-12-30 15:03:19 +08:00
toast.success("已刪除安全庫存設定");
},
onError: () => {
toast.error("刪除失敗");
2025-12-30 15:03:19 +08:00
}
});
};
if (!warehouse) {
return <div className="p-8 text-center text-muted-foreground">...</div>;
}
return (
2026-01-07 13:06:49 +08:00
<AuthenticatedLayout breadcrumbs={getInventoryBreadcrumbs(warehouse.id, warehouse.name, "安全庫存設定")}>
2025-12-30 15:03:19 +08:00
<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-primary-main" />
- {warehouse.name}
</h1>
<p className="text-gray-500 mt-1">
2025-12-30 15:03:19 +08:00
</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>
2025-12-30 15:03:19 +08:00
</div>
</div>
{/* 安全庫存列表 */}
<SafetyStockList
settings={settings}
inventories={inventories}
onEdit={setEditingSetting}
onDelete={setDeleteId}
2025-12-30 15:03:19 +08:00
/>
{/* 新增對話框 */}
<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="button-filled-error"
>
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
2025-12-30 15:03:19 +08:00
</div>
</AuthenticatedLayout>
);
}