Pieces: the Gallery, as a section like Collections
The Gallery is a flat list of peças, each with a title, a gallery of mixed media, and the specs from the client's sitemap — measurement, raw material, colours, and NOTAS: production time and shipping conditions. That is a collection one level flatter, so the panel is shared rather than copied. SubjectPanel and SubjectFields take a section config (subjects.ts); CollectionsPanel and PiecesPanel are one line each. The five template blocks are renamed collection-* to subject-*. They never read anything collection-specific, and copying them per section would have meant twenty kinds by the time Drops and Collabs land. Called Pieces in the nav, not Gallery — there is already a Galleries section meaning a named set of media, and two rows called Gallery and Galleries is a question somebody has to answer every time they look at it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
import type { LucideIcon } from 'lucide-react';
|
||||
import { Boxes, Shapes } from 'lucide-react';
|
||||
|
||||
/**
|
||||
* The two sections whose rows are drawn through a template — collections, and gallery pieces.
|
||||
*
|
||||
* ── Why one shape ──
|
||||
*
|
||||
* They are the same object. A title, an address, some words, a gallery, and a table of properties;
|
||||
* the sections differ in what they GROUP, not in how one is edited. Copying the panel would be three
|
||||
* hundred lines duplicated for an icon and a noun, and the copies would drift the first time one of
|
||||
* them got a fix.
|
||||
*
|
||||
* The site draws them through one set of blocks for exactly the same reason — see
|
||||
* `src/apps/web/components/subject.ts` in the website.
|
||||
*
|
||||
* ── What a section still owns ──
|
||||
*
|
||||
* Its endpoint, the word it puts in the URL, and its SPEC FIELDS. That last one is the real
|
||||
* difference: a collection ends with a delivery time; a piece ends with the sitemap's NOTAS*,
|
||||
* production time and shipping conditions. Both are just rows in a form, and naming them here is what
|
||||
* keeps the form from having to know which section it is drawing.
|
||||
*/
|
||||
|
||||
/** What every subject row has. Snake case: it is the wire, not our shape. */
|
||||
export type SubjectRecord = {
|
||||
id: number;
|
||||
/** The gallery holding its media, or null while nobody has chosen one. */
|
||||
gallery_id: number | null;
|
||||
title: string;
|
||||
slug: string | null;
|
||||
description: string | null;
|
||||
sort_order: number;
|
||||
photo_count: number;
|
||||
};
|
||||
|
||||
export type Collection = SubjectRecord & {
|
||||
descriptive_text: string | null;
|
||||
measurement: string | null;
|
||||
material: string | null;
|
||||
colors: string | null;
|
||||
delivery_time: string | null;
|
||||
};
|
||||
|
||||
export type Piece = SubjectRecord & {
|
||||
descriptive_text: string | null;
|
||||
measurement: string | null;
|
||||
material: string | null;
|
||||
colors: string | null;
|
||||
production_notes: string | null;
|
||||
shipping_notes: string | null;
|
||||
};
|
||||
|
||||
export type SubjectPhoto = { path: string; name: string };
|
||||
|
||||
export type SubjectSection<T extends SubjectRecord> = {
|
||||
/** The query key the selection lives under — `?collection=3`. See docs/navigation-audit.md. */
|
||||
param: string;
|
||||
/** Where the rows are, relative to the plugin's mount. */
|
||||
endpoint: string;
|
||||
/** The site path they live at, for the address hint under the slug field. */
|
||||
base: string;
|
||||
icon: LucideIcon;
|
||||
/** Lower case, mid-sentence: "Choose a collection, or make one." */
|
||||
noun: string;
|
||||
/** Key, label, placeholder — in the order the client's sitemap lists them. */
|
||||
specs: [Extract<keyof T, string>, string, string][];
|
||||
/** Which of those are long enough to want a textarea. */
|
||||
long?: Extract<keyof T, string>[];
|
||||
};
|
||||
|
||||
export const COLLECTIONS: SubjectSection<Collection> = {
|
||||
param: 'collection',
|
||||
endpoint: '/fofos-admin/collections',
|
||||
base: '/collections',
|
||||
icon: Boxes,
|
||||
noun: 'collection',
|
||||
specs: [
|
||||
['descriptive_text', 'Descriptive text', 'What this collection is'],
|
||||
['measurement', 'Measurement', '45cm x 45cm'],
|
||||
['material', 'Raw material', 'Acrylic yarn'],
|
||||
['colors', 'Colours', ''],
|
||||
['delivery_time', 'Delivery time', '4-6 weeks'],
|
||||
],
|
||||
long: ['descriptive_text'],
|
||||
};
|
||||
|
||||
export const PIECES: SubjectSection<Piece> = {
|
||||
param: 'piece',
|
||||
endpoint: '/fofos-admin/pieces',
|
||||
base: '/gallery',
|
||||
icon: Shapes,
|
||||
noun: 'piece',
|
||||
/*
|
||||
The last two are the sitemap's NOTAS* — conditions of buying the object rather than properties of
|
||||
it. Two more fields here, because that is honestly all the difference amounts to; the template is
|
||||
where they are drawn apart.
|
||||
*/
|
||||
specs: [
|
||||
['descriptive_text', 'Descriptive text', 'What this piece is'],
|
||||
['measurement', 'Measurement', '45cm x 45cm'],
|
||||
['material', 'Raw material', 'Acrylic yarn'],
|
||||
['colors', 'Colours', ''],
|
||||
['production_notes', 'Production time', 'Made to order, 4-6 weeks'],
|
||||
['shipping_notes', 'Shipping', 'Ships worldwide, tracked'],
|
||||
],
|
||||
long: ['descriptive_text'],
|
||||
};
|
||||
Reference in New Issue
Block a user