From 23550870e9415c7afad4ae4dc96825a1605bb4eb Mon Sep 17 00:00:00 2001 From: cris lombardo Date: Thu, 1 Feb 2024 21:50:01 -0500 Subject: [PATCH 1/7] refactor: main filter separation of concern --- src/app/page.tsx | 2 +- src/app/results/RaceResults.tsx | 2 +- src/app/results/SeasonResults.tsx | 6 +- src/app/results/[season]/page.tsx | 3 +- src/app/results/layout.tsx | 4 +- src/app/ui/Dropdown.tsx | 3 +- src/app/{ => ui}/MainFilters.tsx | 141 ++++++++++++------------ src/atoms/drivers.tsx | 17 +++ src/atoms/races.tsx | 43 ++++++++ src/atoms/results.tsx | 172 ++++++------------------------ src/atoms/seasons.tsx | 36 +++++++ src/atoms/sessions.tsx | 43 ++++++++ src/atoms/standings.tsx | 38 +++++++ 13 files changed, 286 insertions(+), 224 deletions(-) rename src/app/{ => ui}/MainFilters.tsx (62%) create mode 100644 src/atoms/drivers.tsx create mode 100644 src/atoms/races.tsx create mode 100644 src/atoms/seasons.tsx create mode 100644 src/atoms/sessions.tsx create mode 100644 src/atoms/standings.tsx diff --git a/src/app/page.tsx b/src/app/page.tsx index 2e55eae..fd0811b 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,4 +1,4 @@ -import { MainFilters } from './MainFilters'; +import { MainFilters } from './ui/MainFilters'; export default function Home() { return ( diff --git a/src/app/results/RaceResults.tsx b/src/app/results/RaceResults.tsx index 52f1008..8960bad 100644 --- a/src/app/results/RaceResults.tsx +++ b/src/app/results/RaceResults.tsx @@ -1,8 +1,8 @@ +import { seasonRacesAtom } from '@/atoms/races'; import { useAtom } from 'jotai'; import Image from 'next/image'; import { useMemo } from 'react'; -import { seasonRacesAtom } from '@/atoms/results'; const ResultCard = ({ data }: { data: ScheduleSchema }) => { const eventDate = new Date(data.EventDate); diff --git a/src/app/results/SeasonResults.tsx b/src/app/results/SeasonResults.tsx index 15144f1..f33030d 100644 --- a/src/app/results/SeasonResults.tsx +++ b/src/app/results/SeasonResults.tsx @@ -2,15 +2,11 @@ import { useAtom } from 'jotai'; -import { - constructorStandingsAtom, - driverStandingsAtom, - fetchStandings, -} from '@/atoms/results'; import { RaceSchedule } from './RaceResults'; import { StandingsTimeline } from './StandingsTimeline'; import { Tabs } from '../ui/Tabs'; +import { fetchStandings, constructorStandingsAtom, driverStandingsAtom } from '@/atoms/standings'; export default function ResultsPage() { useAtom(fetchStandings); diff --git a/src/app/results/[season]/page.tsx b/src/app/results/[season]/page.tsx index f5113bd..55728c5 100644 --- a/src/app/results/[season]/page.tsx +++ b/src/app/results/[season]/page.tsx @@ -2,8 +2,9 @@ import { useAtom } from 'jotai'; +import { handleSeasonChangeAtom } from '@/atoms/seasons'; + import ResultsPage from '../SeasonResults'; -import { handleSeasonChangeAtom } from '../../../atoms/results'; export default function Page({ params }: { params: { season: string } }) { const [, handleSeasonChange] = useAtom(handleSeasonChangeAtom); diff --git a/src/app/results/layout.tsx b/src/app/results/layout.tsx index 2910772..174c3ab 100644 --- a/src/app/results/layout.tsx +++ b/src/app/results/layout.tsx @@ -1,6 +1,6 @@ 'use client'; -import { MainFilters } from '../MainFilters'; +import { MainFilters } from '../ui/MainFilters'; // Default Next Layout export default function ResultsLayout({ @@ -10,7 +10,7 @@ export default function ResultsLayout({ }) { return ( <> -
+
{children} diff --git a/src/app/ui/Dropdown.tsx b/src/app/ui/Dropdown.tsx index 018c319..f526147 100644 --- a/src/app/ui/Dropdown.tsx +++ b/src/app/ui/Dropdown.tsx @@ -1,4 +1,5 @@ // 'use client'; +import clsx from 'clsx'; import React from 'react'; import { BsFillCaretDownFill } from 'react-icons/bs'; @@ -21,7 +22,7 @@ export const Dropdown = ({ value, items, action }: IDropdown) => {
{value} {items.length > 0 && }
diff --git a/src/app/MainFilters.tsx b/src/app/ui/MainFilters.tsx similarity index 62% rename from src/app/MainFilters.tsx rename to src/app/ui/MainFilters.tsx index 412e908..a29212b 100644 --- a/src/app/MainFilters.tsx +++ b/src/app/ui/MainFilters.tsx @@ -2,43 +2,77 @@ import { useAtom } from 'jotai/react'; import Link from 'next/link'; -import { useRouter } from 'next/navigation'; +import { usePathname, useRouter } from 'next/navigation'; import React from 'react'; -import { Dropdown } from './ui/Dropdown'; -import { - allSeasonsAtom, - driverAtom, - driversAtom, - fetchDriver, - fetchRaces, - fetchSeasons, - fetchSessions, - handleDriverChangeAtom, - handleRaceChangeAtom, - handleResultsAtom, - handleSeasonChangeAtom, - handleSessionChangeAtom, - raceAtom, - resultUrlAtom, - seasonAtom, - seasonRacesAtom, - sessionAtom, - sessionsAtom, - telemetryDisableAtom, - toggleTelemetryDisableAtom, -} from '../atoms/results'; - -type actionT = { - action: (url: string) => void; +import { Dropdown } from './Dropdown'; +import { driverAtom, allDriversAtom, handleDriverChangeAtom } from '@/atoms/drivers'; +import { raceAtom, handleRaceChangeAtom, seasonRacesAtom, fetchRaces } from '@/atoms/races'; +import { handleMainFilterSubmit, telemetryDisableAtom, toggleTelemetryDisableAtom } from '@/atoms/results'; +import { fetchSeasons, seasonAtom, handleSeasonChangeAtom, allSeasonsAtom } from '@/atoms/seasons'; +import { fetchSessionResults, sessionAtom, handleSessionChangeAtom, allSessionsAtom } from '@/atoms/sessions'; + +interface actionT { + action: (url: string) => void +}; + + +export const MainFilters = () => { + const router = useRouter(); + const pathname = usePathname() + + const [telemetryDisable] = useAtom(telemetryDisableAtom); + // const [resultsUrl] = useAtom(resultUrlAtom); + const [, handleResultsSubmit] = useAtom(handleMainFilterSubmit); + + useAtom(toggleTelemetryDisableAtom); + useAtom(fetchSeasons); + useAtom(fetchSessionResults) + + const changePath = (url: string) => { + // If not home page auto change page + if (pathname !== '/') router.push(url); + }; + + const handleSubmit = () => { + const url = handleResultsSubmit(); + router.push(url) + } + + return ( +
+
+ + +
+ +
+ + in + +
+ +
+ + +
+
+ ); }; const SeasonDropdown = ({ action }: actionT) => { + const [seasons] = useAtom(allSeasonsAtom); const [season] = useAtom(seasonAtom); const [, handleSeasonChange] = useAtom(handleSeasonChangeAtom); - const [seasons] = useAtom(allSeasonsAtom); const handleAction = (val: string) => { + // Submit new value get endpoint handleSeasonChange(val).then((url: string) => { action(url); }); @@ -76,7 +110,7 @@ const RaceDropdown = ({ action }: actionT) => { const DriverDropdown = ({ action }: actionT) => { const [driverName] = useAtom(driverAtom); const [, handleDriverChange] = useAtom(handleDriverChangeAtom); - const [driverList] = useAtom(driversAtom); + const [driverList] = useAtom(allDriversAtom); const handleAction = (val: string) => { handleDriverChange(val).then((url: string) => { @@ -84,15 +118,15 @@ const DriverDropdown = ({ action }: actionT) => { }); }; - useAtom(fetchDriver); + // useAtom(fetchDriver); return ( - + driver.FullName)} action={handleAction} /> ); }; const SessionDropdown = ({ action }: actionT) => { const [sessionName] = useAtom(sessionAtom); const [, handleSessionChange] = useAtom(handleSessionChangeAtom); - const [sessionList] = useAtom(sessionsAtom); + const [sessionList] = useAtom(allSessionsAtom); const handleAction = (val: string) => { handleSessionChange(val).then((url: string) => { @@ -100,48 +134,7 @@ const SessionDropdown = ({ action }: actionT) => { }); }; - useAtom(fetchSessions); return ( - - ); -}; -export const MainFilters = () => { - const router = useRouter(); - - useAtom(toggleTelemetryDisableAtom); - const [telemetryDisable] = useAtom(telemetryDisableAtom); - const [resultsUrl] = useAtom(resultUrlAtom); - const [, handleResultsClick] = useAtom(handleResultsAtom); - useAtom(fetchSeasons); - - const changePath = (url: string) => { - router.push(url); - }; - - return ( -
- {/* */} -
- - -
- -
- - in - -
- -
- - -
-
+ ); }; diff --git a/src/atoms/drivers.tsx b/src/atoms/drivers.tsx new file mode 100644 index 0000000..2983138 --- /dev/null +++ b/src/atoms/drivers.tsx @@ -0,0 +1,17 @@ +import { atom } from 'jotai'; +import { raceAtom } from './races'; +import { seasonAtom } from './seasons'; + +// Drivers +export const allDriversAtom = atom([]); +export const driverAtom = atom('All Drivers'); + +export const handleDriverChangeAtom = atom( + null, + async (get, set, update: string) => { + set(driverAtom, update); + + // return nagivation url + return '/results/' + get(seasonAtom) + '/' + get(raceAtom) + '/' + update; + }, +); diff --git a/src/atoms/races.tsx b/src/atoms/races.tsx new file mode 100644 index 0000000..f0226e9 --- /dev/null +++ b/src/atoms/races.tsx @@ -0,0 +1,43 @@ +import { fetchAPI, lastSession, sessionTitles } from '@/app/lib/utils'; +import { atom } from 'jotai'; +import { atomEffect } from 'jotai-effect'; +import { seasonAtom } from './seasons'; +import { driverAtom } from './drivers'; +import { allSessionsAtom, sessionAtom } from './sessions'; + +// Races +export const seasonRacesAtom = atom([]); +export const raceAtom = atom('All Races'); + +// Get Races per season +export const fetchRaces = atomEffect( + (get, set) => { + const params = get(seasonAtom) && `?year=${get(seasonAtom)}`; + fetchAPI('schedule' + params).then((data) => { + console.log('fetched schedule for season') + // Sync default year with server + set(seasonAtom, data.year); + set(seasonRacesAtom, data.EventSchedule); + }); + }, + // Dependencies: seasonAtom +); + +export const handleRaceChangeAtom = atom( + null, + async (get, set, raceEvent: ScheduleSchema) => { + // Reset Driver + set(driverAtom, 'All Drivers'); + + // Update race + set(raceAtom, raceEvent); + + // Parse race data to get session titles + set(allSessionsAtom, sessionTitles(raceEvent)); + // Set session to last session, ideally race + set(sessionAtom, lastSession(raceEvent)); + + // return navigation url + return '/results/' + get(seasonAtom) + '/' + raceEvent.Location; + }, +); diff --git a/src/atoms/results.tsx b/src/atoms/results.tsx index b7677fc..0467eea 100644 --- a/src/atoms/results.tsx +++ b/src/atoms/results.tsx @@ -1,78 +1,14 @@ import { atom } from 'jotai'; import { atomEffect } from 'jotai-effect'; -import { fetchAPI } from '../app/lib/utils'; +import { raceAtom } from './races'; +import { allDriversAtom, driverAtom } from './drivers'; +import { seasonAtom } from './seasons'; +import { allSessionsAtom, sessionAtom } from './sessions'; -export const raceAtom = atom('All Races'); -export const seasonRacesAtom = atom([]); -export const seasonAtom = atom('2023'); -export const allSeasonsAtom = atom([]); -export const driverAtom = atom('All Drivers'); -export const driversAtom = atom([]); -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 driverStandingsAtom = atom([]); - -// Effect Atoms -// Get Seasons values, this is done clientside -// Dependencies: seasonsAtom -export const fetchSeasons = atomEffect((get, set) => { - // Seasons do not change, only fetch if empty array - if (get(allSeasonsAtom).length <= 0) { - fetchAPI('seasons').then((data) => set(allSeasonsAtom, data)); - } -}); - -// Get Races per year -// Dependencies: seasonAtom -export const fetchRaces = atomEffect((get, set) => { - const params = get(seasonAtom) && `?year=${get(seasonAtom)}`; - fetchAPI('schedule' + params).then((data) => { - set(seasonRacesAtom, data); - }); -}); - -// Get Driver per ...season & race -export const fetchDriver = atomEffect((_get, set) => { - fetchAPI('drivers').then((data) => set(driversAtom, data)); -}); -// Get sessions per ...season & race -export const fetchSessions = atomEffect((_get, set) => { - fetchAPI('sessions').then((data) => set(sessionsAtom, data)); -}); - -// Get Driver & Constructor Standings -export const fetchStandings = atomEffect((get, set) => { - const year = get(seasonAtom) && `?year=${get(seasonAtom)}`; - const round = - typeof get(raceAtom) !== 'string' - ? `&round=${(get(raceAtom) as ScheduleSchema).RoundNumber}` - : ''; - fetchAPI('standings' + year + round).then( - ({ - DriverStandings, - ConstructorStandings, - }: DataConfigSchema['standings']) => { - // Include Drivers in Constructors Info - 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 Active +export const telemetryDisableAtom = atom(true); // Telemetry is disabled if no race and driver are selected export const toggleTelemetryDisableAtom = atomEffect((get, set) => { set( @@ -81,75 +17,33 @@ export const toggleTelemetryDisableAtom = atomEffect((get, set) => { ); }); -// Write only Atoms -// aka Update other atoms - -// Update Values when a season changes -export const handleSeasonChangeAtom = atom( - null, - async (get, set, update: string) => { - set(seasonAtom, update); - set(raceAtom, 'All Races'); - set(driverAtom, 'All Drivers'); - set(sessionAtom, 'Race'); - set(resultUrlAtom, '/results/' + update); - - return get(resultUrlAtom); - }, -); - -// Update Values when a Race changes -export const handleRaceChangeAtom = atom( - null, - async (get, set, update: ScheduleSchema) => { - set(raceAtom, update); - set(driverAtom, 'All Drivers'); - set( - resultUrlAtom, - '/results/' + get(seasonAtom) + '/' + update.RoundNumber, - ); - - return get(resultUrlAtom); - }, -); -// Update Values when a Driver changes -export const handleDriverChangeAtom = atom( - null, - async (get, set, update: string) => { - set(driverAtom, update); - // set(sessionAtom, 'Race'); - set( - resultUrlAtom, - '/results/' + get(seasonAtom) + '/' + get(raceAtom) + '/' + update, - ); - - return get(resultUrlAtom); - }, -); - -// Update Values when a session changes -export const handleSessionChangeAtom = atom( - null, - async (get, set, update: string) => { - set(sessionAtom, update); - set( - resultUrlAtom, - '/results/' + - get(seasonAtom) + - '/' + - get(raceAtom) + - '/' + - get(driverAtom) + - '/' + - update, - ); +export const handleMainFilterSubmit = atom(null, (get) => { + const url = ['/results', get(seasonAtom)]; + const race = get(raceAtom); + const driver = get(driverAtom); + + if (race === 'All Races') + return url.join('/'); + else + url.push(race.Location) + + if (driver === 'All Drivers') + return url.join('/'); + else { + const driverInfo = get(allDriversAtom).find(driver => driver.FullName === get(driverAtom)) + if (driverInfo) + url.push(driverInfo.DriverId) + else + return url.join('/'); + } - return get(resultUrlAtom); - }, -); + if (get(allSessionsAtom).length === 0) + return url.join('/'); + else { + const sessionIndex = get(allSessionsAtom).indexOf(get(sessionAtom)) + 1; + url.push(sessionIndex.toString()) + } -// Handle click of results button in -export const handleResultsAtom = atom(null, (_get, _set) => { - // set(seasonRacesAtom, get(seasonRacesAtom)); -}); + return url.join('/'); +}) \ No newline at end of file diff --git a/src/atoms/seasons.tsx b/src/atoms/seasons.tsx new file mode 100644 index 0000000..400f53d --- /dev/null +++ b/src/atoms/seasons.tsx @@ -0,0 +1,36 @@ +import { atom } from 'jotai'; +import { atomEffect } from 'jotai-effect'; +import { driverAtom } from './drivers'; +import { raceAtom } from './races'; +import { sessionAtom } from './sessions'; +import { f1Seasons } from '@/app/lib/fakerData'; + +// Seasons +export const allSeasonsAtom = atom([]); +export const seasonAtom = atom(''); + +// Get Seasons values, this is done clientside +export const fetchSeasons = atomEffect( + (get, set) => { + // Seasons do not change, only fetch if empty array + if (get(allSeasonsAtom).length <= 0) { + set(allSeasonsAtom, f1Seasons()); + } + }, + // Dependencies: allSeasonsAtom +); + +export const handleSeasonChangeAtom = atom( + null, + async (_get, set, season: string) => { + set(seasonAtom, season); + + // Reset other filter values + set(raceAtom, 'All Races'); + set(driverAtom, 'All Drivers'); + set(sessionAtom, 'Race'); + + // return navigation url + return '/results/' + season; + }, +); diff --git a/src/atoms/sessions.tsx b/src/atoms/sessions.tsx new file mode 100644 index 0000000..5e21f89 --- /dev/null +++ b/src/atoms/sessions.tsx @@ -0,0 +1,43 @@ +import { fetchAPI } from '@/app/lib/utils'; +import { atom } from 'jotai'; +import { atomEffect } from 'jotai-effect'; +import { allDriversAtom, driverAtom } from './drivers'; +import { raceAtom } from './races'; +import { seasonAtom } from './seasons'; + +// Sessions +export const allSessionsAtom = atom([]); +export const sessionAtom = atom('Race'); + +// Get session results +// Set allDriversAtom to drivers from session +export const fetchSessionResults = atomEffect((get, set) => { + const race = get(raceAtom); + + // Confirm race has been selected + if (race && race !== 'All Races') { + const sessions = get(sessionAtom); + let url = `results/${get(seasonAtom)}/${race.RoundNumber}`; + + // If sessions find round and add to url + if (sessions.length > 0) { + const sessionRound = get(allSessionsAtom).indexOf(get(sessionAtom)) + 1; + url += `?session=${sessionRound}`; + } + + fetchAPI(url).then((data) => { + set(allDriversAtom, data); + }); + } +}); + +export const handleSessionChangeAtom = atom( + null, + async (get, set, session: string) => { + set(sessionAtom, session); + + // return navigation url + return `/results/${get(seasonAtom)}/${get(raceAtom)}/ + ${get(driverAtom)}/${session}`; + }, +); diff --git a/src/atoms/standings.tsx b/src/atoms/standings.tsx new file mode 100644 index 0000000..70a1c12 --- /dev/null +++ b/src/atoms/standings.tsx @@ -0,0 +1,38 @@ +import { fetchAPI } from "@/app/lib/utils"; +import { atom } from "jotai"; +import { atomEffect } from "jotai-effect"; +import { raceAtom } from "./races"; +import { seasonAtom } from "./seasons"; + +// Cumulative Standings +export const constructorStandingsAtom = atom([]); +export const driverStandingsAtom = atom([]); + +// Get Driver & Constructor Standings +export const fetchStandings = atomEffect((get, set) => { + const year = get(seasonAtom) && `?year=${get(seasonAtom)}`; + const round = + typeof get(raceAtom) !== 'string' + ? `&round=${(get(raceAtom) as ScheduleSchema).RoundNumber}` + : ''; + fetchAPI('standings' + year + round).then( + ({ + DriverStandings, + ConstructorStandings, + }: DataConfigSchema['standings']) => { + // Include Drivers in Constructors Info + 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); + }, + ); + }); \ No newline at end of file From 98b76c562c5f11f9dd056e0d6818b3914c787f99 Mon Sep 17 00:00:00 2001 From: cris lombardo Date: Thu, 1 Feb 2024 21:54:42 -0500 Subject: [PATCH 2/7] chore: introduce race results data per driver --- src/app/lib/fakerData.tsx | 129 ++++++++++++++++++ src/app/lib/utils.tsx | 158 +++++++--------------- src/app/results/RaceTimeline.tsx | 134 ++++++++++++++++++ src/app/results/[season]/[round]/page.tsx | 22 ++- src/results.d.ts | 35 +++++ 5 files changed, 366 insertions(+), 112 deletions(-) create mode 100644 src/app/lib/fakerData.tsx create mode 100644 src/app/results/RaceTimeline.tsx diff --git a/src/app/lib/fakerData.tsx b/src/app/lib/fakerData.tsx new file mode 100644 index 0000000..0e5e5a5 --- /dev/null +++ b/src/app/lib/fakerData.tsx @@ -0,0 +1,129 @@ +import { faker } from "@faker-js/faker"; + +/** + * @description + * Get all possible seasons/years with results + * @return {*} {string[]} + */ +export const f1Seasons = (): string[] => { + const currDate = new Date(); + const currYear = currDate.getFullYear(); + + // Fill array with values between range + return Array.from({ length: currYear - 1950 + 1 }, (_v, index) => + (currYear - index).toString(), + ); +}; + + +const driverResults: DriverResult[] = Array.from(Array(10).keys()).map(() => ({ + DriverNumber: faker.number.int(99).toString(), + BroadcastName: faker.person.middleName('male'), + Abbreviation: faker.person.middleName('male').slice(0, 3), + DriverId: faker.person.lastName('male'), + TeamName: faker.person.firstName(), + TeamColor: faker.color.rgb(), + TeamId: faker.person.firstName(), + FirstName: faker.person.firstName('male'), + LastName: faker.person.lastName('male'), + FullName: faker.person.fullName(), + HeadshotUrl: faker.image.url({height: 93, width: 93}), + CountryCode: faker.location.countryCode(), + Position: faker.number.int(20), + ClassifiedPosition: faker.number.int(20).toString(), + GridPosition: faker.number.int(20), + Q1: null, + Q2: null, + Q3: null, + Time: faker.number.int(5222624), + Status: faker.helpers.arrayElement(['Finished', '+1 Lap', 'Retired']), + Points: faker.number.int(25) +})) + +export const dataConfig: DataConfigSchema = { + seasons: f1Seasons(), + schedule: Array.from(Array(3).keys()).map(() => ({ + RoundNumber: 0, + Country: faker.location.country(), + Location: faker.location.city(), + OfficialEventName: faker.word.words(5), + EventDate: faker.date.future({ years: 1 }).toString(), + EventName: faker.word.words(3), + EventFormat: 'string', + Session1: 'string', + Session1Date: faker.date.future({ years: 1 }).toString(), + Session1DateUtc: faker.date.future({ years: 1 }).toString(), + Session2: 'string', + Session2Date: faker.date.future({ years: 1 }).toString(), + Session2DateUtc: faker.date.future({ years: 1 }).toString(), + Session3: 'string', + Session3Date: faker.date.future({ years: 1 }).toString(), + Session3DateUtc: faker.date.future({ years: 1 }).toString(), + Session4: 'string', + Session4Date: faker.date.future({ years: 1 }).toString(), + Session4DateUtc: faker.date.future({ years: 1 }).toString(), + Session5: 'string', + Session5Date: faker.date.future({ years: 1 }).toString(), + Session5DateUtc: faker.date.future({ years: 1 }).toString(), + F1ApiSupport: true, + })), + drivers: [ + 'All Drivers', + 'Drive 1', + 'Drive 2', + 'Drive 3', + 'Drive 4', + 'Drive 5', + ], + sessions: [], + standings: { + 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, + }, + + results: { + drivers: driverResults, + constructors: Array.from(Array(5).keys()).map((_, i) => ({ + name: faker.person.firstName(), + points: faker.number.int(44), + position: faker.number.int(5), + drivers: driverResults.filter((_, filterIndex) => filterIndex % 5 === i) + })) + } + }; \ No newline at end of file diff --git a/src/app/lib/utils.tsx b/src/app/lib/utils.tsx index c83d26e..a43494e 100644 --- a/src/app/lib/utils.tsx +++ b/src/app/lib/utils.tsx @@ -1,6 +1,6 @@ -import { faker } from '@faker-js/faker'; import { serverUrl } from '../../constants'; +import { dataConfig } from './fakerData'; export const positionEnding = (position: number | string) => { // Convert to int @@ -12,112 +12,58 @@ export const positionEnding = (position: number | string) => { else return position + 'th'; }; -/** - * @description - * Get all possible seasons/years with results - * @return {*} {string[]} - */ -export const f1Seasons = (): string[] => { - // Discuss : Bump to fetch call to get data - // ! Alter to be dynamic - // const testingDate = new Date('02/22/2024'); - - const currDate = new Date(); - const currYear = currDate.getFullYear(); - - // Compare curr date to testing date (Feb 22 2024) - // If same year as testing and before testing date - // Get previous year - // if ( - // testingDate.getFullYear() === currYear && - // currDate.getTime() < testingDate.getTime() - // ) { - // currYear -= 1; - // } - - // Fill array with values between range - return Array.from({ length: currYear - 1950 + 1 }, (_v, index) => - (currYear - index).toString(), - ); -}; -const dataConfig: DataConfigSchema = { - seasons: f1Seasons(), - schedule: Array.from(Array(3).keys()).map(() => ({ - RoundNumber: 0, - Country: faker.location.country(), - Location: faker.location.city(), - OfficialEventName: faker.word.words(5), - EventDate: faker.date.future({ years: 1 }).toString(), - EventName: faker.word.words(3), - EventFormat: 'string', - Session1: 'string', - Session1Date: faker.date.future({ years: 1 }).toString(), - Session1DateUtc: faker.date.future({ years: 1 }).toString(), - Session2: 'string', - Session2Date: faker.date.future({ years: 1 }).toString(), - Session2DateUtc: faker.date.future({ years: 1 }).toString(), - Session3: 'string', - Session3Date: faker.date.future({ years: 1 }).toString(), - Session3DateUtc: faker.date.future({ years: 1 }).toString(), - Session4: 'string', - Session4Date: faker.date.future({ years: 1 }).toString(), - Session4DateUtc: faker.date.future({ years: 1 }).toString(), - Session5: 'string', - Session5Date: faker.date.future({ years: 1 }).toString(), - Session5DateUtc: faker.date.future({ years: 1 }).toString(), - F1ApiSupport: true, - })), - drivers: [ - 'All Drivers', - 'Drive 1', - 'Drive 2', - 'Drive 3', - 'Drive 4', - 'Drive 5', - ], - sessions: ['Practice 1', 'Practice 2', 'Practice 3', 'Qualifying', 'Race'], - standings: { - 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, - }, -}; +export const toFahrenheit = (temp: number) => { + return (temp * (9/5)) + 32 +} + +export const formatDuration = (durationInMilliseconds: number, timing = "full") => { + // Calculate hours, minutes, seconds, and milliseconds + const hours = Math.floor(durationInMilliseconds / 3600000); + const minutes = Math.floor((durationInMilliseconds % 3600000) / 60000); + const seconds = Math.floor((durationInMilliseconds % 60000) / 1000); + const milliseconds = durationInMilliseconds % 1000; + + // Pad single-digit values with leading zeros + const pad = (value: number) => { + return value < 10 ? "0" + value : value; + }; + + if (hours === 0 && minutes === 0 && seconds === 0 && milliseconds === 0) + return '-' + + // Format based on timing + if (timing === 'seconds' ) + return seconds + "." + pad(milliseconds); + + if (timing === 'minutes' ) + return minutes + ":" + pad(seconds) + "." + pad(milliseconds); + + if (timing === 'full') + return hours + ":" + pad(minutes) + ":" + pad(seconds) + "." + pad(milliseconds); +} + +export const sessionTitles = (event: ScheduleSchema) => { + console.log('event', event) + const titles: string[] = []; + for (let i = 1; i <= 5; i++) { + const key = `Session${i}` as keyof ScheduleSchema + event[key] && event[key] !== 'None' && titles.push(event[key] as string) + } + console.log('titles', titles) + + return titles; +} + +export const lastSession = (event: ScheduleSchema) => { + if (event.Session5 !== 'None') + return event.Session5; + else if (event.Session4 !== 'None') + return event.Session4; + else + return event.Session3; +} + export const fetchAPI = async ( endpoint: string, diff --git a/src/app/results/RaceTimeline.tsx b/src/app/results/RaceTimeline.tsx new file mode 100644 index 0000000..4b4ccfc --- /dev/null +++ b/src/app/results/RaceTimeline.tsx @@ -0,0 +1,134 @@ +import clsx from 'clsx'; + +import { positionEnding } from '../lib/utils'; + +export const RaceTimeline = ({ + data, +}: { + data: DriverStandingSchema[] | ConstructorStandingSchema[]; +}) => { + return ( +
    + {data.map((standing, i: number) => { + const odd = i % 2 === 1; + return ( +
  • + {i !== 0 &&
    } + {/* ! 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; +}) => ( +
+

{pos}

+
+); + +const TimelineMarker = () => ( +
+ + + +
+); diff --git a/src/app/results/[season]/[round]/page.tsx b/src/app/results/[season]/[round]/page.tsx index 999ab15..b006bbc 100644 --- a/src/app/results/[season]/[round]/page.tsx +++ b/src/app/results/[season]/[round]/page.tsx @@ -3,20 +3,30 @@ import { useAtom } from 'jotai'; import { Tabs } from '@/app/ui/Tabs'; -import { - constructorStandingsAtom, - driverStandingsAtom, - fetchStandings, -} from '@/atoms/results'; + // import { RaceSchedule } from '../../RaceResults'; import { StandingsTimeline } from '../../StandingsTimeline'; +import { fetchStandings, constructorStandingsAtom, driverStandingsAtom } from '@/atoms/standings'; +import { handleRaceChangeAtom, seasonRacesAtom } from '@/atoms/races'; -export default function ResultsPage() { +export default function ResultsPage({ params }: { params: { round: string } }) { useAtom(fetchStandings); const [constructorStandings] = useAtom(constructorStandingsAtom); const [driverStandings] = useAtom(driverStandingsAtom); + + const [, handleRaceChange] = useAtom(handleRaceChangeAtom); + const [allRaces] = useAtom(seasonRacesAtom) + // Get all + + // if (allRaces && allRaces.length > 0) { + // console.log('all races', allRaces[parseInt(params.round) - 1]) + + // handleRaceChange(allRaces[parseInt(params.round) - 1]); + // } + + return (
Date: Thu, 1 Feb 2024 22:05:00 -0500 Subject: [PATCH 3/7] refactor: proper linting --- src/app/globals.css | 10 +- src/app/layout.tsx | 2 +- src/app/lib/fakerData.tsx | 169 +++++++++++----------- src/app/lib/utils.tsx | 50 +++---- src/app/not-found.tsx | 11 ++ src/app/results/RaceResults.tsx | 2 +- src/app/results/SeasonResults.tsx | 6 +- src/app/results/StandingsTimeline.tsx | 38 ++--- src/app/results/[season]/[round]/page.tsx | 15 +- src/app/ui/Dropdown.tsx | 5 +- src/app/ui/MainFilters.tsx | 70 ++++++--- src/atoms/drivers.tsx | 1 + src/atoms/races.tsx | 7 +- src/atoms/results.tsx | 30 ++-- src/atoms/seasons.tsx | 4 +- src/atoms/sessions.tsx | 4 +- src/atoms/standings.tsx | 62 ++++---- src/results.d.ts | 14 +- tailwind.config.ts | 4 + tsconfig.json | 10 +- 20 files changed, 279 insertions(+), 235 deletions(-) create mode 100644 src/app/not-found.tsx diff --git a/src/app/globals.css b/src/app/globals.css index 6056993..59c8ba2 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -17,11 +17,7 @@ } body { - color: rgb(var(--foreground-rgb)); - background: linear-gradient( - to top, - transparent, - rgb(var(--background-end-rgb)) - ) - rgb(var(--background-start-rgb)); + color: oklch(var(--bc)); + background: linear-gradient(to top, transparent, oklch(var(--b1))) + oklch(var(--b2)); } diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 39a22db..008e700 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -25,7 +25,7 @@ export default async function RootLayout({ return ( { ); }; - const driverResults: DriverResult[] = Array.from(Array(10).keys()).map(() => ({ DriverNumber: faker.number.int(99).toString(), BroadcastName: faker.person.middleName('male'), @@ -27,7 +26,7 @@ const driverResults: DriverResult[] = Array.from(Array(10).keys()).map(() => ({ FirstName: faker.person.firstName('male'), LastName: faker.person.lastName('male'), FullName: faker.person.fullName(), - HeadshotUrl: faker.image.url({height: 93, width: 93}), + HeadshotUrl: faker.image.url({ height: 93, width: 93 }), CountryCode: faker.location.countryCode(), Position: faker.number.int(20), ClassifiedPosition: faker.number.int(20).toString(), @@ -37,93 +36,93 @@ const driverResults: DriverResult[] = Array.from(Array(10).keys()).map(() => ({ Q3: null, Time: faker.number.int(5222624), Status: faker.helpers.arrayElement(['Finished', '+1 Lap', 'Retired']), - Points: faker.number.int(25) -})) + Points: faker.number.int(25), +})); export const dataConfig: DataConfigSchema = { - seasons: f1Seasons(), - schedule: Array.from(Array(3).keys()).map(() => ({ - RoundNumber: 0, - Country: faker.location.country(), - Location: faker.location.city(), - OfficialEventName: faker.word.words(5), - EventDate: faker.date.future({ years: 1 }).toString(), - EventName: faker.word.words(3), - EventFormat: 'string', - Session1: 'string', - Session1Date: faker.date.future({ years: 1 }).toString(), - Session1DateUtc: faker.date.future({ years: 1 }).toString(), - Session2: 'string', - Session2Date: faker.date.future({ years: 1 }).toString(), - Session2DateUtc: faker.date.future({ years: 1 }).toString(), - Session3: 'string', - Session3Date: faker.date.future({ years: 1 }).toString(), - Session3DateUtc: faker.date.future({ years: 1 }).toString(), - Session4: 'string', - Session4Date: faker.date.future({ years: 1 }).toString(), - Session4DateUtc: faker.date.future({ years: 1 }).toString(), - Session5: 'string', - Session5Date: faker.date.future({ years: 1 }).toString(), - Session5DateUtc: faker.date.future({ years: 1 }).toString(), - F1ApiSupport: true, - })), - drivers: [ - 'All Drivers', - 'Drive 1', - 'Drive 2', - 'Drive 3', - 'Drive 4', - 'Drive 5', - ], - sessions: [], - standings: { - 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: { + seasons: f1Seasons(), + schedule: Array.from(Array(3).keys()).map(() => ({ + RoundNumber: 0, + Country: faker.location.country(), + Location: faker.location.city(), + OfficialEventName: faker.word.words(5), + EventDate: faker.date.future({ years: 1 }).toString(), + EventName: faker.word.words(3), + EventFormat: 'string', + Session1: 'string', + Session1Date: faker.date.future({ years: 1 }).toString(), + Session1DateUtc: faker.date.future({ years: 1 }).toString(), + Session2: 'string', + Session2Date: faker.date.future({ years: 1 }).toString(), + Session2DateUtc: faker.date.future({ years: 1 }).toString(), + Session3: 'string', + Session3Date: faker.date.future({ years: 1 }).toString(), + Session3DateUtc: faker.date.future({ years: 1 }).toString(), + Session4: 'string', + Session4Date: faker.date.future({ years: 1 }).toString(), + Session4DateUtc: faker.date.future({ years: 1 }).toString(), + Session5: 'string', + Session5Date: faker.date.future({ years: 1 }).toString(), + Session5DateUtc: faker.date.future({ years: 1 }).toString(), + F1ApiSupport: true, + })), + drivers: [ + 'All Drivers', + 'Drive 1', + 'Drive 2', + 'Drive 3', + 'Drive 4', + 'Drive 5', + ], + sessions: [], + standings: { + 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(), }, - })), - season: 0, - round: 0, - }, + ], + })), + 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, + }, - results: { - drivers: driverResults, - constructors: Array.from(Array(5).keys()).map((_, i) => ({ - name: faker.person.firstName(), - points: faker.number.int(44), - position: faker.number.int(5), - drivers: driverResults.filter((_, filterIndex) => filterIndex % 5 === i) - })) - } - }; \ No newline at end of file + results: { + drivers: driverResults, + constructors: Array.from(Array(5).keys()).map((_, i) => ({ + name: faker.person.firstName(), + points: faker.number.int(44), + position: faker.number.int(5), + drivers: driverResults.filter((_, filterIndex) => filterIndex % 5 === i), + })), + }, +}; diff --git a/src/app/lib/utils.tsx b/src/app/lib/utils.tsx index a43494e..3ddab3c 100644 --- a/src/app/lib/utils.tsx +++ b/src/app/lib/utils.tsx @@ -1,6 +1,5 @@ - -import { serverUrl } from '../../constants'; import { dataConfig } from './fakerData'; +import { serverUrl } from '../../constants'; export const positionEnding = (position: number | string) => { // Convert to int @@ -12,12 +11,14 @@ export const positionEnding = (position: number | string) => { else return position + 'th'; }; - export const toFahrenheit = (temp: number) => { - return (temp * (9/5)) + 32 -} + return temp * (9 / 5) + 32; +}; -export const formatDuration = (durationInMilliseconds: number, timing = "full") => { +export const formatDuration = ( + durationInMilliseconds: number, + timing = 'full', +) => { // Calculate hours, minutes, seconds, and milliseconds const hours = Math.floor(durationInMilliseconds / 3600000); const minutes = Math.floor((durationInMilliseconds % 3600000) / 60000); @@ -26,44 +27,39 @@ export const formatDuration = (durationInMilliseconds: number, timing = "full") // Pad single-digit values with leading zeros const pad = (value: number) => { - return value < 10 ? "0" + value : value; + return value < 10 ? '0' + value : value; }; if (hours === 0 && minutes === 0 && seconds === 0 && milliseconds === 0) - return '-' + return '-'; // Format based on timing - if (timing === 'seconds' ) - return seconds + "." + pad(milliseconds); + if (timing === 'seconds') return seconds + '.' + pad(milliseconds); - if (timing === 'minutes' ) - return minutes + ":" + pad(seconds) + "." + pad(milliseconds); + if (timing === 'minutes') + return minutes + ':' + pad(seconds) + '.' + pad(milliseconds); if (timing === 'full') - return hours + ":" + pad(minutes) + ":" + pad(seconds) + "." + pad(milliseconds); -} + return ( + hours + ':' + pad(minutes) + ':' + pad(seconds) + '.' + pad(milliseconds) + ); +}; export const sessionTitles = (event: ScheduleSchema) => { - console.log('event', event) const titles: string[] = []; for (let i = 1; i <= 5; i++) { - const key = `Session${i}` as keyof ScheduleSchema - event[key] && event[key] !== 'None' && titles.push(event[key] as string) + const key = `Session${i}` as keyof ScheduleSchema; + event[key] && event[key] !== 'None' && titles.push(event[key] as string); } - console.log('titles', titles) return titles; -} +}; export const lastSession = (event: ScheduleSchema) => { - if (event.Session5 !== 'None') - return event.Session5; - else if (event.Session4 !== 'None') - return event.Session4; - else - return event.Session3; -} - + if (event.Session5 !== 'None') return event.Session5; + else if (event.Session4 !== 'None') return event.Session4; + else return event.Session3; +}; export const fetchAPI = async ( endpoint: string, diff --git a/src/app/not-found.tsx b/src/app/not-found.tsx new file mode 100644 index 0000000..7c2b174 --- /dev/null +++ b/src/app/not-found.tsx @@ -0,0 +1,11 @@ +import Link from 'next/link'; + +export default function NotFound() { + return ( +
+

Not Found

+

Could not find requested resource

+ Return Home +
+ ); +} diff --git a/src/app/results/RaceResults.tsx b/src/app/results/RaceResults.tsx index 8960bad..ea7aec7 100644 --- a/src/app/results/RaceResults.tsx +++ b/src/app/results/RaceResults.tsx @@ -1,8 +1,8 @@ -import { seasonRacesAtom } from '@/atoms/races'; import { useAtom } from 'jotai'; import Image from 'next/image'; import { useMemo } from 'react'; +import { seasonRacesAtom } from '@/atoms/races'; const ResultCard = ({ data }: { data: ScheduleSchema }) => { const eventDate = new Date(data.EventDate); diff --git a/src/app/results/SeasonResults.tsx b/src/app/results/SeasonResults.tsx index f33030d..dcba954 100644 --- a/src/app/results/SeasonResults.tsx +++ b/src/app/results/SeasonResults.tsx @@ -2,11 +2,15 @@ import { useAtom } from 'jotai'; +import { + constructorStandingsAtom, + driverStandingsAtom, + fetchStandings, +} from '@/atoms/standings'; import { RaceSchedule } from './RaceResults'; import { StandingsTimeline } from './StandingsTimeline'; import { Tabs } from '../ui/Tabs'; -import { fetchStandings, constructorStandingsAtom, driverStandingsAtom } from '@/atoms/standings'; export default function ResultsPage() { useAtom(fetchStandings); diff --git a/src/app/results/StandingsTimeline.tsx b/src/app/results/StandingsTimeline.tsx index 45bb34b..bbbdef5 100644 --- a/src/app/results/StandingsTimeline.tsx +++ b/src/app/results/StandingsTimeline.tsx @@ -8,7 +8,7 @@ export const StandingsTimeline = ({ data: DriverStandingSchema[] | ConstructorStandingSchema[]; }) => { return ( -
    +
      {data.map((standing, i: number) => { const odd = i % 2 === 1; return ( @@ -21,12 +21,14 @@ export const StandingsTimeline = ({ > {i !== 0 &&
      } {/* ! Use Flex order ! Yay */} -
      {Object.prototype.hasOwnProperty.call(standing, 'Driver') && ( @@ -40,7 +42,7 @@ export const StandingsTimeline = ({ /> )}
      -
      + {i !== data.length - 1 &&
      } ); })} @@ -57,6 +59,10 @@ const DriverStandingInfo = ({ }) => { return ( <> +

      + {positionEnding(driver.position)} +

      + {/* Driver Standings */} {driver.Driver && (

      { return ( <> +

      + {positionEnding(con.position)} +

      +

      {con.Constructor.name}

      Points: {con.points}

      Wins: {con.wins}

      @@ -104,22 +114,6 @@ const ConstructorStandingInfo = ({ ); }; -const PositionMarker = ({ - pos, - odd = false, -}: { - pos: string; - odd?: boolean; -}) => ( -
      -

      {pos}

      -
      -); - const TimelineMarker = () => (
      ; // Get all // if (allRaces && allRaces.length > 0) { @@ -26,7 +30,6 @@ export default function ResultsPage({ params }: { params: { round: string } }) { // handleRaceChange(allRaces[parseInt(params.round) - 1]); // } - return (
      {
      {value} {items.length > 0 && }
      diff --git a/src/app/ui/MainFilters.tsx b/src/app/ui/MainFilters.tsx index a29212b..749f2f8 100644 --- a/src/app/ui/MainFilters.tsx +++ b/src/app/ui/MainFilters.tsx @@ -1,25 +1,48 @@ 'use client'; import { useAtom } from 'jotai/react'; -import Link from 'next/link'; +// import Link from 'next/link'; import { usePathname, useRouter } from 'next/navigation'; import React from 'react'; +import { + allDriversAtom, + driverAtom, + handleDriverChangeAtom, +} from '@/atoms/drivers'; +import { + fetchRaces, + handleRaceChangeAtom, + raceAtom, + seasonRacesAtom, +} from '@/atoms/races'; +import { + handleMainFilterSubmit, + telemetryDisableAtom, + toggleTelemetryDisableAtom, +} from '@/atoms/results'; +import { + allSeasonsAtom, + fetchSeasons, + handleSeasonChangeAtom, + seasonAtom, +} from '@/atoms/seasons'; +import { + allSessionsAtom, + fetchSessionResults, + handleSessionChangeAtom, + sessionAtom, +} from '@/atoms/sessions'; + import { Dropdown } from './Dropdown'; -import { driverAtom, allDriversAtom, handleDriverChangeAtom } from '@/atoms/drivers'; -import { raceAtom, handleRaceChangeAtom, seasonRacesAtom, fetchRaces } from '@/atoms/races'; -import { handleMainFilterSubmit, telemetryDisableAtom, toggleTelemetryDisableAtom } from '@/atoms/results'; -import { fetchSeasons, seasonAtom, handleSeasonChangeAtom, allSeasonsAtom } from '@/atoms/seasons'; -import { fetchSessionResults, sessionAtom, handleSessionChangeAtom, allSessionsAtom } from '@/atoms/sessions'; interface actionT { - action: (url: string) => void -}; - + action: (url: string) => void; +} export const MainFilters = () => { const router = useRouter(); - const pathname = usePathname() + const pathname = usePathname(); const [telemetryDisable] = useAtom(telemetryDisableAtom); // const [resultsUrl] = useAtom(resultUrlAtom); @@ -27,7 +50,7 @@ export const MainFilters = () => { useAtom(toggleTelemetryDisableAtom); useAtom(fetchSeasons); - useAtom(fetchSessionResults) + useAtom(fetchSessionResults); const changePath = (url: string) => { // If not home page auto change page @@ -36,8 +59,8 @@ export const MainFilters = () => { const handleSubmit = () => { const url = handleResultsSubmit(); - router.push(url) - } + router.push(url); + }; return (
      @@ -54,11 +77,12 @@ export const MainFilters = () => { @@ -120,7 +144,11 @@ const DriverDropdown = ({ action }: actionT) => { // useAtom(fetchDriver); return ( - driver.FullName)} action={handleAction} /> + driver.FullName)} + action={handleAction} + /> ); }; const SessionDropdown = ({ action }: actionT) => { @@ -135,6 +163,10 @@ const SessionDropdown = ({ action }: actionT) => { }; return ( - + ); }; diff --git a/src/atoms/drivers.tsx b/src/atoms/drivers.tsx index 2983138..60e61ae 100644 --- a/src/atoms/drivers.tsx +++ b/src/atoms/drivers.tsx @@ -1,4 +1,5 @@ import { atom } from 'jotai'; + import { raceAtom } from './races'; import { seasonAtom } from './seasons'; diff --git a/src/atoms/races.tsx b/src/atoms/races.tsx index f0226e9..25b61b8 100644 --- a/src/atoms/races.tsx +++ b/src/atoms/races.tsx @@ -1,8 +1,10 @@ -import { fetchAPI, lastSession, sessionTitles } from '@/app/lib/utils'; import { atom } from 'jotai'; import { atomEffect } from 'jotai-effect'; -import { seasonAtom } from './seasons'; + +import { fetchAPI, lastSession, sessionTitles } from '@/app/lib/utils'; + import { driverAtom } from './drivers'; +import { seasonAtom } from './seasons'; import { allSessionsAtom, sessionAtom } from './sessions'; // Races @@ -14,7 +16,6 @@ export const fetchRaces = atomEffect( (get, set) => { const params = get(seasonAtom) && `?year=${get(seasonAtom)}`; fetchAPI('schedule' + params).then((data) => { - console.log('fetched schedule for season') // Sync default year with server set(seasonAtom, data.year); set(seasonRacesAtom, data.EventSchedule); diff --git a/src/atoms/results.tsx b/src/atoms/results.tsx index 0467eea..8ee1ea8 100644 --- a/src/atoms/results.tsx +++ b/src/atoms/results.tsx @@ -1,12 +1,11 @@ import { atom } from 'jotai'; import { atomEffect } from 'jotai-effect'; -import { raceAtom } from './races'; import { allDriversAtom, driverAtom } from './drivers'; +import { raceAtom } from './races'; import { seasonAtom } from './seasons'; import { allSessionsAtom, sessionAtom } from './sessions'; - // Telemetry Active export const telemetryDisableAtom = atom(true); // Telemetry is disabled if no race and driver are selected @@ -17,33 +16,28 @@ export const toggleTelemetryDisableAtom = atomEffect((get, set) => { ); }); - export const handleMainFilterSubmit = atom(null, (get) => { const url = ['/results', get(seasonAtom)]; const race = get(raceAtom); const driver = get(driverAtom); - if (race === 'All Races') - return url.join('/'); - else - url.push(race.Location) + if (race === 'All Races') return url.join('/'); + else url.push(race.Location); - if (driver === 'All Drivers') - return url.join('/'); + if (driver === 'All Drivers') return url.join('/'); else { - const driverInfo = get(allDriversAtom).find(driver => driver.FullName === get(driverAtom)) - if (driverInfo) - url.push(driverInfo.DriverId) - else - return url.join('/'); + const driverInfo = get(allDriversAtom).find( + (driver) => driver.FullName === get(driverAtom), + ); + if (driverInfo) url.push(driverInfo.DriverId); + else return url.join('/'); } - if (get(allSessionsAtom).length === 0) - return url.join('/'); + if (get(allSessionsAtom).length === 0) return url.join('/'); else { const sessionIndex = get(allSessionsAtom).indexOf(get(sessionAtom)) + 1; - url.push(sessionIndex.toString()) + url.push(sessionIndex.toString()); } return url.join('/'); -}) \ No newline at end of file +}); diff --git a/src/atoms/seasons.tsx b/src/atoms/seasons.tsx index 400f53d..b10acc7 100644 --- a/src/atoms/seasons.tsx +++ b/src/atoms/seasons.tsx @@ -1,9 +1,11 @@ import { atom } from 'jotai'; import { atomEffect } from 'jotai-effect'; + +import { f1Seasons } from '@/app/lib/fakerData'; + import { driverAtom } from './drivers'; import { raceAtom } from './races'; import { sessionAtom } from './sessions'; -import { f1Seasons } from '@/app/lib/fakerData'; // Seasons export const allSeasonsAtom = atom([]); diff --git a/src/atoms/sessions.tsx b/src/atoms/sessions.tsx index 5e21f89..59d8fa3 100644 --- a/src/atoms/sessions.tsx +++ b/src/atoms/sessions.tsx @@ -1,6 +1,8 @@ -import { fetchAPI } from '@/app/lib/utils'; import { atom } from 'jotai'; import { atomEffect } from 'jotai-effect'; + +import { fetchAPI } from '@/app/lib/utils'; + import { allDriversAtom, driverAtom } from './drivers'; import { raceAtom } from './races'; import { seasonAtom } from './seasons'; diff --git a/src/atoms/standings.tsx b/src/atoms/standings.tsx index 70a1c12..2c528f1 100644 --- a/src/atoms/standings.tsx +++ b/src/atoms/standings.tsx @@ -1,8 +1,10 @@ -import { fetchAPI } from "@/app/lib/utils"; -import { atom } from "jotai"; -import { atomEffect } from "jotai-effect"; -import { raceAtom } from "./races"; -import { seasonAtom } from "./seasons"; +import { atom } from 'jotai'; +import { atomEffect } from 'jotai-effect'; + +import { fetchAPI } from '@/app/lib/utils'; + +import { raceAtom } from './races'; +import { seasonAtom } from './seasons'; // Cumulative Standings export const constructorStandingsAtom = atom([]); @@ -10,29 +12,29 @@ export const driverStandingsAtom = atom([]); // Get Driver & Constructor Standings export const fetchStandings = atomEffect((get, set) => { - const year = get(seasonAtom) && `?year=${get(seasonAtom)}`; - const round = - typeof get(raceAtom) !== 'string' - ? `&round=${(get(raceAtom) as ScheduleSchema).RoundNumber}` - : ''; - fetchAPI('standings' + year + round).then( - ({ - DriverStandings, - ConstructorStandings, - }: DataConfigSchema['standings']) => { - // Include Drivers in Constructors Info - const constructors = ConstructorStandings.map((cs) => { - const { name } = cs.Constructor; - return { - ...cs, - Drivers: DriverStandings.filter((driver) => - driver.Constructors.find((c) => c.name === name), - ), - }; - }); + const year = get(seasonAtom) && `?year=${get(seasonAtom)}`; + const round = + typeof get(raceAtom) !== 'string' + ? `&round=${(get(raceAtom) as ScheduleSchema).RoundNumber}` + : ''; + fetchAPI('standings' + year + round).then( + ({ + DriverStandings, + ConstructorStandings, + }: DataConfigSchema['standings']) => { + // Include Drivers in Constructors Info + 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); - }, - ); - }); \ No newline at end of file + set(constructorStandingsAtom, constructors); + set(driverStandingsAtom, DriverStandings); + }, + ); +}); diff --git a/src/results.d.ts b/src/results.d.ts index 2a733fd..bee5304 100644 --- a/src/results.d.ts +++ b/src/results.d.ts @@ -85,10 +85,10 @@ interface DriverResult { } interface ConstructorResult { - name: string, - position: number - points: number - drivers: DriverResult[] + name: string; + position: number; + points: number; + drivers: DriverResult[]; } // UI format @@ -104,7 +104,7 @@ interface DataConfigSchema { ConstructorStandings: ConstructorStandingSchema[]; }; results: { - drivers: DriverResult[] - constructors: ConstructorResult[] - } + drivers: DriverResult[]; + constructors: ConstructorResult[]; + }; } diff --git a/tailwind.config.ts b/tailwind.config.ts index bd500b1..1940809 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -19,5 +19,9 @@ const config: Config = { }, plugins: [require('daisyui')], + // daisyui: { + // themes: ['emerald', 'synthwave'], + // darkTheme: "synthwave", + // } }; export default config; diff --git a/tsconfig.json b/tsconfig.json index 4b8fa2e..e59724b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -15,13 +15,13 @@ "incremental": true, "plugins": [ { - "name": "next", - }, + "name": "next" + } ], "paths": { - "@/*": ["./src/*"], - }, + "@/*": ["./src/*"] + } }, "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], - "exclude": ["node_modules"], + "exclude": ["node_modules"] } From 5346c6eec76aca0c9b635610949244ee69e7e08f Mon Sep 17 00:00:00 2001 From: cris lombardo Date: Fri, 2 Feb 2024 16:53:38 -0500 Subject: [PATCH 4/7] refactor: migrate lib out of app router --- src/{app => }/lib/fakerData.tsx | 0 src/{app => }/lib/placerholder-results.tsx | 0 src/{app => }/lib/utils.tsx | 60 +++++++++++++++------- 3 files changed, 42 insertions(+), 18 deletions(-) rename src/{app => }/lib/fakerData.tsx (100%) rename src/{app => }/lib/placerholder-results.tsx (100%) rename src/{app => }/lib/utils.tsx (77%) diff --git a/src/app/lib/fakerData.tsx b/src/lib/fakerData.tsx similarity index 100% rename from src/app/lib/fakerData.tsx rename to src/lib/fakerData.tsx diff --git a/src/app/lib/placerholder-results.tsx b/src/lib/placerholder-results.tsx similarity index 100% rename from src/app/lib/placerholder-results.tsx rename to src/lib/placerholder-results.tsx diff --git a/src/app/lib/utils.tsx b/src/lib/utils.tsx similarity index 77% rename from src/app/lib/utils.tsx rename to src/lib/utils.tsx index 3ddab3c..2b65655 100644 --- a/src/app/lib/utils.tsx +++ b/src/lib/utils.tsx @@ -1,5 +1,5 @@ import { dataConfig } from './fakerData'; -import { serverUrl } from '../../constants'; +import { serverUrl } from '../constants'; export const positionEnding = (position: number | string) => { // Convert to int @@ -15,34 +15,58 @@ export const toFahrenheit = (temp: number) => { return temp * (9 / 5) + 32; }; +export const fastestLap = (position: number, points: number) => { + switch (position) { + case 1: + return points !== 25; + case 2: + return points !== 18; + case 3: + return points !== 15; + case 4: + return points !== 12; + case 5: + return points !== 10; + case 6: + return points !== 8; + case 7: + return points !== 6; + case 8: + return points !== 4; + case 9: + return points !== 2; + case 10: + return points !== 1; + + default: + return false; + } +}; + export const formatDuration = ( durationInMilliseconds: number, - timing = 'full', ) => { + // Pad single-digit values with leading zeros + const pad = (value: number) => { + return value < 10 ? '0' + value : value; + }; + // Calculate hours, minutes, seconds, and milliseconds const hours = Math.floor(durationInMilliseconds / 3600000); const minutes = Math.floor((durationInMilliseconds % 3600000) / 60000); const seconds = Math.floor((durationInMilliseconds % 60000) / 1000); const milliseconds = durationInMilliseconds % 1000; - // Pad single-digit values with leading zeros - const pad = (value: number) => { - return value < 10 ? '0' + value : value; - }; - if (hours === 0 && minutes === 0 && seconds === 0 && milliseconds === 0) return '-'; - - // Format based on timing - if (timing === 'seconds') return seconds + '.' + pad(milliseconds); - - if (timing === 'minutes') - return minutes + ':' + pad(seconds) + '.' + pad(milliseconds); - - if (timing === 'full') - return ( - hours + ':' + pad(minutes) + ':' + pad(seconds) + '.' + pad(milliseconds) - ); + else if (hours === 0 && minutes === 0 && seconds === 0) + return '0.' + pad(milliseconds); + else if (hours === 0 && minutes === 0) + return seconds + '.' + pad(milliseconds) + else if (hours === 0) + return minutes + ':' + pad(seconds) + '.' + pad(milliseconds) + else + return hours + ':' + pad(minutes) + ':' + pad(seconds) + '.' + pad(milliseconds) }; export const sessionTitles = (event: ScheduleSchema) => { From 9ee1d43bd2abc258a018e9ecb80802fe7857b6d9 Mon Sep 17 00:00:00 2001 From: cris lombardo Date: Fri, 2 Feb 2024 16:57:15 -0500 Subject: [PATCH 5/7] chore: successfully test results endpoint --- src/app/(features)/RaceTimeline.tsx | 68 ++++++++++++++ src/app/[season]/[location]/page.tsx | 103 ++++++++++++++++++++ src/app/results/RaceTimeline.tsx | 134 --------------------------- src/atoms/constructors.tsx | 4 + src/atoms/drivers.tsx | 2 +- src/atoms/races.tsx | 5 +- src/atoms/results.tsx | 9 +- src/atoms/seasons.tsx | 4 +- src/atoms/sessions.tsx | 66 ++++++++++++- src/atoms/standings.tsx | 4 +- 10 files changed, 252 insertions(+), 147 deletions(-) create mode 100644 src/app/(features)/RaceTimeline.tsx create mode 100644 src/app/[season]/[location]/page.tsx delete mode 100644 src/app/results/RaceTimeline.tsx create mode 100644 src/atoms/constructors.tsx diff --git a/src/app/(features)/RaceTimeline.tsx b/src/app/(features)/RaceTimeline.tsx new file mode 100644 index 0000000..7d55857 --- /dev/null +++ b/src/app/(features)/RaceTimeline.tsx @@ -0,0 +1,68 @@ +import clsx from 'clsx'; +import { BsFillStarFill } from 'react-icons/bs'; + +import { fastestLap, formatDuration, positionEnding } from '../../lib/utils'; + +export const DriverResultsInfo = ({ + driver, + subEl = false, +}: { + driver: DriverResult; + subEl?: boolean; +}) => { + const durationPlus = driver.Position !== 1 ? '+' : ''; + return ( + <> + {/* Driver Standings */} +

      + {positionEnding(driver.Position)} - {driver.Points} points + {fastestLap(driver.Position, driver.Points) && } +

      +
      +
      +

      + {driver.FullName} +

      +

      + {driver.TeamName} +

      +
      +
      + {!subEl && ( +

      + {driver.Time ? durationPlus + formatDuration(driver.Time) : driver.Status} +

      + )} + + ); +}; + +export const ConstructorResultsInfo = ({ con }: { con: ConstructorResult }) => { + return ( + <> +

      + {positionEnding(con.position)} - {con.points} points +

      +

      {con.name}

      +
      +
      + {con.drivers && + con.drivers.map((driver: DriverResult) => ( +
      + +
      + ))} +
      + + ); +}; diff --git a/src/app/[season]/[location]/page.tsx b/src/app/[season]/[location]/page.tsx new file mode 100644 index 0000000..d353e03 --- /dev/null +++ b/src/app/[season]/[location]/page.tsx @@ -0,0 +1,103 @@ +'use client'; + +import { useAtom } from 'jotai'; + +import { ConstructorResultsInfo, DriverResultsInfo } from '@/app/(features)/RaceTimeline'; +import { Tabs } from '@/app/ui/Tabs'; +import { Timeline, TimelineElement } from '@/app/ui/Timeline'; +import { allDriversAtom } from '@/atoms/drivers'; +import { fetchSessionResults } from '@/atoms/sessions'; +import { + constructorStandingsAtom, + driverStandingsAtom, + fetchStandings, +} from '@/atoms/standings'; +import { allConstructorAtom } from '@/atoms/constructors'; +import { useParams } from 'next/navigation'; +import { useRouter } from 'next/router'; +import { raceAtom, seasonRacesAtom } from '@/atoms/races'; +import { useEffect } from 'react'; + +// import { RaceSchedule } from '../../RaceResults'; +// import { handleRaceChangeAtom, seasonRacesAtom } from '@/atoms/races'; + +export default function ResultsPage({ params }: { params: { location: string } }) { + + + const [races] = useAtom(seasonRacesAtom) + const [_, setRace] = useAtom(raceAtom) + + useEffect(() => { + setRace(races.find(race => race.Location === params.location) || 'All Races') + }, [races]) + // useAtom(fetchStandings); + // useAtom(fetchSessionResults); + useAtom(fetchSessionResults); + + const [constructorStandings] = useAtom(constructorStandingsAtom); + const [driverStandings] = useAtom(driverStandingsAtom); + const [drivers] = useAtom(allDriversAtom); + const [constructors] = useAtom(allConstructorAtom); + + console.log('data', [drivers, constructors]) + // const [driverStandings] = useAtom(driverStandingsAtom); + + // const [, handleRaceChange] = useAtom(handleRaceChangeAtom); + // const [allRaces] = useAtom(seasonRacesAtom) + + // if (!params) return <>; + // Get all + + // if (allRaces && allRaces.length > 0) { + // console.log('all races', allRaces[parseInt(params.round) - 1]) + + // handleRaceChange(allRaces[parseInt(params.round) - 1]); + // } + + return ( +
      + + {drivers.map((driver, index, allDrivers) => ( + + + + ))} + , + + {constructors.map((con, index, allConstructors) => ( + + + + ))} + , + ]} + /> +
      + ); +} + +// +// {constructorStandings.map((constructor, index, allConstructors) => ( +// +// +// +// ))} +// diff --git a/src/app/results/RaceTimeline.tsx b/src/app/results/RaceTimeline.tsx deleted file mode 100644 index 4b4ccfc..0000000 --- a/src/app/results/RaceTimeline.tsx +++ /dev/null @@ -1,134 +0,0 @@ -import clsx from 'clsx'; - -import { positionEnding } from '../lib/utils'; - -export const RaceTimeline = ({ - data, -}: { - data: DriverStandingSchema[] | ConstructorStandingSchema[]; -}) => { - return ( -
        - {data.map((standing, i: number) => { - const odd = i % 2 === 1; - return ( -
      • - {i !== 0 &&
        } - {/* ! 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; -}) => ( -
      -

      {pos}

      -
      -); - -const TimelineMarker = () => ( -
      - - - -
      -); diff --git a/src/atoms/constructors.tsx b/src/atoms/constructors.tsx new file mode 100644 index 0000000..6eeed99 --- /dev/null +++ b/src/atoms/constructors.tsx @@ -0,0 +1,4 @@ +import { atom } from "jotai"; + +// Constructor Results +export const allConstructorAtom = atom([]); \ No newline at end of file diff --git a/src/atoms/drivers.tsx b/src/atoms/drivers.tsx index 60e61ae..fb9bcd5 100644 --- a/src/atoms/drivers.tsx +++ b/src/atoms/drivers.tsx @@ -13,6 +13,6 @@ export const handleDriverChangeAtom = atom( set(driverAtom, update); // return nagivation url - return '/results/' + get(seasonAtom) + '/' + get(raceAtom) + '/' + update; + return '/' + get(seasonAtom) + '/' + get(raceAtom) + '/' + update; }, ); diff --git a/src/atoms/races.tsx b/src/atoms/races.tsx index 25b61b8..5443f84 100644 --- a/src/atoms/races.tsx +++ b/src/atoms/races.tsx @@ -1,7 +1,7 @@ import { atom } from 'jotai'; import { atomEffect } from 'jotai-effect'; -import { fetchAPI, lastSession, sessionTitles } from '@/app/lib/utils'; +import { fetchAPI, lastSession, sessionTitles } from '@/lib/utils'; import { driverAtom } from './drivers'; import { seasonAtom } from './seasons'; @@ -17,6 +17,7 @@ export const fetchRaces = atomEffect( const params = get(seasonAtom) && `?year=${get(seasonAtom)}`; fetchAPI('schedule' + params).then((data) => { // Sync default year with server + set(seasonAtom, data.year); set(seasonRacesAtom, data.EventSchedule); }); @@ -39,6 +40,6 @@ export const handleRaceChangeAtom = atom( set(sessionAtom, lastSession(raceEvent)); // return navigation url - return '/results/' + get(seasonAtom) + '/' + raceEvent.Location; + return '/' + get(seasonAtom) + '/' + raceEvent.Location; }, ); diff --git a/src/atoms/results.tsx b/src/atoms/results.tsx index 8ee1ea8..1d4f816 100644 --- a/src/atoms/results.tsx +++ b/src/atoms/results.tsx @@ -5,6 +5,7 @@ import { allDriversAtom, driverAtom } from './drivers'; import { raceAtom } from './races'; import { seasonAtom } from './seasons'; import { allSessionsAtom, sessionAtom } from './sessions'; +import { useParams } from 'next/navigation'; // Telemetry Active export const telemetryDisableAtom = atom(true); @@ -17,7 +18,7 @@ export const toggleTelemetryDisableAtom = atomEffect((get, set) => { }); export const handleMainFilterSubmit = atom(null, (get) => { - const url = ['/results', get(seasonAtom)]; + const url = [get(seasonAtom)]; const race = get(raceAtom); const driver = get(driverAtom); @@ -38,6 +39,10 @@ export const handleMainFilterSubmit = atom(null, (get) => { const sessionIndex = get(allSessionsAtom).indexOf(get(sessionAtom)) + 1; url.push(sessionIndex.toString()); } - return url.join('/'); }); + + +// export const handleParams = atomEffect((get, set) => { + +// }) \ No newline at end of file diff --git a/src/atoms/seasons.tsx b/src/atoms/seasons.tsx index b10acc7..29a8679 100644 --- a/src/atoms/seasons.tsx +++ b/src/atoms/seasons.tsx @@ -1,7 +1,7 @@ import { atom } from 'jotai'; import { atomEffect } from 'jotai-effect'; -import { f1Seasons } from '@/app/lib/fakerData'; +import { f1Seasons } from '@/lib/fakerData'; import { driverAtom } from './drivers'; import { raceAtom } from './races'; @@ -33,6 +33,6 @@ export const handleSeasonChangeAtom = atom( set(sessionAtom, 'Race'); // return navigation url - return '/results/' + season; + return '/' + season; }, ); diff --git a/src/atoms/sessions.tsx b/src/atoms/sessions.tsx index 59d8fa3..47b73e6 100644 --- a/src/atoms/sessions.tsx +++ b/src/atoms/sessions.tsx @@ -1,11 +1,61 @@ import { atom } from 'jotai'; import { atomEffect } from 'jotai-effect'; -import { fetchAPI } from '@/app/lib/utils'; +import { fetchAPI } from '@/lib/utils'; import { allDriversAtom, driverAtom } from './drivers'; import { raceAtom } from './races'; import { seasonAtom } from './seasons'; +import { allConstructorAtom } from './constructors'; + +/** + * @description Format constructors results based on Driver results from race + * @param {DriverResult[]} drivers + */ +const formatConstructorResults = (drivers: DriverResult[]) => + drivers + .reduce((cons, driver) => { + // *** Find existint team from accumulator + const existingTeamIndex = cons.findIndex( + (team) => team.name === driver.TeamName, + ); + + // If: + // 1. Team exists in accumulator + // *** Update constructor values + if (existingTeamIndex >= 0) { + const update = { ...cons[existingTeamIndex] }; + const points = update.points + driver.Points; + const conDrivers = [...update.drivers, driver]; + + // *** Add Updated Constructor + cons.push({ + ...update, + points, + drivers: conDrivers, + }); + + // *** Remove Old Constructor + cons.splice(existingTeamIndex, 1); + } else { + // *** Add new Constructor + cons.push({ + name: driver.TeamName, + points: driver.Points, + position: driver.Position, // Placeholder + drivers: [driver], + }); + } + + return cons; + }, [] as ConstructorResult[]) + // Sort by points + .sort((a, b) => (a.points > b.points ? -1 : 1)) + // Set proper position + .map((con, i) => { + con.position = i + 1; + return con; + }); // Sessions export const allSessionsAtom = atom([]); @@ -15,20 +65,26 @@ export const sessionAtom = atom('Race'); // Set allDriversAtom to drivers from session export const fetchSessionResults = atomEffect((get, set) => { const race = get(raceAtom); + console.log('fetch', race) // Confirm race has been selected if (race && race !== 'All Races') { const sessions = get(sessionAtom); let url = `results/${get(seasonAtom)}/${race.RoundNumber}`; - // If sessions find round and add to url + // If sessions available find session round and add to url if (sessions.length > 0) { const sessionRound = get(allSessionsAtom).indexOf(get(sessionAtom)) + 1; url += `?session=${sessionRound}`; } - fetchAPI(url).then((data) => { - set(allDriversAtom, data); + fetchAPI(url).then((drivers: DriverResult[]) => { + // Formulate Constructors + const constructors = formatConstructorResults(drivers); + + // Update atom values + set(allDriversAtom, drivers); + set(allConstructorAtom, constructors); }); } }); @@ -39,7 +95,7 @@ export const handleSessionChangeAtom = atom( set(sessionAtom, session); // return navigation url - return `/results/${get(seasonAtom)}/${get(raceAtom)}/ + return `/${get(seasonAtom)}/${get(raceAtom)}/ ${get(driverAtom)}/${session}`; }, ); diff --git a/src/atoms/standings.tsx b/src/atoms/standings.tsx index 2c528f1..d84aa5f 100644 --- a/src/atoms/standings.tsx +++ b/src/atoms/standings.tsx @@ -1,7 +1,7 @@ import { atom } from 'jotai'; import { atomEffect } from 'jotai-effect'; -import { fetchAPI } from '@/app/lib/utils'; +import { fetchAPI } from '@/lib/utils'; import { raceAtom } from './races'; import { seasonAtom } from './seasons'; @@ -12,6 +12,8 @@ export const driverStandingsAtom = atom([]); // Get Driver & Constructor Standings export const fetchStandings = atomEffect((get, set) => { + set(driverStandingsAtom, []); + set(constructorStandingsAtom, []); const year = get(seasonAtom) && `?year=${get(seasonAtom)}`; const round = typeof get(raceAtom) !== 'string' From d25eb8546dbcc7e961881b6fa9f958ad85346c63 Mon Sep 17 00:00:00 2001 From: cris lombardo Date: Fri, 2 Feb 2024 16:59:40 -0500 Subject: [PATCH 6/7] refactor: timeline and remove results endpoint instead of using the /results endpoint we will use the root, /, as the base --- src/app/(features)/StandingsTimeline.tsx | 68 ++++++++++++ src/app/[season]/page.tsx | 58 ++++++++++ src/app/layout.tsx | 2 +- src/app/results/SeasonResults.tsx | 35 ------ src/app/results/StandingsTimeline.tsx | 128 ---------------------- src/app/results/[season]/[round]/page.tsx | 47 -------- src/app/results/[season]/page.tsx | 14 --- src/app/results/page.tsx | 5 - src/app/ui/Timeline.tsx | 49 +++++++++ 9 files changed, 176 insertions(+), 230 deletions(-) create mode 100644 src/app/(features)/StandingsTimeline.tsx create mode 100644 src/app/[season]/page.tsx delete mode 100644 src/app/results/SeasonResults.tsx delete mode 100644 src/app/results/StandingsTimeline.tsx delete mode 100644 src/app/results/[season]/[round]/page.tsx delete mode 100644 src/app/results/[season]/page.tsx delete mode 100644 src/app/results/page.tsx create mode 100644 src/app/ui/Timeline.tsx diff --git a/src/app/(features)/StandingsTimeline.tsx b/src/app/(features)/StandingsTimeline.tsx new file mode 100644 index 0000000..a201338 --- /dev/null +++ b/src/app/(features)/StandingsTimeline.tsx @@ -0,0 +1,68 @@ +import clsx from 'clsx'; + +import { positionEnding } from '../../lib/utils'; + +export const DriverStandingInfo = ({ + driver, + subEl = false, +}: { + driver: DriverStandingSchema; + subEl?: boolean; +}) => { + return ( + <> +

      + {positionEnding(driver.position)} +

      + + {/* Driver Standings */} + {driver.Driver && ( +

      + {driver.Driver.givenName} {driver.Driver.familyName} +

      + )} + {!subEl && ( +

      + {driver.Constructors.map(({ name }) => ( + {name} + ))} +

      + )} +

      Points: {driver.points}

      +

      Wins: {driver.wins}

      + + ); +}; + +// constructor is a keyword cannot be used +export const ConstructorStandingInfo = ({ + con, +}: { + con: ConstructorStandingSchema; +}) => { + return ( + <> +

      + {positionEnding(con.position)} +

      + +

      {con.Constructor.name}

      +

      Points: {con.points}

      +

      Wins: {con.wins}

      +
      +
      + {con.Drivers && + con.Drivers.map((driver) => ( +
      + +
      + ))} +
      + + ); +}; diff --git a/src/app/[season]/page.tsx b/src/app/[season]/page.tsx new file mode 100644 index 0000000..3424958 --- /dev/null +++ b/src/app/[season]/page.tsx @@ -0,0 +1,58 @@ +'use client'; + +import { useAtom } from 'jotai'; + +import { + constructorStandingsAtom, + driverStandingsAtom, + fetchStandings, +} from '@/atoms/standings'; + +import { RaceSchedule } from '../(features)/RaceSchedule'; +import { + ConstructorStandingInfo, + DriverStandingInfo, +} from '../(features)/StandingsTimeline'; +import { Tabs } from '../ui/Tabs'; +import { Timeline, TimelineElement } from '../ui/Timeline'; + +export default function ResultsPage() { + useAtom(fetchStandings); + const [constructorStandings] = useAtom(constructorStandingsAtom); + const [driverStandings] = useAtom(driverStandingsAtom); + + return ( +
      + , + + {driverStandings.map((driver, index, allDrivers) => ( + + + + ))} + , + + {constructorStandings.map((constructor, index, allConstructors) => ( + + + + ))} + , + ]} + /> +
      + ); +} diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 008e700..23a58ff 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -5,8 +5,8 @@ import { Inter } from 'next/font/google'; import './globals.css'; -import { fetchAPI } from './lib/utils'; import { Nav } from './ui/Nav'; +import { fetchAPI } from '../lib/utils'; const inter = Inter({ subsets: ['latin'] }); diff --git a/src/app/results/SeasonResults.tsx b/src/app/results/SeasonResults.tsx deleted file mode 100644 index dcba954..0000000 --- a/src/app/results/SeasonResults.tsx +++ /dev/null @@ -1,35 +0,0 @@ -'use client'; - -import { useAtom } from 'jotai'; - -import { - constructorStandingsAtom, - driverStandingsAtom, - fetchStandings, -} from '@/atoms/standings'; - -import { RaceSchedule } from './RaceResults'; -import { StandingsTimeline } from './StandingsTimeline'; -import { Tabs } from '../ui/Tabs'; - -export default function ResultsPage() { - useAtom(fetchStandings); - const [constructorStandings] = useAtom(constructorStandingsAtom); - const [driverStandings] = useAtom(driverStandingsAtom); - - return ( -
      - , - , - , - ]} - /> -
      - ); -} diff --git a/src/app/results/StandingsTimeline.tsx b/src/app/results/StandingsTimeline.tsx deleted file mode 100644 index bbbdef5..0000000 --- a/src/app/results/StandingsTimeline.tsx +++ /dev/null @@ -1,128 +0,0 @@ -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 ( -
      • - {i !== 0 &&
        } - {/* ! Use Flex order ! Yay */} - -
        - {Object.prototype.hasOwnProperty.call(standing, 'Driver') && ( - - )} - {Object.prototype.hasOwnProperty.call( - standing, - 'Constructor', - ) && ( - - )} -
        - {i !== data.length - 1 &&
        } -
      • - ); - })} -
      - ); -}; - -const DriverStandingInfo = ({ - driver, - subEl = false, -}: { - driver: DriverStandingSchema; - subEl?: boolean; -}) => { - return ( - <> -

      - {positionEnding(driver.position)} -

      - - {/* 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 ( - <> -

      - {positionEnding(con.position)} -

      - -

      {con.Constructor.name}

      -

      Points: {con.points}

      -

      Wins: {con.wins}

      -
      -
      - {con.Drivers && - con.Drivers.map((driver) => ( -
      - -
      - ))} -
      - - ); -}; - -const TimelineMarker = () => ( -
      - - - -
      -); diff --git a/src/app/results/[season]/[round]/page.tsx b/src/app/results/[season]/[round]/page.tsx deleted file mode 100644 index 64a97b1..0000000 --- a/src/app/results/[season]/[round]/page.tsx +++ /dev/null @@ -1,47 +0,0 @@ -'use client'; - -import { useAtom } from 'jotai'; - -import { Tabs } from '@/app/ui/Tabs'; -import { - constructorStandingsAtom, - driverStandingsAtom, - fetchStandings, -} from '@/atoms/standings'; - -// import { RaceSchedule } from '../../RaceResults'; -import { StandingsTimeline } from '../../StandingsTimeline'; -// import { handleRaceChangeAtom, seasonRacesAtom } from '@/atoms/races'; - -export default function ResultsPage({ params }: { params: { round: string } }) { - useAtom(fetchStandings); - const [constructorStandings] = useAtom(constructorStandingsAtom); - const [driverStandings] = useAtom(driverStandingsAtom); - - // const [, handleRaceChange] = useAtom(handleRaceChangeAtom); - // const [allRaces] = useAtom(seasonRacesAtom) - - if (!params) return <>; - // Get all - - // if (allRaces && allRaces.length > 0) { - // console.log('all races', allRaces[parseInt(params.round) - 1]) - - // handleRaceChange(allRaces[parseInt(params.round) - 1]); - // } - - return ( -
      - , - , - ]} - /> -
      - ); -} diff --git a/src/app/results/[season]/page.tsx b/src/app/results/[season]/page.tsx deleted file mode 100644 index 55728c5..0000000 --- a/src/app/results/[season]/page.tsx +++ /dev/null @@ -1,14 +0,0 @@ -'use client'; - -import { useAtom } from 'jotai'; - -import { handleSeasonChangeAtom } from '@/atoms/seasons'; - -import ResultsPage from '../SeasonResults'; - -export default function Page({ params }: { params: { season: string } }) { - const [, handleSeasonChange] = useAtom(handleSeasonChangeAtom); - handleSeasonChange(params.season); - - return ; -} diff --git a/src/app/results/page.tsx b/src/app/results/page.tsx deleted file mode 100644 index daf84ed..0000000 --- a/src/app/results/page.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import ResultsPage from './SeasonResults'; - -export default function Page() { - return ; -} diff --git a/src/app/ui/Timeline.tsx b/src/app/ui/Timeline.tsx new file mode 100644 index 0000000..1e4c233 --- /dev/null +++ b/src/app/ui/Timeline.tsx @@ -0,0 +1,49 @@ +import clsx from 'clsx'; + +export const Timeline = ({ children }: { children: React.ReactNode[] }) => { + return ( +
        + {children} +
      + ); +}; + +interface TimelineElProps { + first: boolean; + last: boolean; + odd: boolean; + children: React.ReactNode; +} + +export const TimelineElement = ({ + first, + last, + odd, + children, +}: TimelineElProps) => ( +
    • + {!first &&
      } + +
      + {children} +
      + {!last &&
      } +
    • +); + +const TimelineMarker = () => ( +
      + + + +
      +); From dc0754de666dc061c4b6dcc9b4679739833e9986 Mon Sep 17 00:00:00 2001 From: cris lombardo Date: Fri, 2 Feb 2024 17:00:41 -0500 Subject: [PATCH 7/7] refactor: main filter main filters needs info from url params --- .../RaceSchedule.tsx} | 77 ++++++++---- src/app/(features)/RaceTimeline.tsx | 4 +- src/app/[season]/[location]/page.tsx | 54 +++----- src/app/{results => [season]}/layout.tsx | 2 +- src/app/ui/Dropdown.tsx | 2 +- src/app/ui/MainFilters.tsx | 20 ++- src/app/ui/Nav.tsx | 119 ++++++++++-------- src/atoms/constructors.tsx | 4 +- src/atoms/results.tsx | 4 +- src/atoms/sessions.tsx | 7 +- src/lib/utils.tsx | 12 +- tsconfig.json | 10 +- 12 files changed, 167 insertions(+), 148 deletions(-) rename src/app/{results/RaceResults.tsx => (features)/RaceSchedule.tsx} (81%) rename src/app/{results => [season]}/layout.tsx (75%) diff --git a/src/app/results/RaceResults.tsx b/src/app/(features)/RaceSchedule.tsx similarity index 81% rename from src/app/results/RaceResults.tsx rename to src/app/(features)/RaceSchedule.tsx index ea7aec7..db2971e 100644 --- a/src/app/results/RaceResults.tsx +++ b/src/app/(features)/RaceSchedule.tsx @@ -4,6 +4,57 @@ import { useMemo } from 'react'; import { seasonRacesAtom } from '@/atoms/races'; +export const RaceSchedule = () => { + const [races] = useAtom(seasonRacesAtom); + + const winterTesting = useMemo( + () => races.find((race) => race.EventFormat === 'testing'), + [races], + ); + const mainEvents = useMemo( + () => races.filter((race) => race.EventFormat !== 'testing'), + [races], + ); + + if (races.length === 0) + return ( +
      +
      + {/* 3 Placeholder Cards */} + {Array.from(Array(4).keys()).map((_, i) => ( + + ))} +
      +
      + ); + + return ( +
      + {/* If seasonAom === current/upcomming season, then add button to bring user to next event */} + {winterTesting && } +
      + {/* 10 Placeholder Cards */} + {mainEvents.map((race) => ( + + ))} +
      +
      + ); +}; + +const SkeletonResultCard = () => ( +
      +
      + +
      +
      +
      + +
      +
      +
      +); + const ResultCard = ({ data }: { data: ScheduleSchema }) => { const eventDate = new Date(data.EventDate); const eventPassed = new Date() > eventDate; @@ -82,29 +133,3 @@ const WinterTesting = ({ data }: { data: ScheduleSchema }) => {
      ); }; - -export const RaceSchedule = () => { - const [races] = useAtom(seasonRacesAtom); - - const winterTesting = useMemo( - () => races.find((race) => race.EventFormat === 'testing'), - [races], - ); - const mainEvents = useMemo( - () => races.filter((race) => race.EventFormat !== 'testing'), - [races], - ); - - return ( -
      - {/* If seasonAom === current/upcomming season, then add button to bring user to next event */} - {winterTesting && } -
      - {/* 10 Placeholder Cards */} - {mainEvents.map((race) => ( - - ))} -
      -
      - ); -}; diff --git a/src/app/(features)/RaceTimeline.tsx b/src/app/(features)/RaceTimeline.tsx index 7d55857..b45cba5 100644 --- a/src/app/(features)/RaceTimeline.tsx +++ b/src/app/(features)/RaceTimeline.tsx @@ -40,7 +40,9 @@ export const DriverResultsInfo = ({
      {!subEl && (

      - {driver.Time ? durationPlus + formatDuration(driver.Time) : driver.Status} + {driver.Time + ? durationPlus + formatDuration(driver.Time) + : driver.Status}

      )} diff --git a/src/app/[season]/[location]/page.tsx b/src/app/[season]/[location]/page.tsx index d353e03..1bd34b6 100644 --- a/src/app/[season]/[location]/page.tsx +++ b/src/app/[season]/[location]/page.tsx @@ -1,45 +1,44 @@ 'use client'; import { useAtom } from 'jotai'; +import { useEffect } from 'react'; -import { ConstructorResultsInfo, DriverResultsInfo } from '@/app/(features)/RaceTimeline'; +import { + ConstructorResultsInfo, + DriverResultsInfo, +} from '@/app/(features)/RaceTimeline'; import { Tabs } from '@/app/ui/Tabs'; import { Timeline, TimelineElement } from '@/app/ui/Timeline'; -import { allDriversAtom } from '@/atoms/drivers'; -import { fetchSessionResults } from '@/atoms/sessions'; -import { - constructorStandingsAtom, - driverStandingsAtom, - fetchStandings, -} from '@/atoms/standings'; import { allConstructorAtom } from '@/atoms/constructors'; -import { useParams } from 'next/navigation'; -import { useRouter } from 'next/router'; +import { allDriversAtom } from '@/atoms/drivers'; import { raceAtom, seasonRacesAtom } from '@/atoms/races'; -import { useEffect } from 'react'; +import { fetchSessionResults } from '@/atoms/sessions'; // import { RaceSchedule } from '../../RaceResults'; // import { handleRaceChangeAtom, seasonRacesAtom } from '@/atoms/races'; -export default function ResultsPage({ params }: { params: { location: string } }) { - - - const [races] = useAtom(seasonRacesAtom) - const [_, setRace] = useAtom(raceAtom) +export default function ResultsPage({ + params, +}: { + params: { location: string }; +}) { + const [races] = useAtom(seasonRacesAtom); + const [_, setRace] = useAtom(raceAtom); useEffect(() => { - setRace(races.find(race => race.Location === params.location) || 'All Races') - }, [races]) + setRace( + races.find((race) => race.Location === params.location) || 'All Races', + ); + }, [races, params.location, setRace]); // useAtom(fetchStandings); // useAtom(fetchSessionResults); useAtom(fetchSessionResults); - const [constructorStandings] = useAtom(constructorStandingsAtom); - const [driverStandings] = useAtom(driverStandingsAtom); + // const [constructorStandings] = useAtom(constructorStandingsAtom); + // const [driverStandings] = useAtom(driverStandingsAtom); const [drivers] = useAtom(allDriversAtom); const [constructors] = useAtom(allConstructorAtom); - console.log('data', [drivers, constructors]) // const [driverStandings] = useAtom(driverStandingsAtom); // const [, handleRaceChange] = useAtom(handleRaceChangeAtom); @@ -88,16 +87,3 @@ export default function ResultsPage({ params }: { params: { location: string } }
); } - -// -// {constructorStandings.map((constructor, index, allConstructors) => ( -// -// -// -// ))} -// diff --git a/src/app/results/layout.tsx b/src/app/[season]/layout.tsx similarity index 75% rename from src/app/results/layout.tsx rename to src/app/[season]/layout.tsx index 174c3ab..2bc8625 100644 --- a/src/app/results/layout.tsx +++ b/src/app/[season]/layout.tsx @@ -10,7 +10,7 @@ export default function ResultsLayout({ }) { return ( <> -
+
{children} diff --git a/src/app/ui/Dropdown.tsx b/src/app/ui/Dropdown.tsx index 61f1217..ffbbb21 100644 --- a/src/app/ui/Dropdown.tsx +++ b/src/app/ui/Dropdown.tsx @@ -24,7 +24,7 @@ export const Dropdown = ({ value, items, action }: IDropdown) => { role='button' className={clsx( { 'pointer-events-none opacity-50': items.length <= 0 }, - 'btn btn-ghost btn-sm rounded-btn px-0 underline', + 'btn btn-ghost btn-sm rounded-btn underline', )} > {value} {items.length > 0 && } diff --git a/src/app/ui/MainFilters.tsx b/src/app/ui/MainFilters.tsx index 749f2f8..9501a58 100644 --- a/src/app/ui/MainFilters.tsx +++ b/src/app/ui/MainFilters.tsx @@ -16,11 +16,7 @@ import { raceAtom, seasonRacesAtom, } from '@/atoms/races'; -import { - handleMainFilterSubmit, - telemetryDisableAtom, - toggleTelemetryDisableAtom, -} from '@/atoms/results'; +import { handleMainFilterSubmit, telemetryDisableAtom } from '@/atoms/results'; import { allSeasonsAtom, fetchSeasons, @@ -29,7 +25,6 @@ import { } from '@/atoms/seasons'; import { allSessionsAtom, - fetchSessionResults, handleSessionChangeAtom, sessionAtom, } from '@/atoms/sessions'; @@ -45,12 +40,11 @@ export const MainFilters = () => { const pathname = usePathname(); const [telemetryDisable] = useAtom(telemetryDisableAtom); - // const [resultsUrl] = useAtom(resultUrlAtom); const [, handleResultsSubmit] = useAtom(handleMainFilterSubmit); - useAtom(toggleTelemetryDisableAtom); - useAtom(fetchSeasons); - useAtom(fetchSessionResults); + // useAtom(fetchSeasons); + // useAtom(toggleTelemetryDisableAtom); + // useAtom(fetchSessionResults); const changePath = (url: string) => { // If not home page auto change page @@ -59,7 +53,7 @@ export const MainFilters = () => { const handleSubmit = () => { const url = handleResultsSubmit(); - router.push(url); + router.push('/' + url); }; return ( @@ -75,7 +69,7 @@ export const MainFilters = () => {
-
+
@@ -103,6 +97,7 @@ const SeasonDropdown = ({ action }: actionT) => { }; useAtom(fetchSeasons); + return ; }; @@ -142,7 +137,6 @@ const DriverDropdown = ({ action }: actionT) => { }); }; - // useAtom(fetchDriver); return ( ( -
-
- - Slick Telemetry - -
-
- -
-
-
-
- - - -
-
    +import { seasonAtom } from '@/atoms/seasons'; + +export const Nav = () => { + const router = useRouter(); + const [season] = useAtom(seasonAtom); + + const handleSubmit = () => { + router.push('/' + season); + }; + + return ( +
    +
    + + Slick Telemetry + +
    +
    +
    -
    -
    -

    - 53 days until Winter Testing -

    +
    +
    +
    + + + +
    + +
    +
    +
    +

    + 53 days until Winter Testing +

    +
    -
    -); + ); +}; diff --git a/src/atoms/constructors.tsx b/src/atoms/constructors.tsx index 6eeed99..ae143d2 100644 --- a/src/atoms/constructors.tsx +++ b/src/atoms/constructors.tsx @@ -1,4 +1,4 @@ -import { atom } from "jotai"; +import { atom } from 'jotai'; // Constructor Results -export const allConstructorAtom = atom([]); \ No newline at end of file +export const allConstructorAtom = atom([]); diff --git a/src/atoms/results.tsx b/src/atoms/results.tsx index 1d4f816..360a8aa 100644 --- a/src/atoms/results.tsx +++ b/src/atoms/results.tsx @@ -5,7 +5,6 @@ import { allDriversAtom, driverAtom } from './drivers'; import { raceAtom } from './races'; import { seasonAtom } from './seasons'; import { allSessionsAtom, sessionAtom } from './sessions'; -import { useParams } from 'next/navigation'; // Telemetry Active export const telemetryDisableAtom = atom(true); @@ -42,7 +41,6 @@ export const handleMainFilterSubmit = atom(null, (get) => { return url.join('/'); }); - // export const handleParams = atomEffect((get, set) => { -// }) \ No newline at end of file +// }) diff --git a/src/atoms/sessions.tsx b/src/atoms/sessions.tsx index 47b73e6..571b18b 100644 --- a/src/atoms/sessions.tsx +++ b/src/atoms/sessions.tsx @@ -3,10 +3,10 @@ import { atomEffect } from 'jotai-effect'; import { fetchAPI } from '@/lib/utils'; +import { allConstructorAtom } from './constructors'; import { allDriversAtom, driverAtom } from './drivers'; import { raceAtom } from './races'; import { seasonAtom } from './seasons'; -import { allConstructorAtom } from './constructors'; /** * @description Format constructors results based on Driver results from race @@ -65,16 +65,15 @@ export const sessionAtom = atom('Race'); // Set allDriversAtom to drivers from session export const fetchSessionResults = atomEffect((get, set) => { const race = get(raceAtom); - console.log('fetch', race) // Confirm race has been selected if (race && race !== 'All Races') { - const sessions = get(sessionAtom); + const sessions = get(allSessionsAtom); let url = `results/${get(seasonAtom)}/${race.RoundNumber}`; // If sessions available find session round and add to url if (sessions.length > 0) { - const sessionRound = get(allSessionsAtom).indexOf(get(sessionAtom)) + 1; + const sessionRound = sessions.indexOf(get(sessionAtom)) + 1; url += `?session=${sessionRound}`; } diff --git a/src/lib/utils.tsx b/src/lib/utils.tsx index 2b65655..9d28c23 100644 --- a/src/lib/utils.tsx +++ b/src/lib/utils.tsx @@ -43,9 +43,7 @@ export const fastestLap = (position: number, points: number) => { } }; -export const formatDuration = ( - durationInMilliseconds: number, -) => { +export const formatDuration = (durationInMilliseconds: number) => { // Pad single-digit values with leading zeros const pad = (value: number) => { return value < 10 ? '0' + value : value; @@ -62,11 +60,13 @@ export const formatDuration = ( else if (hours === 0 && minutes === 0 && seconds === 0) return '0.' + pad(milliseconds); else if (hours === 0 && minutes === 0) - return seconds + '.' + pad(milliseconds) + return seconds + '.' + pad(milliseconds); else if (hours === 0) - return minutes + ':' + pad(seconds) + '.' + pad(milliseconds) + return minutes + ':' + pad(seconds) + '.' + pad(milliseconds); else - return hours + ':' + pad(minutes) + ':' + pad(seconds) + '.' + pad(milliseconds) + return ( + hours + ':' + pad(minutes) + ':' + pad(seconds) + '.' + pad(milliseconds) + ); }; export const sessionTitles = (event: ScheduleSchema) => { diff --git a/tsconfig.json b/tsconfig.json index e59724b..4b8fa2e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -15,13 +15,13 @@ "incremental": true, "plugins": [ { - "name": "next" - } + "name": "next", + }, ], "paths": { - "@/*": ["./src/*"] - } + "@/*": ["./src/*"], + }, }, "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], - "exclude": ["node_modules"] + "exclude": ["node_modules"], }