update request model rewiring
All checks were successful
Deploy to Cloudflare Pages / deploy (push) Successful in 3m46s
All checks were successful
Deploy to Cloudflare Pages / deploy (push) Successful in 3m46s
This commit is contained in:
@@ -41,7 +41,12 @@ export default function ClientPage({ initialAppData }: ClientPageProps) {
|
||||
|
||||
<section className="lg:col-span-1">
|
||||
<h2 className="text-2xl font-bold text-white mb-4">Points Table:</h2>
|
||||
<PointsKey pointsTable={appData.pointsTable} flPoints={appData.flPoints} />
|
||||
<PointsKey
|
||||
pointsTable={appData.pointsTable}
|
||||
flPoints={appData.flPoints}
|
||||
flColor={appData.flColor}
|
||||
flTextColor={appData.flTextColor}
|
||||
/>
|
||||
</section>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
@@ -2,10 +2,12 @@ import { PointStyle } from '@/lib/types';
|
||||
|
||||
interface PointsKeyProps {
|
||||
pointsTable: PointStyle[];
|
||||
flPoints: PointStyle;
|
||||
flPoints: number;
|
||||
flColor: string;
|
||||
flTextColor: string;
|
||||
}
|
||||
|
||||
export default function PointsKey({ pointsTable, flPoints }: PointsKeyProps) {
|
||||
export default function PointsKey({ pointsTable, flPoints, flColor, flTextColor }: PointsKeyProps) {
|
||||
const sortedPoints = [...pointsTable].sort((a, b) => (a.priority || 99) - (b.priority || 99));
|
||||
|
||||
return (
|
||||
@@ -13,7 +15,7 @@ export default function PointsKey({ pointsTable, flPoints }: PointsKeyProps) {
|
||||
<table className="w-full text-center font-bold">
|
||||
<tbody>
|
||||
{sortedPoints.map((style) => (
|
||||
<tr key={style.points}>
|
||||
<tr key={style.id || `pt-${style.points}`}>
|
||||
<td
|
||||
className="px-3 py-2"
|
||||
style={{
|
||||
@@ -33,9 +35,15 @@ export default function PointsKey({ pointsTable, flPoints }: PointsKeyProps) {
|
||||
</tr>
|
||||
))}
|
||||
<tr>
|
||||
<td className="px-3 py-2 points-fl">
|
||||
<td
|
||||
className="px-3 py-2 points-fl"
|
||||
style={{
|
||||
backgroundColor: flColor,
|
||||
color: flTextColor,
|
||||
}}
|
||||
>
|
||||
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', flexDirection: 'row' }}>
|
||||
FL <span className="fl-bg-text" style={{ color: flPoints.textColor }}>(+{flPoints.points})</span>
|
||||
FL <span className="fl-bg-text" style={{ color: flTextColor }}>(+{flPoints})</span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -36,93 +36,97 @@ export default function RaceResultsTable({ appData }: RaceResultsTableProps) {
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="race-table-body" className="divide-y divide-gray-700">
|
||||
{appData.tracks.map((track, trackIndex) => (
|
||||
<tr key={trackIndex}>
|
||||
<td className="font-semibold py-3 px-4 whitespace-nowrap min-w-[200px]">
|
||||
<span>{track.name}</span>
|
||||
</td>
|
||||
<td className="py-3 px-4 font-mono min-w-[100px]">
|
||||
{track.fastestLapInitials || ""}
|
||||
</td>
|
||||
{appData.drivers.map((driver, driverIndex) => {
|
||||
const driverInitials = driver.initials;
|
||||
let result = track.results[driverIndex];
|
||||
// DNS fallback
|
||||
if (result === undefined || result === null || result === "") {
|
||||
result = "DNS";
|
||||
}
|
||||
const pointsNum = parsePoints(result);
|
||||
const style = findStyleByPoints(appData, pointsNum);
|
||||
{appData.tracks.map((track, trackIndex) => {
|
||||
// Find driver name for fastest lap
|
||||
const flDriver = appData.drivers.find(
|
||||
(d) => d.id === track.fastestLapDriverId
|
||||
);
|
||||
return (
|
||||
<tr key={trackIndex}>
|
||||
<td className="font-semibold py-3 px-4 whitespace-nowrap min-w-[200px]">
|
||||
<span>{track.name}</span>
|
||||
</td>
|
||||
<td className="py-3 px-4 font-mono min-w-[100px]">
|
||||
{flDriver?.initials || ""}
|
||||
</td>
|
||||
{appData.drivers.map((driver) => {
|
||||
const raceResult = track.results.find(
|
||||
(r) => r.driverId === driver.id
|
||||
);
|
||||
let result = raceResult?.score;
|
||||
// DNS fallback
|
||||
if (result === undefined || result === null || result === "") {
|
||||
result = "DNS";
|
||||
}
|
||||
const pointsNum = parsePoints(result);
|
||||
const style = findStyleByPoints(appData, pointsNum);
|
||||
|
||||
let cellStyles: React.CSSProperties = {
|
||||
backgroundColor: style.color,
|
||||
color: style.textColor,
|
||||
};
|
||||
let cellStyles: React.CSSProperties = {
|
||||
backgroundColor: style.color,
|
||||
color: style.textColor,
|
||||
};
|
||||
|
||||
let hasFL = false;
|
||||
if (
|
||||
track.fastestLapInitials &&
|
||||
track.fastestLapInitials === driverInitials &&
|
||||
appData.flPoints.points
|
||||
) {
|
||||
hasFL = true;
|
||||
const flColor = appData.flPoints.color;
|
||||
if (pointsNum === 0) {
|
||||
let hasFL = false;
|
||||
if (track.fastestLapDriverId === driver.id && appData.flPoints) {
|
||||
hasFL = true;
|
||||
const flColor = appData.flColor;
|
||||
if (pointsNum === 0) {
|
||||
cellStyles = {
|
||||
backgroundColor: flColor,
|
||||
color: appData.flTextColor,
|
||||
};
|
||||
} else {
|
||||
cellStyles = {
|
||||
background: `linear-gradient(135deg, ${style.color} 50%, ${flColor} 50%)`,
|
||||
color: style.textColor,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (hasFL) {
|
||||
cellStyles = {
|
||||
backgroundColor: flColor,
|
||||
color: appData.flPoints.textColor,
|
||||
...cellStyles,
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
flexDirection: "row",
|
||||
paddingLeft: "0.75rem",
|
||||
paddingRight: "0.75rem",
|
||||
};
|
||||
} else {
|
||||
cellStyles = {
|
||||
background: `linear-gradient(135deg, ${style.color} 50%, ${flColor} 50%)`,
|
||||
color: style.textColor,
|
||||
...cellStyles,
|
||||
textAlign: "center",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (hasFL) {
|
||||
cellStyles = {
|
||||
...cellStyles,
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
flexDirection: "row",
|
||||
paddingLeft: "0.75rem",
|
||||
paddingRight: "0.75rem",
|
||||
};
|
||||
} else {
|
||||
cellStyles = {
|
||||
...cellStyles,
|
||||
textAlign: "center",
|
||||
};
|
||||
}
|
||||
|
||||
return (
|
||||
<td
|
||||
key={driverIndex}
|
||||
className="py-3 px-4 font-mono"
|
||||
style={cellStyles}
|
||||
>
|
||||
{hasFL ? (
|
||||
<>
|
||||
<span style={{ flexGrow: 1, textAlign: "left" }}>
|
||||
{result}
|
||||
</span>
|
||||
<span
|
||||
className="fl-text-only"
|
||||
style={{ color: appData.flPoints.textColor }}
|
||||
>
|
||||
(+{appData.flPoints.points})
|
||||
</span>
|
||||
</>
|
||||
) : (
|
||||
<span>{result}</span>
|
||||
)}
|
||||
</td>
|
||||
);
|
||||
})}
|
||||
</tr>
|
||||
))}
|
||||
return (
|
||||
<td
|
||||
key={driver.id}
|
||||
className="py-3 px-4 font-mono"
|
||||
style={cellStyles}
|
||||
>
|
||||
{hasFL ? (
|
||||
<>
|
||||
<span style={{ flexGrow: 1, textAlign: "left" }}>
|
||||
{result}
|
||||
</span>
|
||||
<span
|
||||
className="fl-text-only"
|
||||
style={{ color: appData.flTextColor }}
|
||||
>
|
||||
(+{appData.flPoints})
|
||||
</span>
|
||||
</>
|
||||
) : (
|
||||
<span>{result}</span>
|
||||
)}
|
||||
</td>
|
||||
);
|
||||
})}
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
11
src/app/not-found.tsx
Normal file
11
src/app/not-found.tsx
Normal file
@@ -0,0 +1,11 @@
|
||||
export default function NotFound() {
|
||||
return (
|
||||
<div className="container mx-auto max-w-7xl space-y-12">
|
||||
<header className="text-center">
|
||||
<h1 className="text-4xl md:text-5xl font-bold text-transparent bg-clip-text bg-gradient-to-r from-blue-400 to-purple-500 mb-4">
|
||||
404 - Page Not Found
|
||||
</h1>
|
||||
</header>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -7,50 +7,52 @@ const defaultColors = [
|
||||
|
||||
export const defaultAppData: AppData = {
|
||||
drivers: [
|
||||
{ name: "Filip Makarijevski", color: defaultColors[0], initials: "F.M." },
|
||||
{ name: "Karlo Steinbruckner", color: defaultColors[1], initials: "K.S." },
|
||||
{ name: "Fran Jurmanović", color: defaultColors[2], initials: "F.J." },
|
||||
{ name: "Borna Bevanda", color: defaultColors[3], initials: "B.B." },
|
||||
{ name: "Matej Jugović", color: defaultColors[4], initials: "M.J." },
|
||||
{ name: "Lovo Bravić", color: defaultColors[5], initials: "L.B." },
|
||||
{ name: "Dominik Dejanović", color: defaultColors[6], initials: "D.D." },
|
||||
{ name: "Josip Kompanović", color: defaultColors[7], initials: "J.K." },
|
||||
{ name: "Tin Vidmar", color: defaultColors[8], initials: "T.V." },
|
||||
{ name: "Ivan Ivezić", color: defaultColors[9], initials: "I.I." },
|
||||
{ name: "Tomislav Glavaš", color: defaultColors[10], initials: "T.G." }
|
||||
{ id: "550e8400-e29b-41d4-a716-446655440000", name: "Filip Makarijevski", color: defaultColors[0], initials: "F.M." },
|
||||
{ id: "550e8400-e29b-41d4-a716-446655440001", name: "Karlo Steinbruckner", color: defaultColors[1], initials: "K.S." },
|
||||
{ id: "550e8400-e29b-41d4-a716-446655440002", name: "Fran Jurmanović", color: defaultColors[2], initials: "F.J." },
|
||||
{ id: "550e8400-e29b-41d4-a716-446655440003", name: "Borna Bevanda", color: defaultColors[3], initials: "B.B." },
|
||||
{ id: "550e8400-e29b-41d4-a716-446655440004", name: "Matej Jugović", color: defaultColors[4], initials: "M.J." },
|
||||
{ id: "550e8400-e29b-41d4-a716-446655440005", name: "Lovo Bravić", color: defaultColors[5], initials: "L.B." },
|
||||
{ id: "550e8400-e29b-41d4-a716-446655440006", name: "Dominik Dejanović", color: defaultColors[6], initials: "D.D." },
|
||||
{ id: "550e8400-e29b-41d4-a716-446655440007", name: "Josip Kompanović", color: defaultColors[7], initials: "J.K." },
|
||||
{ id: "550e8400-e29b-41d4-a716-446655440008", name: "Tin Vidmar", color: defaultColors[8], initials: "T.V." },
|
||||
{ id: "550e8400-e29b-41d4-a716-446655440009", name: "Ivan Ivezić", color: defaultColors[9], initials: "I.I." },
|
||||
{ id: "550e8400-e29b-41d4-a716-44665544000a", name: "Tomislav Glavaš", color: defaultColors[10], initials: "T.G." }
|
||||
],
|
||||
pointsTable: [
|
||||
{ points: 10, label: "1st", color: "#fde047", textColor: "#422006", priority: 1 },
|
||||
{ points: 7, label: "2nd", color: "#9ca3af", textColor: "#1f2937", priority: 2 },
|
||||
{ points: 5, label: "3rd", color: "#f0c0a0", textColor: "#431407", priority: 3 },
|
||||
{ points: 2, label: "4th", color: "#81c784", textColor: "#1F2937", priority: 4 },
|
||||
{ points: 1, label: "5th", color: "#a7d9ef", textColor: "#1F2937", priority: 5 },
|
||||
{ id: "pt-1st", points: 10, label: "1st", color: "#fde047", textColor: "#422006", priority: 1 },
|
||||
{ id: "pt-2nd", points: 7, label: "2nd", color: "#9ca3af", textColor: "#1f2937", priority: 2 },
|
||||
{ id: "pt-3rd", points: 5, label: "3rd", color: "#f0c0a0", textColor: "#431407", priority: 3 },
|
||||
{ id: "pt-4th", points: 2, label: "4th", color: "#81c784", textColor: "#1F2937", priority: 4 },
|
||||
{ id: "pt-5th", points: 1, label: "5th", color: "#a7d9ef", textColor: "#1F2937", priority: 5 },
|
||||
],
|
||||
flPoints: { points: 1, label: "FL +1", color: "#8b5cf6", textColor: "#000000", priority: 6 },
|
||||
flPoints: 1,
|
||||
flColor: "#8b5cf6",
|
||||
flTextColor: "#000000",
|
||||
tracks: [
|
||||
{ name: "Hungaroring", results: [], fastestLapInitials: "" },
|
||||
{ name: "Watkins Glen", results: [], fastestLapInitials: "" },
|
||||
{ name: "Paul Ricard", results: [], fastestLapInitials: "" },
|
||||
{ name: "Silverstone", results: [], fastestLapInitials: "" },
|
||||
{ name: "Zandvoort", results: [], fastestLapInitials: "" },
|
||||
{ name: "Brands Hatch", results: [], fastestLapInitials: "" },
|
||||
{ name: "Circuit Ricardo Tormo (Valencia)", results: [], fastestLapInitials: "" },
|
||||
{ name: "Mount Panorama Circuit", results: [], fastestLapInitials: "" },
|
||||
{ name: "Autodromo Internazionale Enzo e Dino Ferrari - Imola", results: [], fastestLapInitials: "" },
|
||||
{ name: "Barcelona", results: [null, 0, 0], fastestLapInitials: "" },
|
||||
{ name: "Indianapolis", results: [], fastestLapInitials: "" },
|
||||
{ name: "WeatherTech Raceway Laguna Seca", results: [], fastestLapInitials: "" },
|
||||
{ name: "Zolder", results: [], fastestLapInitials: "" },
|
||||
{ name: "Suzuka Circuit", results: [], fastestLapInitials: "" },
|
||||
{ name: "Snetterton", results: [], fastestLapInitials: "" },
|
||||
{ name: "Spa-Francochamps", results: [], fastestLapInitials: "" },
|
||||
{ name: "Circuit of the Americas (COTA)", results: [], fastestLapInitials: "" },
|
||||
{ name: "Donington Park", results: [], fastestLapInitials: "" },
|
||||
{ name: "Monza", results: [], fastestLapInitials: "" },
|
||||
{ name: "Misano", results: [], fastestLapInitials: "" },
|
||||
{ name: "Kyalami Grand Prix Circuit", results: [], fastestLapInitials: "" },
|
||||
{ name: "Oulton Park", results: [], fastestLapInitials: "" },
|
||||
{ name: "Red Bull Ring", results: [], fastestLapInitials: "" },
|
||||
{ name: "Nürburgring", results: [], fastestLapInitials: "" }
|
||||
{ id: "track-hungaroring", name: "Hungaroring", results: [], fastestLapDriverId: "" },
|
||||
{ id: "track-watkins", name: "Watkins Glen", results: [], fastestLapDriverId: "" },
|
||||
{ id: "track-paulricard", name: "Paul Ricard", results: [], fastestLapDriverId: "" },
|
||||
{ id: "track-silverstone", name: "Silverstone", results: [], fastestLapDriverId: "" },
|
||||
{ id: "track-zandvoort", name: "Zandvoort", results: [], fastestLapDriverId: "" },
|
||||
{ id: "track-brands", name: "Brands Hatch", results: [], fastestLapDriverId: "" },
|
||||
{ id: "track-valencia", name: "Circuit Ricardo Tormo (Valencia)", results: [], fastestLapDriverId: "" },
|
||||
{ id: "track-bathurst", name: "Mount Panorama Circuit", results: [], fastestLapDriverId: "" },
|
||||
{ id: "track-imola", name: "Autodromo Internazionale Enzo e Dino Ferrari - Imola", results: [], fastestLapDriverId: "" },
|
||||
{ id: "track-barcelona", name: "Barcelona", results: [], fastestLapDriverId: "" },
|
||||
{ id: "track-indianapolis", name: "Indianapolis", results: [], fastestLapDriverId: "" },
|
||||
{ id: "track-laguna", name: "WeatherTech Raceway Laguna Seca", results: [], fastestLapDriverId: "" },
|
||||
{ id: "track-zolder", name: "Zolder", results: [], fastestLapDriverId: "" },
|
||||
{ id: "track-suzuka", name: "Suzuka Circuit", results: [], fastestLapDriverId: "" },
|
||||
{ id: "track-snetterton", name: "Snetterton", results: [], fastestLapDriverId: "" },
|
||||
{ id: "track-spa", name: "Spa-Francochamps", results: [], fastestLapDriverId: "" },
|
||||
{ id: "track-cota", name: "Circuit of the Americas (COTA)", results: [], fastestLapDriverId: "" },
|
||||
{ id: "track-donington", name: "Donington Park", results: [], fastestLapDriverId: "" },
|
||||
{ id: "track-monza", name: "Monza", results: [], fastestLapDriverId: "" },
|
||||
{ id: "track-misano", name: "Misano", results: [], fastestLapDriverId: "" },
|
||||
{ id: "track-kyalami", name: "Kyalami Grand Prix Circuit", results: [], fastestLapDriverId: "" },
|
||||
{ id: "track-oulton", name: "Oulton Park", results: [], fastestLapDriverId: "" },
|
||||
{ id: "track-redbull", name: "Red Bull Ring", results: [], fastestLapDriverId: "" },
|
||||
{ id: "track-nurburgring", name: "Nürburgring", results: [], fastestLapDriverId: "" }
|
||||
]
|
||||
};
|
||||
|
||||
@@ -11,6 +11,7 @@ export function findStyleByPoints(
|
||||
): PointStyle {
|
||||
return (
|
||||
appData.pointsTable.find((p) => p.points === points) || {
|
||||
id: "unknown",
|
||||
points: 0,
|
||||
label: "???",
|
||||
color: "#374151",
|
||||
@@ -25,20 +26,18 @@ export function calculateStandingsData(appData: AppData): DriverStanding[] {
|
||||
let totalPoints = 0;
|
||||
let cumulativePoints = 0;
|
||||
const raceHistory: RaceHistoryEntry[] = [];
|
||||
const driverInitials = driver.initials;
|
||||
|
||||
appData.tracks.forEach((track) => {
|
||||
let result = track.results[driverIndex];
|
||||
// Find result for this driver by driverId
|
||||
const raceResult = track.results.find((r) => r.driverId === driver.id);
|
||||
let result = raceResult?.score;
|
||||
// DNS fallback: if result is undefined, null, or empty, treat as DNS
|
||||
if (result === undefined || result === null || result === "") {
|
||||
result = "DNS";
|
||||
}
|
||||
let racePoints = parsePoints(result);
|
||||
if (
|
||||
track.fastestLapInitials &&
|
||||
track.fastestLapInitials === driverInitials
|
||||
) {
|
||||
racePoints += appData.flPoints.points ?? 1;
|
||||
if (track.fastestLapDriverId === driver.id) {
|
||||
racePoints += appData.flPoints ?? 1;
|
||||
}
|
||||
totalPoints += racePoints;
|
||||
cumulativePoints += racePoints;
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
export interface Driver {
|
||||
id: string;
|
||||
name: string;
|
||||
color: string;
|
||||
initials: string;
|
||||
}
|
||||
|
||||
export interface PointStyle {
|
||||
id?: string;
|
||||
points: number;
|
||||
label: string;
|
||||
color: string;
|
||||
@@ -12,10 +14,16 @@ export interface PointStyle {
|
||||
priority: number;
|
||||
}
|
||||
|
||||
export interface RaceResult {
|
||||
driverId: string;
|
||||
score: number | string | null | undefined;
|
||||
}
|
||||
|
||||
export interface Track {
|
||||
id: string;
|
||||
name: string;
|
||||
results: (number | string | null | undefined)[];
|
||||
fastestLapInitials: string;
|
||||
results: RaceResult[];
|
||||
fastestLapDriverId: string;
|
||||
}
|
||||
|
||||
export interface RaceHistoryEntry {
|
||||
@@ -34,8 +42,12 @@ export interface DriverStanding {
|
||||
}
|
||||
|
||||
export interface AppData {
|
||||
id?: string;
|
||||
serverId?: string;
|
||||
drivers: Driver[];
|
||||
pointsTable: PointStyle[];
|
||||
flPoints: PointStyle;
|
||||
flPoints: number;
|
||||
flColor: string;
|
||||
flTextColor: string;
|
||||
tracks: Track[];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user