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

[UXIT-1680] Event Page · Include moderators and speakers #767

Merged
merged 11 commits into from
Oct 29, 2024
25 changes: 25 additions & 0 deletions public/admin/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -720,6 +720,31 @@ collections:
- name: "description"
label: "Description"
widget: "text"
required: false
- name: "moderators"
label: "Moderators"
label_singular: "Moderator"
widget: "list"
fields:
- name: "name"
label: "Full Name"
widget: "string"
- name: "company"
label: "Company"
widget: "string"
required: false
- name: "speakers"
label: "Speakers"
label_singular: "Speaker"
widget: "list"
fields:
- name: "name"
label: "Full Name"
widget: "string"
- name: "company"
label: "Company"
widget: "string"
required: false
- name: "start"
label: "Start Time"
widget: "datetime"
Expand Down
6 changes: 2 additions & 4 deletions src/app/_hooks/useSort.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import { useMemo } from 'react'

import { type NextServerSearchParams } from '@/types/searchParams'
import type { NextServerSearchParams } from '@/types/searchParams'
import type { SortConfig } from '@/types/sortTypes'
import { type Object } from '@/types/utils'
import type { NonEmptyReadonlyArray, Object } from '@/types/utils'

import { SORT_KEY } from '@/constants/searchParams'

import { normalizeQueryParam } from '@/utils/queryUtils'

type NonEmptyReadonlyArray<T> = readonly [T, ...Array<T>]

type UseSortProps<
Entry extends Object,
Configs extends NonEmptyReadonlyArray<SortConfig<Entry>>,
Expand Down
2 changes: 2 additions & 0 deletions src/app/_types/utils.ts
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
export type NonEmptyArray<T> = [T, ...Array<T>]
export type NonEmptyReadonlyArray<T> = readonly [T, ...Array<T>]
export type Object = Record<string, unknown>
57 changes: 57 additions & 0 deletions src/app/events/[slug]/components/ScheduleSection/EventDetails.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
'use client'

import { BasicCard } from '@/components/BasicCard'
import { Heading } from '@/components/Heading'
import { TextLink } from '@/components/TextLink'

import type { Event } from '../../../schemas/ScheduleSchema'
import { formatTime } from '../../utils/dateUtils'

import { type ParticipantsProps, ScheduleParticipants } from './Participants'

export function EventDetails(event: Event) {
return (
<BasicCard>
<div className="grid gap-6 lg:grid-cols-3">
<div className="flex gap-6 text-brand-300 lg:flex-col lg:gap-1">
<div className="text-sm font-bold">
<span>{formatTime(event.start)}</span>
{event.end && <span> – {formatTime(event.end)}</span>}
</div>
<span className="text-sm">{event.location}</span>
</div>
<div className="lg:col-span-2">
<Heading tag="h3" variant="lg">
{event.title}
</Heading>
<div className="mt-2 space-y-2">
{event.description && (
<p className="max-w-readable">{event.description}</p>
)}
{event.moderators && event.moderators.length > 0 && (
<ScheduleParticipants
title="Moderators"
participants={
event.moderators as ParticipantsProps['participants']
}
Copy link
Collaborator

@CharlyMartin CharlyMartin Oct 29, 2024

Choose a reason for hiding this comment

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

If the list of participants cannot be empty, then it would be better to reflect in the schema instead of using an as type assertion here

So here we would have:

participants={event.moderators}

And in the EventSchema we would have:

moderators: z.array(ParticipantSchema).nonempty().optional(),

What do you think?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Oh, Yes! That's perfect. Thank you.

/>
)}
{event.speakers && event.speakers.length > 0 && (
<ScheduleParticipants
title="Speakers"
participants={
event.speakers as ParticipantsProps['participants']
Copy link
Collaborator

Choose a reason for hiding this comment

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

Same comment here :)

}
/>
)}
</div>
{event.url && (
<div className="mt-4">
<TextLink href={event.url}>View Details</TextLink>
</div>
)}
</div>
</div>
</BasicCard>
)
}
26 changes: 26 additions & 0 deletions src/app/events/[slug]/components/ScheduleSection/Participants.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import React from 'react'

import type { NonEmptyArray } from '@/types/utils'

import type { Participant } from '../../../schemas/ScheduleSchema'

export type ParticipantsProps = {
title: string
participants: NonEmptyArray<Participant>
}

export function ScheduleParticipants({
title,
participants,
}: ParticipantsProps) {
return (
<p className="text-sm">
<span className="font-semibold">{title}:</span>{' '}
<span>{participants.map(formatParticipant).join('; ')}</span>
</p>
)
}

function formatParticipant({ name, company }: Participant) {
return `${name}, ${company}`
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

UX improvement suggestion.

I find mixing commas and semicolons hard to read, it's difficult to tell if word is a person’s name or a company name when scanning the text.

Would that format be possible? Name (Company), Name (Company) and Name (Company)

I couldn't find a Figma design for this part so maybe this is already good to go.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I like that! Yes, apologies, should've mentioned that there's no Figma design yet.

Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { PageSection } from '@/components/PageSection'

import type { Event } from '../../types/eventType'
import type { Event } from '../../../types/eventType'

import { ScheduleTabs } from './ScheduleTabs'
import { Tabs } from './Tabs'

type ScheduleSectionProps = {
schedule: NonNullable<Event['schedule']>
Expand All @@ -11,7 +11,7 @@ type ScheduleSectionProps = {
export function ScheduleSection({ schedule }: ScheduleSectionProps) {
return (
<PageSection kicker="Join Us" title={schedule.title || 'Schedule'}>
<ScheduleTabs schedule={schedule} />
<Tabs schedule={schedule} />
</PageSection>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,19 @@ import { Tab, TabGroup, TabList, TabPanel, TabPanels } from '@headlessui/react'
import theme from 'tailwindcss/defaultTheme'
import { useIsMounted, useMediaQuery } from 'usehooks-ts'

import { BasicCard } from '@/components/BasicCard'
import { Heading } from '@/components/Heading'
import { TextLink } from '@/components/TextLink'
import type { Event } from '../../../types/eventType'
import { formatDate } from '../../utils/dateUtils'
import { filterAndSortScheduleDays } from '../../utils/filterAndSortScheduleDays'

import type { Event } from '../../types/eventType'
import { formatDate, formatTime } from '../utils/dateUtils'
import { filterAndSortScheduleDays } from '../utils/filterAndSortScheduleDays'
import { EventDetails } from './EventDetails'

type ScheduleTabsProps = {
type TabsProps = {
schedule: NonNullable<Event['schedule']>
}

const { screens } = theme

export function ScheduleTabs({ schedule }: ScheduleTabsProps) {
export function Tabs({ schedule }: TabsProps) {
const sortedDays = filterAndSortScheduleDays(schedule)

const tabGroupRef = useRef<HTMLDivElement>(null)
Expand Down Expand Up @@ -62,28 +60,7 @@ export function ScheduleTabs({ schedule }: ScheduleTabsProps) {
>
<div className="grid gap-4">
{day.events.map((event) => (
<BasicCard key={event.title}>
<div className="grid gap-6 lg:grid-cols-3">
<div className="flex gap-6 text-brand-300 lg:flex-col lg:gap-1">
<div className="text-sm font-bold">
<span>{formatTime(event.start)}</span>
{event.end && <span> – {formatTime(event.end)}</span>}
</div>
<span className="text-sm">{event.location}</span>
</div>
<div className="lg:col-span-2">
<Heading tag="h3" variant="lg">
{event.title}
</Heading>
<p className="mb-4 mt-2 max-w-readable">
{event.description}
</p>
{event.url && (
<TextLink href={event.url}>View Details</TextLink>
)}
</div>
</div>
</BasicCard>
<EventDetails key={event.title} {...event} />
))}
</div>
</TabPanel>
Expand Down
2 changes: 1 addition & 1 deletion src/app/events/[slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { isEventConcluded } from '../utils/isEventConcluded'

import { EventsSection } from './components/EventsSection'
import { RecapSection } from './components/RecapSection'
import { ScheduleSection } from './components/ScheduleSection'
import { ScheduleSection } from './components/ScheduleSection/ScheduleSection'
import { SpeakersSection } from './components/SpeakersSection'
import { SponsorSection } from './components/SponsorSection'
import { buildCtaArray } from './utils/buildCtaArray'
Expand Down
13 changes: 12 additions & 1 deletion src/app/events/schemas/ScheduleSchema.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
import { z } from 'zod'

const ParticipantSchema = z
.object({
name: z.string(),
company: z.string(),
})
.strict()

const EventSchema = z
.object({
title: z.string(),
description: z.string(),
description: z.string().optional(),
moderators: z.array(ParticipantSchema).optional(),
speakers: z.array(ParticipantSchema).optional(),
start: z.coerce.date(),
end: z.coerce.date().optional(),
location: z.string(),
Expand All @@ -25,4 +34,6 @@ export const ScheduleSchema = z
})
.strict()

export type Event = z.infer<typeof EventSchema>
export type Participant = z.infer<typeof ParticipantSchema>
export type Schedule = z.infer<typeof ScheduleSchema>
87 changes: 77 additions & 10 deletions src/content/events/fil-bangkok-2024.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,52 +115,119 @@ schedule:
start: 2024-11-08T09:00:00.000Z
end: 2024-11-08T20:00:00.000Z
- events:
- title: Test With Title Only
start: 2024-11-11T09:00:00.000Z
location: Samyan Mitrtown
- title: Test With Title & Link
start: 2024-11-11T09:00:00.000Z
location: Samyan Mitrtown
url: https://fil-dev.io/FDS-5
- title: Test With Title & Description
start: 2024-11-11T09:00:00.000Z
location: Samyan Mitrtown
description: "An engaging session about decentralized technology."
- title: Test With Title & Participants
start: 2024-11-11T09:00:00.000Z
location: Samyan Mitrtown
speakers:
- name: Clara Tsao
company: Filecoin Foundation
- title: Test With Title & Description & Link
start: 2024-11-11T09:00:00.000Z
location: Samyan Mitrtown
description: "An engaging session about decentralized technology."
url: https://fil-dev.io/FDS-5
- title: Test With Title & Description & Participants
start: 2024-11-11T09:00:00.000Z
location: Samyan Mitrtown
description: "An engaging session about decentralized technology."
speakers:
- name: Clara Tsao
company: Filecoin Foundation
- title: Test With Title & Link & Participants
start: 2024-11-11T09:00:00.000Z
location: Samyan Mitrtown
url: https://fil-dev.io/FDS-5
speakers:
- name: Clara Tsao
company: Filecoin Foundation
- title: Test With Title & Description & Link & Participants
start: 2024-11-11T09:00:00.000Z
location: Samyan Mitrtown
description: "An engaging session about decentralized technology."
url: https://fil-dev.io/FDS-5
speakers:
- name: Clara Tsao
company: Filecoin Foundation
- title: Doors Open
description: "Welcome to FIL Bangkok: DePIN Meets AI! Doors open at 9AM with
description:
"Welcome to FIL Bangkok: DePIN Meets AI! Doors open at 9AM with
Thai milk teas and networking. Come check out our sponsor booths and
massage stations!"
start: 2024-11-11T09:00:00.000Z
location: Samyan Mitrtown
- title: "Where DePIN Meets AI: A Filecoin Ecosystem Survey"
description: "Speaker: Clara Tsao, Filecoin Foundation"
speakers:
- name: Clara Tsao
company: Filecoin Foundation
start: 2024-11-11T15:02:00.000Z
end: 2024-11-11T15:12:00.000Z
location: "Samyan Mitrtown: Main Hall"
- title: "TRUST 2024: Combating Misinformation in Election Media with
Decentralized Tools"
description: "Speaker: Sherry Chung, Numbers Protocol"
speakers:
- name: Sherry Chung
company: Numbers Protocol
start: 2024-11-11T15:45:00.000Z
end: 2024-11-11T15:52:00.000Z
location: "Samyan Mitrtown: Main Hall"
- title: Unlocking Non-Financial Applications on Ethereum
description: "Speaker: Ted Liao, Glitter Protocol"
speakers:
- name: Ted Liao
company: Glitter Protocol
start: 2024-11-11T15:53:00.000Z
end: 2024-11-11T16:00:00.000Z
location: "Samyan Mitrtown: Main Hall"
- title: "L2 Spotlight: Introducing Decentralized Onchain Data Lakes"
description: "Speaker: Stefaan Vervaet, Akave"
speakers:
- name: Stefaan Vervaet
company: Akave.ai
start: 2024-11-11T16:01:00.000Z
end: 2024-11-11T16:08:00.000Z
location: "Samyan Mitrtown: Main Hall"
- title: "L2 Spotlight: Data Storage Doesn’t Have to be Spicy"
description: "Speaker: Alexander Kinstler, Storacha"
speakers:
- name: Alexander Kinstler
company: Storacha
location: "Samyan Mitrtown: Main Hall"
start: 2024-11-11T16:32:00.000Z
end: 2024-11-11T16:39:00.000Z
- title: Closing Remarks
description: "Speaker: Marta Belcher, Filecoin Foundation"
speakers:
- name: Marta Belcher
company: Filecoin Foundation
start: 2024-11-11T16:48:00.000Z
end: 2024-11-11T16:51:00.000Z
location: "Samyan Mitrtown: Main Hall"
- title: Onboarding a Million Users to DePIN
description: "Speaker: Roman, GhostDrive"
speakers:
- name: Roman
company: GhostDrive
start: 2024-11-11T15:29:00.000Z
end: 2024-11-11T15:36:00.000Z
location: "Samyan Mitrtown: Main Hall"
- location: "Samyan Mitrtown: Main Hall"
title: "Filecoin Nexus: Decentralized Tools for Environmental Change"
description: "Participants: Megan Klimen, Filecoin Foundation; Vitalii Yatskiv,
Boosty Labs; Alan Ransil, Devonian; and David Dao, GainForest"
moderators:
- name: Megan Klimen
company: Filecoin Foundation
speakers:
- name: Vitalii Yatskiv
company: Boosty Labs
- name: Alan Ransil
company: Devonian
- name: David Dao
company: GainForest
start: 2024-11-11T12:00:00.000Z
end: 2024-11-11T12:30:00.000Z
date: 2024-11-11T09:00:00.000Z
Expand Down
Loading