import { useEffect, useMemo, useState } from "react"; import axios from "axios"; import { Card, CardBody, Listbox, ListboxItem, type Selection } from "@heroui/react"; import { useTranslation } from "react-i18next"; import { useLanguageStore } from "@/stores/languageStore"; import { useContestStore, type RoundSummary } from "@/stores/contestStore"; import { useContestRefreshStore } from "@/stores/contestRefreshStore"; import { useNavigate } from "react-router-dom"; type RoundItem = RoundSummary & { contest_id: number; description?: string | Record | null; }; type PaginatedResponse = { data: T[]; }; type RoundsOverviewProps = { contestId: number | null; onlyActive?: boolean; showTests?: boolean; className?: string; roundsFromStore?: RoundSummary[] | null; }; const resolveTranslation = (field: any, locale: string): string => { if (!field) return ""; if (typeof field === "string") return field; if (typeof field === "object") { if (field[locale]) return field[locale]; if (field["en"]) return field["en"]; const first = Object.values(field)[0]; return typeof first === "string" ? first : ""; } return String(field); }; export default function RoundsOverview({ contestId, onlyActive = false, showTests = false, className, roundsFromStore = null, }: RoundsOverviewProps) { const { t } = useTranslation("common"); const locale = useLanguageStore((s) => s.locale); const refreshKey = useContestRefreshStore((s) => s.refreshKey); const navigate = useNavigate(); const [items, setItems] = useState(roundsFromStore ?? []); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [selectedKeys, setSelectedKeys] = useState(new Set([])); const setSelectedRound = useContestStore((s) => s.setSelectedRound); const selectedRound = useContestStore((s) => s.selectedRound); // sync selection with store useEffect(() => { if (selectedRound) { setSelectedKeys(new Set([String(selectedRound.id)])); } else { setSelectedKeys(new Set([])); } }, [selectedRound]); useEffect(() => { // pokud máme roundsFromStore s daty, použij je a nefetchuj if (roundsFromStore && roundsFromStore.length > 0) { setItems(roundsFromStore); setLoading(false); setError(null); return; } if (!contestId) { setItems([]); setLoading(false); setError(null); return; } let active = true; (async () => { try { setLoading(true); setError(null); const res = await axios.get | RoundItem[]>( "/api/rounds", { withCredentials: true, headers: { Accept: "application/json" }, params: { lang: locale, contest_id: contestId, only_active: onlyActive ? 1 : 0, include_tests: showTests ? 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_rounds") ?? "Nepodařilo se načíst seznam kol."); } finally { if (active) setLoading(false); } })(); return () => { active = false; }; }, [locale, t, refreshKey, roundsFromStore, contestId, onlyActive, showTests]); const visibleItems = useMemo(() => items, [items]); const isSelected = (id: string | number) => { if (selectedKeys === "all") return false; return Array.from(selectedKeys).some((k) => String(k) === String(id)); }; const handleSelectionChange = (keys: Selection) => { setSelectedKeys(keys); if (keys === "all") return; const id = Array.from(keys)[0]; if (!id) { if (selectedRound) { setSelectedRound(null); if (contestId != null) navigate(`/contests/${contestId}`); } return; } if (selectedRound && String(selectedRound.id) === String(id)) return; const selected = visibleItems.find((r) => String(r.id) === String(id)); if (selected) { setSelectedRound(selected); navigate(`/contests/${selected.contest_id}/rounds/${selected.id}`); } }; return ( {loading ? (
{t("rounds_loading") ?? "Načítám kola…"}
) : error ? (
{error}
) : visibleItems.length === 0 ? (
{t("rounds_empty") ?? "Žádná kola nejsou k dispozici."}
) : ( {visibleItems.map((item) => (
{resolveTranslation(item.name, locale)} {resolveTranslation(item.description ?? null, locale) || "—"}
))}
)}
); }