Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: FRON-11 showcase next upcoming event #26

Merged
merged 1 commit into from
Feb 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions src/app/(features)/LandingNextEvent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
'use client';

import { useAtom } from 'jotai';

import { nextEventAtom, nextEventLiveAtom } from '@/atoms/nextEvent';
import { formatSessionUrl } from '@/utils/transformers';

import { EventCountDown } from '../ui/EventCountdown';

export const LandingNextEvent = () => {
const [nextEvent] = useAtom(nextEventAtom);
const [liveEvent] = useAtom(nextEventLiveAtom);

return (
<div className='flex bg-base-300 px-4 py-8'>
{nextEvent && (
<div className='mx-auto'>
<h2 className='text-4xl font-bold'>{nextEvent.name}</h2>
<p className='text-2xl'>
{liveEvent ? (
nextEvent.session + ' Live Now'
) : (
<>
{formatSessionUrl(nextEvent.session).toUpperCase()} in{' '}
<EventCountDown />
</>
)}
</p>
<button className='btn btn-secondary btn-sm my-2'>
Previous Results
</button>
</div>
)}
</div>
);
};
2 changes: 2 additions & 0 deletions src/app/[season]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import { useAtom } from 'jotai';

import { fetchStandings } from '@/atoms/fetchCalls';
import { seasonAtom } from '@/atoms/seasons';
import {
constructorStandingsAtom,
Expand All @@ -20,6 +21,7 @@ export default function ResultsPage() {
const [constructorStandings] = useAtom(constructorStandingsAtom);
const [driverStandings] = useAtom(driverStandingsAtom);
const [season] = useAtom(seasonAtom);
useAtom(fetchStandings);

return (
<main>
Expand Down
12 changes: 2 additions & 10 deletions src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { LandingNextEvent } from './(features)/LandingNextEvent';
import { MainFilters } from './ui/MainFilters';

export default function Home() {
Expand All @@ -6,7 +7,7 @@ export default function Home() {
<Hero />

{/* Suspense */}
<NextRace />
<LandingNextEvent />
</main>
);
}
Expand All @@ -25,12 +26,3 @@ const Hero = () => (
</div>
</div>
);

const NextRace = () => {
return (
<div className='bg-base-300 px-4 py-8'>
<h2 className='text-2xl'>Winter Testing</h2>
Bahrain Feb 21, 2024
</div>
);
};
18 changes: 18 additions & 0 deletions src/app/ui/Countdown.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import clsx from 'clsx';

import { formatDuration } from '@/utils/helpers';

export const Countdown = ({
time,
className,
}: {
time: number;
className?: string;
}) => {
return (
<span className={clsx(className)}>
{/* Remove last 4 characters which as milliseconds */}
{formatDuration(time).slice(0, -4)}
</span>
);
};
10 changes: 10 additions & 0 deletions src/app/ui/EventCountdown.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { useAtom } from 'jotai';

import { nextEventTimeAtom } from '@/atoms/nextEvent';

import { Countdown } from './Countdown';

export const EventCountDown = () => {
const [nextEventCountdown] = useAtom(nextEventTimeAtom);
return <Countdown time={nextEventCountdown} />;
};
40 changes: 35 additions & 5 deletions src/app/ui/Nav.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,44 @@
'use client';

import clsx from 'clsx';
import { useAtom } from 'jotai';
import Link from 'next/link';
import { useRouter } from 'next/navigation';

import { fetchNextEvent } from '@/atoms/fetchCalls';
import {
nextEventAtom,
nextEventEffect,
nextEventLiveAtom,
} from '@/atoms/nextEvent';
import { seasonAtom } from '@/atoms/seasons';

import { EventCountDown } from './EventCountdown';

export const NextEvent = () => {
const [nextEvent] = useAtom(nextEventAtom);
const [liveEvent] = useAtom(nextEventLiveAtom);

useAtom(fetchNextEvent);
useAtom(nextEventEffect);

if (!nextEvent) return <></>;
return (
<>
<div
className={clsx('h-3 w-3 rounded-full', {
'bg-info': !liveEvent,
'bg-success': liveEvent,
})}
></div>
<p className='text-sm font-bold'>
<span className='underline'>{nextEvent.name}</span> <br />
in <EventCountDown />
</p>
</>
);
};

export const Nav = () => {
const router = useRouter();
const [season] = useAtom(seasonAtom);
Expand Down Expand Up @@ -72,11 +105,8 @@ export const Nav = () => {
</li>
</ul>
</div>
<div className='hidden items-center gap-2 lg:flex'>
<div className='h-3 w-3 rounded-full bg-amber-300'></div>
<p className='text-sm font-bold'>
53 days until <span className='underline'>Winter Testing</span>
</p>
<div className='hidden min-w-48 items-center gap-2 lg:flex'>
<NextEvent />
</div>
</div>
</div>
Expand Down
72 changes: 0 additions & 72 deletions src/app/ui/Table.tsx

This file was deleted.

36 changes: 35 additions & 1 deletion src/atoms/fetchCalls.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,18 @@ import { atomEffect } from 'jotai-effect';

import { f1Seasons } from '@/utils/fakerData';
import { fetchAPI, lastSession, sessionTitles } from '@/utils/helpers';
import { formatConstructorResults } from '@/utils/transformers';
import {
formatConstructorResults,
formatNextEvent,
} from '@/utils/transformers';

import { allConstructorAtom } from './constructors';
import { allDriversAtom } from './drivers';
import {
nextEventAtom,
nextEventLiveAtom,
nextEventTimeAtom,
} from './nextEvent';
import { raceAtom, seasonRacesAtom } from './races';
import { allSeasonsAtom, seasonAtom } from './seasons';
import { allSessionsAtom, sessionAtom } from './sessions';
Expand Down Expand Up @@ -133,3 +141,29 @@ export const fetchStandings = atomEffect((get, set) => {
// seasonAtom
// raceAtom
});

// Get upcoming event this should be done once
export const fetchNextEvent = atomEffect(
(get, set) => {
// Next event do not change, only fetch if null
if (!get(nextEventAtom)) {
fetchAPI('next-event').then((data: ScheduleSchema) => {
// Get session times
const now = Date.now();
const nextEvent = formatNextEvent(data);

if (nextEvent === 'No session') return;

set(nextEventAtom, nextEvent);

if (nextEvent.time < now) {
set(nextEventLiveAtom, true);
set(nextEventTimeAtom, now - nextEvent.endTime);
} else {
set(nextEventTimeAtom, nextEvent.time - now);
}
});
}
},
// Dependencies: nextEventAtom
);
15 changes: 15 additions & 0 deletions src/atoms/nextEvent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { atom } from 'jotai';
import { atomEffect } from 'jotai-effect';

// Next Event
export const nextEventLiveAtom = atom(false);
export const nextEventAtom = atom<NextEventProps | null>(null);
export const nextEventTimeAtom = atom(0);
export const nextEventEffect = atomEffect((get, set) => {
if (get(nextEventTimeAtom) !== 0) {
const intervalId = setInterval(() => {
set(nextEventTimeAtom, (prev: number) => prev - 1000);
}, 1000);
return () => clearInterval(intervalId);
}
});
8 changes: 8 additions & 0 deletions src/results.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,11 @@ interface DataConfigSchema {
constructors: ConstructorResult[];
};
}

// UI Format Next Event
interface NextEventProps {
name: string;
session: string;
time: number;
endTime: number;
}
46 changes: 35 additions & 11 deletions src/utils/helpers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,30 +43,54 @@ export const fastestLap = (position: number, points: number) => {
}
};

export const formatDuration = (durationInMilliseconds: number) => {
const _second = 1000;
const _minute = _second * 60;
const _hour = _minute * 60;
const _day = _hour * 24;

export const formatDuration = (timeInterval: number) => {
// 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;

if (hours === 0 && minutes === 0 && seconds === 0 && milliseconds === 0)
const milliseconds = timeInterval % _second;
const seconds = Math.floor((timeInterval % _minute) / _second);
const minutes = Math.floor((timeInterval % _hour) / _minute);
const hours = Math.floor((timeInterval % _day) / _hour);
const days = Math.floor(timeInterval / _day);

if (
days === 0 &&
hours === 0 &&
minutes === 0 &&
seconds === 0 &&
milliseconds === 0
)
return '-';
else if (hours === 0 && minutes === 0 && seconds === 0)
else if (days === 0 && hours === 0 && minutes === 0 && seconds === 0)
return '0.' + pad(milliseconds);
else if (hours === 0 && minutes === 0)
else if (days === 0 && hours === 0 && minutes === 0)
return seconds + '.' + pad(milliseconds);
else if (hours === 0)
else if (days === 0 && hours === 0)
return minutes + ':' + pad(seconds) + '.' + pad(milliseconds);
else
else if (days === 0)
return (
hours + ':' + pad(minutes) + ':' + pad(seconds) + '.' + pad(milliseconds)
);
else
return (
days +
' days ' +
hours +
':' +
pad(minutes) +
':' +
pad(seconds) +
'.' +
pad(milliseconds)
);
};

export const sessionTitles = (event: ScheduleSchema) => {
Expand Down
Loading
Loading