219 lines
7.6 KiB
TypeScript
219 lines
7.6 KiB
TypeScript
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>
|
|
);
|
|
}
|