import { useState } from "react"; import { useQuery } from "@tanstack/react-query"; import { useAuth } from "../context/AuthContext"; import Forbidden from "../components/Forbidden"; import Box from "@mui/material/Box"; import { projectListOptions } from "../lib/queries/projects"; import { warehouseStockStatusOptions, warehouseBelowMinimumOptions, warehouseCategoryListOptions, warehouseMovementLogOptions, type WarehouseItem, type MovementLogRow, } from "../lib/queries/warehouse"; import { jsonQuery } from "../lib/apiAdapter"; import { formatCurrency, formatDate } from "../utils/formatters"; import { Alert, Button, Card, DataTable, Select, DateField, StatusChip, PageHeader, PageEnter, FilterBar, EmptyState, LoadingState, Tabs, TabPanel, type DataColumn, type TabDef, } from "../ui"; type TabId = | "stock-status" | "project-consumption" | "movement-log" | "below-minimum"; interface ProjectConsumptionRow { item_name: string; project_name: string; total_qty: number; total_value: number; } const TABS: TabDef[] = [ { value: "stock-status", label: "Stav zásob" }, { value: "project-consumption", label: "Spotřeba projektů" }, { value: "movement-log", label: "Pohyby" }, { value: "below-minimum", label: "Pod minimem" }, ]; // Movement type → StatusChip color (legacy: incoming=success, outgoing=info, inventory=info) const MOVEMENT_COLOR: Record = { receipt: "success", issue: "info", inventory: "info", }; export default function WarehouseReports() { const { hasPermission } = useAuth(); const [activeTab, setActiveTab] = useState("stock-status"); // Stock status filters const [categoryId, setCategoryId] = useState(""); // Project consumption filters const [consumptionProjectId, setConsumptionProjectId] = useState( "", ); const [consumptionDateFrom, setConsumptionDateFrom] = useState(""); const [consumptionDateTo, setConsumptionDateTo] = useState(""); // Movement log filters const [movementType, setMovementType] = useState(""); const [movementDateFrom, setMovementDateFrom] = useState(""); const [movementDateTo, setMovementDateTo] = useState(""); if (!hasPermission("warehouse.view")) return ; return ( setActiveTab(v as TabId)} tabs={TABS} /> ); } /* ---------- Tab 1: Stock Status ---------- */ function StockStatusTab({ categoryId, setCategoryId, }: { categoryId: number | ""; setCategoryId: (v: number | "") => void; }) { const { data: categories = [] } = useQuery(warehouseCategoryListOptions()); const { data: items, isPending } = useQuery( warehouseStockStatusOptions({ category_id: categoryId ? Number(categoryId) : undefined, }), ); const columns: DataColumn[] = [ { key: "name", header: "Název", width: "30%", bold: true, render: (item) => item.name, }, { key: "total_quantity", header: "Celkem", width: "12%", mono: true, render: (item) => String(item.total_quantity ?? 0), }, { key: "available_quantity", header: "K dispozici", width: "13%", mono: true, render: (item) => String(item.available_quantity ?? 0), }, { key: "stock_value", header: "Hodnota", width: "17%", mono: true, render: (item) => item.stock_value != null ? formatCurrency(item.stock_value, "CZK") : "0,00 Kč", }, { key: "min_quantity", header: "Min. množství", width: "14%", mono: true, render: (item) => (item.min_quantity ?? "—").toString(), }, { key: "status", header: "Stav", width: "14%", render: (item) => item.below_minimum ? ( ) : ( ), }, ]; return ( {categories.length > 0 && ( setProjectId(val === "" ? "" : Number(val))} options={[ { value: "", label: "Všechny projekty" }, ...projects.map((p) => ({ value: String(p.id), label: `${p.project_number} – ${p.name}`, })), ]} /> )} {!fetched && } {fetched && loading && } {fetched && !loading && error && ( {error instanceof Error ? error.message : "Nepodařilo se načíst spotřebu projektů"} )} {fetched && !loading && !error && data && ( columns={columns} rows={data.map((row, i) => ({ ...row, _idx: i }))} rowKey={(row) => row._idx} empty={} /> )} ); } /* ---------- Tab 3: Movement Log ---------- */ function MovementLogTab({ type, setType, dateFrom, setDateFrom, dateTo, setDateTo, }: { type: string; setType: (v: string) => void; dateFrom: string; setDateFrom: (v: string) => void; dateTo: string; setDateTo: (v: string) => void; }) { // Filters applied on "Zobrazit"; uses the shared movement-log query option so // results land in the ["warehouse"] cache and refresh on warehouse mutations. const [applied, setApplied] = useState<{ type: string; dateFrom: string; dateTo: string; } | null>(null); const { data, isFetching: loading, error, } = useQuery({ ...warehouseMovementLogOptions({ type: applied?.type || undefined, date_from: applied?.dateFrom || undefined, date_to: applied?.dateTo || undefined, }), enabled: applied !== null, }); const fetched = applied !== null; const handleFetch = () => setApplied({ type, dateFrom, dateTo }); const TYPE_LABEL: Record = { receipt: "Příjem", issue: "Výdej", }; const columns: DataColumn[] = [ { key: "date", header: "Datum", width: "12%", mono: true, render: (row) => formatDate(row.date), }, { key: "type", header: "Typ", width: "12%", render: (row) => ( ), }, { key: "document_number", header: "Doklad", width: "14%", mono: true, render: (row) => row.document_number || "—", }, { key: "item_name", header: "Položka", width: "22%", bold: true, render: (row) => row.item_name, }, { key: "quantity", header: "Množství", width: "10%", mono: true, render: (row) => String(row.quantity), }, { key: "unit_price", header: "Cena/ks", width: "12%", mono: true, render: (row) => formatCurrency(row.unit_price, "CZK"), }, { key: "supplier_or_project", header: "Dodavatel / Projekt", width: "18%", render: (row) => row.supplier_or_project || "—", }, ]; return (