import { useState, useCallback } from "react"; import { Button } from "@/Components/ui/button"; import { Input } from "@/Components/ui/input"; import { Label } from "@/Components/ui/label"; import { Filter, Package, RotateCcw, BarChart3, AlertTriangle, CheckCircle2, Clock, ArrowUpDown, ArrowUp, ArrowDown, XCircle } from 'lucide-react'; import AuthenticatedLayout from "@/Layouts/AuthenticatedLayout"; import { Head, router } from "@inertiajs/react"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/Components/ui/table"; import Pagination from "@/Components/shared/Pagination"; import { SearchableSelect } from "@/Components/ui/searchable-select"; import { PageProps } from "@/types/global"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from "@/Components/ui/tooltip"; import { StatusBadge, StatusVariant } from "@/Components/shared/StatusBadge"; interface AnalysisItem { id: number; code: string; name: string; category_name: string; current_stock: string; // decimal string from DB sales_30d: string; last_sale_date: string | null; turnover_days: number; turnover_days_display: string; status: 'dead' | 'slow' | 'normal' | 'out_of_stock'; status_label: string; } interface KPIProps { total_stock_value: number; dead_stock_value: number; dead_stock_count: number; avg_turnover_days: number; } interface PagePropsWithData extends PageProps { analysisData: { data: AnalysisItem[]; links: any[]; total: number; from: number; to: number; current_page: number; }; kpis: KPIProps; warehouses: { id: number; name: string }[]; categories: { id: number; name: string }[]; filters: { warehouse_id?: string; category_id?: string; search?: string; per_page?: string; sort_by?: string; sort_order?: 'asc' | 'desc'; status?: string; }; } // Define status mapping const getStatusVariant = (status: string): StatusVariant => { switch (status) { case 'dead': return 'destructive'; case 'slow': return 'warning'; case 'normal': return 'success'; case 'out_of_stock': return 'neutral'; default: return 'neutral'; } }; const getStatusLabel = (status: string): string => { switch (status) { case 'dead': return '滯銷'; case 'slow': return '週轉慢'; case 'normal': return '正常'; case 'out_of_stock': return '缺貨'; default: return status; } }; const statusOptions = [ { label: "全部狀態", value: "all" }, { label: "滯銷 (>90天)", value: "dead" }, { label: "週轉慢 (>60天)", value: "slow" }, { label: "正常", value: "normal" } ]; export default function InventoryAnalysisIndex({ analysisData, kpis, warehouses, categories, filters }: PagePropsWithData) { const [warehouseId, setWarehouseId] = useState(filters.warehouse_id || "all"); const [categoryId, setCategoryId] = useState(filters.category_id || "all"); const [search, setSearch] = useState(filters.search || ""); const [status, setStatus] = useState(filters.status || "all"); const [perPage, setPerPage] = useState(filters.per_page?.toString() || "10"); const handleFilter = useCallback(() => { router.get( route("inventory.analysis.index"), { warehouse_id: warehouseId === "all" ? "" : warehouseId, category_id: categoryId === "all" ? "" : categoryId, status: status === "all" ? "" : status, search: search, per_page: perPage, sort_by: filters.sort_by, sort_order: filters.sort_order, }, { preserveState: true, preserveScroll: true } ); }, [warehouseId, categoryId, status, search, perPage, filters.sort_by, filters.sort_order]); const handleClearFilters = () => { setWarehouseId("all"); setCategoryId("all"); setStatus("all"); setSearch(""); setPerPage("10"); router.get(route("inventory.analysis.index")); }; 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; } } else { // Default sort order for numeric fields might be desc if (['turnover_days', 'current_stock', 'sales_30d'].includes(field)) { newSortOrder = 'desc'; } } router.get( route("inventory.analysis.index"), { warehouse_id: warehouseId === "all" ? "" : warehouseId, category_id: categoryId === "all" ? "" : categoryId, status: status === "all" ? "" : status, search: search, per_page: perPage, sort_by: newSortBy, sort_order: newSortOrder, }, { preserveState: true, preserveScroll: true } ); }; const handlePerPageChange = (value: string) => { setPerPage(value); // Trigger filter immediately router.get( route("inventory.analysis.index"), { warehouse_id: warehouseId === "all" ? "" : warehouseId, category_id: categoryId === "all" ? "" : categoryId, status: status === "all" ? "" : status, search: search, per_page: value, sort_by: filters.sort_by, sort_order: filters.sort_order, }, { preserveState: true, preserveScroll: true } ); }; const SortIcon = ({ field }: { field: string }) => { if (filters.sort_by !== field) { return ; } if (filters.sort_order === "asc") { return ; } return ; }; return (
{/* Header */}

庫存分析

分析商品庫存週轉率、滯銷品項與庫存健康度

{/* KPI Cards */}

平均週轉天數

{kpis.avg_turnover_days}

滯銷品項數

{kpis.dead_stock_count}

滯銷庫存成本

${Number(kpis.dead_stock_value).toLocaleString()}

在此定義為庫存大於 0 且超過 90 天未銷售的商品成本總和

庫存總成本

${Number(kpis.total_stock_value).toLocaleString()}

{/* Filters */}
{/* Search */}
setSearch(e.target.value)} className="h-9 bg-white" onKeyDown={(e) => e.key === 'Enter' && handleFilter()} />
{/* 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="選擇分類..." />
{/* Action Buttons Integrated */}
{/* Results Table */}
handleSort('products.code')}>
商品代碼
handleSort('products.name')}>
商品名稱
分類 handleSort('current_stock')}>
現有庫存
handleSort('sales_30d')}>
30天銷量
handleSort('turnover_days')}>
週轉天數
handleSort('last_sale_date')}>
最後銷售
狀態
{analysisData.data.length === 0 ? (

無符合條件的資料

) : ( analysisData.data.map((row) => ( {row.code} {row.name} {row.category_name || '-'} {Number(row.current_stock).toLocaleString()} {Number(row.sales_30d).toLocaleString()} {row.turnover_days_display} {row.last_sale_date ? row.last_sale_date.split(' ')[0] : '從未銷售'} {getStatusLabel(row.status)} )) )}
{/* Pagination Footer */}
每頁顯示
); }