import { useEffect, useState } from "react"; import axios from "axios"; import { useTranslation } from "react-i18next"; import { useLanguageStore } from "@/stores/languageStore"; import { useContestRefreshStore } from "@/stores/contestRefreshStore"; import { Table, TableHeader, TableColumn, TableBody, TableRow, TableCell, type Selection, } from "@heroui/react"; export type Contest = { id: number; name: string; description?: string | null; bands?: { id: number; name: string }[]; categories?: { id: number; name: string }[]; power_categories?: { id: number; name: string }[]; evaluator?: string | null; email?: string | null; email2?: string | null; is_mcr: boolean; is_sixhr: boolean; is_active: boolean; is_test: boolean; start_time: string | null; // "HH:MM:SS" duration: number; // hodiny logs_deadline_days: number; }; type PaginatedResponse = { data: T[]; }; type ContestsTableProps = { /** Handler vybraného řádku (klik/označení). */ onRowSelect?: (contest: Contest) => void; /** Handler pro klik na ikonu editace (tužka). */ onEditContest?: (contest: Contest) => void; /** Show/hide the edit (pencil) actions column. Default: true */ enableEdit?: boolean; /** Při kliknutí/označení řádku zavolat onSelectContest. Default: false */ selectOnRowClick?: boolean; /** Filter the list to only active contests (is_active === true). Default: false */ onlyActive?: boolean; /** Show/hide the is_active column. Default: true */ showActiveColumn?: boolean; /** Zahrnout testovací závody. Default: false */ showTests?: boolean; }; export default function ContestsTable({ onRowSelect, onEditContest, enableEdit = true, onlyActive = false, showActiveColumn = true, selectOnRowClick = false, showTests = false, }: ContestsTableProps) { const { t } = useTranslation("common"); const locale = useLanguageStore((s) => s.locale); const refreshKey = useContestRefreshStore((s) => s.refreshKey); const [items, setItems] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [selectedKeys, setSelectedKeys] = useState(new Set([])); useEffect(() => { let active = true; (async () => { try { setLoading(true); setError(null); const res = await axios.get | Contest[]>( "/api/contests", { withCredentials: true, headers: { Accept: "application/json" }, params: { lang: locale, include_tests: showTests ? 1 : 0, only_active: onlyActive ? 1 : 0, }, } ); if (!active) return; const data = Array.isArray(res.data) ? res.data : (res.data as PaginatedResponse).data; setItems(data); } catch { if (!active) return; setError(t("unable_to_load_contests") ?? "Nepodařilo se načíst seznam závodů."); } finally { if (active) setLoading(false); } })(); return () => { active = false; }; }, [locale, t, refreshKey, showTests, onlyActive]); const handleEdit = onEditContest ?? onRowSelect; const handleRowSelect = onRowSelect; const canEdit = Boolean(enableEdit && handleEdit); const visibleItems = onlyActive ? items.filter((c) => c.is_active) : items; if (loading) return
{t("contests_loading") ?? "Načítám závody…"}
; if (error) return
{error}
; if (visibleItems.length === 0) { return (
{t("contests_empty") ?? "Žádné závody nejsou k dispozici."}
); } const columns = [ { key: "name", label: t("contest_name") }, { key: "description", label: t("contest_description") }, { key: "bands", label: t("bands") ?? "Pásma" }, { key: "categories", label: t("categories") ?? "Kategorie" }, { key: "power_categories", label: t("power_categories") ?? "Výkonové kategorie" }, ...(showActiveColumn ? [{ key: "is_active", label: t("contest_active") }] : []), ...(canEdit ? [{ key: "actions", label: "" }] : []), ]; const handleSelectionChange = (keys: Selection) => { setSelectedKeys(keys); if (!selectOnRowClick || !handleRowSelect) return; if (keys === "all") return; const id = Array.from(keys)[0]; if (!id) return; const selected = visibleItems.find((c) => String(c.id) === String(id)); if (selected) handleRowSelect(selected); }; return ( {(column) => ( {column.label} )} {(item) => ( {(columnKey) => { switch (columnKey) { case "name": return ( {item.name} ); case "description": return ( {item.description || "—"} ); case "bands": { const names = item.bands?.map((b) => b.name).join(", "); return ( {names || "—"} ); } case "categories": { const names = item.categories?.map((c) => c.name).join(", "); return ( {names || "—"} ); } case "power_categories": { const names = item.power_categories?.map((p) => p.name).join(", "); return ( {names || "—"} ); } case "is_active": return ( {item.is_active ? t("yes") ?? "Ano" : t("no") ?? "Ne"} ); case "actions": return ( {canEdit && ( )} ); default: return ; } }} )}
); }