import React, { useMemo } from "react"; import { Button, Card, CardBody, CardHeader, Divider, Table, TableBody, TableCell, TableColumn, TableHeader, TableRow, } from "@heroui/react"; import type { LogResult, LogOverride } from "@/components/RoundEvaluationLogOverrides.types"; type GroupedResults = Array<{ key: string; items: LogResult[] }>; type Props = { items: LogResult[]; overrides: Record; onEdit: (logId: number) => void; }; function PencilIcon({ className }: { className?: string }) { return ( ); } function groupBy(items: LogResult[], getKey: (r: LogResult) => string): GroupedResults { const map = new Map(); items.forEach((item) => { const key = getKey(item); if (!map.has(key)) map.set(key, []); map.get(key)!.push(item); }); return Array.from(map.entries()).map(([key, list]) => ({ key, items: list })); } function sortGroups(groups: GroupedResults): GroupedResults { return [...groups].sort((a, b) => { const aItem = a.items[0]; const bItem = b.items[0]; const bandOrder = (aItem?.band?.order ?? Number.MAX_SAFE_INTEGER) - (bItem?.band?.order ?? Number.MAX_SAFE_INTEGER); if (bandOrder !== 0) return bandOrder; const categoryOrder = (aItem?.category?.order ?? Number.MAX_SAFE_INTEGER) - (bItem?.category?.order ?? Number.MAX_SAFE_INTEGER); if (categoryOrder !== 0) return categoryOrder; const powerOrder = (aItem?.power_category?.order ?? Number.MAX_SAFE_INTEGER) - (bItem?.power_category?.order ?? Number.MAX_SAFE_INTEGER); if (powerOrder !== 0) return powerOrder; const bandName = aItem?.band?.name ?? ""; const bandNameB = bItem?.band?.name ?? ""; if (bandName !== bandNameB) return bandName.localeCompare(bandNameB); const categoryName = aItem?.category?.name ?? ""; const categoryNameB = bItem?.category?.name ?? ""; if (categoryName !== categoryNameB) return categoryName.localeCompare(categoryNameB); const powerName = aItem?.power_category?.name ?? ""; const powerNameB = bItem?.power_category?.name ?? ""; return powerName.localeCompare(powerNameB); }); } function resolveNames(item: LogResult): [string | null, string | null, string | null] { const band = item.band?.name ?? null; const category = item.category?.name ?? null; const power = item.power_category?.name ?? null; return [band, category, power]; } type RankField = "rank_overall" | "rank_in_category"; function isCheckCategory(item: LogResult) { const name = item.category?.name?.toLowerCase() ?? ""; return name.includes("check"); } function isPowerA(item: LogResult) { const name = item.power_category?.name?.toLowerCase() ?? ""; return name === "a"; } function sortResults(items: LogResult[], rankField: RankField) { return [...items].sort((a, b) => { const ra = a[rankField] ?? Number.MAX_SAFE_INTEGER; const rb = b[rankField] ?? Number.MAX_SAFE_INTEGER; if (ra !== rb) return ra - rb; const sa = a.official_score ?? 0; const sb = b.official_score ?? 0; if (sa !== sb) return sb - sa; const qa = a.valid_qso_count ?? 0; const qb = b.valid_qso_count ?? 0; return qb - qa; }); } function formatNumber(value: number | null | undefined) { if (value === null || value === undefined) return "—"; if (Number.isInteger(value)) return value.toString(); return value.toFixed(1); } function formatRatio(score?: number | null, qso?: number | null) { if (!score || !qso || qso === 0) return "—"; return (score / qso).toFixed(2); } function renderGroup( group: GroupedResults, title: string, rankField: RankField, includePowerInHeading: boolean, overrides: Record, onEdit: (logId: number) => void ) { if (group.length === 0) return []; return group.map(({ key, items }) => { const sample = items[0]; const [bandName, categoryName, powerName] = resolveNames(sample); const headingParts = [bandName, categoryName, title]; if (includePowerInHeading && !isCheckCategory(sample)) { headingParts.push(powerName); } const heading = headingParts.filter(Boolean).join(" "); const sorted = sortResults(items, rankField); return (
{heading}
Pořadí Značka Lokátor Kategorie Pásmo Výkon [W] Výkonová kategorie Body celkem Počet QSO Body/QSO ODX Anténa Ant. height Status Komentář rozhodčího Akce {(item) => { const override = overrides[item.log_id]; const rowHighlight = !!override && (override.forced_log_status && override.forced_log_status !== "AUTO" || override.forced_band_id || override.forced_category_id || override.forced_power_category_id || override.forced_power_w !== null && override.forced_power_w !== undefined || override.forced_sixhr_category !== null && override.forced_sixhr_category !== undefined); return ( {item[rankField] ?? "—"} {item.log?.pcall ?? "—"} {item.log?.pwwlo ?? item.log?.locator ?? "—"} {item.category?.name ?? "—"} {item.band?.name ?? "—"} {formatNumber(item.log?.power_watt)} {item.powerCategory?.name ?? item.power_category?.name ?? "—"} {formatNumber(item.official_score)} {item.valid_qso_count ?? "—"} {formatRatio(item.official_score, item.valid_qso_count)} {item.log?.codxc ?? "—"} {item.log?.sante ?? "—"} {item.log?.santh ?? "—"} {item.status ?? "—"} {override?.reason ?? "—"} ); }}
); }); } function renderOverallWithPowers( overalls: GroupedResults, powers: GroupedResults, title: string, overrides: Record, onEdit: (logId: number) => void ) { if (overalls.length === 0) return null; const powerIndex = powers.reduce>((acc, group) => { const [bandId, categoryId] = group.key.split("|"); const key = `${bandId}|${categoryId}`; acc[key] = acc[key] || []; acc[key].push(group); return acc; }, {}); return sortGroups(overalls).flatMap(({ key, items }) => { const [bandId, categoryId] = key.split("|"); const powerGroups = sortGroups(powerIndex[`${bandId}|${categoryId}`] ?? []); return [ ...renderGroup([{ key, items }], title, "rank_overall", false, overrides, onEdit), ...renderGroup(powerGroups, "", "rank_in_category", true, overrides, onEdit), ]; }); } export default React.memo(function RoundEvaluationLogOverridesTable({ items, overrides, onEdit, }: Props) { const grouped = useMemo(() => { const isSixhr = (r: LogResult) => (r.log?.sixhr_category ?? false) === true; const sixh = items.filter((r) => isSixhr(r)); const standard = items.filter((r) => !isSixhr(r)); const powerEligible = standard.filter( (r) => !isCheckCategory(r) && !isPowerA(r) && r.power_category && r.rank_in_category !== null ); const groupOverall = (list: LogResult[]) => groupBy(list, (r) => `${r.band?.id ?? "null"}|${r.category?.id ?? "null"}`); const groupPower = (list: LogResult[]) => groupBy( list, (r) => `${r.band?.id ?? "null"}|${r.category?.id ?? "null"}|${r.power_category?.id ?? "null"}` ); return { sixhOverall: groupOverall(sixh), standardOverall: groupOverall(standard), standardPower: groupPower(powerEligible), }; }, [items]); return ( <> {renderOverallWithPowers(grouped.standardOverall, grouped.standardPower, "", overrides, onEdit)} {renderOverallWithPowers(grouped.sixhOverall, [], "6H", overrides, onEdit)} ); });