react-vite-demo
react and vite demo
git clone https://9o.is/git/react-vite-demo.git
EventsPage.tsx
(2339B)
1 import { Checkbox, ComboBox, Select, Progress, Alert, Form, FormDataMap, Button } from '../../headless'
2 import { useNumberFormatter } from '../../hooks'
3 import { useSHEvents } from './useSHEvents'
4 import { FilteredSHEventsInput, useFilteredSHEvents } from './useFilteredSHEvents'
5 import { invariant, isSortType, SORT_TYPES } from '../../../lib'
6 import { useMemo } from 'react'
7
8 export function EventsPage() {
9 const { events: loadedEvents, error, loading } = useSHEvents('node-a')
10 const { events, filterSHEvents } = useFilteredSHEvents(loadedEvents)
11 const priceFormatter = useNumberFormatter({
12 style: 'currency',
13 currency: 'USD',
14 maximumFractionDigits: 0
15 })
16
17 const cities = useMemo(() =>
18 [...new Set(loadedEvents.map(({ city }) => city))], [loadedEvents])
19
20 return (
21 <>
22 <Alert error={error} />
23 <Form aria-label='Filter Events' onSubmit={filterSHEvents} validator={validator}>
24 <ComboBox label="City" name="city" options={cities} />
25 <Select label="Sort by price" name="priceSort" options={SORT_TYPES} />
26 <Checkbox label="Find Cheapest" name="cheapest" />
27 <Button type="submit" disabled={loading}>Search</Button>
28 <Form.Error>{(error) => error}</Form.Error>
29 </Form>
30 <Progress loading={loading}>
31 <Progress.Bar>
32 <Progress.Label>Loading events...</Progress.Label>
33 </Progress.Bar>
34 <Progress.Content>
35 <ul>
36 {events.map(({ id, city, price }) => (
37 <li key={id}>
38 <div>City: {city}</div>
39 <div>Price: {priceFormatter.format(price)}</div>
40 </li>
41 ))}
42 </ul>
43 </Progress.Content>
44 </Progress>
45 </>
46 )
47 }
48
49
50 function validator ({ city, priceSort, cheapest }: FormDataMap): FilteredSHEventsInput {
51 invariant(
52 typeof city === 'string' && city.length < 255,
53 "City is invalid"
54 )
55
56 invariant(
57 cheapest === undefined || cheapest === 'on',
58 "Find cheapest input is invalid"
59 )
60
61 invariant(
62 priceSort === '' || (typeof priceSort === 'string' && isSortType(priceSort)),
63 "Sort by price is invalid"
64 )
65
66 return {
67 city,
68 priceSort: priceSort || undefined,
69 cheapest: cheapest === 'on',
70 }
71 }