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

114
src/pages/admin/adminNav.js Normal file
View file

@ -0,0 +1,114 @@
/* ═══════════════════════════════════════════════════════════════
ADMIN NAVIGATION — src/pages/admin/adminNav.js
Lifted out of AdminLayout because two things read it now: the
header tabs and the card grid on the home page. Adding an entity
stays one entry here plus a descriptor — there's still no second
list to keep in step, it just isn't inside the layout file
any more.
`blurb` is only read by the home cards. The header ignores it.
`superOnly` hides an entry from anyone below superadmin. It
hides, nothing more: the route still has to be guarded and the
API still has to say no. Treating a filtered menu as access
control is how you end up with a URL that works.
Three areas, three titles. The area is derived from the path
rather than declared per route, so a page added under
/admin/panel/… inherits the right title without registering
anything.
═══════════════════════════════════════════════════════════════ */
import { isSuper } from "../../lib/roles.ts";
export const ADMIN_HOME = "/admin/home";
export const ADMIN_PANEL = "/admin/panel";
export const AREA_TITLES = {
home: "NGU Admin Home",
cms: "NGU Admin CMS",
panel: "NGU Admin Panel",
};
/* The CMS tabs. Teams and Awards live under Organizations because
that's where they belong conceptually — a team is part of an org,
an award is given by one — even though each is its own table and
its own page. */
export const CMS_NAV = [
{
to: "/admin/events",
label: "Events",
blurb: "Retreats, conferences and gatherings, with their sections and rosters.",
},
{
to: "/admin/organizations",
label: "Organizations",
blurb: "Regions, chapters and partners — plus the teams and awards they own.",
children: [
{ to: "/admin/organizations", label: "All organizations" },
{ to: "/admin/teams", label: "Teams" },
{ to: "/admin/awards", label: "Awards" },
],
},
{
to: "/admin/people",
label: "People",
blurb: "Bios, contact details, affiliations and awards received.",
},
// Its own tab rather than a child of anything: a timeline entry can
// point at an event, an organization, an award, a person or a team,
// so filing it under one of them would be arbitrary.
{
to: "/admin/timeline",
label: "Timeline",
blurb: "What the history page shows, and the order it shows it in.",
},
];
/* Forms are submissions coming in rather than content going out, so
they get their own group in the header and their own block on the
home page. A group with no `to` of its own opens its first child,
so clicking the word Forms goes somewhere rather than nowhere. */
export const FORMS_NAV = {
label: "Forms",
blurb: "Whatever the public site has sent us.",
separated: true,
children: [
{
to: "/admin/feedback",
label: "Website feedback",
blurb: "The triage queue for the feedback form.",
},
],
};
/* Accounts and server state, not content — which is why it sits
outside the CMS rather than as another tab within it. */
export const PANEL_NAV = {
to: ADMIN_PANEL,
label: "Panel",
blurb: "Accounts, sessions and the state of the server.",
separated: true,
superOnly: true,
};
export const NAV = [...CMS_NAV, FORMS_NAV, PANEL_NAV];
/* What this user may see. Call it with the user from useAuth. */
export function navFor(user) {
return NAV.filter((item) => !item.superOnly || isSuper(user));
}
/* A tab owns its own page and everything below it, so editing
/admin/teams/ngu-board keeps Teams lit. */
export const matches = (pathname, to) =>
Boolean(to) && (pathname === to || pathname.startsWith(`${to}/`));
export const target = (item) => item.to ?? item.children?.[0]?.to;
export function areaFor(pathname) {
if (matches(pathname, ADMIN_HOME)) return "home";
if (matches(pathname, ADMIN_PANEL)) return "panel";
return "cms";
}