import { useState, useId, useRef } from "react"; import { useNavigate, Link as RouterLink } from "react-router-dom"; import { useQuery } from "@tanstack/react-query"; import Box from "@mui/material/Box"; import Typography from "@mui/material/Typography"; import IconButton from "@mui/material/IconButton"; import { useAlert } from "../context/AlertContext"; import { useAuth } from "../context/AuthContext"; import Forbidden from "../components/Forbidden"; import ItemPicker from "../components/warehouse/ItemPicker"; import { warehouseLocationListOptions, type WarehouseLocation, } from "../lib/queries/warehouse"; import { useApiMutation } from "../lib/queries/mutations"; import { Button, Card, TextField, Select, Field, PageEnter } from "../ui"; interface InventoryItem { key: string; item_id: number | null; location_id: number | null; actual_qty: number; } function parseDecimal(raw: string): number { const cleaned = raw.replace(/[eE+-]/g, ""); const n = Number(cleaned); return Number.isFinite(n) ? n : 0; } const BackIcon = ( ); const RemoveIcon = ( ); const PlusIcon = ( ); export default function WarehouseInventoryForm() { const alert = useAlert(); const { hasPermission } = useAuth(); const navigate = useNavigate(); const baseId = useId(); const counterRef = useRef(0); const nextKey = () => `${baseId}-item-${counterRef.current++}`; const [items, setItems] = useState([]); const [notes, setNotes] = useState(""); const [saving, setSaving] = useState(false); const { data: locations } = useQuery(warehouseLocationListOptions()); const createMutation = useApiMutation< { notes: string | null; items: { item_id: number; location_id: number | null; actual_qty: number; }[]; }, { id?: number } >({ url: () => "/api/admin/warehouse/inventory-sessions", method: () => "POST", invalidate: ["warehouse"], onSuccess: (data) => { alert.success("Inventura byla vytvořena"); navigate(`/warehouse/inventory/${data?.id}`); }, }); // All hooks above this line — permission gate is the last thing before render. if (!hasPermission("warehouse.inventory")) return ; const validate = (): boolean => { const validItems = items.filter((l) => l.item_id !== null); if (validItems.length === 0) { alert.error("Přidejte alespoň jednu položku"); return false; } for (const l of validItems) { if (!Number.isFinite(l.actual_qty) || l.actual_qty < 0) { alert.error("Skutečné množství musí být platné číslo"); return false; } } return true; }; const handleSubmit = async () => { if (!validate()) return; setSaving(true); try { await createMutation.mutateAsync({ notes: notes.trim() || null, items: items .filter((l) => l.item_id !== null) .map((l) => ({ item_id: l.item_id!, location_id: l.location_id, actual_qty: l.actual_qty, })), }); } catch (e) { alert.error(e instanceof Error ? e.message : "Chyba připojení"); } finally { setSaving(false); } }; const addItem = () => { setItems((prev) => [ ...prev, { key: nextKey(), item_id: null, location_id: null, actual_qty: 0 }, ]); }; const removeItem = (index: number) => { setItems((prev) => prev.filter((_, i) => i !== index)); }; const updateItem = ( index: number, field: keyof InventoryItem, value: unknown, ) => { setItems((prev) => { const updated = [...prev]; updated[index] = { ...updated[index], [field]: value }; return updated; }); }; const locationOptions = [ { value: "", label: "-- bez lokace --" }, ...(locations ?? []).map((loc: WarehouseLocation) => ({ value: String(loc.id), label: `${loc.code} - ${loc.name}`, })), ]; return ( {/* Header */} {BackIcon} Nová inventura {/* Notes */} Základní údaje setNotes(e.target.value)} placeholder="Volitelné poznámky k inventuře" /> {/* Lines */} Položky {items.map((item, index) => ( updateItem(index, "item_id", id)} />