Files
star-erp/resources/js/Components/Vendor/VendorDialog.tsx
sky121113 106de4e945
All checks were successful
Koori-ERP-Deploy-System / deploy-demo (push) Successful in 53s
Koori-ERP-Deploy-System / deploy-production (push) Has been skipped
feat: 修正庫存與撥補單邏輯並整合文件
1. 修復倉庫統計數據加總與樣式。
2. 修正可用庫存計算邏輯(排除不可銷售倉庫)。
3. 撥補單商品列表加入批號與效期顯示。
4. 修正撥補單儲存邏輯以支援精確批號轉移。
5. 整合 FEATURES.md 至 README.md。
2026-01-26 14:59:24 +08:00

242 lines
10 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 { useEffect } from "react";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@/Components/ui/dialog";
import { Button } from "@/Components/ui/button";
import { Input } from "@/Components/ui/input";
import { Label } from "@/Components/ui/label";
import { Textarea } from "@/Components/ui/textarea";
import { useForm } from "@inertiajs/react";
import { toast } from "sonner";
import type { Vendor } from "@/Pages/Vendor/Index";
interface VendorDialogProps {
open: boolean;
onOpenChange: (open: boolean) => void;
vendor: Vendor | null;
}
export default function VendorDialog({
open,
onOpenChange,
vendor,
}: VendorDialogProps) {
const { data, setData, post, put, processing, errors, reset, clearErrors } = useForm({
name: "",
short_name: "",
tax_id: "",
owner: "",
contact_name: "",
tel: "",
phone: "",
email: "",
address: "",
remark: "",
});
useEffect(() => {
if (open) {
clearErrors();
if (vendor) {
setData({
name: vendor.name,
short_name: vendor.shortName || "",
tax_id: vendor.taxId || "",
owner: vendor.owner || "",
contact_name: vendor.contactName || "",
tel: vendor.tel || "",
phone: vendor.phone || "",
email: vendor.email || "",
address: vendor.address || "",
remark: vendor.remark || "",
});
} else {
reset();
}
}
}, [open, vendor]);
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (vendor) {
put(route("vendors.update", vendor.id), {
onSuccess: () => {
toast.success("廠商資料已更新");
onOpenChange(false);
reset();
},
onError: () => {
toast.error("更新失敗,請檢查輸入資料");
}
});
} else {
post(route("vendors.store"), {
onSuccess: () => {
toast.success("廠商已新增");
onOpenChange(false);
reset();
},
onError: () => {
toast.error("新增失敗,請檢查輸入資料");
}
});
}
};
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="max-w-3xl max-h-[90vh] overflow-y-auto">
<DialogHeader>
<DialogTitle>{vendor ? "編輯廠商" : "新增廠商"}</DialogTitle>
<DialogDescription>
{vendor ? "修改廠商基本資料與聯絡資訊" : "建立新的廠商資料"}
</DialogDescription>
</DialogHeader>
<form onSubmit={handleSubmit} className="space-y-6 py-4">
{/* 基本資訊區塊 */}
<div className="space-y-4">
<h3 className="text-lg font-medium border-b pb-2 text-primary"></h3>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="space-y-2">
<Label htmlFor="name">
<span className="text-red-500">*</span>
</Label>
<Input
id="name"
value={data.name}
onChange={(e) => setData("name", e.target.value)}
placeholder="例:宏達食品有限公司"
className={errors.name ? "border-red-500" : ""}
/>
{errors.name && <p className="text-sm text-red-500">{errors.name}</p>}
</div>
<div className="space-y-2">
<Label htmlFor="short_name"></Label>
<Input
id="short_name"
value={data.short_name}
onChange={(e) => setData("short_name", e.target.value)}
placeholder="例:宏達食品"
/>
</div>
<div className="space-y-2">
<Label htmlFor="tax_id"></Label>
<Input
id="tax_id"
value={data.tax_id}
onChange={(e) => setData("tax_id", e.target.value)}
placeholder="8 位數字"
maxLength={8}
/>
{errors.tax_id && <p className="text-sm text-red-500">{errors.tax_id}</p>}
</div>
<div className="space-y-2">
<Label htmlFor="owner"></Label>
<Input
id="owner"
value={data.owner}
onChange={(e) => setData("owner", e.target.value)}
placeholder="負責人姓名"
/>
</div>
</div>
</div>
{/* 聯絡資訊區塊 */}
<div className="space-y-4">
<h3 className="text-lg font-medium border-b pb-2 text-primary"></h3>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="space-y-2">
<Label htmlFor="contact_name"></Label>
<Input
id="contact_name"
value={data.contact_name}
onChange={(e) => setData("contact_name", e.target.value)}
placeholder="業務或聯絡窗口姓名"
/>
</div>
<div className="space-y-2">
<Label htmlFor="email"></Label>
<Input
id="email"
type="email"
value={data.email}
onChange={(e) => setData("email", e.target.value)}
placeholder="example@mail.com"
/>
</div>
<div className="space-y-2">
<Label htmlFor="tel"></Label>
<Input
id="tel"
value={data.tel}
onChange={(e) => setData("tel", e.target.value)}
placeholder="例02-23456789"
/>
</div>
<div className="space-y-2">
<Label htmlFor="phone"></Label>
<Input
id="phone"
value={data.phone}
onChange={(e) => setData("phone", e.target.value)}
placeholder="例0912-345-678"
/>
</div>
<div className="space-y-2 col-span-2">
<Label htmlFor="address"></Label>
<Input
id="address"
value={data.address}
onChange={(e) => setData("address", e.target.value)}
placeholder="完整的營業或通訊地址"
/>
</div>
</div>
</div>
{/* 備註區塊 */}
<div className="space-y-2">
<Label htmlFor="remark"></Label>
<Textarea
id="remark"
value={data.remark}
onChange={(e) => setData("remark", e.target.value)}
placeholder="特殊交易習慣、配送時間要求等..."
className="resize-none"
/>
</div>
<DialogFooter>
<Button
type="button"
variant="outline"
onClick={() => onOpenChange(false)}
className="button-outlined-primary"
>
</Button>
<Button type="submit" className="button-filled-primary" disabled={processing}>
{processing ? "儲存... " : (vendor ? "儲存變更" : "新增廠商")}
</Button>
</DialogFooter>
</form>
</DialogContent>
</Dialog>
);
}