diff --git a/src/app/(main)/community/events/event-card.tsx b/src/app/(main)/community/events/event-card.tsx index d199988f22..d1052a2330 100644 --- a/src/app/(main)/community/events/event-card.tsx +++ b/src/app/(main)/community/events/event-card.tsx @@ -4,7 +4,11 @@ import { clsx } from "clsx" import { CalendarIcon } from "@/app/conf/_design-system/pixelarticons/calendar-icon" import { PinIcon } from "@/app/conf/_design-system/pixelarticons/pin-icon" import { Tag } from "@/app/conf/_design-system/tag" -import { EventKind, eventTagColors } from "./event-filter-tag" +import { + EventKind, + eventTagColors, + eventTagDescriptions, +} from "./event-filter-tag" const dateFormatter = new Intl.DateTimeFormat("en", { day: "numeric", @@ -55,7 +59,8 @@ export interface EventCardProps { city: ReactNode name: ReactNode meta?: ReactNode - kind: "meetup" | "conference" | "working-group" | "foundation-meeting" + kind: EventKind + compact?: boolean className?: string } @@ -65,6 +70,7 @@ const classNameFromKind = { meetup: "bg-[hsl(var(--color-sec-base)/var(--bg-opacity))]", "working-group": "bg-[hsl(229deg_100%_70.4%_/_var(--bg-opacity))]", "foundation-meeting": "bg-[hsl(35deg_100%_63%_/_var(--bg-opacity))]", + workshop: "bg-[hsl(152deg_53%_48%_/_var(--bg-opacity))]", } satisfies { [kind in EventKind]: string } export function EventCard({ @@ -74,6 +80,7 @@ export function EventCard({ name, meta, kind, + compact, className, }: EventCardProps) { const dateLabel = formatDateLabel(date) @@ -82,26 +89,46 @@ export function EventCard({ -
+
- {kind.replace("-", " ")} - {!!meta && ( + + {kind.replace("-", " ")} + + {!!meta && compact && ( + + Hosted by: {meta} + + )} + {!!meta && !compact && ( -
+
{name}
diff --git a/src/app/(main)/community/events/events-list.tsx b/src/app/(main)/community/events/events-list.tsx index 003c9ddec5..dff8d45478 100644 --- a/src/app/(main)/community/events/events-list.tsx +++ b/src/app/(main)/community/events/events-list.tsx @@ -6,10 +6,11 @@ import { clsx } from "clsx" import type { WorkingGroupMeeting as CalendarEvent } from "@/../scripts/sync-working-groups/sync-working-groups" import { EventCard } from "./event-card" -import { EventsScrollview } from "./events-scrollview" import type { Event } from "./events" import { EventFilterTag, EventKind } from "./event-filter-tag" +const PAGE_SIZE = 12 + interface FilterChipProps extends ComponentPropsWithoutRef<"button"> { active?: boolean count?: number @@ -61,10 +62,13 @@ type AnyEvent = Event | CalendarEvent const DEFAULT_VISIBILITY = { conference: true, meetup: true, - "working-group": true, - "foundation-meeting": true, + "working-group": false, + "foundation-meeting": false, + workshop: true, } satisfies Record +const tags = Object.keys(DEFAULT_VISIBILITY) as EventKind[] + const DAY_IN_MS = 24 * 60 * 60 * 1000 // We want to show at least one of each WG, a month might be 31 days long and @@ -164,30 +168,21 @@ export function EventsList({ className?: string children?: React.ReactNode }) { - const [kindFilters, setKindFilters] = useState(DEFAULT_VISIBILITY) + const [kindFilters, setKindFilters] = + useState>(DEFAULT_VISIBILITY) + const [limit, setLimit] = useState(PAGE_SIZE) - const { events, tags } = useMemo(() => { - const visibleTags: Set = new Set() - /** Determines if this event kind should be shown, and if so ensures the tag is present in tags */ + const events = useMemo(() => { + /** Determines if this event kind should be shown */ const shouldShow = (kind: EventKind, date?: Date): boolean => { if (kind === "working-group" || kind === "foundation-meeting") { if (!date) return false // we filter out all working groups further in the future than 30 days if (!isSoon(date)) return false } - visibleTags.add(kind) return kindFilters[kind] } - const majorEvents: AnyEvent[] = [] - const minorEvents: AnyEvent[] = [] - - // Prioritize major vs minor events - const target = { - conference: majorEvents, - meetup: majorEvents, - "working-group": minorEvents, - "foundation-meeting": minorEvents, - } satisfies { [kind in EventKind]: AnyEvent[] } + const events: AnyEvent[] = [] for (const event of allEvents) { const kind = categorizeEvent(event) @@ -203,30 +198,23 @@ export function EventsList({ } else if ("start" in event) { const date = new Date(event.start) if (shouldShow(kind, date)) { - target[kind].push(event) + events.push(event) } } else { if (shouldShow(kind)) { - target[kind].push(event) + events.push(event) } } } - // Ensure that major events are surfaced higher than minor events, even though this breaks date order - const events = [...majorEvents, ...minorEvents] - const keys = Object.keys(DEFAULT_VISIBILITY) - // Sort tags into a sensible order - const tags = [...visibleTags].sort( - (a, z) => keys.indexOf(a) - keys.indexOf(z), - ) - return { events, tags } + return events }, [allEvents, kindFilters]) return (
-
+
Event type -
+
{tags.map(tag => ( {children}
- - {events.map(event => { +
+ {events.slice(0, limit).map(event => { const kind = categorizeEvent(event) if (kind === "duplicate" || kind == null) { return null @@ -263,6 +251,7 @@ export function EventsList({ name={calendarEventName(event)} city={calendarEventCity(event)} kind={kind} + compact /> ) : ( ) })} - + {events.length > limit && ( + + )} + {events.length <= limit && tags.some(tag => !kindFilters[tag]) && ( + + )} +
) } diff --git a/src/app/(main)/community/events/events.ts b/src/app/(main)/community/events/events.ts index 5f16be22bd..e586aea282 100644 --- a/src/app/(main)/community/events/events.ts +++ b/src/app/(main)/community/events/events.ts @@ -9,7 +9,7 @@ export interface Event { date: string coverImage?: string eventLink: string - host: string + host?: string hostLink?: string kind?: EventKind } @@ -30,52 +30,61 @@ const allEvents: Event[] = [ location: "Zoom", date: "2026-09-22T17:00:00Z", eventLink: "https://guild.host/events/graphql-virtual-meetup-zywc3q", - host: "GraphQL Foundation", kind: "meetup", }, { - name: "GraphQL Day at FOST London", + name: "From Zero to Agentic Query in One Afternoon", + slug: "jonthebeach-workshop-2026", + location: "Málaga, Spain", + date: "2026-10-29T09:00:00+02:00", + eventLink: "https://jonthebeach.com/workshops/from-zero-to-agentic/", + host: "J On The Beach", + hostLink: "https://jonthebeach.com", + kind: "workshop" as const, + }, + { + name: "GraphQL Day London", slug: "graphql-day-fost-london-2026", location: "London", date: "2026-10-01T09:00:00+01:00", eventLink: "/day/2026/london", - host: "FOST & GraphQL Community", + host: "FOST London", hostLink: "https://www.joinfost.io", }, { - name: "GraphQL Days at FOST Paris", + name: "GraphQL Days Paris", slug: "graphql-day-fost-paris-2026", location: "Paris", date: "2026-12-01T09:00:00+01:00", eventLink: "/day/2026/paris", - host: "FOST & GraphQL Community", + host: "FOST Paris", hostLink: "https://www.joinfost.io", }, { - name: "GraphQL Day at FOST Melbourne", + name: "GraphQL Day Melbourne", slug: "graphql-day-fost-melbourne-2026", location: "Melbourne", date: "2026-10-28T09:00:00+11:00", eventLink: "/day/2026/melbourne", - host: "FOST & GraphQL Community", + host: "FOST Melbourne", hostLink: "https://www.joinfost.io", }, { - name: "GraphQL Day at FOST Bengaluru", + name: "GraphQL Day Bengaluru", slug: "graphql-day-fost-bengaluru-2026", location: "Bengaluru", date: "2026-08-19T09:00:00+05:30", eventLink: "/day/2026/bengaluru", - host: "FOST & GraphQL Community", + host: "FOST Bengaluru", hostLink: "https://www.joinfost.io", }, { - name: "GraphQL Day at FOST Amsterdam", + name: "GraphQL Day Amsterdam", slug: "graphql-day-fost-amsterdam-2026", location: "Amsterdam", date: "2026-06-09T09:00:00+01:00", eventLink: "/day/2026/amsterdam", - host: "FOST & GraphQL Community", + host: "FOST Amsterdam", hostLink: "https://www.joinfost.io", }, { @@ -84,15 +93,14 @@ const allEvents: Event[] = [ location: "Fremont, California", date: "2026-05-21T16:30:00+00:00", eventLink: "/conf/2026/wg-day", - host: "GraphQL Foundation", }, { - name: "GraphQL Day at FOST NYC", + name: "GraphQL Day NYC", slug: "graphql-day-fost-nyc-2026", location: "New York", date: "2026-05-13T09:00:00-04:00", eventLink: "/day/2026/nyc", - host: "FOST & GraphQL Community", + host: "FOST NYC", hostLink: "https://www.joinfost.io", }, { @@ -101,15 +109,14 @@ const allEvents: Event[] = [ location: "Fremont, California", date: "2026-05-19T16:00:00+00:00", eventLink: "/conf/2026", - host: "GraphQL Foundation", }, { - name: "GraphQL Day at FOST Singapore", + name: "GraphQL Day Singapore", slug: "graphql-day-fost-singapore-2026", location: "Singapore", date: "2026-04-14T09:00:00+08:00", eventLink: "/day/2026/singapore", - host: "FOST & GraphQL Community", + host: "FOST Singapore", hostLink: "https://www.joinfost.io", }, { @@ -127,7 +134,6 @@ const allEvents: Event[] = [ location: "Amsterdam", date: "2025-09-08T07:00:00+00:00", eventLink: "/conf/2025", - host: "GraphQL Foundation", }, { name: "GraphQLConf 2024", @@ -135,7 +141,6 @@ const allEvents: Event[] = [ location: "San Francisco", date: "2024-09-09T07:00:00+00:00", eventLink: "/conf/2024", - host: "GraphQL Foundation", }, { name: "GraphQL Bali 001", diff --git a/src/app/(main)/community/events/page.tsx b/src/app/(main)/community/events/page.tsx index e6fdf2f396..6211721821 100644 --- a/src/app/(main)/community/events/page.tsx +++ b/src/app/(main)/community/events/page.tsx @@ -36,7 +36,7 @@ export default async function EventsPage() { sections={[ "Upcoming events", "Meetups", - { name: "Past events & meetups", href: "#past-events-and-meetups" }, + { name: "Past events", href: "#past-events" }, "Events gallery", "Benefits of getting involved", "Get your meetup noticed", @@ -86,7 +86,7 @@ export default async function EventsPage() { - @@ -104,8 +104,8 @@ export default async function EventsPage() { -
-

Past events & meetups

+
+

Past events

A look back at how the GraphQL community connects and grows together. diff --git a/src/app/(main)/community/events/rss.xml/route.tsx b/src/app/(main)/community/events/rss.xml/route.tsx index 90c35d4f5e..3bbfcc0433 100644 --- a/src/app/(main)/community/events/rss.xml/route.tsx +++ b/src/app/(main)/community/events/rss.xml/route.tsx @@ -30,7 +30,7 @@ export async function GET() { title: event.name, date: event.date, url: event.eventLink, - description: `Host: ${event.host} | Location: ${event.location}`, + description: `${event.host ? `Host: ${event.host} | ` : ""}Location: ${event.location}`, }) } }