nextjs-demo
next.js demo using react 19 rc
git clone https://9o.is/git/nextjs-demo.git
EventsPage.tsx
(2398B)
1 "use client"
2
3 import { Checkbox, ComboBox, Select, Loading, Alert, Form, FormDataMap, Button } from '../../headless'
4 import { useNumberFormatter } from '../../hooks'
5 import { SHEvent } from './useSHEvents'
6 import { FilteredSHEventsInput, useFilteredSHEvents } from './useFilteredSHEvents'
7 import { invariant, isSortType, SORT_TYPES } from '../../../lib'
8 import { Suspense, use, useTransition } from 'react'
9 import { eventsAction } from './eventsAction'
10
11 type EventsProps = {
12 events: Promise<SHEvent[]>
13 cities: Promise<string[]>
14 }
15
16 export function EventsPage({ events, cities }: EventsProps) {
17 const [isPending, startTransition] = useTransition()
18 // const { events, filterSHEvents } = useFilteredSHEvents(loadedEvents)
19
20 return (
21 <Alert.Context>
22 <Alert />
23 <Form aria-label='Filter Events' action={eventsAction} validator={validator}>
24 <ComboBox label="City" name="city" options={[]} />
25 <Select label="Sort by price" name="priceSort" options={SORT_TYPES} />
26 <Checkbox label="Find Cheapest" name="cheapest" />
27 <Button type="submit" disabled={false}>Search</Button>
28 <Form.Error>{(error) => error}</Form.Error>
29 </Form>
30 <Alert.ErrorBoundary fallback="Failed to get list of events">
31 <Suspense fallback={<Loading />}>
32 <EventList events={events} />
33 </Suspense>
34 </Alert.ErrorBoundary>
35 </Alert.Context>
36 )
37 }
38
39 type EventListProps = {
40 events: Promise<SHEvent[]>
41 }
42
43 function EventList({ events }: EventListProps) {
44 const priceFormatter = useNumberFormatter({
45 style: 'currency',
46 currency: 'USD',
47 maximumFractionDigits: 0
48 })
49
50 return (
51 <ul>
52 {use(events).map(({ id, city, price }) => (
53 <li key={id}>
54 <div>City: {city}</div>
55 <div>Price: {priceFormatter.format(price)}</div>
56 </li>
57 ))}
58 </ul>
59 )
60 }
61
62
63 function validator ({ city, priceSort, cheapest }: FormDataMap): FilteredSHEventsInput {
64 invariant(
65 typeof city === 'string' && city.length < 255,
66 "City is invalid"
67 )
68
69 invariant(
70 cheapest === undefined || cheapest === 'on',
71 "Find cheapest input is invalid"
72 )
73
74 invariant(
75 priceSort === '' || (typeof priceSort === 'string' && isSortType(priceSort)),
76 "Sort by price is invalid"
77 )
78
79 return {
80 city,
81 priceSort: priceSort || undefined,
82 cheapest: cheapest === 'on',
83 }
84 }