CMS · Content categories
Hierarchical, multi-language category tree used to classify proposals and stores, with parent selection, per-language names and a CSV export of the whole taxonomy.
| Area | CMS |
| Routes | cms.categories · cms.categories.index · cms.categories.new · cms.categories.edit |
| Centre modules | cms |
| Staff levels | root · admin |
| Models | category · category-language |
| UI label | title.header.cms.categories / menu.navbar.category.show |
| Coverage | 4 use cases · 1 happy · 4 failure |
How it works
Root/admin only — the tightest CMS permission, because categories are structural. searchParentCategories + selectParentCategory build the hierarchy, and updateTranslationName keeps per-language names in step. downloadCSV exports the tree. Categories are also referenced by the app-button manager: MOF types stores and proposals require a category (error.mof.category.required), so deleting a category can break app navigation.
Use cases
Why someone opens this (4)
- Defining the content taxonomy a centre’s app navigation is built on.
- Adding a sub-category under an existing parent.
- Translating category names for a multilingual centre.
- Exporting the taxonomy for review or migration.
Path map
flowchart LR F(["Content categories"]):::feat F --> H1["Sub-category created and translated"]:::ok F -.-> X1["Name missing"]:::bad F -.-> X2["Missing translation"]:::bad F -.-> X3["Deleting a category in use"]:::bad F -.-> X4["Export fails"]:::bad classDef feat fill:#6a4a8f,stroke:#6a4a8f,color:#ffffff,font-weight:bold classDef ok fill:#e3f1ea,stroke:#0d6b42,color:#0d4a2e classDef bad fill:#f7e6e2,stroke:#9a2d1c,color:#6e1f13
Happy paths
Sub-category created and translated
- Admin opens CMS → Categories → new and enters the default-language name.
- Searches and selects a parent category.
- Fills the name for each additional language.
- Saves; the category becomes selectable on proposals, stores and app buttons.
Failure paths
Name missing
Cause — Blank name.
What happens — Model validation rejects via the shared form save pipeline.
Missing translation
Cause — A translatable name left blank for a non-default language.
What happens —
error.all-translation-fields.requiredwhere the multi-language guard is enforced.
Deleting a category in use
Cause — Proposals, stores or app buttons still reference it.
What happens — Rejected by the API; the generic form error path shows the detail. Note the app-button validation only checks presence, so an orphaned reference surfaces as a broken button rather than a clear error.
Export fails
Cause — API error on the CSV download.
What happens — No dedicated error message on
downloadCSV.
QA checklist
Tick these off while regression-testing this feature. The boxes are plain Markdown, so progress is visible in git diffs.
- ✅ Sub-category created and translated
- ⛔ Name missing
- ⛔ Missing translation
- ⛔ Deleting a category in use
- ⛔ Export fails
Implementation
app/controllers/cms/categories/index.jsapp/controllers/cms/categories/new.jsapp/routes/cms/categories/*.jsapp/models/category.jsapp/models/category-language.jsapp/abilities/category.js
Translation keys (6)
Key English title.header.cms.categoriesCategories menu.navbar.category.showCategories cms.new-categoryNew category cms.categories.general-dataGeneral data error.all-translation-fields.requiredAll translatable fields must be translate error.mof.category.requiredCategory is required
Related
- Same area — Proposal list & publication workflow · Proposal editor (content, coupon, push, AI images) · Push notification list & publishing · Push notification editor (scheduling, geofencing, deep links) · App button manager (MOFs) · Multicentre proposals
- Shares data with — Centre editor (modules, app config, integrations)
Generated from doc/feature-inventory.json · id cms-categories · commit 276788ca6