163 lines
7.0 KiB
TypeScript
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>
|
|
);
|
|
}
|