Files
star-erp/resources/js/Components/Warehouse/SafetyStock/SafetyStockList.tsx
sky121113 4fa87925a2
All checks were successful
Koori-ERP-Deploy-System / deploy-demo (push) Has been skipped
Koori-ERP-Deploy-System / deploy-production (push) Successful in 1m8s
UI優化: 全系統狀態標籤 (StatusBadge) 統一化重構完成 (Phase 3 & 4)
2026-02-13 13:16:05 +08:00

163 lines
7.0 KiB
TypeScript

/**
* 安全庫存設定列表
*/
import { Trash2, Pencil, Package } from "lucide-react";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/Components/ui/table";
import { Button } from "@/Components/ui/button";
import { StatusBadge } from "@/Components/shared/StatusBadge";
import { SafetyStockSetting, WarehouseInventory } from "@/types/warehouse";
import { calculateProductTotalStock, getSafetyStockStatus } from "@/utils/inventory";
import { Can } from "@/Components/Permission/Can";
interface SafetyStockListProps {
settings: SafetyStockSetting[];
inventories: WarehouseInventory[];
onEdit: (setting: SafetyStockSetting) => void;
onDelete: (id: string) => void;
}
export default function SafetyStockList({
settings,
inventories,
onEdit,
onDelete,
}: SafetyStockListProps) {
if (settings.length === 0) {
return (
<div className="bg-white rounded-lg border border-dashed p-12 text-center">
<div className="mx-auto w-12 h-12 bg-gray-50 rounded-full flex items-center justify-center mb-4">
<Package className="h-6 w-6 text-gray-400" />
</div>
<h3 className="text-lg font-medium text-gray-900"></h3>
<p className="text-gray-500 mt-1 max-w-xs mx-auto">
</p>
</div>
);
}
// 按產品類型與名稱排序
const sortedSettings = [...settings].sort((a, b) => {
if (a.productType !== b.productType) {
return a.productType.localeCompare(b.productType, "zh-TW");
}
return a.productName.localeCompare(b.productName, "zh-TW");
});
// 獲取狀態徽章 (與 InventoryTable 保持一致)
const getStatusBadge = (quantity: number, safetyStock: number, isNew?: boolean) => {
// 如果是自動帶入的品項且尚未存檔,顯示「未設定」
if (isNew) {
return (
<StatusBadge variant="neutral" className="border-gray-200 font-normal text-gray-400">
</StatusBadge>
);
}
const status = getSafetyStockStatus(quantity, safetyStock);
if (status === '正常') {
return (
<StatusBadge variant="success">
</StatusBadge>
);
}
if (status === '接近') { // 數量 <= 安全庫存 * 1.2
return (
<StatusBadge variant="warning">
</StatusBadge>
);
}
if (status === '低於') { // 數量 < 安全庫存
return (
<StatusBadge variant="destructive">
</StatusBadge>
);
}
return null;
};
return (
<div className="bg-white rounded-lg border shadow-sm overflow-hidden">
<Table>
<TableHeader>
<TableRow className="bg-gray-50/50">
<TableHead className="w-[50px]">#</TableHead>
<TableHead className="w-[250px]"></TableHead>
<TableHead className="w-[120px]"></TableHead>
<TableHead className="w-[150px] text-right"></TableHead>
<TableHead className="w-[150px] text-right"></TableHead>
<TableHead className="w-[150px]"></TableHead>
<TableHead className="w-[100px] text-right"></TableHead>
</TableRow>
</TableHeader>
<TableBody>
{sortedSettings.map((setting, index) => {
const currentStock = calculateProductTotalStock(inventories, setting.productId);
return (
<TableRow key={setting.id}>
<TableCell className="text-gray-500 font-medium">
{index + 1}
</TableCell>
<TableCell className="font-medium text-gray-900">
{setting.productName}
</TableCell>
<TableCell>
<StatusBadge variant="neutral">
{setting.productType}
</StatusBadge>
</TableCell>
<TableCell className="text-right font-semibold">
{setting.safetyStock} {setting.unit || '個'}
</TableCell>
<TableCell className="text-right">
<span className={currentStock < setting.safetyStock ? "text-orange-600 font-bold" : "text-gray-700"}>
{currentStock} {setting.unit || '個'}
</span>
</TableCell>
<TableCell>
{getStatusBadge(currentStock, setting.safetyStock, setting.isNew)}
</TableCell>
<TableCell className="text-right">
<div className="flex justify-end gap-2">
<Can permission="inventory.safety_stock">
<Button
variant="outline"
size="sm"
onClick={() => onEdit(setting)}
className="button-outlined-primary"
>
<Pencil className="h-4 w-4" />
</Button>
<Button
variant="outline"
size="sm"
onClick={() => onDelete(setting.id)}
className="button-outlined-error"
>
<Trash2 className="h-4 w-4" />
</Button>
</Can>
</div>
</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
</div>
);
}