v1.5 - history and timeline as well as many datastructure updates added, polished, fixes

This commit is contained in:
Zaldimmar 2026-09-25 02:38:51 -05:00
parent 1f0aa3078f
commit 1d84400aef
63 changed files with 7927 additions and 208 deletions

View file

@ -7,10 +7,17 @@
narrows the result to what it shows.
useEvents({ section: "national" }) one band
useEvents({ host: "northwest" }) a region's own events
useEvents({ host: "northwest" }) one host's events
useEvents({ status: "upcoming" }) a home page strip
useEvents({ type: "workshop" }) one kind, wherever it is
useEvents({ type: ["class", "workshop"] })
useEvents() everything
`section` and `type` are different questions and stack rather
than overlap: the section is which band of the page an event
belongs to, the type is what kind of gathering it is. A regional
class matches both { section: "regional" } and { type: "class" }.
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
@ -23,18 +30,30 @@ import { useResource } from "../lib/useResource.js";
const EMPTY = { events: [] };
export function useEvents({ section, host, status } = {}) {
export function useEvents({ section, host, status, type } = {}) {
const { data, error, loading } = useResource("/events", { fallback: EMPTY });
const all = data?.events;
/* An array prop is a new identity on every render, which would
restart the memo each time. Joining it gives the dependency
list something stable to compare. */
const typeKey = Array.isArray(type) ? type.join(",") : (type ?? "");
const events = useMemo(() => {
let list = all ?? [];
if (section) list = list.filter(e => e.section_id === section);
if (host) list = list.filter(e => e.host?.id === host);
// `host` is an organization or a person slug, and an event can
// have several of either — co-hosting puts one event on both
// hosts' lists, which is the point.
if (host) list = list.filter(e => e.hosts?.some(h => h.id === host));
if (status) list = list.filter(e => e.status === status);
if (typeKey) {
const wanted = new Set(typeKey.split(","));
list = list.filter(e => wanted.has(e.event_type));
}
return list;
}, [all, section, host, status]);
}, [all, section, host, status, typeKey]);
return { events, loading, error };
}
@ -52,3 +71,12 @@ export function splitByStatus(events = []) {
return { upcoming, past };
}
/* Which of the declared types a list actually contains, in
EVENT_TYPES order rather than whatever order the rows arrived
in. A section with one type has nothing to filter, which is what
lets the chip bar hide itself. */
export function typesPresent(events = [], declared = []) {
const seen = new Set(events.map(e => e.event_type));
return declared.filter(entry => seen.has(entry.id));
}

View file

@ -0,0 +1,63 @@
/**
* Decade headers for the history page.
*
* Not in the database on purpose. There are four of them, they change
* about never, and they're editorial voice rather than record — the same
* reasoning that keeps the map grid config in code. A table for four
* rows that nobody edits is a migration and an admin page for nothing.
*
* `preProgram` is what draws the gap: a dashed rail, hollow year dots,
* and the "before the program" marker. It lives on the decade rather
* than being a hardcoded year check, so the gap moves if the founding
* date is ever revised.
*/
import type { DecadeMeta } from '../lib/timeline'
export const HISTORY_DECADES: DecadeMeta[] = [
{
decade: 2020,
title: 'The Age of Autonomy',
tagline: 'Growth, unprecedented support, returning to our roots',
blurb:
'Covid-19 sends everyone home for 2 years and from the resurgence comes a new iteration of the program',
},
{
decade: 2010,
title: 'Millennials run the show',
tagline: 'A time of something',
blurb:
'Need to update what happened here, mostly documented on facebook we asumme',
},
{
decade: 2000,
title: 'NGU - The new acronym',
tagline: 'Turn of the century forms a national explosion',
blurb:
'Unity\'s young adult program becomes Next Generation of Unity',
},
{
decade: 1990,
title: 'Before NGU there was YAU',
tagline: 'Young adult ministry existed',
preProgram: true,
blurb:
'Anything listed here predates our named program. We are looking to document this history more',
},
{
decade: 1980,
title: 'YAU?',
tagline: 'Young adult ministry existed',
preProgram: true,
blurb:
'Anything listed here predates our named program. We are looking to document this history more',
},
{
decade: 1970,
title: 'YAU?',
tagline: 'Young adult ministry existed',
preProgram: true,
blurb:
'Anything listed here predates our named program. We are looking to document this history more',
},
]

299
src/data/old-historyMock.ts Normal file
View file

@ -0,0 +1,299 @@
/**
* PLACEHOLDER DATA — delete this file once `GET /api/history` lands.
*
* Shaped exactly like the aggregation route's payload, so swapping it out
* is a two-line change in History.tsx.
*
* Note what is and isn't here. Entries that point at a record carry a
* `ref` and little else: no venue, no host name, no copy that also lives
* in `events`. The few that do carry a title are overriding it on
* purpose. Titles and blurbs below are invented scaffolding, not real NGU
* history — replace them before anyone sees this.
*
* Decade headers are not here — they live in historyDecades.ts and stay
* in code even after the API lands.
*
* Upcoming items are not flagged. `partitionByDate` decides what's
* upcoming by comparing dates to the wall clock, so nothing here needs
* editing as dates pass.
*/
import type { TimelineItem } from '../lib/timeline'
export const MOCK_ITEMS: TimelineItem[] = [
// ——— Upcoming (relative to the wall clock, not a flag) ———
{
id: 'tl-0001',
date: '2027-06',
precision: 'month',
kind: 'event',
title: 'Summer Conference 2027',
meta: 'Unity Village, MO',
ref: { kind: 'event', id: 'summer-2027' },
logo: { file: 'summer-conference.svg', kind: 'event' },
},
{
id: 'tl-0002',
date: '2026-11-14',
precision: 'day',
kind: 'event',
title: 'Fall Regional Rally',
meta: 'Southeast',
ref: { kind: 'event', id: 'fall-rally-2026' },
logo: { file: 'regional-rally.svg', kind: 'event' },
},
{
id: 'tl-0003',
date: '2026-10-03',
precision: 'day',
kind: 'event',
title: 'Chapter Leads Intensive',
meta: 'Online',
ref: { kind: 'event', id: 'leads-intensive-2026' },
},
// ——— 2020s ———
{
id: 'tl-0010',
date: '2026-03',
precision: 'month',
kind: 'organization',
title: 'Twentieth chapter chartered',
blurb:
'The first new charter in the region since 2017, started by three people who met at a rally two summers earlier.',
featured: true,
ref: { kind: 'organization', id: 'boise', orgKind: 'chapter' },
logo: { file: 'boise.svg', kind: 'organization' },
},
{
id: 'tl-0011',
date: '2026-04-17',
precision: 'day',
kind: 'event',
title: 'Spring Regional Rally',
ref: { kind: 'event', id: 'spring-rally-2026' },
logo: { file: 'regional-rally.svg', kind: 'event' },
},
{
id: 'tl-0012',
date: '2026-01',
precision: 'month',
kind: 'people',
title: 'New leadership team seated',
blurb: 'A four-person exec on a two-year term, the first under the 2024 bylaws.',
ref: { kind: 'team', id: 'ngu-leadership' },
team: { id: 'ngu-leadership', name: 'Leadership Team', orgId: 'ngu', orgName: 'NGU National' },
people: [
{ id: 'jane-doe', name: 'Jane Doe', title: 'Chair', photo: 'jane-doe.jpg' },
{ id: 'sam-ruiz', name: 'Sam Ruiz', title: 'Vice Chair', photo: 'sam-ruiz.jpg' },
{ id: 'ada-mensah', name: 'Ada Mensah', title: 'Secretary' },
{ id: 'tom-baird', name: 'Tom Baird', title: 'Treasurer', photo: 'tom-baird.jpg' },
],
},
{
id: 'tl-0013',
date: '2025-12-28',
precision: 'day',
kind: 'event',
title: 'Winter Gathering',
ref: { kind: 'event', id: 'winter-gathering-2025' },
},
{
id: 'tl-0014',
date: '2025-07-19',
precision: 'day',
kind: 'award',
title: 'Service Award',
meta: 'Presented to the Southeast chapter leads',
ref: { kind: 'award', id: 'service' },
logo: { file: 'service-award.svg', kind: 'award' },
},
{
id: 'tl-0015',
date: '2025-07-17',
precision: 'day',
kind: 'event',
title: 'Summer Conference',
meta: 'Three days, 140 attendees',
ref: { kind: 'event', id: 'summer-2025' },
logo: { file: 'summer-conference.svg', kind: 'event' },
},
{
id: 'tl-0016',
date: '2025',
precision: 'year',
kind: 'milestone',
title: 'Photo archive digitized',
blurb: 'Roughly 4,000 images from 2003 onward, scanned by volunteers.',
href: 'https://archive.nextgenerationofunity.org',
},
{
id: 'tl-0017',
date: '2024-09',
precision: 'month',
kind: 'milestone',
title: 'Bylaws rewritten',
blurb:
'Term limits, a defined handoff window, and the first written process for chartering a chapter.',
featured: true,
},
{
id: 'tl-0018',
date: '2024-07-11',
precision: 'day',
kind: 'event',
title: 'Summer Conference',
ref: { kind: 'event', id: 'summer-2024' },
logo: { file: 'summer-conference.svg', kind: 'event' },
},
{
id: 'tl-0019',
date: '2024-07-13',
precision: 'day',
kind: 'award',
title: 'Emerging Leader Award',
meta: 'First year the award was given',
ref: { kind: 'award', id: 'emerging-leader' },
logo: { file: 'emerging-leader.svg', kind: 'award' },
},
{
id: 'tl-0020',
date: '2022-06',
precision: 'month',
kind: 'event',
title: 'First in-person rally since 2019',
blurb: 'Sixty-one people, most of whom had only ever met on a video call.',
featured: true,
ref: { kind: 'event', id: 'return-rally-2022' },
},
{
id: 'tl-0021',
date: '2021-08',
precision: 'month',
kind: 'event',
title: 'Online Summer Intensive',
meta: 'Six sessions across two weeks',
ref: { kind: 'event', id: 'online-intensive-2021' },
},
{
id: 'tl-0022',
date: '2020-03',
precision: 'month',
kind: 'milestone',
title: 'All gatherings suspended',
blurb: 'Weekly online rooms started the following week and ran for 118 weeks.',
},
// ——— 2010s ———
{
id: 'tl-0030',
date: '2018-05',
precision: 'month',
kind: 'organization',
title: 'Eighth region recognized',
featured: true,
ref: { kind: 'organization', id: 'northwest', orgKind: 'region' },
logo: { file: 'northwest.svg', kind: 'organization' },
},
{
id: 'tl-0031',
date: '2018-07-20',
precision: 'day',
kind: 'event',
title: 'Summer Conference',
ref: { kind: 'event', id: 'summer-2018' },
logo: { file: 'summer-conference.svg', kind: 'event' },
},
{
id: 'tl-0032',
date: '2016-02',
precision: 'month',
kind: 'people',
title: 'Retreat Team formed',
blurb:
'Programming had been whoever volunteered. This made it a standing team with a handoff.',
ref: { kind: 'team', id: 'ngu-retreat-team' },
team: { id: 'ngu-retreat-team', name: 'Retreat Team', orgId: 'ngu', orgName: 'NGU National' },
people: [
{ id: 'marcus-hale', name: 'Marcus Hale', title: 'Founding lead', photo: 'marcus-hale.jpg' },
{ id: 'priya-nair', name: 'Priya Nair', photo: 'priya-nair.jpg' },
],
},
{
id: 'tl-0033',
date: '2015-07',
precision: 'month',
kind: 'award',
title: 'First Service Award presented',
blurb: 'Created to name the work that had been going unnamed for a decade.',
featured: true,
ref: { kind: 'award', id: 'service' },
logo: { file: 'service-award.svg', kind: 'award' },
},
{
id: 'tl-0034',
date: '2015-02',
precision: 'month',
kind: 'milestone',
title: 'Shared calendar goes live',
blurb: 'Regions stop scheduling on top of each other.',
},
{
id: 'tl-0035',
date: '2012-06',
precision: 'month',
kind: 'event',
title: 'Summer Conference',
meta: 'The year attendance first passed 100',
ref: { kind: 'event', id: 'summer-2012' },
logo: { file: 'summer-conference.svg', kind: 'event' },
},
{
id: 'tl-0036',
date: '2012',
precision: 'year',
kind: 'milestone',
title: 'Adopted the name Next Generation of Unity',
},
// ——— 2000s ———
{
id: 'tl-0040',
date: '2009',
precision: 'year',
kind: 'milestone',
title: 'Four regions drawn on a map for the first time',
},
{
id: 'tl-0041',
date: '2005-08',
precision: 'month',
kind: 'milestone',
title: 'The gathering becomes annual',
blurb: 'Before this it happened when someone had the energy to organize it.',
featured: true,
},
{
id: 'tl-0042',
date: '2002-08',
precision: 'month',
kind: 'event',
title: 'First young adult retreat',
meta: 'Nineteen attendees',
blurb:
'Organized over six weeks at a borrowed retreat center. Everything else on this page follows from it.',
featured: true,
ref: { kind: 'event', id: 'first-retreat-2002' },
},
// ——— Pre-program ———
{
id: 'tl-0050',
date: '1997',
precision: 'year',
kind: 'milestone',
title: 'Regional youth programs running independently',
blurb:
'Not NGU, and not connected to each other — but the people who started NGU came out of these.',
},
]

View file

@ -32,7 +32,7 @@ export function useOrganizations(kind) {
? `/organizations?kind=${encodeURIComponent(kind)}`
: "/organizations";
const { data, error, loading } = useResource(path, { fallback: EMPTY });
const { data, error, loading } = useResource(path);
return {
organizations: data?.organizations ?? [],