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

264 lines
12 KiB
TypeScript
Raw Normal View History

2025-12-30 15:03:19 +08:00
import { useState } from "react";
import { Head, Link, useForm } from "@inertiajs/react";
import AuthenticatedLayout from "@/Layouts/AuthenticatedLayout";
import { Button } from "@/Components/ui/button";
import { Input } from "@/Components/ui/input";
import { Label } from "@/Components/ui/label";
import { ArrowLeft, Save, Trash2, Boxes } from "lucide-react";
2025-12-30 15:03:19 +08:00
import { Warehouse, WarehouseInventory } from "@/types/warehouse";
import { toast } from "sonner";
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
} from "@/Components/ui/alert-dialog";
import TransactionTable, { Transaction } from "@/Components/Warehouse/Inventory/TransactionTable";
2026-01-07 13:06:49 +08:00
import { getShowBreadcrumbs } from "@/utils/breadcrumb";
2025-12-30 15:03:19 +08:00
interface Props {
warehouse: Warehouse;
inventory: WarehouseInventory;
transactions: Transaction[];
}
export default function EditInventory({ warehouse, inventory, transactions = [] }: Props) {
const { data, setData, put, delete: destroy, processing, errors } = useForm({
quantity: inventory.quantity,
batchNumber: inventory.batchNumber || "",
expiryDate: inventory.expiryDate || "",
lastInboundDate: inventory.lastInboundDate || "",
lastOutboundDate: inventory.lastOutboundDate || "",
// 為了記錄異動原因,還是需要傳這兩個欄位,雖然 UI 上原本的 EditPage 沒有原因輸入框
// 但為了符合我們後端的交易紀錄邏輯,我們可能需要預設一個,或者偷加一個欄位?
// 原 source code 沒有原因欄位。
// 我們可以預設 reason 為 "手動編輯更新"
reason: "編輯頁面手動更新",
});
const [showDeleteDialog, setShowDeleteDialog] = useState(false);
const handleSave = () => {
if (data.quantity < 0) {
toast.error("庫存數量不可為負數");
return;
}
put(route("warehouses.inventory.update", { warehouse: warehouse.id, inventory: inventory.id }), {
onSuccess: () => {
toast.success("庫存資料已更新");
},
onError: () => {
toast.error("更新失敗,請檢查欄位");
}
});
};
const handleDelete = () => {
destroy(route("warehouses.inventory.destroy", { warehouse: warehouse.id, inventory: inventory.id }), {
onSuccess: () => {
toast.success("庫存品項已刪除");
setShowDeleteDialog(false);
},
onError: () => {
toast.error("刪除失敗");
}
});
};
return (
2026-01-07 13:06:49 +08:00
<AuthenticatedLayout breadcrumbs={getShowBreadcrumbs("warehouses", "修正庫存")}>
2025-12-30 15:03:19 +08:00
<Head title={`編輯庫存 - ${inventory.productName} `} />
<div className="container mx-auto p-6 max-w-4xl">
{/* 頁面標題與麵包屑 */}
<div className="mb-6">
<Link href={`/warehouses/${warehouse.id}/inventory`}>
<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">
<Boxes className="h-6 w-6 text-[#01ab83]" />
</h1>
<p className="text-gray-500 mt-1">
2025-12-30 15:03:19 +08:00
<span className="font-medium text-gray-900">{warehouse.name}</span>
</p>
</div>
<div className="flex gap-3">
<Button
onClick={() => setShowDeleteDialog(true)}
variant="outline"
className="button-outlined-error"
2025-12-30 15:03:19 +08:00
>
<Trash2 className="mr-2 h-4 w-4" />
</Button>
<Button onClick={handleSave} className="button-filled-primary" disabled={processing}>
<Save className="mr-2 h-4 w-4" />
</Button>
</div>
</div>
</div >
{/* 表單內容 */}
< div className="bg-white rounded-lg shadow-sm border p-6 mb-6" >
<div className="space-y-6">
{/* 商品基本資訊 */}
<div className="space-y-4">
<h3 className="font-medium border-b pb-2 text-lg"></h3>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div className="space-y-2">
<Label htmlFor="productName">
<span className="text-red-500">*</span>
</Label>
<Input
id="productName"
value={inventory.productName}
disabled
className="bg-gray-100"
/>
<p className="text-sm text-gray-500">
</p>
</div>
<div className="space-y-2">
<Label htmlFor="batchNumber"></Label>
<Input
id="batchNumber"
type="text"
value={data.batchNumber}
onChange={(e) => setData("batchNumber", e.target.value)}
placeholder="例FL20251101"
className="button-outlined-primary"
// 目前後端可能尚未支援儲存,但依需求顯示
/>
</div>
</div>
</div>
{/* 庫存數量 */}
<div className="space-y-4">
<h3 className="font-medium border-b pb-2 text-lg"></h3>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div className="space-y-2">
<Label htmlFor="quantity">
<span className="text-red-500">*</span>
</Label>
<Input
id="quantity"
type="number"
min="0"
step="0.01"
value={data.quantity}
onChange={(e) =>
setData("quantity", parseFloat(e.target.value) || 0)
}
placeholder="0"
className={`button-outlined-primary ${errors.quantity ? "border-red-500" : ""}`}
/>
{errors.quantity && <p className="text-xs text-red-500">{errors.quantity}</p>}
<p className="text-sm text-gray-500">
</p>
</div>
</div>
</div>
{/* 日期資訊 */}
<div className="space-y-4">
<h3 className="font-medium border-b pb-2 text-lg"></h3>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div className="space-y-2">
<Label htmlFor="expiryDate"></Label>
<Input
id="expiryDate"
type="date"
value={data.expiryDate}
onChange={(e) => setData("expiryDate", e.target.value)}
className="button-outlined-primary"
/>
</div>
<div className="space-y-2">
<Label htmlFor="lastInboundDate"></Label>
<Input
id="lastInboundDate"
type="date"
value={data.lastInboundDate}
onChange={(e) =>
setData("lastInboundDate", e.target.value)
}
className="button-outlined-primary"
/>
</div>
<div className="space-y-2">
<Label htmlFor="lastOutboundDate"></Label>
<Input
id="lastOutboundDate"
type="date"
value={data.lastOutboundDate}
onChange={(e) =>
setData("lastOutboundDate", e.target.value)
}
className="button-outlined-primary"
/>
</div>
</div>
</div>
</div>
</div >
{/* 庫存異動紀錄 */}
< div className="bg-white rounded-lg shadow-sm border p-6" >
<h3 className="font-medium text-lg border-b pb-4 mb-4"></h3>
<TransactionTable transactions={transactions} />
</div >
{/* 刪除確認對話框 */}
< AlertDialog open={showDeleteDialog} onOpenChange={setShowDeleteDialog} >
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle></AlertDialogTitle>
<AlertDialogDescription>
{inventory.productName}
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel className="button-outlined-primary">
</AlertDialogCancel>
<AlertDialogAction
onClick={handleDelete}
className="button-filled-error"
2025-12-30 15:03:19 +08:00
>
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog >
</div >
</AuthenticatedLayout >
);
}