Initial commit

This commit is contained in:
Zdeněk Burda
2026-01-09 21:26:40 +01:00
parent e83aec6dca
commit 41e3ce6f25
404 changed files with 61250 additions and 28 deletions

View File

@@ -0,0 +1,218 @@
import {
Button,
Modal,
ModalBody,
ModalContent,
ModalHeader,
Select,
SelectItem,
Textarea,
type Selection,
} from "@heroui/react";
import type {
LogOverride,
LogResult,
OverrideForm,
} from "@/components/RoundEvaluationLogOverrides.types";
type Props = {
isOpen: boolean;
onOpenChange: (isOpen: boolean) => void;
onClose: () => void;
activeItem: LogResult | null;
override?: LogOverride;
form?: OverrideForm;
bands: { id: number; name: string }[];
categories: { id: number; name: string }[];
powerCategories: { id: number; name: string }[];
onFieldChange: (logId: number, field: keyof OverrideForm, value: string) => void;
onSave: (logId: number) => void;
resolveName: (list: { id: number; name: string }[], id?: number | null) => string;
};
const statusOptions = [
{ value: "AUTO", label: "AUTO" },
{ value: "OK", label: "OK" },
{ value: "CHECK", label: "CHECK" },
{ value: "DQ", label: "DQ" },
{ value: "IGNORED", label: "IGNORED" },
];
const getFirstSelection = (keys: Selection) => {
if (keys === "all") return "";
const [first] = Array.from(keys);
return typeof first === "string" || typeof first === "number" ? String(first) : "";
};
export default function RoundEvaluationLogOverridesModal({
isOpen,
onOpenChange,
onClose,
activeItem,
override,
form,
bands,
categories,
powerCategories,
onFieldChange,
onSave,
resolveName,
}: Props) {
return (
<Modal isOpen={isOpen} onOpenChange={onOpenChange} size="2xl" scrollBehavior="inside">
<ModalContent>
<ModalHeader className="flex flex-col gap-1">
Upravit log #{activeItem?.log_id ?? "—"}
</ModalHeader>
<ModalBody>
{activeItem && (
<>
<div className="text-xs text-foreground-500 mb-2">
<div>
{activeItem.log?.pcall ?? "—"} | {activeItem.band?.name ?? "—"} /{" "}
{activeItem.category?.name ?? "—"} /{" "}
{activeItem.powerCategory?.name ?? activeItem.power_category?.name ?? "—"}
</div>
<div>
Score: {activeItem.official_score ?? "—"} | Rank:{" "}
{activeItem.rank_overall ?? "—"} | Status: {activeItem.status ?? "—"}
</div>
</div>
{override && (
<div className="text-xs text-foreground-500 mb-2">
Override: {override.forced_log_status ?? "AUTO"} /{" "}
{resolveName(bands, override.forced_band_id)} /{" "}
{resolveName(categories, override.forced_category_id)} /{" "}
{resolveName(powerCategories, override.forced_power_category_id)}
</div>
)}
<div className="grid gap-2 md:grid-cols-2">
<Select
aria-label="Status"
size="sm"
variant="bordered"
label="Status"
selectedKeys={new Set([form?.status ?? "AUTO"])}
onSelectionChange={(keys) =>
onFieldChange(
activeItem.log_id,
"status",
getFirstSelection(keys) || "AUTO"
)
}
selectionMode="single"
disallowEmptySelection={true}
>
{statusOptions.map((opt) => (
<SelectItem key={opt.value} textValue={opt.label}>
{opt.label}
</SelectItem>
))}
</Select>
<Select
aria-label="Band"
size="sm"
variant="bordered"
label="Band"
selectedKeys={new Set([form?.bandId || "auto"])}
onSelectionChange={(keys) =>
onFieldChange(
activeItem.log_id,
"bandId",
getFirstSelection(keys) === "auto" ? "" : getFirstSelection(keys)
)
}
selectionMode="single"
disallowEmptySelection={true}
>
<SelectItem key="auto" textValue="AUTO">
AUTO
</SelectItem>
{bands.map((band) => (
<SelectItem key={String(band.id)} textValue={band.name}>
{band.name}
</SelectItem>
))}
</Select>
<Select
aria-label="Kategorie"
size="sm"
variant="bordered"
label="Kategorie"
selectedKeys={new Set([form?.categoryId || "auto"])}
onSelectionChange={(keys) =>
onFieldChange(
activeItem.log_id,
"categoryId",
getFirstSelection(keys) === "auto" ? "" : getFirstSelection(keys)
)
}
selectionMode="single"
disallowEmptySelection={true}
>
<SelectItem key="auto" textValue="AUTO">
AUTO
</SelectItem>
{categories.map((cat) => (
<SelectItem key={String(cat.id)} textValue={cat.name}>
{cat.name}
</SelectItem>
))}
</Select>
<Select
aria-label="Výkon"
size="sm"
variant="bordered"
label="Výkon"
selectedKeys={new Set([form?.powerCategoryId || "auto"])}
onSelectionChange={(keys) =>
onFieldChange(
activeItem.log_id,
"powerCategoryId",
getFirstSelection(keys) === "auto" ? "" : getFirstSelection(keys)
)
}
selectionMode="single"
disallowEmptySelection={true}
>
<SelectItem key="auto" textValue="AUTO">
AUTO
</SelectItem>
{powerCategories.map((cat) => (
<SelectItem key={String(cat.id)} textValue={cat.name}>
{cat.name}
</SelectItem>
))}
</Select>
</div>
<Textarea
label="Důvod změny"
size="sm"
variant="bordered"
minRows={2}
value={form?.reason ?? ""}
onChange={(e) => onFieldChange(activeItem.log_id, "reason", e.target.value)}
placeholder="Krátce popiš, proč zasahuješ."
/>
<div className="flex items-center gap-2 text-xs">
<Button
size="sm"
color="primary"
onPress={() => onSave(activeItem.log_id)}
isDisabled={form?.saving}
>
{form?.saving ? "Ukládám…" : "Uložit"}
</Button>
<Button size="sm" variant="light" onPress={onClose}>
Zavřít
</Button>
{form?.error && <span className="text-red-600">{form.error}</span>}
{form?.success && <span className="text-green-600">{form.success}</span>}
</div>
</>
)}
</ModalBody>
</ModalContent>
</Modal>
);
}