From 9ca6077e2cd51b881f6b961b5c1a602bc3f4f368 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Padez?= Date: Sat, 5 Sep 2026 13:42:26 +0100 Subject: [PATCH] Collabs, as a third configuration of one panel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The same object as a collection and a gallery piece, so it is a config in subjects.ts and a one-line panel — no new screen. Two differences, both deliberate. It has no SPECS: a collab is a project rather than an object, so there is no measurement or raw material and the form is a name, an address and a description. And its title column is `project_name`, which is the client's word and stays — the site's API publishes both that and `title` so this panel reads one field like every other section, and the translation lives in one router rather than as a branch in every screen. Co-Authored-By: Claude Opus 5 (1M context) --- web/DetailPanel.tsx | 2 ++ web/sections.ts | 20 +++++++++++++++++++- web/sections/AddComponentDialog.tsx | 2 ++ web/sections/CollabsPanel.tsx | 5 +++++ web/sections/PageBuilderPanel.tsx | 2 ++ web/sections/subjects.ts | 28 +++++++++++++++++++++++++++- 6 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 web/sections/CollabsPanel.tsx diff --git a/web/DetailPanel.tsx b/web/DetailPanel.tsx index ab76815..052f78e 100644 --- a/web/DetailPanel.tsx +++ b/web/DetailPanel.tsx @@ -6,6 +6,7 @@ import { sectionFor, sectionOpen, type SectionKey } from './sections'; import { AdministrationPanel } from './AdministrationPanel'; import { MediaPanel } from './MediaPanel'; import { CollectionsPanel } from './sections/CollectionsPanel'; +import { CollabsPanel } from './sections/CollabsPanel'; import { PiecesPanel } from './sections/PiecesPanel'; import { CarouselsPanel } from './sections/CarouselsPanel'; import { LayoutPanel } from './sections/LayoutPanel'; @@ -50,6 +51,7 @@ const PANELS: Partial React.JSX.Element>> = { layout: LayoutPanel, collections: CollectionsPanel, pieces: PiecesPanel, + collabs: CollabsPanel, carousels: CarouselsPanel, messages: MessagesPanel, administration: AdministrationPanel, diff --git a/web/sections.ts b/web/sections.ts index e3bdea8..29fcb7d 100644 --- a/web/sections.ts +++ b/web/sections.ts @@ -1,5 +1,5 @@ import type { LucideIcon } from 'lucide-react'; -import { Boxes, Cog, Files, FolderOpen, GalleryHorizontal, Images, Mail, PanelsTopLeft, Shapes } from 'lucide-react'; +import { Boxes, Cog, Files, FolderOpen, GalleryHorizontal, Handshake, Images, Mail, PanelsTopLeft, Shapes } from 'lucide-react'; /** * The sections of the fofos admin, in nav order. @@ -38,6 +38,7 @@ import { Boxes, Cog, Files, FolderOpen, GalleryHorizontal, Images, Mail, PanelsT export type SectionKey = | 'administration' + | 'collabs' | 'collections' | 'pieces' | 'carousels' @@ -171,6 +172,23 @@ export const SECTIONS: Section[] = [ requires: 'connection', pinned: true, }, + { + /* + Collaborations — the third section drawn through a template, and the flattest of the three. + + Below Pieces because that is the order the sitemap puts them in, and it shares their panel: a + collab is the same object with the client's own word for its title and no specs. + */ + key: 'collabs', + label: 'Collabs', + icon: Handshake, + hint: 'Projects made with somebody else', + path: '/fofos-admin/collabs', + built: true, + // A collab is a ROW read through the site's admin API, like Collections and Pieces above it. + requires: 'connection', + pinned: true, + }, { key: 'carousels', label: 'Carousels', diff --git a/web/sections/AddComponentDialog.tsx b/web/sections/AddComponentDialog.tsx index 356bb0f..893c1e2 100644 --- a/web/sections/AddComponentDialog.tsx +++ b/web/sections/AddComponentDialog.tsx @@ -31,6 +31,7 @@ export type ComponentKind = | 'partners' | 'collections' | 'pieces' + | 'collabs' | 'subject-cover' | 'subject-title' | 'subject-description' @@ -93,6 +94,7 @@ export const CHOICES: Choice[] = [ { kind: 'partners', label: 'Partners', hint: 'The partner logos, from Site', Icon: Handshake }, { kind: 'collections', label: 'Collections', hint: 'Every collection, as a grid', Icon: LayoutGrid }, { kind: 'pieces', label: 'Pieces', hint: 'Every gallery piece, as a grid', Icon: LayoutGrid }, + { kind: 'collabs', label: 'Collabs', hint: 'Every collaboration, as a grid', Icon: LayoutGrid }, /* The five that draw whatever the page is ABOUT, on a template. diff --git a/web/sections/CollabsPanel.tsx b/web/sections/CollabsPanel.tsx new file mode 100644 index 0000000..de47e72 --- /dev/null +++ b/web/sections/CollabsPanel.tsx @@ -0,0 +1,5 @@ +import { SubjectPanel } from './SubjectPanel'; +import { COLLABS } from './subjects'; + +/** Collaborations. The panel is shared with Collections and Pieces — see subjects.ts. */ +export const CollabsPanel = () => ; diff --git a/web/sections/PageBuilderPanel.tsx b/web/sections/PageBuilderPanel.tsx index 7a56453..0328599 100644 --- a/web/sections/PageBuilderPanel.tsx +++ b/web/sections/PageBuilderPanel.tsx @@ -24,6 +24,7 @@ const LABELS: Record = { partners: 'Partners', collections: 'Collections', pieces: 'Pieces', + collabs: 'Collabs', 'subject-cover': 'Lead photograph', 'subject-title': 'Name', 'subject-description': 'Description', @@ -42,6 +43,7 @@ const PLACES_ONLY: string[] = [ 'partners', 'collections', 'pieces', + 'collabs', // The subject blocks hold nothing either: what they draw is the row the URL names. 'subject-cover', 'subject-title', diff --git a/web/sections/subjects.ts b/web/sections/subjects.ts index e8ff88a..3b44e7f 100644 --- a/web/sections/subjects.ts +++ b/web/sections/subjects.ts @@ -1,5 +1,5 @@ import type { LucideIcon } from 'lucide-react'; -import { Boxes, Shapes } from 'lucide-react'; +import { Boxes, Handshake, Shapes } from 'lucide-react'; /** * The two sections whose rows are drawn through a template — collections, and gallery pieces. @@ -42,6 +42,16 @@ export type Collection = SubjectRecord & { delivery_time: string | null; }; +export type Collab = SubjectRecord & { + /** + * The client's word for the title, carried alongside `title`. + * + * The column really is `project_name` — see the site's collabs schema — and the wire publishes both + * so this panel can go on reading `title` like every other section. Writes accept either. + */ + project_name: string; +}; + export type Piece = SubjectRecord & { descriptive_text: string | null; measurement: string | null; @@ -85,6 +95,22 @@ export const COLLECTIONS: SubjectSection = { long: ['descriptive_text'], }; +export const COLLABS: SubjectSection = { + param: 'collab', + endpoint: '/fofos-admin/collabs', + base: '/collabs', + icon: Handshake, + noun: 'collab', + /* + No specs, and that is the difference. + + A collab is a PROJECT rather than an object — there is no measurement, no raw material, no + colours to list. The form is a name, an address and a description, which is all the sitemap gives + it, and the template carries no specs block to draw. + */ + specs: [], +}; + export const PIECES: SubjectSection = { param: 'piece', endpoint: '/fofos-admin/pieces',