diff --git a/package.json b/package.json
index 5d92989..b4c5d2e 100644
--- a/package.json
+++ b/package.json
@@ -21,7 +21,7 @@
"typecheck": "tsc --pretty --noEmit --incremental false"
},
"dependencies": {
- "axios": "^1.6.5",
+ "axios": "latest",
"clsx": "latest",
"jotai": "latest",
"jotai-effect": "latest",
diff --git a/src/app/lib/utils.tsx b/src/app/lib/utils.tsx
index e1fde80..966d4f8 100644
--- a/src/app/lib/utils.tsx
+++ b/src/app/lib/utils.tsx
@@ -40,64 +40,7 @@ export const f1Seasons = (): string[] => {
);
};
-export interface ISchedule {
- RoundNumber: number;
- Country: string;
- Location: string;
- OfficialEventName: string;
- EventDate: string;
- EventName: string;
- EventFormat: string;
- Session1: string;
- Session1Date: string;
- Session1DateUtc: string;
- Session2: string;
- Session2Date: string;
- Session2DateUtc: string;
- Session3: string;
- Session3Date: string;
- Session3DateUtc: string;
- Session4: string;
- Session4Date: string;
- Session4DateUtc: string;
- Session5: string;
- Session5Date: string;
- Session5DateUtc: string;
- F1ApiSupport: boolean;
-}
-
-// Raw Fetch Format
-export type IConstructorStandingsFetch = {
- [key in 'position' | 'points' | 'wins']: string;
-} & {
- Constructor: {
- name: string;
- };
-};
-// UI format
-export type IConstructorStandings = {
- [key in 'pos' | 'points' | 'wins' | 'name']: string;
-};
-
-interface IDataConfigs {
- seasons: string[];
- schedule: ISchedule[];
- drivers: string[];
- sessions: string[];
- standings: {
- // drivers: {
- // position: string,
- // points: string,
- // wins: string,
- // Constructor?: {
- // name: string,
- // }
- // }[],
- constructors: IConstructorStandingsFetch[];
- };
-}
-
-const dataConfig: IDataConfigs = {
+const dataConfig: DataConfigSchema = {
seasons: f1Seasons(),
schedule: Array.from(Array(3).keys()).map(() => ({
RoundNumber: 0,
@@ -134,21 +77,48 @@ const dataConfig: IDataConfigs = {
],
sessions: ['Practice 1', 'Practice 2', 'Practice 3', 'Qualifying', 'Race'],
standings: {
- // drivers: {
-
- // },
- constructors: Array.from(Array(10).keys()).map(() => ({
+ DriverStandings: Array.from(Array(5).keys()).map(() => ({
+ positionText: faker.number.int(20).toString(),
+ position: faker.number.int(20).toString(),
+ points: faker.number.int(25).toString(),
+ wins: faker.number.int(10).toString(),
+ Driver: {
+ driverId: faker.person.middleName(),
+ permanentNumber: faker.number.int(99).toString(),
+ code: faker.word.sample(3),
+ url: faker.internet.domainName(),
+ givenName: faker.person.firstName(),
+ familyName: faker.person.lastName(),
+ dateOfBirth: faker.date.birthdate().toString(),
+ nationality: faker.location.country(),
+ },
+ Constructors: [
+ {
+ constructorId: faker.person.middleName(),
+ url: faker.internet.domainName(),
+ name: faker.person.middleName(),
+ nationality: faker.location.country(),
+ },
+ ],
+ })),
+ ConstructorStandings: Array.from(Array(5).keys()).map(() => ({
+ positionText: faker.number.int(20).toString(),
position: faker.number.int(20).toString(),
points: faker.number.int(25).toString(),
wins: faker.number.int(10).toString(),
Constructor: {
+ constructorId: faker.person.middleName(),
+ url: faker.internet.domainName(),
name: faker.person.middleName(),
+ nationality: faker.location.country(),
},
})),
+ season: 0,
+ round: 0,
},
};
-const serverURL = 'http://0.0.0.0:8081';
+const serverURL = 'http://127.0.0.1:8081';
export const fetchAPI = async (
endpoint: string,
statusCheck: boolean = false,
@@ -158,7 +128,7 @@ export const fetchAPI = async (
const options = statusCheck ? { headers: { cache: 'no-store' } } : {};
// Get dummy data or return false
- const dummy: string[] | ISchedule[] | false =
+ const dummy: string[] | ScheduleSchema[] | false =
dataConfig[
endpoint.split('?')[0] as 'seasons' | 'schedule' | 'drivers' | 'sessions'
] || false;
diff --git a/src/app/results/RaceResults.tsx b/src/app/results/RaceResults.tsx
index 05501ea..52f1008 100644
--- a/src/app/results/RaceResults.tsx
+++ b/src/app/results/RaceResults.tsx
@@ -4,9 +4,7 @@ import { useMemo } from 'react';
import { seasonRacesAtom } from '@/atoms/results';
-import { ISchedule } from '../lib/utils';
-
-const ResultCard = ({ data }: { data: ISchedule }) => {
+const ResultCard = ({ data }: { data: ScheduleSchema }) => {
const eventDate = new Date(data.EventDate);
const eventPassed = new Date() > eventDate;
@@ -49,7 +47,7 @@ const ResultCard = ({ data }: { data: ISchedule }) => {
);
};
-const WinterTesting = ({ data }: { data: ISchedule }) => {
+const WinterTesting = ({ data }: { data: ScheduleSchema }) => {
const eventDate = new Date(data.EventDate);
const eventPassed = new Date() > eventDate;
diff --git a/src/app/results/SeasonResults.tsx b/src/app/results/SeasonResults.tsx
index bf4a2bc..15144f1 100644
--- a/src/app/results/SeasonResults.tsx
+++ b/src/app/results/SeasonResults.tsx
@@ -2,76 +2,33 @@
import { useAtom } from 'jotai';
-import { constructorStandingsAtom, fetchStandings } from '@/atoms/results';
+import {
+ constructorStandingsAtom,
+ driverStandingsAtom,
+ fetchStandings,
+} from '@/atoms/results';
import { RaceSchedule } from './RaceResults';
-import { driverData, DriverHeadings } from '../lib/placerholder-results';
-import { IConstructorStandings, positionEnding } from '../lib/utils';
-import { Table } from '../ui/Table';
+import { StandingsTimeline } from './StandingsTimeline';
import { Tabs } from '../ui/Tabs';
-const ConstuctorHeadings = ['position', 'points', 'wins', 'name'];
-
-const ConstructorCard = ({ data }: { data: IConstructorStandings }) => (
-
-
-
- {positionEnding(data.pos)} {data.name}
-
-
-
- Points:
-
- {data.points}
-
-
- Wins:
-
- {data.wins}
-
-
-
-
-);
-
-const ConstructorResults = () => {
- const [constructorStandings] = useAtom(constructorStandingsAtom);
+export default function ResultsPage() {
useAtom(fetchStandings);
+ const [constructorStandings] = useAtom(constructorStandingsAtom);
+ const [driverStandings] = useAtom(driverStandingsAtom);
- return (
- <>
-
- {constructorStandings.map((constructor) => (
-
- ))}
-
-
- >
- );
-};
-
-const tabHeaders = ['Races', 'Drivers', 'Constructors'];
-const tabs = [
- ,
- ,
-];
-
-export default function ResultsPage() {
return (
]}
+ headers={['Races', 'Drivers', 'Constructors']}
+ containers={[
+ ,
+ ,
+ ,
+ ]}
/>
);
diff --git a/src/app/results/StandingsTimeline.tsx b/src/app/results/StandingsTimeline.tsx
new file mode 100644
index 0000000..42ffa36
--- /dev/null
+++ b/src/app/results/StandingsTimeline.tsx
@@ -0,0 +1,137 @@
+import clsx from 'clsx';
+
+import { positionEnding } from '../lib/utils';
+
+export const StandingsTimeline = ({
+ data,
+}: {
+ data: DriverStandingSchema[] | ConstructorStandingSchema[];
+}) => {
+ return (
+
+ {data.map((standing, i: number) => {
+ const odd = i % 2 === 1;
+ return (
+ -
+ {/* ! Use Flex order ! Yay */}
+
+
+
+ {Object.prototype.hasOwnProperty.call(standing, 'Driver') && (
+
+ )}
+ {Object.prototype.hasOwnProperty.call(
+ standing,
+ 'Constructor',
+ ) && (
+
+ )}
+
+
+
+ );
+ })}
+
+ );
+};
+
+const DriverStandingInfo = ({
+ driver,
+ subEl = false,
+}: {
+ driver: DriverStandingSchema;
+ subEl?: boolean;
+}) => {
+ return (
+ <>
+ {/* Driver Standings */}
+ {driver.Driver && (
+
+ {driver.Driver.givenName} {driver.Driver.familyName}
+
+ )}
+ {!subEl && (
+
+ {driver.Constructors.map(({ name }) => (
+ {name}
+ ))}
+
+ )}
+ Points: {driver.points}
+ Wins: {driver.wins}
+ >
+ );
+};
+
+const ConstructorStandingInfo = ({
+ con,
+}: {
+ con: ConstructorStandingSchema;
+}) => {
+ return (
+ <>
+ {con.Constructor.name}
+ Points: {con.points}
+ Wins: {con.wins}
+
+
+ {con.Drivers &&
+ con.Drivers.map((driver) => (
+
+
+
+ ))}
+
+ >
+ );
+};
+
+const PositionMarker = ({
+ pos,
+ odd = false,
+}: {
+ pos: string;
+ odd?: boolean;
+}) => (
+
+);
+
+const TimelineMarker = () => (
+
+);
diff --git a/src/app/ui/Tabs.tsx b/src/app/ui/Tabs.tsx
index fa06c91..8ff3f2d 100644
--- a/src/app/ui/Tabs.tsx
+++ b/src/app/ui/Tabs.tsx
@@ -13,16 +13,24 @@ export const Tabs = ({ headers, containers }: ITabs) => {
const [tabIndex, setTabIndex] = useAtom(tabView);
// Active tab has matching tabIndex
- const TabButtons = headers.map((header, i) => (
- setTabIndex(i)}
- >
- {header}
-
- ));
+ const TabButtons = headers.map((header, i) => {
+ const switchTabs = () => {
+ setTabIndex(i);
+ window.scrollTo(0, 0);
+ };
+ return (
+
+ {header}
+
+ );
+ });
// Hide containers not matching tabIndex
const TabContainers = containers.map((tab, i) => (
@@ -39,8 +47,8 @@ export const Tabs = ({ headers, containers }: ITabs) => {
return (
{TabContainers}
-
-
+
diff --git a/src/atoms/results.tsx b/src/atoms/results.tsx
index 95c20b6..fdb2638 100644
--- a/src/atoms/results.tsx
+++ b/src/atoms/results.tsx
@@ -1,15 +1,10 @@
import { atom } from 'jotai';
import { atomEffect } from 'jotai-effect';
-import {
- fetchAPI,
- IConstructorStandings,
- IConstructorStandingsFetch,
- ISchedule,
-} from '../app/lib/utils';
+import { fetchAPI } from '../app/lib/utils';
export const raceAtom = atom('All Races');
-export const seasonRacesAtom = atom
([]);
+export const seasonRacesAtom = atom([]);
export const seasonAtom = atom('2023');
export const allSeasonsAtom = atom([]);
export const driverAtom = atom('All Drivers');
@@ -18,7 +13,8 @@ export const sessionAtom = atom('Race');
export const sessionsAtom = atom([]);
export const telemetryDisableAtom = atom(true);
export const resultUrlAtom = atom('/results');
-export const constructorStandingsAtom = atom([]);
+export const constructorStandingsAtom = atom([]);
+export const driverStandingsAtom = atom([]);
// Derived Atoms
@@ -50,28 +46,41 @@ export const fetchRaces = atomEffect((get, set) => {
});
// Get Driver per ...season & race
-export const fetchDriver = atomEffect((get, set) => {
+export const fetchDriver = atomEffect((_get, set) => {
fetchAPI('drivers').then((data) => set(driversAtom, data));
});
// Get sessions per ...season & race
-export const fetchSessions = atomEffect((get, set) => {
+export const fetchSessions = atomEffect((_get, set) => {
fetchAPI('sessions').then((data) => set(sessionsAtom, data));
});
// Get Driver & Constructor Standings
export const fetchStandings = atomEffect((get, set) => {
- fetchAPI('standings').then((data) => {
- // Flatten constructor values
- const constructors = data.constructors.map(
- (con: IConstructorStandingsFetch) => ({
- pos: con.position,
- name: con.Constructor.name,
- points: con.points,
- wins: con.wins,
- }),
- );
- set(constructorStandingsAtom, constructors);
- });
+ fetchAPI('standings').then(
+ ({
+ season,
+ DriverStandings,
+ ConstructorStandings,
+ }: DataConfigSchema['standings']) => {
+ if (season.toString() !== get(seasonAtom)) {
+ // Todo: Resolve error by setting the year with get(seasonAtom) in original fetch
+ return;
+ }
+
+ const constructors = ConstructorStandings.map((cs) => {
+ const { name } = cs.Constructor;
+ return {
+ ...cs,
+ Drivers: DriverStandings.filter((driver) =>
+ driver.Constructors.find((c) => c.name === name),
+ ),
+ };
+ });
+
+ set(constructorStandingsAtom, constructors);
+ set(driverStandingsAtom, DriverStandings);
+ },
+ );
});
// Telemetry is disabled if no race and driver are selected
diff --git a/src/results.d.ts b/src/results.d.ts
new file mode 100644
index 0000000..6520355
--- /dev/null
+++ b/src/results.d.ts
@@ -0,0 +1,75 @@
+interface ScheduleSchema {
+ RoundNumber: number;
+ Country: string;
+ Location: string;
+ OfficialEventName: string;
+ EventDate: string;
+ EventName: string;
+ EventFormat: string;
+ Session1: string;
+ Session1Date: string;
+ Session1DateUtc: string;
+ Session2: string;
+ Session2Date: string;
+ Session2DateUtc: string;
+ Session3: string;
+ Session3Date: string;
+ Session3DateUtc: string;
+ Session4: string;
+ Session4Date: string;
+ Session4DateUtc: string;
+ Session5: string;
+ Session5Date: string;
+ Session5DateUtc: string;
+ F1ApiSupport: boolean;
+}
+
+// Raw Fetch Format
+interface DriverSchema {
+ driverId: string;
+ permanentNumber: string;
+ code: string;
+ url: string;
+ givenName: string;
+ familyName: string;
+ dateOfBirth: string;
+ nationality: string;
+}
+
+interface ConstructorSchema {
+ constructorId: string;
+ url: string;
+ name: string;
+ nationality: string;
+}
+
+interface StandingsSchema {
+ position: string;
+ positionText: string;
+ points: string;
+ wins: string;
+}
+
+interface DriverStandingSchema extends StandingsSchema {
+ Constructors: ConstructorSchema[];
+ Driver: DriverSchema;
+}
+
+interface ConstructorStandingSchema extends StandingsSchema {
+ Constructor: ConstructorSchema;
+ Drivers?: DriverStandingSchema[];
+}
+
+// UI format
+interface DataConfigSchema {
+ seasons: string[];
+ schedule: ScheduleSchema[];
+ drivers: string[];
+ sessions: string[];
+ standings: {
+ season: number;
+ round: number;
+ DriverStandings: DriverStandingSchema[];
+ ConstructorStandings: ConstructorStandingSchema[];
+ };
+}