From 807daea4f5c49b2e3a098b570fbc2a59cc17a77d Mon Sep 17 00:00:00 2001 From: Martin Bonnin Date: Tue, 23 Jun 2026 14:21:21 +0200 Subject: [PATCH 1/7] Add workshop events and https://jonthebeach.com/workshops/from-zero-to-agentic/ as the first workshop --- src/app/(main)/community/events/event-card.tsx | 3 ++- src/app/(main)/community/events/event-filter-tag.tsx | 2 ++ src/app/(main)/community/events/events-list.tsx | 2 ++ src/app/(main)/community/events/events.ts | 11 +++++++++++ 4 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/app/(main)/community/events/event-card.tsx b/src/app/(main)/community/events/event-card.tsx index d199988f22..b2a20a2515 100644 --- a/src/app/(main)/community/events/event-card.tsx +++ b/src/app/(main)/community/events/event-card.tsx @@ -55,7 +55,7 @@ export interface EventCardProps { city: ReactNode name: ReactNode meta?: ReactNode - kind: "meetup" | "conference" | "working-group" | "foundation-meeting" + kind: EventKind className?: string } @@ -65,6 +65,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({ diff --git a/src/app/(main)/community/events/event-filter-tag.tsx b/src/app/(main)/community/events/event-filter-tag.tsx index e1ba7978c6..e5f1675613 100644 --- a/src/app/(main)/community/events/event-filter-tag.tsx +++ b/src/app/(main)/community/events/event-filter-tag.tsx @@ -7,12 +7,14 @@ export type EventKind = | "conference" | "working-group" | "foundation-meeting" + | "workshop" export const eventTagColors = { conference: "hsl(var(--color-pri-base))", meetup: "hsl(var(--color-sec-dark))", "working-group": "#6883FF", "foundation-meeting": "#FFB140", + workshop: "#3DBE7E", } satisfies { [kind in EventKind]: string } export interface EventFilterTagProps diff --git a/src/app/(main)/community/events/events-list.tsx b/src/app/(main)/community/events/events-list.tsx index 003c9ddec5..39fafc7f73 100644 --- a/src/app/(main)/community/events/events-list.tsx +++ b/src/app/(main)/community/events/events-list.tsx @@ -61,6 +61,7 @@ type AnyEvent = Event | CalendarEvent const DEFAULT_VISIBILITY = { conference: true, meetup: true, + workshop: true, "working-group": true, "foundation-meeting": true, } satisfies Record @@ -185,6 +186,7 @@ export function EventsList({ const target = { conference: majorEvents, meetup: majorEvents, + workshop: majorEvents, "working-group": minorEvents, "foundation-meeting": minorEvents, } satisfies { [kind in EventKind]: AnyEvent[] } diff --git a/src/app/(main)/community/events/events.ts b/src/app/(main)/community/events/events.ts index 5f16be22bd..57c5a79c4d 100644 --- a/src/app/(main)/community/events/events.ts +++ b/src/app/(main)/community/events/events.ts @@ -1,6 +1,7 @@ import type { EventKind } from "./event-filter-tag" export * from "./event-card" +import type { EventKind } from "./event-filter-tag" export interface Event { name: string @@ -33,6 +34,16 @@ const allEvents: Event[] = [ host: "GraphQL Foundation", kind: "meetup", }, + { + 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 at FOST London", slug: "graphql-day-fost-london-2026", From 1274e7db49e66cde7e2de2feda330466e565d3de Mon Sep 17 00:00:00 2001 From: Martin Bonnin Date: Tue, 20 Oct 2026 20:35:11 +0200 Subject: [PATCH 2/7] Sort in a single dimension --- .../(main)/community/events/event-card.tsx | 44 ++++++++++++--- .../(main)/community/events/events-list.tsx | 56 +++++++++---------- src/app/(main)/community/events/events.ts | 28 +++++----- src/app/(main)/community/events/page.tsx | 6 +- 4 files changed, 79 insertions(+), 55 deletions(-) diff --git a/src/app/(main)/community/events/event-card.tsx b/src/app/(main)/community/events/event-card.tsx index b2a20a2515..fdab820af9 100644 --- a/src/app/(main)/community/events/event-card.tsx +++ b/src/app/(main)/community/events/event-card.tsx @@ -56,6 +56,7 @@ export interface EventCardProps { name: ReactNode meta?: ReactNode kind: EventKind + compact?: boolean className?: string } @@ -75,6 +76,7 @@ export function EventCard({ name, meta, kind, + compact, className, }: EventCardProps) { const dateLabel = formatDateLabel(date) @@ -83,26 +85,44 @@ export function EventCard({ -
+
{kind.replace("-", " ")} - {!!meta && ( + {!!meta && compact && ( + + Hosted by: {meta} + + )} + {!!meta && !compact && ( -
+
{name}
{ active?: boolean count?: number @@ -61,11 +62,13 @@ type AnyEvent = Event | CalendarEvent const DEFAULT_VISIBILITY = { conference: true, meetup: true, - workshop: true, "working-group": true, "foundation-meeting": true, + 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 @@ -166,30 +169,19 @@ export function EventsList({ children?: React.ReactNode }) { 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, - workshop: majorEvents, - "working-group": minorEvents, - "foundation-meeting": minorEvents, - } satisfies { [kind in EventKind]: AnyEvent[] } + const events: AnyEvent[] = [] for (const event of allEvents) { const kind = categorizeEvent(event) @@ -205,22 +197,15 @@ 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 ( @@ -250,8 +235,8 @@ export function EventsList({ {children}
- - {events.map(event => { +
+ {events.slice(0, limit).map(event => { const kind = categorizeEvent(event) if (kind === "duplicate" || kind == null) { return null @@ -265,6 +250,7 @@ export function EventsList({ name={calendarEventName(event)} city={calendarEventCity(event)} kind={kind} + compact /> ) : ( ) })} - + {events.length > limit && ( + + )} +
) } diff --git a/src/app/(main)/community/events/events.ts b/src/app/(main)/community/events/events.ts index 57c5a79c4d..18830592ed 100644 --- a/src/app/(main)/community/events/events.ts +++ b/src/app/(main)/community/events/events.ts @@ -45,48 +45,48 @@ const allEvents: Event[] = [ kind: "workshop" as const, }, { - name: "GraphQL Day at FOST London", + 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", }, { @@ -98,12 +98,12 @@ const allEvents: Event[] = [ 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", }, { @@ -115,12 +115,12 @@ const allEvents: Event[] = [ 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", }, { diff --git a/src/app/(main)/community/events/page.tsx b/src/app/(main)/community/events/page.tsx index e6fdf2f396..8e700568de 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", @@ -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. From e827a2fcd9e6773ba3ce2ba5a793aadcbaadcb1c Mon Sep 17 00:00:00 2001 From: Martin Bonnin Date: Wed, 21 Oct 2026 10:42:47 +0200 Subject: [PATCH 3/7] fix build --- src/app/(main)/community/events/events.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/app/(main)/community/events/events.ts b/src/app/(main)/community/events/events.ts index 18830592ed..8c5dc45876 100644 --- a/src/app/(main)/community/events/events.ts +++ b/src/app/(main)/community/events/events.ts @@ -1,7 +1,6 @@ import type { EventKind } from "./event-filter-tag" export * from "./event-card" -import type { EventKind } from "./event-filter-tag" export interface Event { name: string From d54d9678dee605fcf8850c7d1c3d37ca7d266eb3 Mon Sep 17 00:00:00 2001 From: Martin Bonnin Date: Thu, 22 Oct 2026 12:29:28 +0200 Subject: [PATCH 4/7] remove "Hosted by:" for the virtual meetup --- src/app/(main)/community/events/events.ts | 3 +-- src/app/(main)/community/events/rss.xml/route.tsx | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/app/(main)/community/events/events.ts b/src/app/(main)/community/events/events.ts index 8c5dc45876..7cb9fcda81 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,7 +30,6 @@ 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", }, { 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}`, }) } } From cb1bafa688d267b7bc60d3b714b8a5a613afcf6a Mon Sep 17 00:00:00 2001 From: Martin Bonnin Date: Fri, 23 Oct 2026 11:13:40 +0200 Subject: [PATCH 5/7] Filter out working groups by default Add a "Show all event types button" if there are not a lot of events Fix mobile UI --- .../(main)/community/events/events-list.tsx | 27 +++++++++++++++---- src/app/(main)/community/events/page.tsx | 2 +- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/app/(main)/community/events/events-list.tsx b/src/app/(main)/community/events/events-list.tsx index 144ad6a1a5..dff8d45478 100644 --- a/src/app/(main)/community/events/events-list.tsx +++ b/src/app/(main)/community/events/events-list.tsx @@ -62,8 +62,8 @@ 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 @@ -168,7 +168,8 @@ 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 = useMemo(() => { @@ -210,10 +211,10 @@ export function EventsList({ return (

-
+
Event type -
+
{tags.map(tag => ( )} + {events.length <= limit && tags.some(tag => !kindFilters[tag]) && ( + + )}
) diff --git a/src/app/(main)/community/events/page.tsx b/src/app/(main)/community/events/page.tsx index 8e700568de..6211721821 100644 --- a/src/app/(main)/community/events/page.tsx +++ b/src/app/(main)/community/events/page.tsx @@ -86,7 +86,7 @@ export default async function EventsPage() { -
From 5a2ee3b444a391be5e768e698cef8571af3b7d76 Mon Sep 17 00:00:00 2001 From: Martin Bonnin Date: Fri, 23 Oct 2026 11:31:05 +0200 Subject: [PATCH 6/7] add tooltip --- src/app/(main)/community/events/event-card.tsx | 10 ++++++++-- src/app/(main)/community/events/event-filter-tag.tsx | 10 ++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/app/(main)/community/events/event-card.tsx b/src/app/(main)/community/events/event-card.tsx index fdab820af9..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", @@ -113,7 +117,9 @@ export function EventCard({ : "-mb-2 pt-2 xs:-mb-4 xs:pt-3"), )} > - {kind.replace("-", " ")} + + {kind.replace("-", " ")} + {!!meta && compact && ( , "onChange"> { kind: EventKind @@ -34,6 +43,7 @@ export function EventFilterTag({ }: EventFilterTagProps) { return (