import { useState, useCallback, useEffect } from "react"; import AuthenticatedLayout from "@/Layouts/AuthenticatedLayout"; import { Head, Link, router } from "@inertiajs/react"; import { debounce } from "lodash"; import { SearchableSelect } from "@/Components/ui/searchable-select"; 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, DialogDescription, } from "@/Components/ui/dialog"; import { Label } from "@/Components/ui/label"; import { Plus, Search, ArrowLeftRight, Loader2, Eye, Pencil, Trash2, X, } from "lucide-react"; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from "@/Components/ui/alert-dialog"; import Pagination from "@/Components/shared/Pagination"; import { toast } from "sonner"; import { Can } from '@/Components/Permission/Can'; export default function Index({ warehouses, orders, filters }: any) { const [searchTerm, setSearchTerm] = useState(filters.search || ""); const [warehouseFilter, setWarehouseFilter] = useState(filters.warehouse_id || "all"); const [perPage, setPerPage] = useState(filters.per_page || "10"); // Sync state with props useEffect(() => { setSearchTerm(filters.search || ""); setWarehouseFilter(filters.warehouse_id || "all"); setPerPage(filters.per_page || "10"); }, [filters]); // Create Dialog State const [isCreateOpen, setIsCreateOpen] = useState(false); const [sourceWarehouseId, setSourceWarehouseId] = useState(""); const [targetWarehouseId, setTargetWarehouseId] = useState(""); const [creating, setCreating] = useState(false); const [deleteId, setDeleteId] = useState(null); // Debounced Search Handler const debouncedSearch = useCallback( debounce((term: string, warehouse: string) => { router.get( route('inventory.transfer.index'), { ...filters, search: term, warehouse_id: warehouse === "all" ? "" : warehouse }, { preserveState: true, replace: true, preserveScroll: true } ); }, 500), [filters] ); const handleSearchChange = (term: string) => { setSearchTerm(term); debouncedSearch(term, warehouseFilter); }; const handleFilterChange = (value: string) => { setWarehouseFilter(value); router.get( route('inventory.transfer.index'), { ...filters, warehouse_id: value === "all" ? "" : value }, { preserveState: false, replace: true, preserveScroll: true } ); }; const handleClearSearch = () => { setSearchTerm(""); router.get( route('inventory.transfer.index'), { ...filters, search: "", warehouse_id: warehouseFilter === "all" ? "" : warehouseFilter }, { preserveState: true, replace: true, preserveScroll: true } ); }; const handlePerPageChange = (value: string) => { setPerPage(value); router.get( route('inventory.transfer.index'), { ...filters, per_page: value }, { preserveState: false, replace: true, preserveScroll: 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 confirmDelete = (id: string) => { setDeleteId(id); }; const handleDelete = () => { if (deleteId) { router.delete(route('inventory.transfer.destroy', [deleteId]), { onSuccess: () => { setDeleteId(null); toast.success("已成功刪除"); }, onError: () => setDeleteId(null), }); } }; const getStatusBadge = (status: string) => { switch (status) { case 'draft': return 草稿; case 'completed': return 已完成; case 'voided': return 已作廢; default: return {status}; } }; return (

庫存調撥管理

建立與管理倉庫間的商品調撥單,追蹤庫存轉移紀錄。

{/* Toolbar */}
{/* Search */}
handleSearchChange(e.target.value)} className="pl-10 pr-10 h-9" /> {searchTerm && ( )}
{/* Warehouse Filter */} ({ label: w.name, value: w.id.toString() })) ]} placeholder="選擇倉庫" className="w-full md:w-[200px] h-9" /> {/* Action Buttons */}
建立新調撥單 請選擇來源倉庫與目的倉庫。
({ label: w.name, value: w.id.toString() }))} placeholder="請選擇來源倉庫" className="h-9" />
w.id.toString() !== sourceWarehouseId).map((w: any) => ({ label: w.name, value: w.id.toString() }))} placeholder="請選擇目的倉庫" className="h-9" />
# 單號 狀態 來源倉庫 目的倉庫 建立日期 過帳日期 建立人員 操作 {orders.data.length === 0 ? ( 尚無調撥紀錄 ) : ( orders.data.map((order: any, index: number) => ( router.visit(route('inventory.transfer.show', [order.id]))} > {(orders.current_page - 1) * orders.per_page + index + 1} {order.doc_no} {getStatusBadge(order.status)} {order.from_warehouse_name} {order.to_warehouse_name} {order.created_at} {order.posted_at || '-'} {order.created_by}
e.stopPropagation()}> {order.status === 'draft' && ( )}
)) )}
每頁顯示
共 {orders.total} 筆紀錄
!open && setDeleteId(null)}> 確定要刪除此調撥單嗎? 此動作無法復原。如果單據已存在重要資料,請謹慎操作。 取消 確認刪除
); }