03 — Someli-Designer architecture¶
A guided tour.
Entry point — nuxt.config.js¶
Already covered in 02-stack.md. Re-read it after you finish this doc; it's short and dense.
Request lifecycle¶
Browser
│
▼
Nuxt router
│
▼
middleware/axios.js (the ONE global middleware)
└─ adds base64(`Apptype`) header
│
▼
Page (pages/<name>.vue)
│
▼
Component lifecycle (mounted) → this.$axios.get('/...')
│ (NO store/api cache — direct call to backend)
│
▼
axios interceptor (above) → request fires
│
▼
designer-api at process.env.baseURL
Compare to someli-platform: there is no /auth/ prefix check for token injection (designer-api has its own hand-rolled auth, the FE manages it differently), no router-level redirect middleware, no URL rewriting.
Pages — flat, with many close cousins¶
pages/ has 73 files, all at the top level (no subdirectories). Many pairs and triples differ by one character:
topics.vue/topicsNew.vuecontent_library.vue/master_library.vue/master_libraryNew.vueconditionedContent.vue/conditionedContentNew.vueautomationTemplate.vue/nonAutomationTemplate.vue/tempsets.vue/carousaltemplates.vuetemplateEditor.vue/templateEditor1.vue/carousaltemplateEditor.vuepostEditor.vue/postcreator.vue/postdesigner.vue/mediaEditor.vue/MastermediaEditor.vue/PreproductionPostEditor.vuePosts.vue/postsMismatching.vue/contentMismatching.vuePostColorCorrection.vue/TemplateColorCorrection.vuedynamicPost.vue/dynamicPost - Copy.vue(yes, with the space)News.vue/LatestNews.vuepopularSubjects.vue/subjects.vue/subjectsDemands.vue/subjectsBasedJobs.vue/generateSubject.vue/generatedSubjectContent.vue
Junior gotcha: these are not always meaningful pairs. Some are A/B variants; some are legacy. Some are vestigial (
Template.vuelikely is). Don't assume that "new" / "1" / "Copy" suffixes are the canonical version — they sometimes are, sometimes aren't.
The pages mostly do the same thing: fetch data via $axios, render a table or card list, allow CRUD operations.
See the page list in 02-stack.md and consult the team if you're unsure which page is the canonical one for a feature.
Components — only four¶
| File | Purpose |
|---|---|
components/Navbar.vue |
Top navigation; role_type-based nav gating |
components/Notification.vue |
Notification toast container |
components/skeletonCard.vue |
Loading skeleton for cards |
components/ToasterProgress.vue |
Toast progress indicator |
Vue components are auto-registered (
components: true) — but with only 4 files in the folder, you'll mostly write inline templates inpages/*.vuerather than extracting reusable components. If you find yourself extracting a component, decide explicitly whether to create it incomponents/or inline it. The repo's convention is "mostly inline".
Layouts¶
| Layout | Used for |
|---|---|
layouts/default.vue |
Authenticated UI (Navbar + content + notifications) |
layouts/blank.vue |
Login screen (bare, no Navbar) |
A page picks a layout via the layout option:
State — single Vuex module¶
store/index.js is the only Vuex file. State, getters, mutations, actions all here.
Common patterns you'll find:
- Auth/login state mirrored from
@nuxtjs/authcookies - A list of templates / topics / posts currently loaded for the active page
- UI flags (
isLoading,modalOpen) - No backend-response caching layer
Identity / auth¶
| Concern | Where |
|---|---|
| Strategy | @nuxtjs/auth v4 local strategy |
| Login endpoint | webauthenticate (on designer-api) |
| User-fetch endpoint | me |
| Logout | Disabled at strategy level; cleared client-side |
| Token storage | Cookies (cookie-universal-nuxt) |
| Apptype header | Added by middleware/axios.js |
See ../../audit/Someli-Designer/authentication-client.md.
Polotno editor — the lean fork¶
polotno-editor/ is its own React 18 / MobX-State-Tree / Parcel subproject. ~50 KB of code (the customer-app fork is ~221 KB).
| File | Purpose |
|---|---|
index.html / index.js / App.js |
React entry |
alltemplatesPanel.js |
All templates browser |
templatesPanel.js |
Per-category templates |
carousaltemplatesPanel.js |
Carousel templates |
brandkitPanel.js (capital P) |
Brand kit panel — the customer fork uses brandkitpanel.js (lowercase p) |
customUploads.js, mediaGrid.js, Pexels.js, PexelVideos.js |
Asset panels |
CustomPageControls.js, CustomWorkspace.js |
Editor chrome |
QrSection.js, ModalPortal.js, icons.js, helper.js |
UI helpers |
Diverged from someli-platform/polotno-editor/¶
- File-name casing differs (
brandkitPanel.jsvsbrandkitpanel.js) - The customer fork has additional panels (e.g.,
customTextSection/,common/,helper/subfolders) that don't exist here - Same-named files have drifted
Edits do not auto-port. See ../../audit/CODE-OVERLAP-MATRIX.md § 6.
Build process¶
# In polotno-editor/ — for interactive dev
yarn dev # Parcel dev server on http://localhost:1234
# In polotno-editor/ — for the actual bundle
yarn build # writes polotno-bundle.js into the parent repo root
# At the repo root — Nuxt
yarn dev # Nuxt dev server on http://localhost:3000
yarn build && yarn start # production build + start
External deps listed in polotno-editor/package.json are not bundled into polotno-bundle.js. They must also be installed in the parent Nuxt app's node_modules. The README explains.
Role-based UI gating¶
components/Navbar.vue reads role_type from auth state and conditionally renders nav items. The role IDs come from env vars (mirrored from admin_console_R/src/config/env.ts).
This is a known drift risk: if the backend changes a role number, the FE must be redeployed with the new env var value. There is no central registry.
Junior gotcha: if you add a nav item, decide which roles can see it and gate accordingly. The backend has matching access checks — failing to gate the FE just hides the button; the backend will still 403. Failing to gate the backend is the security issue.
Where to put a new file¶
| Adding | Put it in |
|---|---|
| A new page | pages/<name>.vue (flat; no subdirectories) |
| A new component | components/<Name>.vue — but think first: should it be inlined in the page? |
| A new helper | helpers/<name>.js |
| A new plugin (singleton) | plugins/<name>.js, register in nuxt.config.js |
| A new middleware | middleware/<name>.js, register in nuxt.config.js → router.middleware (currently only axios lives there) |
| A new Polotno panel | polotno-editor/<Panel>.js, register in polotno-editor/App.js, yarn build |
| A new global state field | Extend store/index.js (no sub-modules) |
Where to start when you have a feature to build¶
- Find or create the page in
pages/. - Identify the backend endpoint the page will call — look in
../../audit/designer-api/API-inventory.md. - Wire
this.$axios.get/post(...)in the page'smounted()or methods. - Add any state to
store/index.js. - Consider role gating — does this page need a nav-link in
Navbar.vueand arole_typeguard?