Skip to content

Commit

Permalink
race results prototype (#17)
Browse files Browse the repository at this point in the history
* refactor: main filter separation of concern

* chore: introduce race results data per driver

* refactor: proper linting

* refactor: migrate lib out of app router

* chore: successfully test results endpoint

* refactor: timeline and remove results endpoint

instead of using the /results endpoint we will use the root, /, as the base

* refactor: main filter

main filters needs info from url params
  • Loading branch information
Lombardoc4 authored Feb 2, 2024
1 parent 0232eaa commit 68bd1f4
Show file tree
Hide file tree
Showing 31 changed files with 1,047 additions and 593 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,58 @@ import { useAtom } from 'jotai';
import Image from 'next/image';
import { useMemo } from 'react';

import { seasonRacesAtom } from '@/atoms/results';
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 (
<div className='px-4 lg:px-0'>
<div className='mt-8 grid gap-8 md:grid-cols-2 xl:grid-cols-3 xl:gap-x-4'>
{/* 3 Placeholder Cards */}
{Array.from(Array(4).keys()).map((_, i) => (
<SkeletonResultCard key={'skeleton result card - ' + i} />
))}
</div>
</div>
);

return (
<div className='px-4 lg:px-0'>
{/* If seasonAom === current/upcomming season, then add button to bring user to next event */}
{winterTesting && <WinterTesting data={winterTesting} />}
<div className='mt-8 grid gap-8 md:grid-cols-2 xl:grid-cols-3 xl:gap-x-4'>
{/* 10 Placeholder Cards */}
{mainEvents.map((race) => (
<ResultCard key={race.EventName} data={race} />
))}
</div>
</div>
);
};

const SkeletonResultCard = () => (
<div className='card overflow-hidden p-4 shadow-xl'>
<div className='skeleton h-32 w-full'></div>

<div className='card-body p-0 pt-2'>
<div className='skeleton h-4 w-full'></div>
<div className='skeleton h-4 w-full'></div>

<div className='skeleton mx-auto h-[32px] w-[222px]'></div>
</div>
</div>
);

const ResultCard = ({ data }: { data: ScheduleSchema }) => {
const eventDate = new Date(data.EventDate);
Expand Down Expand Up @@ -82,29 +133,3 @@ const WinterTesting = ({ data }: { data: ScheduleSchema }) => {
</div>
);
};

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 (
<div className='px-4 lg:px-0'>
{/* If seasonAom === current/upcomming season, then add button to bring user to next event */}
{winterTesting && <WinterTesting data={winterTesting} />}
<div className='mt-8 grid gap-8 md:grid-cols-2 xl:grid-cols-3 xl:gap-x-4'>
{/* 10 Placeholder Cards */}
{mainEvents.map((race) => (
<ResultCard key={race.EventName} data={race} />
))}
</div>
</div>
);
};
70 changes: 70 additions & 0 deletions src/app/(features)/RaceTimeline.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
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 */}
<p className='font-mono text-2xl italic'>
{positionEnding(driver.Position)} - {driver.Points} points
{fastestLap(driver.Position, driver.Points) && <BsFillStarFill />}
</p>
<div>
<div>
<h3
className={clsx('font-bold', {
'text-2xl': subEl,
'text-4xl': !subEl,
})}
>
{driver.FullName}
</h3>
<h4
className={clsx({
'text-1xl': subEl,
'text-3xl': !subEl,
})}
>
{driver.TeamName}
</h4>
</div>
</div>
{!subEl && (
<h3 className='text-2xl font-bold'>
{driver.Time
? durationPlus + formatDuration(driver.Time)
: driver.Status}
</h3>
)}
</>
);
};

export const ConstructorResultsInfo = ({ con }: { con: ConstructorResult }) => {
return (
<>
<p className='font-mono text-2xl italic'>
{positionEnding(con.position)} - {con.points} points
</p>
<h3 className='text-4xl font-bold'>{con.name}</h3>
<hr className='my-2' />
<div className='flex flex-col gap-4 md:flex-row'>
{con.drivers &&
con.drivers.map((driver: DriverResult) => (
<div key={driver.FullName}>
<DriverResultsInfo driver={driver} subEl={true} />
</div>
))}
</div>
</>
);
};
68 changes: 68 additions & 0 deletions src/app/(features)/StandingsTimeline.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import clsx from 'clsx';

import { positionEnding } from '../../lib/utils';

export const DriverStandingInfo = ({
driver,
subEl = false,
}: {
driver: DriverStandingSchema;
subEl?: boolean;
}) => {
return (
<>
<p className='font-mono text-2xl italic'>
{positionEnding(driver.position)}
</p>

{/* Driver Standings */}
{driver.Driver && (
<h3
className={clsx('font-bold', {
'text-2xl': subEl,
'text-4xl': !subEl,
})}
>
{driver.Driver.givenName} {driver.Driver.familyName}
</h3>
)}
{!subEl && (
<h3 className='text-2xl font-bold'>
{driver.Constructors.map(({ name }) => (
<span key={name}>{name}</span>
))}
</h3>
)}
<p>Points: {driver.points}</p>
<p>Wins: {driver.wins}</p>
</>
);
};

// constructor is a keyword cannot be used
export const ConstructorStandingInfo = ({
con,
}: {
con: ConstructorStandingSchema;
}) => {
return (
<>
<p className='font-mono text-2xl italic'>
{positionEnding(con.position)}
</p>

<h3 className='text-4xl font-bold'>{con.Constructor.name}</h3>
<p>Points: {con.points}</p>
<p>Wins: {con.wins}</p>
<hr className='my-2' />
<div className='flex flex-col gap-4 md:flex-row'>
{con.Drivers &&
con.Drivers.map((driver) => (
<div key={driver.Driver.dateOfBirth}>
<DriverStandingInfo driver={driver} subEl={true} />
</div>
))}
</div>
</>
);
};
89 changes: 89 additions & 0 deletions src/app/[season]/[location]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
'use client';

import { useAtom } from 'jotai';
import { useEffect } from 'react';

import {
ConstructorResultsInfo,
DriverResultsInfo,
} from '@/app/(features)/RaceTimeline';
import { Tabs } from '@/app/ui/Tabs';
import { Timeline, TimelineElement } from '@/app/ui/Timeline';
import { allConstructorAtom } from '@/atoms/constructors';
import { allDriversAtom } from '@/atoms/drivers';
import { raceAtom, seasonRacesAtom } from '@/atoms/races';
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);

useEffect(() => {
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 [drivers] = useAtom(allDriversAtom);
const [constructors] = useAtom(allConstructorAtom);

// 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 (
<main>
<Tabs
headers={['Drivers', 'Constructors']}
containers={[
<Timeline key='Driver Results'>
{drivers.map((driver, index, allDrivers) => (
<TimelineElement
key={driver.FullName}
first={index === 0}
last={index === allDrivers.length - 1}
odd={index % 2 === 0}
>
<DriverResultsInfo driver={driver} />
</TimelineElement>
))}
</Timeline>,
<Timeline key='Constructor Results'>
{constructors.map((con, index, allConstructors) => (
<TimelineElement
key={con.name}
first={index === 0}
last={index === allConstructors.length - 1}
odd={index % 2 === 0}
>
<ConstructorResultsInfo con={con} />
</TimelineElement>
))}
</Timeline>,
]}
/>
</main>
);
}
4 changes: 2 additions & 2 deletions src/app/results/layout.tsx → src/app/[season]/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use client';

import { MainFilters } from '../MainFilters';
import { MainFilters } from '../ui/MainFilters';

// Default Next Layout
export default function ResultsLayout({
Expand All @@ -10,7 +10,7 @@ export default function ResultsLayout({
}) {
return (
<>
<div className='container mx-auto mb-4 rounded-box bg-base-300 p-4 md:my-4'>
<div className='container mx-auto mb-4 rounded-box bg-base-300 p-4 px-2 md:my-4'>
<MainFilters />
</div>
{children}
Expand Down
58 changes: 58 additions & 0 deletions src/app/[season]/page.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<main>
<Tabs
headers={['Races', 'Drivers', 'Constructors']}
containers={[
<RaceSchedule key='Race Schedule' />,
<Timeline key='Driver Standings'>
{driverStandings.map((driver, index, allDrivers) => (
<TimelineElement
key={driver.Driver.givenName}
first={index === 0}
last={index === allDrivers.length - 1}
odd={index % 2 === 0}
>
<DriverStandingInfo driver={driver} />
</TimelineElement>
))}
</Timeline>,
<Timeline key='Constructor Standings'>
{constructorStandings.map((constructor, index, allConstructors) => (
<TimelineElement
key={constructor.Constructor.name}
first={index === 0}
last={index === allConstructors.length - 1}
odd={index % 2 === 0}
>
<ConstructorStandingInfo con={constructor} />
</TimelineElement>
))}
</Timeline>,
]}
/>
</main>
);
}
Loading

0 comments on commit 68bd1f4

Please sign in to comment.