import { useState } from "react"; import { useQuery } from "@tanstack/react-query"; import { useAuth } from "../context/AuthContext"; import Forbidden from "../components/Forbidden"; import { motion } from "framer-motion"; import Box from "@mui/material/Box"; import { projectListOptions } from "../lib/queries/projects"; import { warehouseStockStatusOptions, warehouseBelowMinimumOptions, warehouseCategoryListOptions, type WarehouseItem, } from "../lib/queries/warehouse"; import apiFetch from "../utils/api"; import { formatCurrency } from "../utils/formatters"; import { Button, Card, DataTable, Select, DateField, StatusChip, PageHeader, 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; } interface MovementLogRow { date: string; type: string; document_number: string; item_name: string; quantity: number; unit_price: number; supplier_or_project: string; } 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 && 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; }) { const [data, setData] = useState(null); const [loading, setLoading] = useState(false); const [fetched, setFetched] = useState(false); const handleFetch = async () => { setLoading(true); setFetched(true); try { const params = new URLSearchParams(); if (type) params.set("type", type); if (dateFrom) params.set("date_from", dateFrom); if (dateTo) params.set("date_to", dateTo); const qs = params.toString(); const response = await apiFetch( `/api/admin/warehouse/reports/movement-log${qs ? `?${qs}` : ""}`, ); const result = await response.json(); if (result.success) { setData(result.data ?? []); } else { setData([]); } } catch { setData([]); } finally { setLoading(false); } }; const TYPE_LABEL: Record = { receipt: "Příjem", issue: "Výdej", }; const columns: DataColumn[] = [ { key: "date", header: "Datum", width: "12%", mono: true, render: (row) => 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 (