import { useState } from "react"; import { useAlert } from "../context/AlertContext"; import { useAuth } from "../context/AuthContext"; import { Link } from "react-router-dom"; import Forbidden from "../components/Forbidden"; import { Skeleton } from "boneyard-js/react"; import OrdersFixture from "../fixtures/OrdersFixture"; import { motion } from "framer-motion"; import ConfirmModal from "../components/ConfirmModal"; import apiFetch from "../utils/api"; import { formatCurrency, formatDate, czechPlural } from "../utils/formatters"; import SortIcon from "../components/SortIcon"; import useTableSort from "../hooks/useTableSort"; import { useQueryClient } from "@tanstack/react-query"; import { usePaginatedQuery } from "../hooks/usePaginatedQuery"; import { orderListOptions } from "../lib/queries/orders"; import Pagination from "../components/Pagination"; const API_BASE = "/api/admin"; const STATUS_LABELS: Record = { prijata: "Přijatá", v_realizaci: "V realizaci", dokoncena: "Dokončená", stornovana: "Stornována", }; const STATUS_CLASSES: Record = { prijata: "admin-badge-order-prijata", v_realizaci: "admin-badge-order-realizace", dokoncena: "admin-badge-order-dokoncena", stornovana: "admin-badge-order-stornovana", }; interface Order { id: number; order_number: string; quotation_id: number; quotation_number: string; customer_name: string; status: string; created_at: string; total: number; currency: string; invoice_id?: number; } export default function Orders() { const alert = useAlert(); const { hasPermission } = useAuth(); const { sort, order, handleSort, activeSort } = useTableSort("order_number"); const [search, setSearch] = useState(""); const [page, setPage] = useState(1); const [deleteConfirm, setDeleteConfirm] = useState<{ show: boolean; order: Order | null; }>({ show: false, order: null }); const [deleting, setDeleting] = useState(false); const [deleteFiles, setDeleteFiles] = useState(false); const queryClient = useQueryClient(); const { items: orders, pagination, isPending, isFetching, } = usePaginatedQuery(orderListOptions({ search, sort, order, page })); if (!hasPermission("orders.view")) return ; const handleDelete = async () => { if (!deleteConfirm.order) return; setDeleting(true); try { const response = await apiFetch( `${API_BASE}/orders/${deleteConfirm.order.id}`, { method: "DELETE", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ delete_files: deleteFiles }), }, ); const result = await response.json(); if (result.success) { setDeleteConfirm({ show: false, order: null }); setDeleteFiles(false); alert.success(result.message || "Objednávka byla smazána"); queryClient.invalidateQueries({ queryKey: ["orders"] }); queryClient.invalidateQueries({ queryKey: ["offers"] }); queryClient.invalidateQueries({ queryKey: ["projects"] }); queryClient.invalidateQueries({ queryKey: ["invoices"] }); } else { alert.error(result.error || "Nepodařilo se smazat objednávku"); } } catch { alert.error("Chyba připojení"); } finally { setDeleting(false); } }; return ( }>

Objednávky

{pagination?.total ?? orders.length}{" "} {czechPlural( pagination?.total ?? orders.length, "objednávka", "objednávky", "objednávek", )}

{ setSearch(e.target.value); setPage(1); }} className="admin-form-input" placeholder="Hledat podle čísla, nabídky, projektu nebo zákazníka..." />
{orders.length === 0 ? (

Zatím nejsou žádné objednávky.

Objednávky se vytvářejí z nabídek.

) : (
{(orders as Order[]).map((o) => ( ))}
handleSort("order_number")} > Číslo{" "} Nabídka Zákazník handleSort("status")} > Stav{" "} handleSort("created_at")} > Datum{" "} Celkem Akce
{o.order_number} {o.quotation_number} {o.customer_name || "—"} {STATUS_LABELS[o.status] || o.status} {formatDate(o.created_at)} {formatCurrency(o.total, o.currency)}
{o.invoice_id ? ( F ) : ( hasPermission("invoices.create") && ( ) )} {hasPermission("orders.delete") && ( )}
)}
{ setDeleteConfirm({ show: false, order: null }); setDeleteFiles(false); }} onConfirm={handleDelete} title="Smazat objednávku" message={ <> Opravdu chcete smazat objednávku " {deleteConfirm.order?.order_number}"? Bude smazán i přidružený projekt. Tato akce je nevratná. } confirmText="Smazat" cancelText="Zrušit" type="danger" loading={deleting} />
); }