Let the owner manage categories, and give the menus a shelf
Categories were baked into config/<site>.site.json, so adding one meant a rebuild and a redeploy. They are now data: data/sections.json, seeded from the config the first time, and managed in the admin (add, rename, reorder, remove). A category's web address is fixed when it is created because that is what a QR code points at, and a category cannot be removed while it still holds menus. The landing and the top bar were the ugliest parts. The landing was a column of hairlines with tiny meta — a broadsheet, and the paper that makes this app worth looking at was nowhere on it. It is now a shelf: each category is a row whose right edge is a sliver of that menu's first page, bleeding off the screen, with the shelf edge in brass and the name in heavy tight Archivo. The bar was a row of teal pills that would not survive more categories: it now names the menu you are reading and opens a sheet listing the rest, each with its own paper sliver, and the current one is the only teal thing on screen. The sheet is a link to the landing page, so navigating still works without the script. Also fixes the category manager's own layout, which I shipped unstyled, and the markup contract the route tests described.
This commit is contained in:
1 parent
7bf67cc299
commit
c31b73d553
25 files changed
+1225
-190
No files matched your search
@@ -1,3 +1,5 @@
|
||||
**Status**: superseded by [ADR 0007](./0007-categories-are-content.md).
|
||||
|
||||
# Sections come from configuration, not from the admin
|
||||
|
||||
A Site's Sections are declared in its config file and change only with a redeploy; the admin manages the Menu Sets inside Sections and cannot create, rename or reorder Sections. Sections are few and stable, and keeping them out of the admin removed a whole class of editing, reordering and deletion edge cases from the content model.
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
# Categories are content, not configuration
|
||||
|
||||
Sections moved out of `config/<site>.site.json` and into the data directory as `data/sections.json`, managed from the admin: add, rename, reorder and remove. The config's Section list is now only the seed for a Site that has never been opened, which keeps a fresh deployment useful without making a redeploy the price of adding a category. This supersedes ADR 0005.
|
||||
|
||||
Two rules make the change safe to live with. A Section's **slug is fixed once created** — the label can change at any time, but the web address cannot, because it is what a table card or a printed QR code points at. And a Section **cannot be removed while it still holds menus**, so a category cannot silently orphan or delete content; the admin says how many sets are in the way.
|
||||
|
||||
**Considered Options**: keeping Sections in the config and adding an admin that edits the file (rejected: the config is mounted read-only into the container, and every change would still need a redeploy); storing them in the manifests (rejected: a Section exists before it has any Menu Set, so it needs a home of its own).
|
||||
+11
-4
@@ -12,7 +12,8 @@ For whoever is on the other end of "the menu is wrong". The design is in
|
||||
| Menu content | `/var/lib/menu-app.<site>/data` — `originals/`, `drafts/`, `published/` |
|
||||
| Admin | `https://<vhost>/admin` |
|
||||
| The password | `/var/lib/menu-app.<site>/site.env` (mode 600, never in git) |
|
||||
| Sections and branding | `config/<site>.site.json` in this repo, baked into the image |
|
||||
| Categories | `data/sections.json`, edited in the admin |
|
||||
| Brand and theme | `config/<site>.site.json` in this repo, baked into the image |
|
||||
|
||||
## Everyday tasks
|
||||
|
||||
@@ -31,9 +32,15 @@ bash /tmp/provision-site-env.sh /var/lib/menu-app.hopsngrains.com --force
|
||||
docker restart menu-app.hopsngrains.com
|
||||
```
|
||||
|
||||
**Add, rename or reorder a Section.** Edit `config/<site>.site.json`, rebuild and
|
||||
push (`./deploy.sh <tag>`), then run the playbook with the new tag. Sections are
|
||||
deliberately not editable from the admin (ADR 0005).
|
||||
**Add, rename, reorder or remove a category.** In the admin, open the
|
||||
**Categories** fold at the bottom of the dashboard. A new category starts empty,
|
||||
so customers do not see it until you publish a menu into it. Reordering there is
|
||||
the order customers see. Two rules: a category's web address is fixed when it is
|
||||
created (rename the name as often as you like — a printed QR code keeps working),
|
||||
and a category can only be removed once it holds no menus.
|
||||
|
||||
**Change brand or theme.** Edit `config/<site>.site.json`, rebuild and push
|
||||
(`./deploy.sh <tag>`), then run the playbook with the new tag.
|
||||
|
||||
**Fix a missing image.** If a Page's thumbnail or viewing image is lost while its
|
||||
Original survives, the app can rebuild them:
|
||||
|
||||
+6
-6
@@ -20,7 +20,7 @@ Replaces the `scp`-a-JPG workflow for `menu.hopsngrains.com` with a small self-h
|
||||
| Stack | Node + Fastify + `sharp`, one container |
|
||||
| Metadata | JSON manifests, state encoded in the directory layout (ADR 0002) |
|
||||
| Scope | One app instance per Site (ADR 0004); other two Sites migrate later |
|
||||
| Sections | Per-Site config in git; not editable in the admin (ADR 0005) |
|
||||
| Categories | Content, not configuration: added, renamed, reordered and removed in the admin (ADR 0007, superseding ADR 0005) |
|
||||
| Lifecycle | Draft → Current → Archive; publishing auto-archives the previous set; archives unlisted but reachable. A published set's Pages are fixed, but the Current set's order can be corrected (ADR 0006) |
|
||||
| Auth | Single shared password per Site; app login; hash + session secret in an untracked `.env` on the server |
|
||||
| Images | 1600px viewing WebP + JPEG fallback, 480px admin-only thumbnails, Originals kept and never served |
|
||||
@@ -29,7 +29,7 @@ Replaces the `scp`-a-JPG workflow for `menu.hopsngrains.com` with a small self-h
|
||||
|
||||
## Domain
|
||||
|
||||
`CONTEXT.md` is the glossary. Sections for this Site: Food, Bar, Wine, Straight Drinks, Delivery, What's On. One Current Menu Set per Section; a Section with no Current set does not appear on the landing page.
|
||||
`CONTEXT.md` is the glossary. This Site starts with Food, Bar, Wine, Straight Drinks, Delivery and What's On; the owner adds and renames categories from the admin. One Current Menu Set per Section, and a Section with no Current set does not appear on the landing page.
|
||||
|
||||
## Architecture
|
||||
|
||||
@@ -64,7 +64,7 @@ caddy (80/443, TLS) ──reverse_proxy──▶ menu-app.hopsngrains.com:3000
|
||||
|
||||
| Route | Purpose |
|
||||
| --- | --- |
|
||||
| `GET /` | Landing page: Sections with a Current set, text rows |
|
||||
| `GET /` | Landing page: the shelves, one per Section with a Current set |
|
||||
| `GET /<section>/` | Current Menu Set |
|
||||
| `GET /<section>/<slug>/` | Archived Menu Set (unlisted, still reachable) |
|
||||
| `GET /<section>/#page-N` | Deep link to one Page |
|
||||
@@ -89,7 +89,7 @@ Protection: a `scrypt` hash (from `node:crypto`, so no native build dependency)
|
||||
|
||||
### Customer design
|
||||
|
||||
Text-led landing page; Sections as tap-list rows with copper hairlines; no hero photograph, no cards, no shadows.
|
||||
The chrome is the shelf and the menus are the paper on it: each Section is a shelf row whose right edge is a sliver of that menu's first page, bleeding off the screen. The bar names the menu you are reading and opens a sheet listing the rest, which scales to any number of categories; a 2px brass line under it tracks how far through the pages you are.
|
||||
|
||||
```
|
||||
┌────────────────────────────┐ ┌────────────────────────────┐
|
||||
@@ -114,7 +114,7 @@ Text-led landing page; Sections as tap-list rows with copper hairlines; no hero
|
||||
|
||||
- New repo `menu-admin` on `git.tanshu.com`: app source, `Dockerfile`, `compose.yaml` for local dev, `docs/` (this plan, `CONTEXT.md`, the ADRs), `node --test` tests, and `deploy.sh` (tests, build, push).
|
||||
- The container is `node:24-alpine` running TypeScript directly, so there is no build step; sharp's musl binaries come from `npm ci` on the image's own platform. It writes only under `MENU_ROOT` and healthchecks its own `/healthz`.
|
||||
- The Site's Sections, brand and theme live in `config/<site>.site.json`, baked into the image and selected with `MENU_SITE_CONFIG`. Changing Sections is therefore a commit, a new image and a redeploy; the playbook only names the Site.
|
||||
- The Site's brand and theme live in `config/<site>.site.json`, baked into the image and selected with `MENU_SITE_CONFIG`; its Section list only seeds `data/sections.json` the first time. Categories are edited in the admin from then on (ADR 0007), so adding one does not need a redeploy.
|
||||
- Build locally and push `registry.tanshu.com/menu-admin:<tag>`, matching the sibling `deploy.sh` / `make build-production TAG=` convention.
|
||||
- New `menu-admin` playbook in `ansible-playbooks` with per-Site vars (`vars/hopsngrains.yml` first, the other two stubbed): roles `network`, `menu-app` (pull image, create `/var/lib/menu-app.<site>`, start container, health check, then `verify` the data directory) and `caddy` (snippet upsert).
|
||||
- The existing `roles/caddy` only *adds* a snippet when its exact text is missing, which cannot change a proxy target; the new playbook's role uses a stable marker so `blockinfile` updates in place, and removes the old role's per-hostname marker at cutover so Caddy never holds two blocks for one hostname.
|
||||
@@ -136,4 +136,4 @@ Deleting the old 319MB directory is the only irreversible step, so it is last.
|
||||
|
||||
## Out of scope
|
||||
|
||||
Backups (owner already runs them), Prometheus metrics, Promtail/Loki integration, multi-user accounts, scheduled publishing, admin-editable Sections, migrating `menu.hngmohali.com` and `menu.greatbear.in`, and git history for the image content.
|
||||
Backups (owner already runs them), Prometheus metrics, Promtail/Loki integration, multi-user accounts, scheduled publishing, per-Section theming, migrating `menu.hngmohali.com` and `menu.greatbear.in`, and git history for the image content.
|
||||
Reference in new issue
Block a user