-
Notifications
You must be signed in to change notification settings - Fork 0
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: Add events page #66
base: dev
Are you sure you want to change the base?
Changes from 45 commits
7fe9d78
71ecab2
f596d8c
354c13e
a4623d9
e0d572d
8f40388
b55d8e0
af75395
84acf43
b2fa718
d3cc9a7
0ea7d99
f57378e
53772c0
fc6d96a
e19585e
8d29c29
1ff7d79
6bd2d07
df46e61
e40dffc
5fbca9c
1f6f993
cf9c8de
9b6367c
338fae4
7243def
fb4f949
00861fd
54cf2b6
52b9084
c3ccccb
024069b
7f1e439
eab2829
4ba5ca7
d47dc56
0c64648
0630307
b035055
a528ed5
4fe8c16
05ddfbd
dda95b6
0a46cc2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import { Button } from '@/components/ui/Button'; | ||
import { Link } from '@/lib/locale/navigation'; | ||
import { ArrowLeftIcon } from 'lucide-react'; | ||
import { getTranslations } from 'next-intl/server'; | ||
|
||
export default async function EventDetailsLayout({ | ||
children, | ||
}: { children: React.ReactNode }) { | ||
const t = await getTranslations('events'); | ||
return ( | ||
<> | ||
<Button variant='secondary' aria-label='Back to Events' asChild> | ||
<Link href='/events' className='flex gap-2'> | ||
<ArrowLeftIcon aria-hidden='true' /> | ||
{t('backToEvents')} | ||
</Link> | ||
</Button> | ||
{children} | ||
</> | ||
); | ||
} |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,27 @@ | ||||||
import { Separator } from '@/components/ui/Separator'; | ||||||
import { Skeleton } from '@/components/ui/Skeleton'; | ||||||
import { CalendarIcon, MapPinIcon } from 'lucide-react'; | ||||||
|
||||||
export default function EventDetailsLoading() { | ||||||
return ( | ||||||
<> | ||||||
<Skeleton className='my-4 h-12 w-3/4 rounded-lg' /> | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
To keep same layout as in the actual page |
||||||
<Skeleton className='h-8 w-1/2 rounded-lg' /> | ||||||
<div className='mt-4 space-y-4'> | ||||||
<div className='flex items-center gap-2'> | ||||||
<CalendarIcon className='h-8 w-8' /> | ||||||
<Skeleton className='h-6 w-64 rounded-lg' /> | ||||||
</div> | ||||||
<div className='flex items-center gap-2'> | ||||||
<MapPinIcon className='h-8 w-8' /> | ||||||
<Skeleton className='h-6 w-32 rounded-lg' /> | ||||||
</div> | ||||||
<Separator /> | ||||||
<div className='flex justify-between'> | ||||||
<Skeleton className='h-36 w-3/5 rounded-lg' /> | ||||||
<Skeleton className='h-48 w-48 rounded-full' /> | ||||||
</div> | ||||||
</div> | ||||||
</> | ||||||
); | ||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
import { format, isSameDay } from 'date-fns'; | ||
import { CalendarIcon, MapPinIcon } from 'lucide-react'; | ||
import { getTranslations, setRequestLocale } from 'next-intl/server'; | ||
import { notFound } from 'next/navigation'; | ||
|
||
import { Avatar, AvatarImage } from '@/components/ui/Avatar'; | ||
import { Badge } from '@/components/ui/Badge'; | ||
import { Separator } from '@/components/ui/Separator'; | ||
// TODO: Must be replaced with actual events | ||
import { events } from '@/mock-data/events'; | ||
|
||
export async function generateMetadata({ | ||
ZeroWave022 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
params, | ||
}: { | ||
params: Promise<{ locale: string; id: string }>; | ||
}) { | ||
const { id } = await params; | ||
const event = events.find((event) => event.id.toString() === id); | ||
|
||
return { | ||
title: `${event?.title}`, | ||
}; | ||
} | ||
|
||
export default async function EventDetailsPage({ | ||
params, | ||
}: { | ||
params: Promise<{ locale: string; id: string }>; | ||
}) { | ||
const { locale, id } = await params; | ||
setRequestLocale(locale); | ||
|
||
const t = await getTranslations('ui'); | ||
const event = events.find((event) => event.id.toString() === id); | ||
|
||
if (!event) return notFound(); | ||
|
||
const startDate = new Date(event.startTime); | ||
const endDate = new Date(event.endTime); | ||
|
||
const formattedRange = isSameDay(startDate, endDate) | ||
? `${format(startDate, 'HH:mm')} - ${format(endDate, 'HH:mm, dd.MM.yyyy')}` | ||
: `${format(startDate, 'HH:mm, dd/MM/yyyy')} - ${format(endDate, 'HH:mm, dd.MM.yyyy')}`; | ||
|
||
return ( | ||
<> | ||
<h1 className='my-4'>{event.title}</h1> | ||
<h2 className='border-b-0 text-2xl'>{event.subheader}</h2> | ||
<div className='mt-4 space-y-4'> | ||
{event.internal && ( | ||
<Badge className='rounded-full'>{t('internal')}</Badge> | ||
)} | ||
<div className='flex items-center gap-2'> | ||
<CalendarIcon className='h-8 w-8' /> | ||
{formattedRange} | ||
</div> | ||
<div className='flex items-center gap-2'> | ||
<MapPinIcon className='h-8 w-8' /> | ||
{event.location} | ||
</div> | ||
<Separator /> | ||
<div className='flex justify-between'> | ||
<div className='max-w-prose'> | ||
<p>{event.description}</p> | ||
</div> | ||
<Avatar className='h-48 w-48'> | ||
<AvatarImage src='/event.webp' alt='' className='object-cover' /> | ||
ZeroWave022 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
</Avatar> | ||
</div> | ||
</div> | ||
</> | ||
); | ||
} |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,27 @@ | ||||||
import { EventCardSkeleton } from '@/components/events/EventCardSkeleton'; | ||||||
import { getTranslations } from 'next-intl/server'; | ||||||
|
||||||
export default async function EventsSkeleton() { | ||||||
const t = await getTranslations('events'); | ||||||
return ( | ||||||
<> | ||||||
<h1 className='my-4'>{t('title')}</h1> | ||||||
<h2 className='my-2'>{t('activeEvents')}</h2> | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Consistent styling with other h2 elements |
||||||
<EventCardSkeleton /> | ||||||
<h2 className='my-4'>{t('upcomingEvents')}</h2> | ||||||
<div className='grid grid-cols-1 gap-2 lg:grid-cols-2'> | ||||||
<EventCardSkeleton /> | ||||||
<EventCardSkeleton /> | ||||||
<EventCardSkeleton /> | ||||||
<EventCardSkeleton /> | ||||||
</div> | ||||||
<h2 className='my-4'>{t('pastEvents')}</h2> | ||||||
<div className='grid grid-cols-1 gap-2 lg:grid-cols-2'> | ||||||
<EventCardSkeleton /> | ||||||
<EventCardSkeleton /> | ||||||
<EventCardSkeleton /> | ||||||
<EventCardSkeleton /> | ||||||
</div> | ||||||
</> | ||||||
); | ||||||
} |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -1,4 +1,9 @@ | ||||||
import { getTranslations, setRequestLocale } from 'next-intl/server'; | ||||||
import Link from 'next/link'; // using this instead of next-intl's link because we're staying on the same page | ||||||
|
||||||
import { EventCard } from '@/components/events/EventCard'; | ||||||
// TODO: Must be replaced with actual events | ||||||
import { events } from '@/mock-data/events'; | ||||||
|
||||||
export async function generateMetadata({ | ||||||
params, | ||||||
|
@@ -21,5 +26,84 @@ export default async function EventsPage({ | |||||
}) { | ||||||
const { locale } = await params; | ||||||
setRequestLocale(locale); | ||||||
return <div>This should be events page</div>; | ||||||
const t = await getTranslations('events'); | ||||||
const tUi = await getTranslations('ui'); | ||||||
|
||||||
const translations = { | ||||||
internal: tUi('internal'), | ||||||
startsAt: t('startsAt'), | ||||||
startedAt: t('startedAt'), | ||||||
endsAt: t('endsAt'), | ||||||
endedAt: t('endedAt'), | ||||||
}; | ||||||
|
||||||
return ( | ||||||
<> | ||||||
<h1 className='my-4'>{t('title')}</h1> | ||||||
<Link href='#active'> | ||||||
<h2 className='my-2' id='active'> | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Should have consistent spacing |
||||||
{t('activeEvents')} | ||||||
</h2> | ||||||
</Link> | ||||||
{events.slice(0, 1).map((event) => ( | ||||||
<EventCard | ||||||
key={event.id} | ||||||
wrapperClassName='block' | ||||||
event={event} | ||||||
t={{ | ||||||
detailsAboutEvent: t('detailsAboutEvent', { | ||||||
eventName: event.title, | ||||||
}), | ||||||
photoOf: tUi('photoOf', { name: event.title }), | ||||||
...translations, | ||||||
}} | ||||||
_active | ||||||
/> | ||||||
))} | ||||||
<Link href='#upcoming'> | ||||||
<h2 className='my-4' id='upcoming'> | ||||||
{t('upcomingEvents')} | ||||||
</h2> | ||||||
</Link> | ||||||
<div className='grid grid-cols-1 gap-2 lg:grid-cols-2'> | ||||||
{events.slice(1, 5).map((event) => ( | ||||||
<EventCard | ||||||
key={event.id} | ||||||
event={event} | ||||||
wrapperClassName='lg:last:odd:col-span-2' | ||||||
cardClassName='h-full' | ||||||
t={{ | ||||||
detailsAboutEvent: t('detailsAboutEvent', { | ||||||
eventName: event.title, | ||||||
}), | ||||||
photoOf: tUi('photoOf', { name: event.title }), | ||||||
...translations, | ||||||
}} | ||||||
/> | ||||||
))} | ||||||
</div> | ||||||
<Link href='#past'> | ||||||
<h2 className='my-4' id='past'> | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
mt-4 and my-4 doesn't add space to the top for some reason, pt-4 mb-4 will give the same styling as in loading.tsx |
||||||
{t('pastEvents')} | ||||||
</h2> | ||||||
</Link> | ||||||
<div className='grid grid-cols-1 gap-2 lg:grid-cols-2'> | ||||||
{events.slice(5).map((event) => ( | ||||||
<EventCard | ||||||
ZeroWave022 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
key={event.id} | ||||||
event={event} | ||||||
wrapperClassName='lg:last:odd:col-span-2' | ||||||
cardClassName='h-full' | ||||||
t={{ | ||||||
detailsAboutEvent: t('detailsAboutEvent', { | ||||||
eventName: event.title, | ||||||
}), | ||||||
photoOf: tUi('photoOf', { name: event.title }), | ||||||
...translations, | ||||||
}} | ||||||
/> | ||||||
))} | ||||||
</div> | ||||||
</> | ||||||
); | ||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Aria-label isn't translated