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: Add events page #66

Open
wants to merge 46 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 45 commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
7fe9d78
feat: Update Avatar component
ZeroWave022 Oct 24, 2024
71ecab2
feat: Add events page
ZeroWave022 Oct 24, 2024
f596d8c
feat: Update Avatar component
ZeroWave022 Oct 24, 2024
354c13e
feat: Add events page
ZeroWave022 Oct 24, 2024
a4623d9
Merge branch 'events' of https://github.com/hackerspace-ntnu/website-…
ZeroWave022 Oct 24, 2024
e0d572d
perf: Convert jpg to webp
ZeroWave022 Oct 26, 2024
8f40388
feat: Make EventCards into links
ZeroWave022 Oct 26, 2024
b55d8e0
refactor: Simpler keys for mock data
ZeroWave022 Oct 26, 2024
af75395
feat: Add page for each event
ZeroWave022 Oct 26, 2024
84acf43
refactor: Move event details back button to layout
ZeroWave022 Oct 26, 2024
b2fa718
feat: Add loading to events page
ZeroWave022 Oct 27, 2024
d3cc9a7
fix: Add ARIA label to button
ZeroWave022 Oct 27, 2024
0ea7d99
chore: Add page to lighthouse CI
ZeroWave022 Oct 27, 2024
f57378e
fix: ARIA label for event cards
ZeroWave022 Oct 27, 2024
53772c0
fix: Make event cards grid responsive
ZeroWave022 Oct 27, 2024
fc6d96a
Merge branch 'dev' into events
ZeroWave022 Oct 28, 2024
e19585e
fix: Use event.id as key instead of useId
ZeroWave022 Oct 28, 2024
8d29c29
feat: Add loading page to event details page
ZeroWave022 Oct 31, 2024
1ff7d79
feat: Translate event pages
ZeroWave022 Oct 31, 2024
6bd2d07
feat: Mark internal events with badge
ZeroWave022 Oct 31, 2024
df46e61
Merge branch 'dev' into events
ZeroWave022 Oct 31, 2024
e40dffc
refactor: Linting
ZeroWave022 Oct 31, 2024
5fbca9c
fix: Ignore label content name mismatch warning
ZeroWave022 Nov 7, 2024
1f6f993
refactor: lint
ZeroWave022 Nov 7, 2024
cf9c8de
fix: Event card skeletons on mobile show correctly
ZeroWave022 Nov 8, 2024
9b6367c
fix: Correct grid cols on events loading
ZeroWave022 Nov 8, 2024
338fae4
fix: Use 24-hour clock on event cards, add alt to image
ZeroWave022 Nov 8, 2024
7243def
fix: Clean up css for event card skeletons, fix regressions on mobile
ZeroWave022 Nov 8, 2024
fb4f949
refactor: Use absolute path for component imports
ZeroWave022 Nov 8, 2024
00861fd
fix: Translate events loading
ZeroWave022 Nov 8, 2024
54cf2b6
refactor: Use more sensible order of components in events layout
ZeroWave022 Nov 8, 2024
52b9084
fix: Translate internal badge
ZeroWave022 Nov 8, 2024
c3ccccb
Merge branch 'dev' into events
ZeroWave022 Nov 8, 2024
024069b
refactor: Use date-fns for dates
ZeroWave022 Nov 10, 2024
7f1e439
fix: Change h3 to h2
ZeroWave022 Nov 10, 2024
eab2829
refactor: Add event prop to EventCard
ZeroWave022 Nov 10, 2024
4ba5ca7
fix: Correct routing of event page
ZeroWave022 Nov 10, 2024
d47dc56
fix: Add translation to image alt
ZeroWave022 Nov 10, 2024
0c64648
refactor: Use p instead of span for event card time info
ZeroWave022 Nov 10, 2024
0630307
refactor: lint
ZeroWave022 Nov 10, 2024
b035055
fix: event card skeleton on smaller screens
ZeroWave022 Nov 14, 2024
a528ed5
refactor: Make event cards a client component, fix rings
ZeroWave022 Nov 14, 2024
4fe8c16
feat: Add hover effect to event cards
ZeroWave022 Nov 21, 2024
05ddfbd
feat: Starts/ends event card text in bold
ZeroWave022 Nov 21, 2024
dda95b6
feat: Event headings are clickable
ZeroWave022 Nov 21, 2024
0a46cc2
fix: Skeleton for small screens on single event page
ZeroWave022 Nov 21, 2024
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
16 changes: 15 additions & 1 deletion lighthouserc.cjs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const PAGES_EXCLUDED = ['news', 'storage'];
const PAGES_EXCLUDED = ['news', 'events', 'storage'];

// Do not convert into an ES6 export.
// lighthouse-ci (as of 0.14.0) uses require() to import, and this is not supported with ES6 modules.
Expand All @@ -9,6 +9,7 @@ const config = {
'http://localhost:3000/en/', // Trailing slash required, else the regex for default lighthouse rules won't catch this one
'http://localhost:3000/en/about',
'http://localhost:3000/en/events',
'http://localhost:3000/en/events/1',
'http://localhost:3000/en/news',
'http://localhost:3000/en/news/1',
'http://localhost:3000/en/storage',
Expand Down Expand Up @@ -68,6 +69,19 @@ const config = {
'image-aspect-ratio': 'off', // Should be removed when we obtain images from backend
},
},
{
matchingUrlPattern: 'http://.*/en/events.*',
preset: 'lighthouse:recommended',
assertions: {
'bf-cache': 'off',
'color-contrast': 'off',
'heading-order': 'off',
'largest-contentful-paint': 'off',
'render-blocking-resources': 'off',
'uses-responsive-images': 'off',
'label-content-name-mismatch': 'off',
},
},
],
},
},
Expand Down
15 changes: 14 additions & 1 deletion messages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
"selectMonth": "Select month",
"selectYear": "Select year",
"pickDate": "Pick a date",
"dateFormat": "dd/MM/yyyy"
"dateFormat": "dd/MM/yyyy",
"internal": "Internal"
},
"error": {
"notFound": "404 - Page not found",
Expand Down Expand Up @@ -156,5 +157,17 @@
}
}
}
},
"events": {
"title": "Events",
"activeEvents": "Active events",
"upcomingEvents": "Upcoming events",
"pastEvents": "Past events",
"detailsAboutEvent": "Read more about {eventName}",
"startsAt": "Starts at",
"startedAt": "Started at",
"endsAt": "Ends at",
"endedAt": "Ended at",
"backToEvents": "Back to Events"
}
}
15 changes: 14 additions & 1 deletion messages/no.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
"selectMonth": "Velg måned",
"selectYear": "Velg år",
"pickDate": "Velg en dato",
"dateFormat": "dd.MM.yyyy"
"dateFormat": "dd.MM.yyyy",
"internal": "Intern"
},
"error": {
"notFound": "404 - Siden ble ikke funnet",
Expand Down Expand Up @@ -156,5 +157,17 @@
}
}
}
},
"events": {
"title": "Arrangementer",
"activeEvents": "Pågående arrangementer",
"upcomingEvents": "Kommende arrangementer",
"pastEvents": "Tidligere arrangementer",
"detailsAboutEvent": "Les mer om {eventName}",
"startsAt": "Starter kl.",
"startedAt": "Startet kl.",
"endsAt": "Slutter kl.",
"endedAt": "Sluttet kl.",
"backToEvents": "Tilbake til arrangementer"
}
}
Binary file added public/event.webp
Binary file not shown.
21 changes: 21 additions & 0 deletions src/app/[locale]/(default)/events/[id]/layout.tsx
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>
Copy link
Member

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

<Link href='/events' className='flex gap-2'>
<ArrowLeftIcon aria-hidden='true' />
{t('backToEvents')}
</Link>
</Button>
{children}
</>
);
}
27 changes: 27 additions & 0 deletions src/app/[locale]/(default)/events/[id]/loading.tsx
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' />
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
<Skeleton className='my-4 h-12 w-3/4 rounded-lg' />
<Skeleton className='mt-6 mb-4 h-12 w-3/4 rounded-lg' />

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>
</>
);
}
73 changes: 73 additions & 0 deletions src/app/[locale]/(default)/events/[id]/page.tsx
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>
</>
);
}
27 changes: 27 additions & 0 deletions src/app/[locale]/(default)/events/loading.tsx
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>
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
<h2 className='my-2'>{t('activeEvents')}</h2>
<h2 className='my-4'>{t('activeEvents')}</h2>

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>
</>
);
}
86 changes: 85 additions & 1 deletion src/app/[locale]/(default)/events/page.tsx
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,
Expand All @@ -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'>
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
<h2 className='my-2' id='active'>
<h2 className='pt-4 mb-4' id='active'>

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'>
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
<h2 className='my-4' id='past'>
<h2 className='pt-4 mb-4' id='past'>

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>
</>
);
}
2 changes: 1 addition & 1 deletion src/components/composites/CategorySelector.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
'use client';

import { Combobox } from '@/components/ui/Combobox';
import { useQueryState } from 'nuqs';
import { parseAsString } from 'nuqs/server';
import { Combobox } from '../ui/Combobox';

type CategorySelectorProps = {
categories: {
Expand Down
Loading