it was last in SECTIONS, so it rendered as the tenth row and then stopped — directly under Messages, with empty panel beneath it. that is the bottom of the LIST. what it wanted is the bottom of the PANEL, the way Update-all sits in the plugins nav. `mt-auto` on a second group, divided by the same border the rows already use. it holds whether there are ten sections or two, and it does not need a spacer element to push it down. the split is a `pinned` flag on the section rather than the nav testing for the key. which sections are the client's site and which are this plugin's own machinery is a fact about the section, and the nav should be reading it, not knowing it. one array still — `sectionFor` and `isSectionKey` have to resolve every key, and a second exported list is a second place to forget one. the row body came out into `renderRow` so both groups draw the same row. no change to what a row looks like. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
159 lines
4.7 KiB
TypeScript
159 lines
4.7 KiB
TypeScript
import type { LucideIcon } from 'lucide-react';
|
|
import { Home, Info, Layers, Images, Handshake, Sparkles, Wrench, Settings, Mail, Cog } from 'lucide-react';
|
|
|
|
/**
|
|
* The sections of the fofos admin, in nav order.
|
|
*
|
|
* ── The order is the client's site, not the database's ──
|
|
*
|
|
* It follows the sitemap: the pages someone edits most, first. Site settings, Messages and
|
|
* Administration sit at the bottom because they are not pages — the site's settings, an inbox, and the
|
|
* plugin's own machinery.
|
|
*
|
|
* ── `site` and `administration` are different things ──
|
|
*
|
|
* "Site settings" is the CLIENT's — partners, social links, contact details, all of it public copy that
|
|
* whoever edits the site edits. "Administration" is OFFICER's end: where fofos is, how to reach it, and
|
|
* whatever else this plugin needs to know to do its job. One is content, the other is plumbing, and the
|
|
* people who touch them are not the same people. Nothing in Administration ever appears on the site.
|
|
*
|
|
* ── `built` ──
|
|
*
|
|
* The panels arrive one section at a time, each verified against the live site before the next is
|
|
* started. A section that is not built yet says so, plainly, with what it will do — which is a better
|
|
* answer than a nav that hides most of itself and gives no sense of what is coming.
|
|
*
|
|
* It mirrors `built` in fofos's own `src/apps/web/site-map.ts`, and for the same reason: one list, so
|
|
* the nav and what actually renders cannot disagree.
|
|
*/
|
|
|
|
export type SectionKey =
|
|
| 'administration'
|
|
| 'messages'
|
|
| 'site'
|
|
| 'about'
|
|
| 'home'
|
|
| 'gallery'
|
|
| 'drops'
|
|
| 'collabs'
|
|
| 'collections'
|
|
| 'services';
|
|
|
|
export type Section = {
|
|
key: SectionKey;
|
|
label: string;
|
|
icon: LucideIcon;
|
|
/** One line under the label in the nav — what this section is, for somebody who has not opened it. */
|
|
hint: string;
|
|
/** The fofos path this reads, relative to the plugin's mount. Null when nothing is built. */
|
|
path: string | null;
|
|
built: boolean;
|
|
/**
|
|
* Pinned to the FOOT of the nav rather than sitting in the list.
|
|
*
|
|
* A flag rather than the nav hard-coding a key, because "which sections are the site's and which are
|
|
* the plugin's own" is a fact about the section. It is also why this stays in one array: `sectionFor`
|
|
* and `isSectionKey` have to resolve every key, and a second exported list would be a second place to
|
|
* forget one.
|
|
*/
|
|
pinned?: boolean;
|
|
};
|
|
|
|
export const SECTIONS: Section[] = [
|
|
{
|
|
key: 'home',
|
|
label: 'Home',
|
|
icon: Home,
|
|
hint: 'The hero, the intro, and the gallery',
|
|
path: '/fofos-admin/home',
|
|
built: false,
|
|
},
|
|
{
|
|
key: 'about',
|
|
label: 'About Us',
|
|
icon: Info,
|
|
hint: 'The header photograph and the copy',
|
|
path: '/fofos-admin/about',
|
|
built: false,
|
|
},
|
|
{
|
|
key: 'collections',
|
|
label: 'Collections',
|
|
icon: Layers,
|
|
hint: 'Collections, and the items in them',
|
|
path: '/fofos-admin/collections',
|
|
built: false,
|
|
},
|
|
{
|
|
key: 'gallery',
|
|
label: 'Gallery',
|
|
icon: Images,
|
|
hint: 'Standalone pieces',
|
|
path: '/fofos-admin/gallery',
|
|
built: false,
|
|
},
|
|
{
|
|
key: 'collabs',
|
|
label: 'Collabs',
|
|
icon: Handshake,
|
|
hint: 'Projects with other people',
|
|
path: '/fofos-admin/collabs',
|
|
built: false,
|
|
},
|
|
{
|
|
key: 'drops',
|
|
label: 'Drops',
|
|
icon: Sparkles,
|
|
hint: 'Releases',
|
|
path: '/fofos-admin/drops',
|
|
built: false,
|
|
},
|
|
{
|
|
key: 'services',
|
|
label: 'Services',
|
|
icon: Wrench,
|
|
hint: 'A fixed tree — the words, not the shape',
|
|
path: '/fofos-admin/services',
|
|
built: false,
|
|
},
|
|
{
|
|
key: 'site',
|
|
label: 'Site settings',
|
|
icon: Settings,
|
|
hint: 'Partners, social links, contact details',
|
|
path: '/fofos-admin/site',
|
|
built: false,
|
|
},
|
|
{
|
|
key: 'messages',
|
|
label: 'Messages',
|
|
icon: Mail,
|
|
hint: 'What people sent through the contact form',
|
|
path: '/fofos-admin/messages',
|
|
built: true,
|
|
},
|
|
{
|
|
/*
|
|
Last, and `path: null` — it reads nothing from fofos. Every other section is a window onto the
|
|
client's site; this one is the window onto the connection itself, which is why it keeps working
|
|
when fofos is unreachable and the others do not.
|
|
*/
|
|
key: 'administration',
|
|
label: 'Administration',
|
|
icon: Cog,
|
|
hint: 'Where fofos is, and the token to reach it',
|
|
path: null,
|
|
built: true,
|
|
pinned: true,
|
|
},
|
|
];
|
|
|
|
/** Where the detail panel goes when the URL says nothing, or says something it does not recognise. */
|
|
export const DEFAULT_SECTION: SectionKey = 'messages';
|
|
|
|
export const isSectionKey = (value: string | null): value is SectionKey =>
|
|
SECTIONS.some((section) => section.key === value);
|
|
|
|
export const sectionFor = (key: SectionKey): Section =>
|
|
SECTIONS.find((section) => section.key === key) ?? SECTIONS[SECTIONS.length - 1]!;
|