import { useState, useCallback } from "react"; import { Button } from "@/Components/ui/button"; import { Input } from "@/Components/ui/input"; import { Label } from "@/Components/ui/label"; import { Download, Calendar, Filter, Package, RotateCcw, FileSpreadsheet, ArrowUpFromLine, ArrowDownToLine, ArrowRightLeft, TrendingUp, ArrowUpDown, ArrowUp, ArrowDown } from 'lucide-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 { getDateRange } from "@/utils/format"; import Pagination from "@/Components/shared/Pagination"; import { SearchableSelect } from "@/Components/ui/searchable-select"; import { Can } from "@/Components/Permission/Can"; import { PageProps } from "@/types/global"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from "@/Components/ui/tooltip"; interface ReportData { product_code: string; product_name: string; category_name: string; product_id: number; inbound_qty: number; outbound_qty: number; transfer_in_qty: number; transfer_out_qty: number; adjust_qty: number; net_change: number; } interface SummaryData { total_inbound: number; total_outbound: number; total_transfer_in: number; total_transfer_out: number; total_adjust: number; total_net_change: number; } interface InventoryReportProps extends PageProps { reportData: { data: ReportData[]; links: any[]; total: number; from: number; to: number; current_page: number; }; summary: SummaryData; warehouses: { id: number; name: string }[]; categories: { id: number; name: string }[]; filters: { date_from: string; date_to: string; warehouse_id: string; category_id: string; search: string; per_page?: number; sort_by?: string; sort_order?: 'asc' | 'desc'; }; } export default function InventoryReportIndex({ reportData, summary, warehouses, categories, filters }: InventoryReportProps) { const [dateStart, setDateStart] = useState(filters.date_from || ""); const [dateEnd, setDateEnd] = useState(filters.date_to || ""); const [warehouseId, setWarehouseId] = useState(filters.warehouse_id || "all"); const [categoryId, setCategoryId] = useState(filters.category_id || "all"); const [search, setSearch] = useState(filters.search || ""); const [perPage, setPerPage] = useState(filters.per_page?.toString() || "10"); // Determine initial range type based on date pairs const getInitialRangeType = () => { const { start: todayS, end: todayE } = getDateRange('today'); const { start: yestS, end: yestE } = getDateRange('yesterday'); const { start: weekS, end: weekE } = getDateRange('this_week'); const { start: monthS, end: monthE } = getDateRange('this_month'); const { start: lastMS, end: lastME } = getDateRange('last_month'); const fS = filters.date_from || ""; const fE = filters.date_to || ""; if (fS === todayS && fE === todayE) return "today"; if (fS === yestS && fE === yestE) return "yesterday"; if (fS === weekS && fE === weekE) return "this_week"; if (fS === monthS && fE === monthE) return "this_month"; if (fS === lastMS && fE === lastME) return "last_month"; return "custom"; }; const [dateRangeType, setDateRangeType] = useState(getInitialRangeType()); const handleDateRangeChange = (type: string) => { setDateRangeType(type); if (type === "custom") return; const { start, end } = getDateRange(type); setDateStart(start); setDateEnd(end); }; const handleFilter = useCallback(() => { router.get( route("inventory.report.index"), { date_from: dateStart, date_to: dateEnd, warehouse_id: warehouseId === "all" ? "" : warehouseId, category_id: categoryId === "all" ? "" : categoryId, search: search, per_page: perPage, }, { preserveState: true, preserveScroll: true } ); }, [dateStart, dateEnd, warehouseId, categoryId, search, perPage]); const handlePerPageChange = (value: string) => { setPerPage(value); router.get( route("inventory.report.index"), { date_from: dateStart, date_to: dateEnd, warehouse_id: warehouseId === "all" ? "" : warehouseId, category_id: categoryId === "all" ? "" : categoryId, search: search, per_page: value, }, { preserveState: true, preserveScroll: true } ); }; const handleClearFilters = () => { // Service defaults: -7 days to today. // Let's just clear params and let backend decide or set explicitly. // Or simply reset to "daily" and "all" setWarehouseId("all"); setCategoryId("all"); setSearch(""); setDateStart(""); // Will trigger service default setDateEnd(""); setDateRangeType("custom"); setPerPage("10"); router.get(route("inventory.report.index")); }; const handleExport = () => { const query: any = { date_from: dateStart, date_to: dateEnd, warehouse_id: warehouseId === "all" ? "" : warehouseId, category_id: categoryId === "all" ? "" : categoryId, search: search, sort_by: filters.sort_by, sort_order: filters.sort_order, }; window.location.href = route("inventory.report.export", query); }; const handleSort = (field: string) => { let newSortBy: string | undefined = field; let newSortOrder: 'asc' | 'desc' | undefined = 'asc'; if (filters.sort_by === field) { if (filters.sort_order === 'asc') { newSortOrder = 'desc'; } else { newSortBy = undefined; newSortOrder = undefined; } } router.get( route("inventory.report.index"), { date_from: dateStart, date_to: dateEnd, warehouse_id: warehouseId === "all" ? "" : warehouseId, category_id: categoryId === "all" ? "" : categoryId, search: search, per_page: perPage, sort_by: newSortBy, sort_order: newSortOrder, }, { preserveState: true, preserveScroll: true } ); }; const SortIcon = ({ field }: { field: string }) => { if (filters.sort_by !== field) { return ; } if (filters.sort_order === "asc") { return ; } return ; }; return (
{/* Header */}

庫存報表

統計區間: {filters.date_from && filters.date_to ? ( <>{filters.date_from}{filters.date_to} ) : ( 全部期間 )} 內的進貨、出貨與庫存變動匯總

{/* Filters */}
{/* Top Config: Date Range & Quick Buttons */}
{[ { label: "今日", value: "today" }, { label: "昨日", value: "yesterday" }, { label: "本週", value: "this_week" }, { label: "本月", value: "this_month" }, { label: "上月", value: "last_month" }, ].map((opt) => ( ))}
{/* Date Inputs */}
{ setDateStart(e.target.value); setDateRangeType('custom'); }} className="pl-9 block w-full h-9 bg-white" />
{ setDateEnd(e.target.value); setDateRangeType('custom'); }} className="pl-9 block w-full h-9 bg-white" />
{/* Detailed Filters row */}
{/* Warehouse & Category */}
({ label: w.name, value: w.id.toString() }))]} className="w-full h-9" placeholder="選擇倉庫..." />
({ label: c.name, value: c.id.toString() }))]} className="w-full h-9" placeholder="選擇分類..." />
{/* Search */}
setSearch(e.target.value)} className="h-9 bg-white" onKeyDown={(e) => e.key === 'Enter' && handleFilter()} />
{/* Action Buttons Integrated */}
{/* Summary Cards */}
進貨量 {Number(summary?.total_inbound || 0).toLocaleString()}

{Number(summary?.total_inbound || 0).toLocaleString()}

出貨量 {Number(summary?.total_outbound || 0).toLocaleString()}

{Number(summary?.total_outbound || 0).toLocaleString()}

調撥入 {Number(summary?.total_transfer_in || 0).toLocaleString()}

{Number(summary?.total_transfer_in || 0).toLocaleString()}

調撥出 {Number(summary?.total_transfer_out || 0).toLocaleString()}

{Number(summary?.total_transfer_out || 0).toLocaleString()}

調整量 {Number(summary?.total_adjust || 0).toLocaleString()}

{Number(summary?.total_adjust || 0).toLocaleString()}

淨變動 = 0 ? "text-emerald-600" : "text-red-600"}`}> {summary?.total_net_change > 0 ? "+" : ""}{Number(summary?.total_net_change || 0).toLocaleString()}

{summary?.total_net_change > 0 ? "+" : ""}{Number(summary?.total_net_change || 0).toLocaleString()}

{/* Results Table */}
商品代碼 商品名稱 分類 handleSort('inbound_qty')}>
進貨量
handleSort('outbound_qty')}>
出貨量
handleSort('transfer_in_qty')}>
調撥入
handleSort('transfer_out_qty')}>
調撥出
handleSort('adjust_qty')}>
調整量
handleSort('net_change')}>
淨變動
{reportData.data.length === 0 ? (

無符合條件的報表資料

) : ( reportData.data.map((row) => ( {row.product_code} {row.product_name} {row.category_name || '-'} {row.inbound_qty > 0 ? `+${row.inbound_qty}` : "-"} {row.outbound_qty > 0 ? `-${row.outbound_qty}` : "-"} {row.transfer_in_qty > 0 ? `+${row.transfer_in_qty}` : "-"} {row.transfer_out_qty > 0 ? `-${row.transfer_out_qty}` : "-"} {row.adjust_qty !== 0 ? (row.adjust_qty > 0 ? `+${row.adjust_qty}` : row.adjust_qty) : "-"} = 0 ? 'text-gray-900' : 'text-red-500'}`}> {Number(row.net_change) > 0 ? `+${row.net_change}` : row.net_change} )) )}
{/* Pagination Footer */}
每頁顯示
); }