first commit
This commit is contained in:
152
resources/js/Components/SafetyStock/SafetyStockList.tsx
Normal file
152
resources/js/Components/SafetyStock/SafetyStockList.tsx
Normal file
@@ -0,0 +1,152 @@
|
||||
/**
|
||||
* 安全庫存列表組件
|
||||
*/
|
||||
|
||||
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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user