chore: redesign article localization before implementation
This commit is contained in:
1 parent
99a74dc3a2
commit
8c4a1825ab
31 files changed
+3365
-882
No files matched your search
@@ -1,34 +1,76 @@
|
||||
# Editor: language navigation
|
||||
# Localization: editor header, permissions, publishing
|
||||
Board: RenewCMS Screens Localization. Frame: LocaleFrame. Data: locale-fixtures.js (single fixture source), tables in locale-spec.js. UI copy says "article", never "story". No revision numbers, IDs or HTTP status names in user-facing text.
|
||||
|
||||
Visual reference: `RenewCMS Screens Localization.dc.html`, group "L5 Editor: language navigation".
|
||||
## L5 Editor: header, actions and title
|
||||
Route: /article/{id}/edit (one id per version)
|
||||
API: proposed: GET /v1/article/{id} → translations[], capabilities[]
|
||||
|
||||
Read [localization.md](../localization.md) first. Its integration decisions supersede conflicting board annotations. These states describe target behavior, not shipped behavior.
|
||||
Header priority and the action matrix are in the tables below. One filled primary button at most: Save and publish when allowed, dirty and offline; otherwise Save when dirty; otherwise Publish when clean and offline. Save stays separate and secondary. Desktop collapse order as width shrinks (1440 → 1024): reference button loses its label, Unpublish moves to the overflow menu, the freshness badge becomes icon-only. Mobile: title, language, publication and dirty badges stay at the top; Save, primary and an overflow button sit in a fixed bottom bar; reference, Unpublish, Edit slug, Move and Delete go to the overflow sheet. The language control is a menu button with a menu (not a listbox).
|
||||
|
||||
Route: `/article/{id}/edit (one id per version)`
|
||||
| ID | State | Trigger | Selector | Sizes | Fixture |
|
||||
|---|---|---|---|---|---|
|
||||
| loc.l5.default | Clean draft | Open a draft you can publish; title pristine | LocaleFrame{s:{v:"ed",x:"default"}} | 1440, 1024, 390 | Journal |
|
||||
| loc.l5.dirty | Dirty draft | Can edit, can't publish: Save is primary | LocaleFrame{s:{v:"ed",x:"dirty"}} | 1440, 1024, 390 | Journal |
|
||||
| loc.l5.dirtyPublish | Dirty draft, publish allowed | Save and publish French is primary; Save secondary | LocaleFrame{s:{v:"ed",x:"dirtyPublish"}} | 1440, 1024, 390 | Journal |
|
||||
| loc.l5.cleanOnline | Clean online | Unpublish only; no primary | LocaleFrame{s:{v:"ed",x:"cleanOnline"}} | 1440, 1024, 390 | Journal |
|
||||
| loc.l5.onlineDirty | Dirty online | Save updates the live page; Unpublish stays | LocaleFrame{s:{v:"ed",x:"onlineDirty"}} | 1440, 1024, 390 | Journal |
|
||||
| loc.l5.saving | Saving | Save in flight | LocaleFrame{s:{v:"ed",x:"saving"}} | 1440, 390 | Journal |
|
||||
| loc.l5.saved | Saved | Toast | LocaleFrame{s:{v:"ed",x:"saved"}} | 1440, 390 | Journal |
|
||||
| loc.l5.saveFail | Save failed | Stays unsaved; Try saving again; nothing published | LocaleFrame{s:{v:"ed",x:"saveFail"}} | 1440, 1024, 390 | Journal |
|
||||
| loc.l5.pubFail | Publish failed after save | Saved as a draft; Retry publish; edits shown as saved | LocaleFrame{s:{v:"ed",x:"pubFail"}} | 1440, 1024, 390 | Journal |
|
||||
| loc.l5.ovf | Overflow menu | More actions (sheet on mobile) | LocaleFrame{s:{v:"ed",x:"ovf"}} | 1440, 390 | Journal |
|
||||
| loc.l5.menu | Menu: four languages | Open menu | LocaleFrame{s:{v:"ed",x:"menu"}} | 1440, 390 | Journal |
|
||||
| loc.l5.two | Menu: two languages | Two-language site | LocaleFrame{s:{v:"ed",x:"two"}} | 1440, 390 | Journal |
|
||||
| loc.l5.many | Menu: many languages | Scrolls inside the viewport, with filter | LocaleFrame{s:{v:"ed",x:"many"}} | 1440, 390 | Journal |
|
||||
| loc.l5.unsaved | Switch with unsaved changes | Choose English while dirty | LocaleFrame{s:{v:"ed",x:"unsaved"}} | 1440, 390 | Journal |
|
||||
| loc.l5.loading | Loading version | Slow switch | LocaleFrame{s:{v:"ed",x:"loading"}} | 1440, 390 | Journal |
|
||||
| loc.l5.navFail | Switch failed | Stays on current version | LocaleFrame{s:{v:"ed",x:"navFail"}} | 1440, 390 | Journal |
|
||||
| loc.l5.gone | Version unavailable | Deleted or no access (neutral) | LocaleFrame{s:{v:"ed",x:"gone"}} | 1440, 390 | Journal |
|
||||
| loc.l5.focus | Title: focus | Field focused, not changed | LocaleFrame{s:{v:"ed",x:"focus"}} | 1440, 390 | Journal |
|
||||
| loc.l5.titleEdit | Title: editing | Typing; counter shown | LocaleFrame{s:{v:"ed",x:"titleEdit"}} | 1440, 390 | Journal |
|
||||
| loc.l5.titleUnsaved | Title: unsaved | Changed, focus left | LocaleFrame{s:{v:"ed",x:"titleUnsaved"}} | 1440, 390 | Journal |
|
||||
| loc.l5.invalid | Title: invalid | Empty: "Enter a title" | LocaleFrame{s:{v:"ed",x:"invalid"}} | 1440, 390 | Journal |
|
||||
| loc.l5.titleShort | Title: too short | Under 3 characters | LocaleFrame{s:{v:"ed",x:"titleShort"}} | 1440, 390 | Journal |
|
||||
| loc.l5.titleLong | Title: too long | Over 200 characters | LocaleFrame{s:{v:"ed",x:"titleLong"}} | 1440, 390 | Journal |
|
||||
| loc.l5.titleSaving | Title: saving | Save in flight | LocaleFrame{s:{v:"ed",x:"titleSaving"}} | 1440, 390 | Journal |
|
||||
| loc.l5.failed | Title: save failed | Text kept, still unsaved, Try again | LocaleFrame{s:{v:"ed",x:"failed"}} | 1440, 390 | Journal |
|
||||
|
||||
### Editor: language navigation (group "L5 Editor: language navigation", 18 states)
|
||||
## L5b Permission states (per version)
|
||||
Route: /article/{id}/edit
|
||||
API: capabilities[] on GET /v1/article/{id}; every PATCH/DELETE rechecked on the server
|
||||
|
||||
| # | State | Trigger | Stable ID | Frame selector |
|
||||
|---|---|---|---|---|
|
||||
| 1 | Default (French draft) | Editable title, language menu, badges | `loc.l5.default` | `ed:default` |
|
||||
| 2 | Menu: four languages | Open menu | `loc.l5.menu` | `ed:menu` |
|
||||
| 3 | Menu: two languages | Two-language site | `loc.l5.two` | `ed:two` |
|
||||
| 4 | Menu: many languages | Scrolls inside the viewport, with filter | `loc.l5.many` | `ed:many` |
|
||||
| 5 | Unsaved changes | Dirty badge; Save enabled | `loc.l5.dirty` | `ed:dirty` |
|
||||
| 6 | Unsaved draft: Save and publish | Button reads Save and publish French; Save stays separate | `loc.l5.dirtyPublish` | `ed:dirtyPublish` |
|
||||
| 7 | Unsaved online version | Save updates the live page; Unpublish stays available | `loc.l5.onlineDirty` | `ed:onlineDirty` |
|
||||
| 8 | Save failed | Stays unsaved; Try saving again; nothing published | `loc.l5.saveFail` | `ed:saveFail` |
|
||||
| 9 | Switch with unsaved changes | Choose English while dirty | `loc.l5.unsaved` | `ed:unsaved` |
|
||||
| 10 | Saving | Save in flight | `loc.l5.saving` | `ed:saving` |
|
||||
| 11 | Saved | Toast | `loc.l5.saved` | `ed:saved` |
|
||||
| 12 | Loading version | Slow switch | `loc.l5.loading` | `ed:loading` |
|
||||
| 13 | Switch failed | Stays on current version | `loc.l5.navFail` | `ed:navFail` |
|
||||
| 14 | Version unavailable | Deleted or no access (neutral) | `loc.l5.gone` | `ed:gone` |
|
||||
| 15 | Title: editing | Focus | `loc.l5.focus` | `ed:focus` |
|
||||
| 16 | Title: invalid | Empty | `loc.l5.invalid` | `ed:invalid` |
|
||||
| 17 | Title: saving | | `loc.l5.titleSaving` | `ed:titleSaving` |
|
||||
| 18 | Title: save failed | Text kept, still unsaved, Try again | `loc.l5.failed` | `ed:failed` |
|
||||
Every action renders from its own capability (read, edit, publish, delete, create translation, review). See the capability matrix. Missing-translation rows appear only for users who can create; unreadable siblings are absent with no placeholder and no count. The formatting toolbar is hidden whenever edit is missing.
|
||||
|
||||
Exact rendered copy and geometry are in `source/LocaleFrame.dc.html` for the selector above. Use Nuxt UI mappings in `components.md`; the frame markup is a visual reference, not production component code.
|
||||
| ID | State | Trigger | Selector | Sizes | Fixture |
|
||||
|---|---|---|---|---|---|
|
||||
| loc.perm.readonly | P1 Read-only version | Banner, read-only fields, no Save, Publish, Delete, toolbar; reference stays | LocaleFrame{s:{v:"perm",x:"ro"}} | 1440, 1024, 390 | Journal |
|
||||
| loc.l5b.roMenu | P1 menu | No Add translation row | LocaleFrame{s:{v:"perm",x:"roMenu"}} | 1440, 390 | Journal |
|
||||
| loc.perm.editNoPublish | P2 Edit, no publish | Save only; hint about the publish permission | LocaleFrame{s:{v:"perm",x:"p2"}} | 1440, 1024, 390 | Journal |
|
||||
| loc.perm.publishNoEdit | P3 Publish, no edit | Publish only; fields read-only | LocaleFrame{s:{v:"perm",x:"p3"}} | 1440, 1024, 390 | Journal |
|
||||
| loc.perm.siblingReadOnly | P4 Sibling readable, not editable | Arabic row tagged Read only | LocaleFrame{s:{v:"perm",x:"p4"}} | 1440, 390 | Journal |
|
||||
| loc.perm.siblingHidden | P5 Sibling not readable | German absent, no count, no placeholder | LocaleFrame{s:{v:"perm",x:"p5"}} | 1440, 390 | Journal |
|
||||
| loc.perm.createNotAll | P6 Can create, not modify all | Delete article and Move disabled with the reason; Delete French version available | LocaleFrame{s:{v:"perm",x:"p6"}} | 1440, 390 | Journal |
|
||||
| loc.l5b.noAdd | Menu without Add translation | No create permission; Arabic is missing but offers nothing | LocaleFrame{s:{v:"perm",x:"noAdd"}} | 1440, 390 | Journal |
|
||||
| loc.l5b.withAdd | Menu with Add translation | Same article, user may create | LocaleFrame{s:{v:"perm",x:"withAdd"}} | 1440, 390 | Journal |
|
||||
|
||||
The standard captures are 1440×900 and 390×844 in light and dark. The narrow reference state also uses 1024×768. The visual harness must support these sources and sizes before this screen can pass its gate. No visual exceptions or new clock-sensitive skips are approved.
|
||||
## L8 Publishing and deletion
|
||||
Route: same as the editor
|
||||
API: existing PATCH /v1/article/{id}/publish and /unpublish and DELETE per article id
|
||||
|
||||
Every action names its language. No bulk publish. With unsaved edits the primary reads Save and publish and saves first; if the save fails nothing is published and the edits stay unsaved; if the save works and publishing fails the edits show as saved. Unpublish never saves: the saved version goes offline and local edits stay in the editor. Saving an online version updates the live page immediately, and the editor says so. Deletion confirmations name only readable versions.
|
||||
|
||||
| ID | State | Trigger | Selector | Sizes | Fixture |
|
||||
|---|---|---|---|---|---|
|
||||
| loc.l8.publish | Publish French | Confirm, English untouched | LocaleFrame{s:{v:"pd",x:"publish"}} | 1440, 390 | Journal |
|
||||
| loc.lang.publishDisabled | Publishing blocked: language disabled | French disabled on the site | LocaleFrame{s:{v:"pd",x:"publishDisabled"}} | 1440, 390 | Journal |
|
||||
| loc.l8.saveAndPublish | Save and publish French | Unsaved edits: saves first, then publishes | LocaleFrame{s:{v:"pd",x:"saveAndPublish"}} | 1440, 390 | Journal |
|
||||
| loc.l8.saveFailPublish | Save failed, nothing published | Toast; edits kept and retryable | LocaleFrame{s:{v:"pd",x:"saveFailPublish"}} | 1440, 390 | Journal |
|
||||
| loc.l8.unpublish | Unpublish French | Confirm | LocaleFrame{s:{v:"pd",x:"unpublish"}} | 1440, 390 | Journal |
|
||||
| loc.l8.unpublishDirty | Unpublish with unsaved edits | Saved version goes offline; edits kept; nothing saved | LocaleFrame{s:{v:"pd",x:"unpublishDirty"}} | 1440, 390 | Journal |
|
||||
| loc.l8.delete | Delete French version | Confirm | LocaleFrame{s:{v:"pd",x:"delete"}} | 1440, 390 | Journal |
|
||||
| loc.l8.deleteSource | Delete a source version | Warns about review tracking | LocaleFrame{s:{v:"pd",x:"deleteSource"}} | 1440, 390 | Journal |
|
||||
| loc.l8.deleteLast | Delete the only version | Deletes the article | LocaleFrame{s:{v:"pd",x:"deleteLast"}} | 1440, 390 | Journal |
|
||||
| loc.l8.deleteStoryOk | Delete article (all readable versions) | Names readable versions only | LocaleFrame{s:{v:"pd",x:"deleteStoryOk"}} | 1440, 390 | Journal |
|
||||
| loc.l8.deleteStory | Delete article blocked | Neutral, no version named | LocaleFrame{s:{v:"pd",x:"deleteStory"}} | 1440, 390 | Journal |
|
||||
| loc.l8.done | Published | Toast | LocaleFrame{s:{v:"pd",x:"done"}} | 1440, 390 | Journal |
|
||||
| loc.l8.fail | Publish failed | Toast | LocaleFrame{s:{v:"pd",x:"fail"}} | 1440, 390 | Journal |
|
||||
Reference in new issue
Block a user