Skip to content

Commit 7747ef0

Browse files
authored
fix filtering in events page (supabase#40697)
* fix filtering in events page * make agency on-demand
1 parent b709428 commit 7747ef0

File tree

7 files changed

+129
-17
lines changed

7 files changed

+129
-17
lines changed

apps/www/_events/2025-11-20-supabase-agency-webinar.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ meta_description: >-
88
Learn how leading agencies use Postgres expertise and strategic services to
99
command premium pricing while competitors race to the bottom on AI-built MVPs.
1010
type: webinar
11-
onDemand: false
11+
onDemand: true
1212
date: '2025-11-20T07:00:00.000-08:00'
1313
timezone: America/Los_Angeles
1414
duration: 45 mins

apps/www/app/events/context.tsx

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@
22

33
import { createContext, useContext, useState, useEffect, useMemo, ReactNode } from 'react'
44
import { SupabaseEvent, SUPABASE_HOST } from '~/lib/eventsTypes'
5-
import { cover } from 'three/src/extras/TextureUtils.js'
65

76
interface EventsContextValue {
87
// Events data
98
allEvents: SupabaseEvent[]
109
filteredEvents: SupabaseEvent[]
10+
filteredOnDemandEvents: SupabaseEvent[]
1111
staticEvents: SupabaseEvent[]
12+
onDemandEvents: SupabaseEvent[]
1213
lumaEvents: SupabaseEvent[]
1314
featuredEvent: SupabaseEvent | undefined
1415

@@ -30,9 +31,10 @@ const EventsContext = createContext<EventsContextValue | undefined>(undefined)
3031
interface EventsProviderProps {
3132
children: ReactNode
3233
staticEvents: SupabaseEvent[]
34+
onDemandEvents: SupabaseEvent[]
3335
}
3436

35-
export function EventsProvider({ children, staticEvents }: EventsProviderProps) {
37+
export function EventsProvider({ children, staticEvents, onDemandEvents }: EventsProviderProps) {
3638
const [lumaEvents, setLumaEvents] = useState<SupabaseEvent[]>([])
3739
const [isLoading, setIsLoading] = useState(true)
3840
const [searchQuery, setSearchQuery] = useState('')
@@ -99,9 +101,12 @@ export function EventsProvider({ children, staticEvents }: EventsProviderProps)
99101
}, [staticEvents, lumaEvents])
100102

101103
// Calculate categories with counts
104+
// - Webinar: count only upcoming webinars (not on-demand)
105+
// - On-demand: count only on-demand events
102106
const categories = useMemo(() => {
103107
const categoryCounts: { [key: string]: number } = { all: 0 }
104108

109+
// Count upcoming events (excluding on-demand)
105110
allEvents.forEach((event) => {
106111
categoryCounts.all += 1
107112

@@ -110,8 +115,15 @@ export function EventsProvider({ children, staticEvents }: EventsProviderProps)
110115
})
111116
})
112117

118+
// Count on-demand events separately
119+
onDemandEvents.forEach((event) => {
120+
categoryCounts.all += 1
121+
// Add to 'on-demand' category instead of 'webinar'
122+
categoryCounts['on-demand'] = (categoryCounts['on-demand'] || 0) + 1
123+
})
124+
113125
return categoryCounts
114-
}, [allEvents])
126+
}, [allEvents, onDemandEvents])
115127

116128
// Toggle category selection
117129
const toggleCategory = (category: string) => {
@@ -135,8 +147,13 @@ export function EventsProvider({ children, staticEvents }: EventsProviderProps)
135147
})
136148
}
137149

138-
// Filter events by search query and category
150+
// Filter upcoming events by search query and category
139151
const filteredEvents = useMemo(() => {
152+
// If 'on-demand' is selected, don't show upcoming events
153+
if (selectedCategories.includes('on-demand') && !selectedCategories.includes('all')) {
154+
return []
155+
}
156+
140157
let filtered = allEvents
141158

142159
// Filter by categories (multiple selection)
@@ -167,6 +184,31 @@ export function EventsProvider({ children, staticEvents }: EventsProviderProps)
167184
})
168185
}, [allEvents, selectedCategories, searchQuery])
169186

187+
// Filter on-demand events separately by search query and category
188+
const filteredOnDemandEvents = useMemo(() => {
189+
// If specific categories are selected (not 'all' and not 'on-demand'), don't show on-demand events
190+
if (!selectedCategories.includes('all') && !selectedCategories.includes('on-demand')) {
191+
return []
192+
}
193+
194+
let filtered = onDemandEvents
195+
196+
// Filter by search query
197+
if (searchQuery.trim()) {
198+
const query = searchQuery.toLowerCase()
199+
filtered = filtered.filter((event) => {
200+
const titleMatch = event.title?.toLowerCase().includes(query)
201+
const descriptionMatch = event.description?.toLowerCase().includes(query)
202+
const locationMatch = event.location?.toLowerCase().includes(query)
203+
const tagsMatch = event.tags?.some((tag) => tag.toLowerCase().includes(query))
204+
205+
return titleMatch || descriptionMatch || locationMatch || tagsMatch
206+
})
207+
}
208+
209+
return filtered
210+
}, [onDemandEvents, selectedCategories, searchQuery])
211+
170212
// Featured event: nearest upcoming event, or if none, the most recent past event
171213
const featuredEvent = useMemo(() => {
172214
if (allEvents.length === 0) return undefined
@@ -208,7 +250,9 @@ export function EventsProvider({ children, staticEvents }: EventsProviderProps)
208250
const value: EventsContextValue = {
209251
allEvents,
210252
filteredEvents,
253+
filteredOnDemandEvents,
211254
staticEvents,
255+
onDemandEvents,
212256
lumaEvents,
213257
isLoading,
214258
searchQuery,

apps/www/app/events/page.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export const metadata: Metadata = {
1010

1111
export default async function EventsPage() {
1212
// This needs to be server-side as we use FS api.
13-
const staticEvents = await getStaticEvents()
13+
const { upcomingEvents, onDemandEvents } = await getStaticEvents()
1414

15-
return <EventClientRenderer staticEvents={staticEvents.upcomingEvents} />
15+
return <EventClientRenderer staticEvents={upcomingEvents} onDemandEvents={onDemandEvents} />
1616
}

apps/www/components/Events/new/EventBanner.tsx

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import { CalendarIcon, MapPinIcon } from 'lucide-react'
44
import Image from 'next/image'
55
import Link from 'next/link'
6-
import { cn, Button } from 'ui'
6+
import { cn, Button, Badge } from 'ui'
77
import { useEvents } from '~/app/events/context'
88
import { formatHosts } from '~/lib/eventsUtils'
99

@@ -17,7 +17,9 @@ export function EventBanner() {
1717
if (!featuredEvent) return null
1818

1919
return (
20-
<section className={cn('grid md:grid-cols-[minmax(320px,35%),1fr] gap-6 lg:gap-12')}>
20+
<section
21+
className={cn('grid md:grid-cols-[minmax(320px,35%),1fr] items-start gap-6 lg:gap-12')}
22+
>
2123
<CoverImage url={featuredEvent.cover_url} />
2224

2325
<article className="flex flex-col md:justify-center gap-6 lg:py-2">
@@ -140,14 +142,20 @@ const LocationWidget = ({ location }: { location?: string }) => {
140142
const CoverImage = ({ url }: { url?: string }) => {
141143
if (!url)
142144
return (
143-
<div className="w-full bg-surface-100 aspect-square border rounded-lg hidden md:grid place-items-center">
145+
<div className="w-full bg-surface-100 aspect-square border rounded-lg hidden md:grid place-items-center relative">
144146
<Logo />
147+
<Badge variant="brand" className="absolute bottom-4 right-4">
148+
Upcoming
149+
</Badge>
145150
</div>
146151
)
147152

148153
return (
149154
<div className="w-full bg-surface-100 hidden md:block aspect-square border rounded-lg overflow-hidden relative">
150155
<img src={url} alt="Event Cover" className="object-cover object-center w-full" />
156+
<Badge variant="brand" className="absolute bottom-4 right-4">
157+
Upcoming
158+
</Badge>
151159
</div>
152160
)
153161
}

apps/www/components/Events/new/EventClientRenderer.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,15 @@ import { EventBanner } from '~/components/Events/new/EventBanner'
77
import { EventsProvider } from '~/app/events/context'
88
import { EventGallery } from './EventGallery'
99

10-
export function EventClientRenderer({ staticEvents }: { staticEvents: SupabaseEvent[] }) {
10+
export function EventClientRenderer({
11+
staticEvents,
12+
onDemandEvents,
13+
}: {
14+
staticEvents: SupabaseEvent[]
15+
onDemandEvents: SupabaseEvent[]
16+
}) {
1117
return (
12-
<EventsProvider staticEvents={staticEvents}>
18+
<EventsProvider staticEvents={staticEvents} onDemandEvents={onDemandEvents}>
1319
<DefaultLayout className="flex flex-col">
1420
<SectionContainer className="border-x border-b !py-8">
1521
<h1 className="h3 !p-0 !m-0">

apps/www/components/Events/new/EventList.tsx

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ const CATEGORIES_FILTERS = [
1616
]
1717

1818
export function EventList() {
19-
const { isLoading, filteredEvents } = useEvents()
19+
const { isLoading, filteredEvents, filteredOnDemandEvents } = useEvents()
2020

2121
const getCategoryLabel = (value: string) => {
2222
const category = CATEGORIES_FILTERS.find((cat) => cat.value === value)
@@ -27,7 +27,7 @@ export function EventList() {
2727
return <EventListSkeleton />
2828
}
2929

30-
// Group events by date
30+
// Group upcoming events by date
3131
const eventsByDate = filteredEvents.reduce(
3232
(acc, event) => {
3333
const eventDate = new Date(event.date).toLocaleDateString('en-US', {
@@ -46,6 +46,9 @@ export function EventList() {
4646
{} as Record<string, typeof filteredEvents>
4747
)
4848

49+
const hasUpcomingEvents = Object.keys(eventsByDate).length > 0
50+
const hasOnDemandEvents = filteredOnDemandEvents.length > 0
51+
4952
return (
5053
<div className="flex flex-col gap-y-8 min-h-72">
5154
{Object.entries(eventsByDate).map(([date, events], index) => (
@@ -99,8 +102,55 @@ export function EventList() {
99102
</div>
100103
))}
101104

102-
{/* emtpy state */}
103-
{Object.keys(eventsByDate).length === 0 && (
105+
{/* On-demand events section */}
106+
{hasOnDemandEvents && (
107+
<div className="flex flex-col gap-y-2 relative mt-8">
108+
<div className="absolute top-2 -left-[calc(48px+11px)] rounded-full size-1.5 bg-foreground-muted" />
109+
110+
<h3 className="text-foreground-light font-normal">On Demand</h3>
111+
112+
<div className="flex flex-col gap-y-4">
113+
{filteredOnDemandEvents.map((event, idx) => (
114+
<div
115+
key={`on-demand-${idx}-${event.url}`}
116+
className="bg-surface-100 border rounded-md p-3 flex justify-between items-start relative"
117+
>
118+
<Link
119+
className="inset-0 absolute"
120+
href={event.url}
121+
target={event.url.startsWith('http') ? '_blank' : '_self'}
122+
title="Go to event page"
123+
aria-hidden
124+
/>
125+
126+
<div className="flex flex-col gap-2">
127+
<h3>{event.title}</h3>
128+
129+
<div className="flex gap-2 items-center text-sm text-foreground-light">
130+
<div className="size-5 rounded-full border bg-gradient-to-br from-background-surface-100 to-background-surface-200 relative">
131+
{event.hosts[0]?.avatar_url && (
132+
<img
133+
src={event.hosts[0].avatar_url}
134+
alt={event.hosts[0].name || 'Host image'}
135+
className="absolute inset-0 w-full h-full object-cover rounded-full"
136+
/>
137+
)}
138+
</div>
139+
Hosted by {formatHosts(event.hosts).displayText}
140+
</div>
141+
</div>
142+
143+
{event.categories.map((tag, idx) => (
144+
<Badge key={`tag-${idx}`}>{getCategoryLabel(tag)}</Badge>
145+
))}
146+
</div>
147+
))}
148+
</div>
149+
</div>
150+
)}
151+
152+
{/* empty state */}
153+
{!hasUpcomingEvents && !hasOnDemandEvents && (
104154
<div className="self-center text-muted my-auto flex flex-col items-center gap-y-4">
105155
<Rows3Icon className="size-8" />
106156
<p className="">Oops! No events found.</p>

apps/www/lib/events.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,11 @@ export const getStaticEvents = async (): Promise<{
252252
thumb: post.thumb || '',
253253
cover_url: (post as any).cover_url || '',
254254
path: post.path || '',
255-
url: post.url || '',
255+
// For webinars, use internal path; for other events, use external link if available
256+
url:
257+
(post as any).type === 'webinar'
258+
? post.url || post.path || ''
259+
: post.link?.href || post.url || post.path || '',
256260
tags: post.tags || [],
257261
categories: post.categories || [],
258262
timezone: (post as any).timezone || 'America/Los_Angeles',

0 commit comments

Comments
 (0)