Files
summercms/.planning/quick/261006-t5r-make-spa-slug-preset-follow-mltext-sourc/261006-t5r-SUMMARY.md
2026-10-06 21:21:15 +02:00

124 lines
7.0 KiB
Markdown

---
phase: quick-261006-t5r
plan: 01
subsystem: cabana forms / admin SPA
tags: [preset, mltext, slug, admin-spa, forms]
status: complete
requires: []
provides:
- "fields.yaml preset on text and mltext targets with text or mltext sources"
- "SPA preset follow per locale (formState.presetUpdates, changedLocales)"
affects:
- sm-journal-plugin (post and category slug presets; YAML snippet below, not applied here)
tech-stack:
added: []
patterns:
- "Pure preset-follow rules in formState.ts; FormView only tracks edits and the active ML locale"
key-files:
created:
- modules/cabana/preset_ml_test.go
modified:
- modules/cabana/form_schema.go
- modules/cabana/phase121_form_test.go
- modules/cabana/phase121_schema_boot_test.go
- admin/src/components/form/formState.ts
- admin/src/views/FormView.vue
- admin/tests/form/formState.test.ts
- admin/tests/form/FormView.test.ts
- modules/boardwalk/dist/index.html
- modules/boardwalk/dist/assets/index-DPCIeFlT.js
- modules/cabana/schema_types.go
- modules/cabana/admin_openapi.go
- admin/openapi/admin.json
- admin/src/api/schema.d.ts
- modules/cabana/README.md
- docs/backend/forms.md
decisions:
- "ML -> ML presets follow per locale: only locales whose source text changed are rewritten, each from its own locale"
- "Hand edits of an ML preset target are tracked per locale; editing locale L stops only L"
- "A preset never writes a locale the administrator neither typed in nor sees: text -> mltext writes only the active locale"
- "mltext -> text follows the source's active-locale string (FormView tracks the active ML locale via ML_LOCALE_CHANGE)"
- "mlmarkdown stays out: server admits only text/mltext targets and sources"
metrics:
duration: "~13 min"
completed: 2026-10-06
estimate:
tokens: 70000
tasks: 3
actuals:
tokens: 13500
tasks: 3
commits: 3
plan_head_before: 7e2b1a909bb24eae22662a9edea23fec38cc0cd1
plan_head_after: 593217214809f8b8f45a7c2d2ea9ffd311ff2bfe
---
# Quick 261006-t5r: Make SPA slug preset follow mltext source Summary
fields.yaml `preset` now works on `mltext` fields: cabana accepts text/mltext targets and sources, and the admin SPA follows an mltext title into an mltext slug per locale, stopping only the locale the administrator edits by hand.
## What changed
**Task 1 - cabana (7afad73, `feat(cabana): accept preset on mltext fields`)**
- `compilePresetKey` admits `type: text` or `type: mltext`; anything else fails with `preset is only valid on type: text or mltext`.
- `checkPresets` admits a text or mltext source; otherwise `field %s: preset field %s must be a text or mltext field`.
- New DB-free `TestPresetML` (RED confirmed before the change) covers mltext->mltext (mapping, short form, exact), text->mltext, mltext->text, and the refusals (mlmarkdown and markdown targets, mlmarkdown source). The Journal-shaped field serializes with `"type":"mltext"` and `"preset":{"field":"title","type":"slug"}`.
- `TestPresetSchema` / `TestPhase121BootErrors` rows updated to the new messages (testcontainers Postgres, green). Settings and relation forms still refuse `preset`.
**Task 2 - admin SPA (b5d8506, `feat(admin): follow presets on mltext fields per locale`)**
- `formState.ts`: `changedLocales(previous, next)` and `presetUpdates(fields, source, previous, next, values, activeLocale, isEdited)` (plus `PresetEdited` type) hold every preset-follow rule; all values still go through `presetValue`, so slug/exact remain one implementation.
- `FormView.vue`: `update()` reads the previous value, records per-locale hand edits of ML fields in `editedLocales`, and on create merges `presetUpdates` into the next values. `mlLocale` follows the window `ML_LOCALE_CHANGE` event, starts on the first enabled locale after load, and resets to it when `activeTab` changes. Update mode unchanged.
- Tests: 6 new formState unit cases, 4 new FormView cases (Journal ML->ML per-locale with POST body check, update mode, mltext->text, text->mltext). The existing text preset test is unchanged and green. Full Vitest: 1064 passed; vue-tsc clean.
- `modules/boardwalk/dist` rebuilt (old `index-7UEAlsFO.js` removed, `index-DPCIeFlT.js` added; intentional hashed-bundle swap); `check-admin-dist.sh` reports a match.
**Task 3 - docs (5932172, `docs(cabana): document preset on mltext fields`)**
- `schema_types.go` Preset/FieldPreset comments and the form-schema swag description in `admin_openapi.go` name text or mltext; `admin.json` and `schema.d.ts` regenerated with `scripts/check-admin-openapi.sh` (the `--check` gate passes).
- `modules/cabana/README.md` (Features bullet, `cabana.FieldPreset` row) and `docs/backend/forms.md` (Field options list, preset paragraph, new multilingual-rules paragraph, start-up refusal sentence, and a pointer from the Markdown and multilingual fields section to `#field-options`).
- `TestDocsTree`, `go vet ./...` and `go test ./...` green.
## Discretion choices (from the plan)
- ML -> ML follows per locale: only the locales whose source text changed in this update are rewritten, each from its own locale.
- Edits of an ML target are tracked per locale: a hand edit of locale L stops L only.
- A preset never writes a locale the administrator neither typed in (source side) nor is looking at (target side): text -> mltext writes only the active locale; untouched locales stay empty so the server's translation fallback applies, and no unseen duplicate translations get saved.
- mltext -> text follows the source's active-locale string (FormView tracks the active ML locale).
- mlmarkdown stays out: the server admits text/mltext targets and text/mltext sources only.
## Plugin follow-up (sm-journal-plugin, NOT edited in this task)
sm-journal-plugin was not edited. Apply in that repo:
models/post/fields.yaml, the slug field gains a preset:
```yaml
slug:
label: golem15.journal::lang.post.slug
type: mltext
span: right
tab: golem15.journal::lang.post.tab_edit
required: true
preset:
field: title
type: slug
```
models/category/fields.yaml, the slug field gains `preset: { field: name, type: slug }` the same way (its source `name` is mltext). models/tag/fields.yaml already has a text -> text preset and needs nothing.
## Deviations from Plan
None - plan executed exactly as written. (Process note: the formState helpers were written just before their tests; RED was then demonstrated by running the new tests against the committed `formState.ts`/`FormView.vue` — 9 failures — before restoring the implementation.)
## Known Stubs
None.
## Threat Flags
None. No new routes or trust-boundary surface; the server-side widening admits exactly text and mltext (pinned by TestPresetML), and preset output remains client convenience validated by the normal save path.
## Self-Check: PASSED
- FOUND: modules/cabana/preset_ml_test.go
- FOUND: commits 7afad73, b5d8506, 5932172 (no co-author trailers, no .planning files)
- Gates: check-admin-dist, check-admin-openapi --check, TestDocsTree, go vet ./..., go test ./..., Vitest full suite, vue-tsc