Add a reusable event calendar and put it on the front page
EventCalendar shows events on a month grid or as a list of the month. Multi-day events are lane-packed bars that break at week edges, series events appear on every meeting with their time, and clicking a day lists everything on it. Phones get the list. Visitors can narrow by scope, type, online only and search, all starting at "all"; a page can pin section, host or type through props, which hides that control. Migration 019 rebuilds front_page_sections to allow a 'calendar' band and slots it in after the retreats carousel, so it can be reordered, retitled or hidden from the Front page editor. useEvents drops its empty fallback so a failed request surfaces as an error rather than an empty list, and returns the scope list alongside the events for the calendar's scope filter. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
641ab167b0
commit
2e125b4629
10 changed files with 947 additions and 8 deletions
|
|
@ -18,6 +18,13 @@
|
|||
belongs to, the type is what kind of gathering it is. A regional
|
||||
class matches both { section: "regional" } and { type: "class" }.
|
||||
|
||||
The event_sections rows (the scope list) come back alongside, for
|
||||
anything that offers a scope filter.
|
||||
|
||||
No fallback. An empty list on a failed request would read as
|
||||
"nothing scheduled" when the truth is "the server is down", so
|
||||
the error comes back and each caller says so.
|
||||
|
||||
Filtering here rather than in the query keeps the endpoint to
|
||||
one cached response. At a few dozen events that's the right
|
||||
trade; if the list ever runs to hundreds, move the filters into
|
||||
|
|
@ -28,12 +35,15 @@ import { useMemo } from "react";
|
|||
|
||||
import { useResource } from "../lib/useResource.js";
|
||||
|
||||
const EMPTY = { events: [] };
|
||||
/* One shared empty list, so a memo keyed on `sections` doesn't
|
||||
restart on every render before the data arrives. */
|
||||
const NO_SECTIONS = [];
|
||||
|
||||
export function useEvents({ section, host, status, type } = {}) {
|
||||
const { data, error, loading } = useResource("/events", { fallback: EMPTY });
|
||||
const { data, error, loading } = useResource("/events");
|
||||
|
||||
const all = data?.events;
|
||||
const sections = data?.sections ?? NO_SECTIONS;
|
||||
|
||||
/* An array prop is a new identity on every render, which would
|
||||
restart the memo each time. Joining it gives the dependency
|
||||
|
|
@ -55,7 +65,7 @@ export function useEvents({ section, host, status, type } = {}) {
|
|||
return list;
|
||||
}, [all, section, host, status, typeKey]);
|
||||
|
||||
return { events, loading, error };
|
||||
return { events, sections, loading, error };
|
||||
}
|
||||
|
||||
/* Past and upcoming, split. `status` arrives already resolved — the
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue