Files
star-erp/resources/js/Components/Warehouse/Inventory/BatchAdjustmentModal.tsx

171 lines
6.0 KiB
TypeScript
Raw Normal View History

2026-01-22 15:39:35 +08:00
import { useState, useEffect } from "react";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogFooter,
} from "@/Components/ui/dialog";
import { Button } from "@/Components/ui/button";
import { Input } from "@/Components/ui/input";
import { Label } from "@/Components/ui/label";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/Components/ui/select";
import { AlertCircle } from "lucide-react";
interface BatchAdjustmentModalProps {
isOpen: boolean;
onClose: () => void;
onConfirm: (data: {
operation: "add" | "subtract" | "set";
quantity: number;
reason: string;
}) => void;
batch?: {
id: string;
batchNumber: string;
currentQuantity: number;
productName: string;
};
processing?: boolean;
}
export default function BatchAdjustmentModal({
isOpen,
onClose,
onConfirm,
batch,
processing = false,
}: BatchAdjustmentModalProps) {
const [operation, setOperation] = useState<"add" | "subtract" | "set">("add");
const [quantity, setQuantity] = useState<string>("");
const [reason, setReason] = useState<string>("手動調整庫存");
// 當開啟時重置
useEffect(() => {
if (isOpen) {
setOperation("add");
setQuantity("");
setReason("手動調整庫存");
}
}, [isOpen]);
const handleConfirm = () => {
const numQty = parseFloat(quantity);
if (isNaN(numQty) || numQty <= 0 && operation !== "set") {
return;
}
onConfirm({
operation,
quantity: numQty,
reason,
});
};
const previewQuantity = () => {
if (!batch) return 0;
const numQty = parseFloat(quantity) || 0;
if (operation === "add") return batch.currentQuantity + numQty;
if (operation === "subtract") return Math.max(0, batch.currentQuantity - numQty);
if (operation === "set") return numQty;
return batch.currentQuantity;
};
if (!batch) return null;
return (
<Dialog open={isOpen} onOpenChange={(open) => !open && onClose()}>
<DialogContent className="sm:max-w-[425px]">
<DialogHeader>
<DialogTitle className="flex items-center gap-2">
調 - {batch.productName}
</DialogTitle>
</DialogHeader>
<div className="grid gap-4 py-4">
<div className="bg-gray-50 p-3 rounded-md border text-sm space-y-1">
<div className="flex justify-between">
<span className="text-gray-500"></span>
<span className="font-mono font-medium">{batch.batchNumber || "-"}</span>
</div>
<div className="flex justify-between">
<span className="text-gray-500"></span>
<span className="font-medium text-primary-main">{batch.currentQuantity}</span>
</div>
</div>
<div className="space-y-2">
<Label>調</Label>
<Select
value={operation}
onValueChange={(value: any) => setOperation(value)}
>
<SelectTrigger>
<SelectValue placeholder="選擇調整方式" />
</SelectTrigger>
<SelectContent>
<SelectItem value="add"> (+)</SelectItem>
<SelectItem value="subtract"> (-)</SelectItem>
<SelectItem value="set"> (=)</SelectItem>
</SelectContent>
</Select>
</div>
<div className="space-y-2">
<Label htmlFor="adj-qty"></Label>
<Input
id="adj-qty"
type="number"
step="any"
2026-01-22 15:39:35 +08:00
min="0"
value={quantity}
onChange={(e) => setQuantity(e.target.value)}
placeholder="請輸入數量"
/>
</div>
<div className="space-y-2">
<Label htmlFor="adj-reason">調</Label>
<Input
id="adj-reason"
value={reason}
onChange={(e) => setReason(e.target.value)}
placeholder="例:手動盤點修正"
/>
</div>
<div className="flex items-center gap-2 mt-2 p-3 bg-primary/5 rounded-md border border-primary/20 text-sm">
<AlertCircle className="h-4 w-4 text-primary-main" />
<span>
調
<span className="font-bold text-primary-main ml-1">
{previewQuantity()}
</span>
</span>
</div>
</div>
<DialogFooter>
<Button variant="outline" onClick={onClose} disabled={processing}>
</Button>
<Button
onClick={handleConfirm}
disabled={processing || !quantity || parseFloat(quantity) < 0}
className="button-filled-primary"
>
調
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
}