Files
star-erp/resources/js/Pages/Warehouse/EditInventory.tsx
2026-01-07 13:06:49 +08:00

271 lines
13 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 } from "lucide-react";
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";
import { getShowBreadcrumbs } from "@/utils/breadcrumb";
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 (
<AuthenticatedLayout breadcrumbs={getShowBreadcrumbs("warehouses", "修正庫存")}>
<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 gap-2 text-sm text-gray-500 mb-2">
<span></span>
<span>/</span>
<span></span>
<span>/</span>
<span></span>
<span>/</span>
<span className="text-gray-900"></span>
</div>
<div className="flex items-center justify-between">
<div>
<h1 className="mb-2"></h1>
<p className="text-gray-600">
<span className="font-medium text-gray-900">{warehouse.name}</span>
</p>
</div>
<div className="flex gap-3">
<Button
onClick={() => setShowDeleteDialog(true)}
variant="outline"
className="group border-red-200 text-red-600 hover:bg-red-50 hover:text-red-700 hover:border-red-300"
>
<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="bg-red-600 text-white hover:bg-red-700"
>
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog >
</div >
</AuthenticatedLayout >
);
}