move static data
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { useMemo, useState } from "react";
|
||||
import { usePenaltyTypesReference } from "@/hooks/usePenaltyTypesReference";
|
||||
import type { PenaltyValueKindDTO } from "@/lib/types/PenaltyTypesReferenceDTO";
|
||||
import { ProtestViewModel } from "../../lib/view-models/ProtestViewModel";
|
||||
import Modal from "../ui/Modal";
|
||||
import Button from "../ui/Button";
|
||||
@@ -21,7 +23,7 @@ import {
|
||||
FileWarning,
|
||||
} from "lucide-react";
|
||||
|
||||
type PenaltyType = "time_penalty" | "grid_penalty" | "points_deduction" | "disqualification" | "warning" | "license_points" | "probation" | "fine" | "race_ban";
|
||||
type PenaltyType = string;
|
||||
|
||||
interface ReviewProtestModalProps {
|
||||
protest: ProtestViewModel | null;
|
||||
@@ -94,25 +96,63 @@ export function ReviewProtestModal({
|
||||
}
|
||||
};
|
||||
|
||||
const getPenaltyLabel = (type: PenaltyType) => {
|
||||
const getPenaltyName = (type: PenaltyType) => {
|
||||
switch (type) {
|
||||
case "time_penalty":
|
||||
return "seconds";
|
||||
return "Time Penalty";
|
||||
case "grid_penalty":
|
||||
return "grid positions";
|
||||
return "Grid Penalty";
|
||||
case "points_deduction":
|
||||
return "points";
|
||||
return "Points Deduction";
|
||||
case "disqualification":
|
||||
return "Disqualification";
|
||||
case "warning":
|
||||
return "Warning";
|
||||
case "license_points":
|
||||
return "points";
|
||||
return "License Points";
|
||||
case "probation":
|
||||
return "Probation";
|
||||
case "fine":
|
||||
return "points";
|
||||
return "Fine";
|
||||
case "race_ban":
|
||||
return "races";
|
||||
return "Race Ban";
|
||||
default:
|
||||
return type.replaceAll("_", " ");
|
||||
}
|
||||
};
|
||||
|
||||
const getPenaltyValueLabel = (valueKind: PenaltyValueKindDTO): string => {
|
||||
switch (valueKind) {
|
||||
case "seconds":
|
||||
return "seconds";
|
||||
case "grid_positions":
|
||||
return "grid positions";
|
||||
case "points":
|
||||
return "points";
|
||||
case "races":
|
||||
return "races";
|
||||
case "none":
|
||||
return "";
|
||||
}
|
||||
};
|
||||
|
||||
const getPenaltyDefaultValue = (type: PenaltyType, valueKind: PenaltyValueKindDTO): number => {
|
||||
if (type === "license_points") return 2;
|
||||
if (type === "race_ban") return 1;
|
||||
switch (valueKind) {
|
||||
case "seconds":
|
||||
return 5;
|
||||
case "grid_positions":
|
||||
return 3;
|
||||
case "points":
|
||||
return 5;
|
||||
case "races":
|
||||
return 1;
|
||||
case "none":
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
const getPenaltyColor = (type: PenaltyType) => {
|
||||
switch (type) {
|
||||
case "time_penalty":
|
||||
@@ -138,6 +178,25 @@ export function ReviewProtestModal({
|
||||
}
|
||||
};
|
||||
|
||||
const { data: penaltyTypesReference, isLoading: penaltyTypesLoading } = usePenaltyTypesReference();
|
||||
|
||||
const penaltyOptions = useMemo(() => {
|
||||
const refs = penaltyTypesReference?.penaltyTypes ?? [];
|
||||
return refs.map((ref) => ({
|
||||
type: ref.type as PenaltyType,
|
||||
name: getPenaltyName(ref.type),
|
||||
requiresValue: ref.requiresValue,
|
||||
valueLabel: getPenaltyValueLabel(ref.valueKind),
|
||||
defaultValue: getPenaltyDefaultValue(ref.type, ref.valueKind),
|
||||
Icon: getPenaltyIcon(ref.type),
|
||||
colorClass: getPenaltyColor(ref.type),
|
||||
}));
|
||||
}, [penaltyTypesReference]);
|
||||
|
||||
const selectedPenalty = useMemo(() => {
|
||||
return penaltyOptions.find((p) => p.type === penaltyType);
|
||||
}, [penaltyOptions, penaltyType]);
|
||||
|
||||
if (showConfirmation) {
|
||||
return (
|
||||
<Modal title="Confirm Decision" isOpen={true} onOpenChange={() => setShowConfirmation(false)}>
|
||||
@@ -160,7 +219,9 @@ export function ReviewProtestModal({
|
||||
<h3 className="text-xl font-bold text-white">Confirm Decision</h3>
|
||||
<p className="text-gray-400 mt-2">
|
||||
{decision === "accept"
|
||||
? `Issue ${penaltyValue} ${getPenaltyLabel(penaltyType)} penalty?`
|
||||
? (selectedPenalty?.requiresValue
|
||||
? `Issue ${penaltyValue} ${selectedPenalty.valueLabel} penalty?`
|
||||
: `Issue ${selectedPenalty?.name ?? penaltyType} penalty?`)
|
||||
: "Reject this protest?"}
|
||||
</p>
|
||||
</div>
|
||||
@@ -300,43 +361,39 @@ export function ReviewProtestModal({
|
||||
<label className="text-sm font-medium text-gray-400 mb-2 block">
|
||||
Penalty Type
|
||||
</label>
|
||||
<div className="grid grid-cols-3 gap-2">
|
||||
{[
|
||||
{ type: "time_penalty" as PenaltyType, label: "Time Penalty" },
|
||||
{ type: "grid_penalty" as PenaltyType, label: "Grid Penalty" },
|
||||
{ type: "points_deduction" as PenaltyType, label: "Points Deduction" },
|
||||
{ type: "disqualification" as PenaltyType, label: "Disqualification" },
|
||||
{ type: "warning" as PenaltyType, label: "Warning" },
|
||||
{ type: "license_points" as PenaltyType, label: "License Points" },
|
||||
{ type: "probation" as PenaltyType, label: "Probation" },
|
||||
{ type: "fine" as PenaltyType, label: "Fine" },
|
||||
{ type: "race_ban" as PenaltyType, label: "Race Ban" },
|
||||
].map(({ type, label }) => {
|
||||
const Icon = getPenaltyIcon(type);
|
||||
const colorClass = getPenaltyColor(type);
|
||||
const isSelected = penaltyType === type;
|
||||
return (
|
||||
<button
|
||||
key={type}
|
||||
onClick={() => setPenaltyType(type)}
|
||||
className={`p-3 rounded-lg border transition-all ${
|
||||
isSelected
|
||||
? `${colorClass} border-2`
|
||||
: "border-charcoal-outline hover:border-gray-600 bg-iron-gray/50"
|
||||
}`}
|
||||
>
|
||||
<Icon className={`h-5 w-5 mx-auto mb-1 ${isSelected ? '' : 'text-gray-400'}`} />
|
||||
<p className={`text-xs font-medium ${isSelected ? '' : 'text-gray-400'}`}>{label}</p>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
{penaltyTypesLoading ? (
|
||||
<div className="text-sm text-gray-500">Loading penalty types…</div>
|
||||
) : (
|
||||
<div className="grid grid-cols-3 gap-2">
|
||||
{penaltyOptions.map(({ type, name, Icon, colorClass, defaultValue }) => {
|
||||
const isSelected = penaltyType === type;
|
||||
return (
|
||||
<button
|
||||
key={type}
|
||||
onClick={() => {
|
||||
setPenaltyType(type);
|
||||
setPenaltyValue(defaultValue);
|
||||
}}
|
||||
className={`p-3 rounded-lg border transition-all ${
|
||||
isSelected
|
||||
? `${colorClass} border-2`
|
||||
: "border-charcoal-outline hover:border-gray-600 bg-iron-gray/50"
|
||||
}`}
|
||||
>
|
||||
<Icon className={`h-5 w-5 mx-auto mb-1 ${isSelected ? "" : "text-gray-400"}`} />
|
||||
<p className={`text-xs font-medium ${isSelected ? "" : "text-gray-400"}`}>{name}</p>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{['time_penalty', 'grid_penalty', 'points_deduction', 'license_points', 'fine', 'race_ban'].includes(penaltyType) && (
|
||||
{selectedPenalty?.requiresValue && (
|
||||
<div>
|
||||
<label className="text-sm font-medium text-gray-400 mb-2 block">
|
||||
Penalty Value ({getPenaltyLabel(penaltyType)})
|
||||
Penalty Value ({selectedPenalty.valueLabel})
|
||||
</label>
|
||||
<input
|
||||
type="number"
|
||||
|
||||
Reference in New Issue
Block a user