Frontends
Admin panel data flow
admin/src/ follows a strict four-layer path. Nothing skips a layer.
View
└─ store src/stores/<area>.ts
└─ API composable src/composables/apis/use<Area>Api.ts
└─ useApi- Views declare what to show and call store actions. They never call
useApidirectly and never hold acatchblock. - Stores run every API call through
runActionand exposeerrorandfieldErrorsfor the view to read. - API composables wrap each endpoint in one typed function and call
useApi. useApisends every request with the CSRF header andX-Language.
Client portal data flow
client/ adds one layer: the portal's own server routes sit between the API composable and the backend API.
Page
└─ store
└─ API composable
└─ useApi
└─ server route server/api/portal/**
└─ APIThe layer order is enforced by the lint rules and client/scripts/check-layers.mjs. Templates for server-rendered pages live under client/templates/<template>/.
The component kit
Both apps share the same component conventions:
- No raw HTML controls in views. Always use a component from the kit.
- Every input has a placeholder.
- Every interactive element has a
data-testidattribute in the form<area>-<element>. - JSDoc on every exported function and component.
Translations
Each area has its own locale files. Run lint:i18n before pushing any translation change.
Run
lint:i18n before pushing translations. It catches missing keys before CI does. See Translating Hostpanel for the full translation workflow.