153 lines
5.0 KiB
TypeScript
153 lines
5.0 KiB
TypeScript
/**
|
|
* 安全庫存列表組件
|
|
*/
|
|
|
|
import { Edit, Trash2, AlertCircle, CheckCircle, AlertTriangle } from "lucide-react";
|
|
import {
|
|
Table,
|
|
TableBody,
|
|
TableCell,
|
|
TableHead,
|
|
TableHeader,
|
|
TableRow,
|
|
} from "@/Components/ui/table";
|
|
import { Button } from "@/Components/ui/button";
|
|
import { SafetyStockSetting, WarehouseInventory, SafetyStockStatus } from "@/types/warehouse";
|
|
import { Badge } from "@/Components/ui/badge";
|
|
|
|
interface SafetyStockListProps {
|
|
settings: SafetyStockSetting[];
|
|
inventories: WarehouseInventory[];
|
|
onEdit: (setting: SafetyStockSetting) => void;
|
|
onDelete: (id: string) => void;
|
|
}
|
|
|
|
// 計算安全庫存狀態
|
|
function getSafetyStockStatus(
|
|
currentStock: number,
|
|
safetyStock: number
|
|
): SafetyStockStatus {
|
|
const ratio = currentStock / safetyStock;
|
|
if (ratio >= 1.2) return "正常";
|
|
if (ratio >= 1.0) return "接近";
|
|
return "低於";
|
|
}
|
|
|
|
// 獲取狀態徽章
|
|
function getStatusBadge(status: SafetyStockStatus) {
|
|
switch (status) {
|
|
case "正常":
|
|
return (
|
|
<Badge className="bg-green-100 text-green-700 border-green-300">
|
|
<CheckCircle className="mr-1 h-3 w-3" />
|
|
正常
|
|
</Badge>
|
|
);
|
|
case "接近":
|
|
return (
|
|
<Badge className="bg-yellow-100 text-yellow-700 border-yellow-300">
|
|
<AlertTriangle className="mr-1 h-3 w-3" />
|
|
接近
|
|
</Badge>
|
|
);
|
|
case "低於":
|
|
return (
|
|
<Badge className="bg-red-100 text-red-700 border-red-300">
|
|
<AlertCircle className="mr-1 h-3 w-3" />
|
|
低於
|
|
</Badge>
|
|
);
|
|
}
|
|
}
|
|
|
|
export default function SafetyStockList({
|
|
settings,
|
|
inventories,
|
|
onEdit,
|
|
onDelete,
|
|
}: SafetyStockListProps) {
|
|
if (settings.length === 0) {
|
|
return (
|
|
<div className="bg-white rounded-lg shadow-sm border p-12 text-center text-gray-400">
|
|
<p>尚未設定任何安全庫存</p>
|
|
<p className="text-sm mt-1">點擊「新增安全庫存」開始設定</p>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
// 計算每個商品的目前總庫存
|
|
const getCurrentStock = (productId: string): number => {
|
|
return inventories
|
|
.filter((inv) => inv.productId === productId)
|
|
.reduce((sum, inv) => sum + inv.quantity, 0);
|
|
};
|
|
|
|
return (
|
|
<div className="bg-white rounded-lg shadow-sm border overflow-hidden">
|
|
<div className="overflow-x-auto">
|
|
<Table>
|
|
<TableHeader>
|
|
<TableRow>
|
|
<TableHead className="w-[5%]">#</TableHead>
|
|
<TableHead className="w-[25%]">商品名稱</TableHead>
|
|
<TableHead className="w-[12%]">商品類型</TableHead>
|
|
<TableHead className="w-[12%]">目前庫存</TableHead>
|
|
<TableHead className="w-[12%]">安全庫存</TableHead>
|
|
<TableHead className="w-[15%]">狀態</TableHead>
|
|
<TableHead className="w-[12%] text-right">操作</TableHead>
|
|
</TableRow>
|
|
</TableHeader>
|
|
<TableBody>
|
|
{settings.map((setting, index) => {
|
|
const currentStock = getCurrentStock(setting.productId);
|
|
const status = getSafetyStockStatus(currentStock, setting.safetyStock);
|
|
const isLowStock = status === "低於";
|
|
|
|
return (
|
|
<TableRow key={setting.id} className={isLowStock ? "bg-red-50" : ""}>
|
|
<TableCell className="text-grey-2">{index + 1}</TableCell>
|
|
<TableCell className="font-medium">{setting.productName}</TableCell>
|
|
<TableCell>
|
|
<Badge variant="outline">{setting.productType}</Badge>
|
|
</TableCell>
|
|
<TableCell>
|
|
<span className={isLowStock ? "text-red-600 font-medium" : ""}>
|
|
{currentStock}
|
|
</span>
|
|
</TableCell>
|
|
<TableCell>{setting.safetyStock}</TableCell>
|
|
<TableCell>{getStatusBadge(status)}</TableCell>
|
|
<TableCell className="text-right">
|
|
<div className="flex items-center justify-end gap-2">
|
|
<Button
|
|
type="button"
|
|
variant="ghost"
|
|
size="sm"
|
|
onClick={() => onEdit(setting)}
|
|
className="hover:bg-primary/10 hover:text-primary"
|
|
>
|
|
<Edit className="h-4 w-4 mr-1" />
|
|
編輯
|
|
</Button>
|
|
<Button
|
|
type="button"
|
|
variant="ghost"
|
|
size="sm"
|
|
onClick={() => onDelete(setting.id)}
|
|
className="hover:bg-red-50 hover:text-red-600"
|
|
>
|
|
<Trash2 className="h-4 w-4 mr-1" />
|
|
移除
|
|
</Button>
|
|
</div>
|
|
</TableCell>
|
|
</TableRow>
|
|
);
|
|
})}
|
|
</TableBody>
|
|
</Table>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|