Files
star-erp/resources/js/Pages/Inventory/Transfer/Index.tsx
sky121113 56e30a85bb
All checks were successful
Koori-ERP-Deploy-System / deploy-demo (push) Successful in 1m6s
Koori-ERP-Deploy-System / deploy-production (push) Has been skipped
refactor: changes to inventory status (approved/unapprove)
2026-01-29 13:04:54 +08:00

256 lines
13 KiB
TypeScript

import { useState, useEffect } from "react";
import AuthenticatedLayout from "@/Layouts/AuthenticatedLayout";
import { Head, Link, router } from "@inertiajs/react";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/Components/ui/table";
import { Button } from "@/Components/ui/button";
import { Input } from "@/Components/ui/input";
import { Badge } from "@/Components/ui/badge";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogTrigger,
DialogFooter,
} from "@/Components/ui/dialog";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/Components/ui/select";
import { Label } from "@/Components/ui/label";
import {
Plus,
Search,
FileText,
ArrowLeftRight,
Loader2,
} from "lucide-react";
import { format } from "date-fns";
import Pagination from "@/Components/shared/Pagination";
import { toast } from "sonner";
export default function Index({ auth, orders, warehouses, filters }) {
const [searchQuery, setSearchQuery] = useState("");
// Create Dialog State
const [isCreateOpen, setIsCreateOpen] = useState(false);
const [sourceWarehouseId, setSourceWarehouseId] = useState("");
const [targetWarehouseId, setTargetWarehouseId] = useState("");
const [creating, setCreating] = useState(false);
// Handle warehouse filter change
const handleFilterChange = (value) => {
router.get(route('inventory.transfer.index'), { warehouse_id: value }, {
preserveState: true,
replace: true,
});
};
const handleCreate = () => {
if (!sourceWarehouseId) {
toast.error("請選擇來源倉庫");
return;
}
if (!targetWarehouseId) {
toast.error("請選擇目的倉庫");
return;
}
if (sourceWarehouseId === targetWarehouseId) {
toast.error("來源與目的倉庫不能相同");
return;
}
setCreating(true);
router.post(route('inventory.transfer.store'), {
from_warehouse_id: sourceWarehouseId,
to_warehouse_id: targetWarehouseId
}, {
onFinish: () => setCreating(false),
onSuccess: () => {
setIsCreateOpen(false);
setSourceWarehouseId("");
setTargetWarehouseId("");
}
});
};
const getStatusBadge = (status) => {
switch (status) {
case 'draft':
return <Badge variant="secondary">稿</Badge>;
case 'completed':
return <Badge className="bg-green-600"></Badge>;
case 'voided':
return <Badge variant="destructive"></Badge>;
default:
return <Badge variant="outline">{status}</Badge>;
}
};
return (
<AuthenticatedLayout
user={auth.user}
header={
<div className="flex justify-between items-center">
<h2 className="font-semibold text-xl text-gray-800 leading-tight flex items-center gap-2">
<ArrowLeftRight className="h-5 w-5" />
調
</h2>
</div>
}
>
<Head title="庫存調撥" />
<div className="py-12">
<div className="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div className="bg-white overflow-hidden shadow-sm sm:rounded-lg">
<div className="p-6">
<div className="flex justify-between items-center mb-6">
<div className="flex items-center gap-4">
<div className="w-[200px]">
<Select
value={filters.warehouse_id || "all"}
onValueChange={(val) => handleFilterChange(val === "all" ? "" : val)}
>
<SelectTrigger>
<SelectValue placeholder="篩選倉庫..." />
</SelectTrigger>
<SelectContent>
<SelectItem value="all"></SelectItem>
{warehouses.map((w) => (
<SelectItem key={w.id} value={String(w.id)}>{w.name}</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="relative flex-1 max-w-sm">
<Search className="absolute left-2.5 top-2.5 h-4 w-4 text-gray-500" />
<Input
placeholder="搜尋調撥單號..."
className="pl-9"
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
/>
</div>
</div>
<Dialog open={isCreateOpen} onOpenChange={setIsCreateOpen}>
<DialogTrigger asChild>
<Button>
<Plus className="h-4 w-4 mr-2" />
調
</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>調</DialogTitle>
</DialogHeader>
<div className="space-y-4 py-4">
<div className="space-y-2">
<Label></Label>
<Select onValueChange={setSourceWarehouseId} value={sourceWarehouseId}>
<SelectTrigger>
<SelectValue placeholder="選擇來源倉庫" />
</SelectTrigger>
<SelectContent>
{warehouses.map(w => (
<SelectItem key={w.id} value={String(w.id)}>{w.name}</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="space-y-2">
<Label></Label>
<Select onValueChange={setTargetWarehouseId} value={targetWarehouseId}>
<SelectTrigger>
<SelectValue placeholder="選擇目的倉庫" />
</SelectTrigger>
<SelectContent>
{warehouses.filter(w => String(w.id) !== sourceWarehouseId).map(w => (
<SelectItem key={w.id} value={String(w.id)}>{w.name}</SelectItem>
))}
</SelectContent>
</Select>
</div>
</div>
<DialogFooter>
<Button variant="outline" className="button-outlined-primary" onClick={() => setIsCreateOpen(false)}></Button>
<Button onClick={handleCreate} className="button-filled-primary" disabled={creating || !sourceWarehouseId || !targetWarehouseId}>
{creating && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</div>
<div className="rounded-md border">
<Table>
<TableHeader>
<TableRow>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead className="text-right"></TableHead>
</TableRow>
</TableHeader>
<TableBody>
{orders.data.length === 0 ? (
<TableRow>
<TableCell colSpan={8} className="text-center h-24 text-gray-500">
調
</TableCell>
</TableRow>
) : (
orders.data.map((order) => (
<TableRow key={order.id}>
<TableCell className="font-medium">{order.doc_no}</TableCell>
<TableCell>{getStatusBadge(order.status)}</TableCell>
<TableCell>{order.from_warehouse_name}</TableCell>
<TableCell>{order.to_warehouse_name}</TableCell>
<TableCell>{order.created_at}</TableCell>
<TableCell>{order.posted_at}</TableCell>
<TableCell>{order.created_by}</TableCell>
<TableCell className="text-right">
<Button
variant="ghost"
size="sm"
asChild
>
<Link href={route('inventory.transfer.show', [order.id])}>
</Link>
</Button>
</TableCell>
</TableRow>
))
)}
</TableBody>
</Table>
</div>
<div className="mt-4">
<Pagination links={orders.links} />
</div>
</div>
</div>
</div>
</div>
</AuthenticatedLayout>
);
}