blocks that draw whatever the page is about
`/collections/amadoras-mythology` and `/collections/70s` are the same page with a different subject. Building each would be absurd; hard-coding the arrangement would put it out of reach of the admin. So the arrangement is built once — a template page, composed exactly like any other — and every collection is drawn through it. Four blocks read the subject: its name, its description, its specs, its photographs. They hold nothing at all, not even a reference. `collections` holds no content because the list is a query; these hold none because the subject is the URL, and the same block draws a different collection on the next request. Offered only on a template, and HIDDEN rather than disabled elsewhere. A greyed-out row invites "why not here", and the answer — this page has no subject — is not something a tooltip fixes. Available and silently drawing nothing is the worse failure. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { Handshake, Image, Images, LayoutGrid, Mail, Type, type LucideIcon } from 'lucide-react';
|
||||
import { Handshake, Heading, Image, Images, LayoutGrid, Mail, Ruler, Type, type LucideIcon } from 'lucide-react';
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog';
|
||||
|
||||
/**
|
||||
@@ -23,7 +23,30 @@ import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/u
|
||||
* 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' | 'collections';
|
||||
export type ComponentKind =
|
||||
| 'free-text'
|
||||
| 'media'
|
||||
| 'gallery'
|
||||
| 'contact-form'
|
||||
| 'partners'
|
||||
| 'collections'
|
||||
| 'collection-title'
|
||||
| 'collection-description'
|
||||
| 'collection-specs'
|
||||
| 'collection-gallery';
|
||||
|
||||
/*
|
||||
The blocks that draw whatever the page is ABOUT.
|
||||
|
||||
Only offered on a template. On an ordinary page they would render nothing, which is the worst kind of
|
||||
block: one that is available, adds cleanly, and silently does nothing.
|
||||
*/
|
||||
export const SUBJECT_KINDS: ComponentKind[] = [
|
||||
'collection-title',
|
||||
'collection-description',
|
||||
'collection-specs',
|
||||
'collection-gallery',
|
||||
];
|
||||
|
||||
type Choice = {
|
||||
kind: ComponentKind | null;
|
||||
@@ -66,16 +89,36 @@ export const CHOICES: Choice[] = [
|
||||
{ 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 },
|
||||
{ kind: 'collections', label: 'Collections', hint: 'Every collection, as a grid', Icon: LayoutGrid },
|
||||
|
||||
/*
|
||||
The four that draw THIS collection, on the template.
|
||||
|
||||
They hold nothing — not even a reference. The subject is the URL, so one block draws a different
|
||||
collection on every request, and that is what lets one arrangement serve all of them.
|
||||
*/
|
||||
{ kind: 'collection-title', label: 'Its name', hint: 'The collection this page is about', Icon: Heading },
|
||||
{ kind: 'collection-description', label: 'Its description', hint: 'What was written about it', Icon: Type },
|
||||
{ kind: 'collection-specs', label: 'Its specs', hint: 'Measurement, material, colours, delivery', Icon: Ruler },
|
||||
{ kind: 'collection-gallery', label: 'Its photographs', hint: 'The gallery it points at', Icon: Images },
|
||||
];
|
||||
|
||||
export const AddComponentDialog = ({
|
||||
open,
|
||||
onCancel,
|
||||
onChoose,
|
||||
template,
|
||||
}: {
|
||||
open: boolean;
|
||||
onCancel: () => void;
|
||||
onChoose: (kind: ComponentKind) => void;
|
||||
/**
|
||||
* Whether this page is a TEMPLATE, which decides whether the subject blocks are offered.
|
||||
*
|
||||
* Hidden rather than disabled elsewhere. A greyed-out row invites the question "why not here", and
|
||||
* the answer — this page has no subject — is not something a tooltip fixes. On a page that has one
|
||||
* they are the point; on any other they would render nothing at all.
|
||||
*/
|
||||
template?: boolean;
|
||||
}) => (
|
||||
<Dialog open={open} onOpenChange={(next) => !next && onCancel()}>
|
||||
<DialogContent className="sm:max-w-lg">
|
||||
@@ -84,7 +127,8 @@ export const AddComponentDialog = ({
|
||||
</DialogHeader>
|
||||
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
{CHOICES.map(({ kind, label, hint, Icon }) => (
|
||||
{CHOICES.filter((choice) => template || !SUBJECT_KINDS.includes(choice.kind)).map(
|
||||
({ kind, label, hint, Icon }) => (
|
||||
<button
|
||||
key={label}
|
||||
type="button"
|
||||
@@ -107,7 +151,8 @@ export const AddComponentDialog = ({
|
||||
</span>
|
||||
<span className="text-[11px] text-muted-foreground">{hint}</span>
|
||||
</button>
|
||||
))}
|
||||
),
|
||||
)}
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
@@ -1011,7 +1011,13 @@ export const PageBuilderPanel = () => {
|
||||
onCreate={(values) => void createSection(values)}
|
||||
/>
|
||||
|
||||
<AddComponentDialog open={choosing !== null} onCancel={() => setChoosing(null)} onChoose={chooseKind} />
|
||||
<AddComponentDialog
|
||||
open={choosing !== null}
|
||||
onCancel={() => setChoosing(null)}
|
||||
onChoose={chooseKind}
|
||||
// Which decides whether the subject blocks are offered — they render nothing anywhere else.
|
||||
template={page?.is_template}
|
||||
/>
|
||||
|
||||
<MediaPicker
|
||||
open={picking !== null}
|
||||
|
||||
@@ -11,6 +11,14 @@ export type Page = {
|
||||
menu_label: string | null;
|
||||
position: number;
|
||||
is_home: boolean;
|
||||
/**
|
||||
* The arrangement a whole CLASS of pages is drawn with, rather than a page anybody visits.
|
||||
*
|
||||
* `collection` is the one today: every `/collections/<slug>` is drawn through it. Listed beside the
|
||||
* pages because it is built exactly like one — a separate section for a single row would be a second
|
||||
* place to learn — and marked so nothing offers to rename it, reorder it or put it in a menu.
|
||||
*/
|
||||
is_template?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user