Skip to content
Closed
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
68 changes: 68 additions & 0 deletions app/(home)/components/CountdownTimer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
'use client';

import { useState, useEffect, useCallback } from 'react';

interface CountdownTimerProps {
eventDate: string;
}

export default function CountdownTimer({ eventDate }: CountdownTimerProps) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function needs to be wrapped in a useCallback()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is not resolved

const formatNumber = (num: number) => (num < 10 ? `0${num}` : `${num}`);

const calculateTimeLeft = useCallback(() => {
const difference = +new Date(eventDate) - +new Date();
if (difference > 0) {
return {
days: formatNumber(Math.floor(difference / (1000 * 60 * 60 * 24))),
hours: formatNumber(Math.floor((difference / (1000 * 60 * 60)) % 24)),
minutes: formatNumber(Math.floor((difference / 1000 / 60) % 60)),
seconds: formatNumber(Math.floor((difference / 1000) % 60)),
};
}
return { days: '00', hours: '00', minutes: '00', seconds: '00' };
}, [eventDate]); // Dependency array ensures the function updates when `eventDate` changes

const [timeLeft, setTimeLeft] = useState(calculateTimeLeft());
const [isClient, setIsClient] = useState(false);

useEffect(() => {
setIsClient(true);

const timer = setInterval(() => {
setTimeLeft(calculateTimeLeft());
}, 1000);

return () => clearInterval(timer);
}, [calculateTimeLeft]); // Dependency array ensures the effect re-runs if `calculateTimeLeft` changes

if (!isClient) return null;

return (
<div className="bg-none p-2 w-full text-[#36FF90] md:w-auto">
<h2 className="text-sm font-bold mb-8 md:mb-6 xl:text-5xl text-center hidden xl:block">
COMING SOON
</h2>
<h2 className="text-[1rem] font-bold mb-2 md:mb-3 lg:text-[40px] md:text-3xl text-center block lg:block xl:hidden">
PYCON COUNTDOWN
</h2>
<div className="flex justify-center gap-4 md:space-x-8 text-center text-2xl lg:text-[75px] md:text-[55px] xl:text-[60px] leading-[100%]">
<div>
<p className="text-center tabular-nums tracking-[-0.05em]">{`${timeLeft.days}`}</p>
<p className="text-sm md:text-xl">DAYS</p>
</div>
<div>
<p className="text-center tabular-nums tracking-[-0.05em]">{`${timeLeft.hours}`}</p>
<p className="text-sm md:text-xl">HRS</p>
</div>
<div>
<p className="text-center tabular-nums tracking-[-0.05em]">{`${timeLeft.minutes}`}</p>
<p className="text-sm md:text-xl">MIN</p>
</div>
<div>
<p className="text-center tabular-nums tracking-[-0.05em]">{`${timeLeft.seconds}`}</p>
<p className="text-sm md:text-xl">SEC</p>
</div>
</div>
</div>
);
}
62 changes: 62 additions & 0 deletions app/(home)/components/EventCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { Button } from '@/components/ui/button';
import { cn } from '@/lib/utils';
import CountdownTimer from './CountdownTimer';
import Link from 'next/link';

interface Event {
title: string;
date: string;
location: string;
variant: 'main' | 'regular'; // "main" for the featured event, "regular" for other events
link: string;
}

export default function EventCard({ event }: { event: Event }) {
return (
<div
className={cn(
'rounded-md border py-4 border-midori-green bg-medium-dark-green text-center xl:py-10',
event.variant !== 'main' &&
'transition-transform duration-200 hover:scale-105',
event.variant === 'main' &&
'bg-gradient-ltr-darkgreen-lightgreen py-8 lg:py-14 max-w-[80%] md:max-w-[80%] lg:max-w-[80%] mx-auto xl:text-left md:px-6 xl:px-14 xl:py-7 xl:flex xl:max-w-[100%] xl:justify-between hover:'
)}
>
<div>
<h2
className={cn(
'font-semibold text-sm lg:text-2xl md:text-xl xl:text-[40px] xl:mb-2',
event.variant === 'main' &&
'text-[20px] mb-2 lg:text-5xl lg:mb-3 md:text-3xl xl:text-[45px]'
)}
>
{event.title}
</h2>
<p
className={cn(
'text-[8px] lg:text-[15px] md:text-[11px] xl:text-xl space-y-1',
event.variant === 'main' &&
'text-xs lg:text-xl md:text-[17px] md:leading-tight'
)}
>
{event.date} <br /> {event.location}
</p>
{event.variant === 'main' && (
<Button className="mx-auto mt-5 text-[10px] font-medium text-dark-green px-[10px] py-[6px] bg-primary hover:scale-105 hover:bg-primary transition-transform duration-300 rounded-full md:py-[9px] md:px-5 md:text-[12px] lg:py-[12px] lg:px-6 lg:text-[16px] xl:mx-0 xl:text-2xl">
<Link href="/404">Register Here</Link>
</Button>
)}
</div>

{event.variant === 'main' && (
<div className="hidden xl:block border-4 rounded-full border-midori-green"></div>
)}

{event.variant === 'main' && (
<div className="hidden xl:block mr-8">
<CountdownTimer eventDate={new Date(event.date).toISOString()} />
</div>
)}
</div>
);
}
78 changes: 78 additions & 0 deletions app/(home)/components/UpcomingEvents.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { Container } from '@/components/ui/container';
import React from 'react';
import EventCard from './EventCard';
import CountdownTimer from './CountdownTimer';
import { Button } from '@/components/ui/button';
import Link from 'next/link';

interface Event {
title: string;
date: string;
location: string;
variant: 'main' | 'regular';
link: string;
}

const EVENTS: Event[] = [
{
title: 'Pycon Mini Davao',
date: 'June 20, 2025',
location: 'Mugna Tech, Davao City',
variant: 'main',
link: '/404',
},
{
title: 'RAGs & DAGs',
date: 'June 20, 2025',
location: 'Mugna Tech, Davao City',
variant: 'regular',
link: '/404',
},
{
title: 'RAGs & DAGs',
date: 'June 20, 2025',
location: 'Mugna Tech, Davao City',
variant: 'regular',
link: '/404',
},
{
title: 'RAGs & DAGs',
date: 'June 20, 2025',
location: 'Mugna Tech, Davao City',
variant: 'regular',
link: '/404',
},
];

const UpcomingEvents = () => {
return (
<Container className="text-white space-y-4 lg:space-y-8">
{/* Title */}
<h1 className="font-montserrat font-bold text-center text-2xl xl:text-left lg:text-[80px] md:text-[70px] leading-[100%] xl:leading-normal">
Upcoming <span className="text-primary md:block xl:inline">Events</span>
</h1>

{/* Featured */}
<EventCard event={EVENTS[0]} />

{/* Countdown Timer */}
<div className="block lg:block xl:hidden">
<CountdownTimer eventDate={new Date(EVENTS[0].date).toISOString()} />
</div>

{/* Other events */}
<div className="grid grid-cols-1 gap-5 max-w-[40%] lg:max-w-[80%] md:max-w-[80%] mx-auto md:grid-cols-3 lg:grid-cols-3 xl:grid-cols-3 xl:max-w-full">
{EVENTS.slice(1).map((event, idx) => (
<EventCard key={idx} event={event} />
))}
</div>

{/* See more Events Button */}
<Button className="bg-primary hover:bg-primary mx-auto max-w-[80%] lg:max-w-[80%] md:max-w-[80%] w-full md:py-2 xl:py-4 md:font-medium text-dark-green lg:py-4 lg:text-[15px] py-[4px] font-semibold text-[11px] xl:text-2xl xl:max-w-full">
<Link href="/404">See More Events</Link>
</Button>
</Container>
);
};

export default UpcomingEvents;
4 changes: 4 additions & 0 deletions app/(home)/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { CTASection } from './components/CTASection';
import { StatsAndReviews } from './components/StatsAndReviews';
import { PythonFoundation } from './components//PythonFoundation';
import { Partners } from './components//Partners';
import UpcomingEvents from './components//UpcomingEvents';
import { Footer } from './components//Footer';

export default function HomePage() {
return (
Expand All @@ -14,6 +16,8 @@ export default function HomePage() {
<StatsAndReviews />
<PythonFoundation />
<Partners />
<UpcomingEvents />
<Footer />
</main>
);
}