import { useState, useEffect, useRef, useId } from "react"; import { useParams, useNavigate, useLocation, Link } from "react-router-dom"; import { useQuery } from "@tanstack/react-query"; import { useAlert } from "../context/AlertContext"; import { useAuth } from "../context/AuthContext"; import Forbidden from "../components/Forbidden"; import { motion } from "framer-motion"; import FormField from "../components/FormField"; import WarehouseMovementTable, { type MovementItem, } from "../components/warehouse/WarehouseMovementTable"; import { warehouseIssueDetailOptions } from "../lib/queries/warehouse"; import { projectListOptions, type Project } from "../lib/queries/projects"; import { useApiMutation } from "../lib/queries/mutations"; interface IssueForm { project_id: number | null; notes: string; } export default function WarehouseIssueForm() { const { id } = useParams(); const alert = useAlert(); const { hasPermission } = useAuth(); const navigate = useNavigate(); const location = useLocation(); const baseId = useId(); const counterRef = useRef(0); const nextKey = () => `${baseId}-item-${counterRef.current++}`; // Pre-fill data from reservation ("Create výdej" button) const prefill = location.state as { reservationId?: number; itemId?: number; projectId?: number; quantity?: number; itemName?: string; } | null; const isEdit = id !== undefined && id !== "new"; const [form, setForm] = useState({ project_id: prefill?.projectId ?? null, notes: "", }); const [items, setItems] = useState(() => isEdit ? [] : prefill?.itemId ? [ { key: nextKey(), item_id: prefill.itemId, item_name: prefill.itemName, quantity: prefill.quantity ?? 0, unit_price: 0, location_id: null, batch_id: null, reservation_id: prefill.reservationId ?? null, notes: null, }, ] : [ { key: nextKey(), item_id: null, quantity: 0, unit_price: 0, location_id: null, batch_id: null, reservation_id: null, notes: null, }, ], ); const [errors, setErrors] = useState>({}); const [saving, setSaving] = useState(false); const formInitialized = useRef(false); const { data: issue, isPending } = useQuery( warehouseIssueDetailOptions(isEdit ? id : undefined), ); const { data: projectsData } = useQuery(projectListOptions({ perPage: 100 })); const projects = projectsData?.data ?? []; useEffect(() => { formInitialized.current = false; }, [id]); useEffect(() => { if (isEdit && issue && !formInitialized.current) { setForm({ project_id: issue.project_id, notes: issue.notes || "", }); if (issue.items && issue.items.length > 0) { setItems( issue.items.map((item) => ({ key: nextKey(), item_id: item.item_id, item_name: item.item?.name, quantity: Number(item.quantity), unit_price: item.batch ? Number(item.batch.unit_price) : 0, location_id: item.location_id, batch_id: item.batch_id, reservation_id: item.reservation_id, notes: item.notes, })), ); } formInitialized.current = true; } }, [isEdit, issue]); if (!hasPermission("warehouse.operate")) return ; const addItem = () => { setItems((prev) => [ ...prev, { key: nextKey(), item_id: null, quantity: 0, unit_price: 0, location_id: null, batch_id: null, reservation_id: null, notes: null, }, ]); }; const validate = (): boolean => { const newErrors: Record = {}; if (!form.project_id) { newErrors.project_id = "Projekt je povinný"; } const validItems = items.filter((l) => l.item_id !== null); if (validItems.length === 0) { newErrors.items = "Přidejte alespoň jednu položku"; } for (const item of validItems) { if (item.quantity <= 0) { newErrors.items = "Množství musí být větší než 0"; break; } } setErrors(newErrors); return Object.keys(newErrors).length === 0; }; const buildPayload = () => ({ project_id: form.project_id, notes: form.notes.trim() || null, items: items .filter((l) => l.item_id !== null) .map((l) => ({ item_id: l.item_id!, batch_id: l.batch_id, quantity: l.quantity, location_id: l.location_id, reservation_id: l.reservation_id, notes: l.notes?.trim() || null, })), }); const saveMutation = useApiMutation< ReturnType, { id?: number } >({ url: () => isEdit ? `/api/admin/warehouse/issues/${id}` : "/api/admin/warehouse/issues", method: () => (isEdit ? "PUT" : "POST"), invalidate: ["warehouse"], }); const [confirmIssueId, setConfirmIssueId] = useState(null); const confirmMutation = useApiMutation({ url: () => confirmIssueId ? `/api/admin/warehouse/issues/${confirmIssueId}/confirm` : "/api/admin/warehouse/issues/0/confirm", method: () => "POST", invalidate: ["warehouse"], onSuccess: () => { alert.success("Výdejka byla potvrzena"); }, }); const saveIssue = async (): Promise => { try { const data = await saveMutation.mutateAsync(buildPayload()); return data?.id ?? Number(id); } catch (e) { alert.error( e instanceof Error ? e.message : "Nepodařilo se uložit výdejku", ); return null; } }; const confirmIssue = async (issueId: number) => { setConfirmIssueId(issueId); try { await confirmMutation.mutateAsync(undefined); } catch (e) { alert.error(e instanceof Error ? e.message : "Chyba připojení"); } finally { setConfirmIssueId(null); } }; const handleSaveDraft = async () => { if (!validate()) return; setSaving(true); try { const savedId = await saveIssue(); if (savedId) { alert.success("Výdejka byla uložena jako návrh"); navigate(`/warehouse/issues/${savedId}`); } } finally { setSaving(false); } }; const handleSaveAndConfirm = async () => { if (!validate()) return; setSaving(true); try { const savedId = await saveIssue(); if (savedId) { await confirmIssue(savedId); navigate(`/warehouse/issues/${savedId}`); } } finally { setSaving(false); } }; if (isEdit && isPending) { return (
); } return (
{/* Header */}

{isEdit ? "Upravit výdejku" : "Nová výdejka"}

{/* Header fields */}

Základní údaje

{errors.project_id && (
{errors.project_id}
)}