From 4435537ae2deeed2799583f3dbc5e1ade4dae5ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Padez?= Date: Tue, 1 Sep 2026 20:55:07 +0100 Subject: [PATCH] a partners block edits the site's partners MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The block places the list; it does not hold one. So this edits the singleton and every page showing them changes together — which the panel says out loud, because the editor sits inside a block on a page and everything else in that column belongs to the page. Same gesture as the gallery items: a grip on the left, live reorder, one write at the end carrying every id. Two ordered lists, one thing to learn. A new partner is named from its filename and renamed in the field beside it — the API refuses one with no name, and asking in a second dialog before the logo has been seen is a form in front of a form. Co-Authored-By: Claude Opus 5 (1M context) --- web/sections/PageBuilderPanel.tsx | 7 + web/sections/PartnersEditor.tsx | 204 ++++++++++++++++++++++++++++++ 2 files changed, 211 insertions(+) create mode 100644 web/sections/PartnersEditor.tsx diff --git a/web/sections/PageBuilderPanel.tsx b/web/sections/PageBuilderPanel.tsx index 990b1d2..aada984 100644 --- a/web/sections/PageBuilderPanel.tsx +++ b/web/sections/PageBuilderPanel.tsx @@ -37,6 +37,7 @@ import { SpacingEditor, type Padding } from './SpacingEditor'; import { BandSettings } from './BandSettings'; import { BlockSettings } from './BlockSettings'; import { GalleryItems } from './GalleryItems'; +import { PartnersEditor } from './PartnersEditor'; import { useEditorSlot } from '../useEditorSlot'; import { AddComponentDialog, type ComponentKind } from './AddComponentDialog'; import { MediaPicker } from '../MediaPicker'; @@ -657,6 +658,12 @@ export const PageBuilderPanel = () => { A button rather than the modal opening itself, so picking the block and changing what is in it are two acts — see the note in the selection effect. */} + {/* + Partners are the SITE's, not this page's — the block places the list rather than holding + one. So this edits the singleton, and every page showing them changes together. + */} + {block.kind === 'partners' && setRendered((n) => n + 1)} />} + {block.kind === 'gallery' && (
+ + ))} + + + + + {error &&

{error}

} + + setAdding(false)} + onPick={(url) => { + setAdding(false); + /* + Named from the FILE, and renamed in place afterwards. + + The API refuses a partner with no name, and asking for one in a second dialog before the + logo has even been seen is a form in front of a form. The filename is a real answer often + enough, and the field beside the logo is where it gets corrected. + */ + const name = (url.split('/').pop() ?? 'Partner').replace(/\.[^.]+$/, ''); + void send(() => clientRef.current.post('/fofos-admin/site/partners', { name, logo_url: url })); + }} + title="Choose a partner's logo" + /> +
+ ); +};