import { Handshake, Image, Images, Mail, Type, type LucideIcon } from 'lucide-react'; import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'; /** * Choosing what to put in a column. * * ── One option is still a choice worth showing ── * * I had argued the opposite: Free Text is the only kind that works, so opening the editor straight away * saves a click that asks to be got past. That was wrong in the way a shortcut usually is — it optimised * the state of the app today and hid the shape of it. Somebody adding a component cannot see what a * component IS, or that there will be others, and the day the second kind lands the interaction changes * underneath them. * * It is also the pattern this admin already uses. The nav lists unbuilt sections as `soon` rather than * hiding them, on the reasoning that a nav showing only what is finished gives no sense of what the * thing is for. The same argument applies here. * * ── The list is in the plugin, not the API ── * * The API decides what may be STORED — `free-text`, today. This decides what may be OFFERED, and it has * to live here because every kind needs a label, an icon and an editor that only exists in this plugin. * A kind the server would accept and this has never heard of is not creatable anyway. */ export type ComponentKind = 'free-text' | 'media' | 'gallery' | 'contact-form' | 'partners'; type Choice = { kind: ComponentKind | null; label: string; hint: string; Icon: LucideIcon; }; /** * `kind: null` means not built. * * Named rather than omitted, and the ones named are things the site already HAS — media, video and * galleries all exist as concepts here. That is the difference between saying what is coming and * inventing a roadmap. Gallery stopped being one of them on 2026-08-31: the set, its ordering and the * carousel that draws it were all already built, so the block is a reference to one. */ const CHOICES: Choice[] = [ { kind: 'free-text', label: 'Text', hint: 'Words, headings and links', Icon: Type }, /* One entry, either kind, because the block does not store which. Image and Video were two rows — one of them greyed out as `soon` — and the distinction was never the editor's to make: the path names a real file and what that file IS is a fact about it. So the picker asks for a file and the site works out the rest, which is also what a gallery item has always done. */ { kind: 'media', label: 'Media', hint: 'A photograph or a video, from the media store', Icon: Image }, { kind: 'gallery', label: 'Gallery', hint: 'A named set of media', Icon: Images }, /* Two that place something the site already draws. Nothing to choose and nothing to type, so adding one is a single click and there is no editor behind it. They are here rather than hidden because a page is built by picking blocks, and a block somebody cannot find is a block that does not exist. */ { kind: 'contact-form', label: 'Contact form', hint: 'The site\'s form, as it is', Icon: Mail }, { kind: 'partners', label: 'Partners', hint: 'The partner logos, from Site', Icon: Handshake }, ]; export const AddComponentDialog = ({ open, onCancel, onChoose, }: { open: boolean; onCancel: () => void; onChoose: (kind: ComponentKind) => void; }) => ( !next && onCancel()}> Add a component
{CHOICES.map(({ kind, label, hint, Icon }) => ( ))}
);