Routing & State Management¶
Status: Audit v0.1 (2026-05-09).
1. Routing¶
1.1 Library¶
The app uses Nuxt 2's file-based routing (no third-party router; vue-router is wrapped by Nuxt). Routes are derived from files under pages/.
| Property | Value | Source |
|---|---|---|
| Library | Nuxt 2's wrapper around vue-router 3 | nuxt: 2.18.1 in package.json:67 |
| File-based routing | Yes | pages/ directory |
| Route extension | Possible via router.extendRoutes |
nuxt.config.js:228-240 |
| Active-link CSS class | node-active (non-default) |
nuxt.config.js:241 |
| Layout transitions | layout-leave/enter, mode out-in |
nuxt.config.js:257-260 |
| Page transitions | page-leave/enter, mode out-in |
nuxt.config.js:261-264 |
1.2 Route inventory¶
There are 84 page files (find pages -name '*.vue' -type f \| wc -l). Many of these are routes; some are sub-folders or _id-prefixed dynamic segments. The flat list of route paths (derived from find pages -name '*.vue' -type f \| sed 's\|^pages/\|\|; s\|/index\.vue$\|\|; s\|\.vue$\|\|' \| sort):
Public / unauthenticated routes (auth: false)¶
These pages opt out of @nuxtjs/auth. Confirmed via grep -rn "auth: false" pages \| wc -l = 14:
| Route | File | Purpose |
|---|---|---|
/ |
pages/index.vue |
Login / register landing page |
/redirect |
pages/redirect.vue |
Post-login intermediate redirect skeleton |
/callback |
pages/callback/index.vue |
OAuth callback handler |
/socialAuth |
pages/socialAuth/index.vue |
Social-auth completion handler |
/password_setup |
pages/password_setup/index.vue |
Password setup for invited users |
/privacy |
pages/privacy/index.vue |
Privacy policy |
/terms_and_conditions |
pages/terms_and_conditions.vue |
Terms |
/plan |
pages/plan/index.vue |
Plan landing |
/pay |
pages/pay.vue |
Legacy payment page |
/fbDeletionStatus |
pages/fbDeletionStatus/index.vue |
Facebook compliance: data-deletion request status |
/sample |
pages/sample/index.vue |
Sample/development page |
/forgot_password |
pages/forgot_password/index.vue |
Password reset request |
/reset |
pages/reset/index.vue |
Password reset confirmation |
/heartbeat |
pages/heartbeat/index.vue |
Health-check ping page |
Authenticated routes — account-scoped (pages/_accid/*)¶
These are the live, account-scoped routes. Cataloged via find pages/_accid -name 'index.vue' = 32 entries:
| Route shape | Purpose |
|---|---|
/<accid>/contentplanner |
Content planner calendar |
/<accid>/contentplanner/<id>/<id> |
Inline editor |
/<accid>/dashboard/... |
Dashboards (overview, ROI, reach, leaderboard, hashtag-analytics, leads, new-connections, action-items, roadmap, website-traffic, google-reviews) |
/<accid>/userSetting |
User settings (multi-tab) |
/<accid>/userprofile |
User profile |
/<accid>/Organisationprofile |
Organisation profile (onboarding step) |
/<accid>/billing |
Billing |
/<accid>/payment |
Payment / checkout |
/<accid>/brandkit |
Brand kit |
/<accid>/topics |
Topics |
/<accid>/subjects_services |
Subjects & services (onboarding step) |
/<accid>/goals_objectives |
Goals & objectives (onboarding step) |
/<accid>/chaptername |
Chapter (BNI onboarding) |
/<accid>/help_center |
Help center |
/<accid>/my_library |
Image / content library |
/<accid>/posteditor/<id> |
Post editor (Polotno) |
/<accid>/cuseditor/<id> |
Custom-content editor (Polotno) |
/<accid>/usereditor/<id> |
User-media editor (Polotno) |
/<accid>/usermediaditor/<id> |
User-media editor variant (Polotno) |
/<accid>/sharedposteditor/<id> |
Shared-post editor (Polotno) |
/<accid>/templateeditor |
Template editor (Polotno) |
Legacy / non-_accid authenticated routes¶
Several routes exist outside _accid and accept bare unprefixed URLs. The appendUrl middleware redirects bare hits to the _accid-prefixed version. Confirmed via middleware/appendUrl.js:6-36:
/contentplanner, /dashboard/*, /userSetting, /billing, /topics, /cuseditor, /usereditor, /posteditor, /subjects_services, /Organisationprofile, /brandkit, /payment, /chaptername, /goals_objectives, /usermediaditor, /sharedposteditor, /templateeditor, /my_library, /help_center.
These are legacy duplicates of the _accid-scoped routes. The middleware silently rewrites the URL, but the page files still exist and contain near-duplicate code (see architecture-overview.md § Finding WC-ARCH-2).
Other / unclassified¶
/designer— Polotno editor outside the_accidtree/editor/<id>and/editor/<id>/<id>— generic editor variants/users— possibly a dev/test page (pay.vue:18referencesusers.vuepatterns)/charts,/forms,/typography,/components,/components/tables,/components/notifications— template/sample pages from a Vue 2 admin-template starter, never deleted/pages,/pages/my-profile— also template artifacts/dayTimeSchedule,/roi,/social_accounts,/special_offer,/corporate_offer,/categories,/industry_news,/someli_network,/my_designs,/change_password,/carouseleditor*/(3 variants),/carouselcpeditor— assorted feature surfaces
Finding [WC-RT-1] (Severity: Medium): The pages/ tree contains template/sample artifacts from the original admin-dashboard starter (/charts, /forms, /typography, /components, /components/tables, /components/notifications, /pages, /pages/my-profile, /users.vue, /typography.vue, /forms.vue). They appear to be live routes — verify whether they're reachable from the production app's navigation. [VERIFY-RT-1]: navigate the deployed app and check whether any of these are accessible. If not, they should be deleted (Phase 0).
Finding [WC-RT-2] (Severity: Medium): Three carousel-editor variants exist (carouseleditor, carouseleditor1, carouselcpeditor). [VERIFY-RT-2]: which is canonical? The 1 variant especially looks like a leftover.
1.3 The _accid dynamic segment¶
Most authenticated routes are nested under pages/_accid/. This is a Nuxt dynamic segment whose matched value is route.params.accid. URLs look like /<accountId>/contentplanner. After login, the redirect middleware constructs these paths from app.$auth.user.accountId.
Detail: see ../04-routing.md (existing project documentation).
1.4 Route auto-prefixing (middleware/appendUrl.js)¶
Every navigation runs through appendUrl middleware which checks localStorage.accountId and rewrites unprefixed URLs from the hardcoded list above into /<accountId>/.... Important caveats:
- Reads
accountIdfromlocalStorage, not cookies. IflocalStorage.accountIdis empty (e.g., direct URL hit without prior login flow), the middleware silently no-ops. - The
localStorage.accountIdwrite is inmiddleware/head.js, which is not wired into the global middleware chain. So it's a real bug — see../04-routing.mdand../03-auth-and-sessions.md. - Editor routes get a positional shift —
/editor/<id>puts theaccountIdat index 1 instead of 0.
Finding [WC-RT-3] (Severity: High): The localStorage.accountId sync gap is a real, reproducible bug. Wiring head.js into the middleware chain or moving its localStorage.setItem('accountId', ...) line into redirect.js is the fix. Phase 0 effort.
1.5 Global router middleware¶
From nuxt.config.js:242:
| Slot | File | Effect | Source |
|---|---|---|---|
auth |
middleware/redirect.js |
Post-login decision tree (redirects to landing_url, payment, or /<accid>/contentplanner) |
nuxt.config.js:242, middleware/redirect.js |
axios |
middleware/axios.js |
Installs an axios interceptor adding token/accountId/apptype headers; saves currentUrl cookie |
middleware/axios.js:1-17 |
validUrl |
middleware/validUrl.js |
Saves localStorage.fallback-path if the route resolves cleanly |
middleware/validUrl.js:1-7 |
appendUTM |
middleware/appendUTM.js |
Decorates <a> and <form> targets with UTM params on /topics |
middleware/appendUTM.js |
appendUrl |
middleware/appendUrl.js |
Account-ID auto-prefix (see above) | middleware/appendUrl.js |
The auth slot does not point at the @nuxtjs/auth built-in middleware. It points at redirect.js. This is non-obvious and has surprised contributors.
There are also unwired middleware files: middleware/head.js, middleware/socialLogin.js. Per-page middleware: middleware/sociallink.js (used on 5 pages).
1.6 Route extensions¶
nuxt.config.js:228-240 adds two legacy redirects:
{ path: '/mission-control/overview', redirect: '/dashboard/overview' }
{ path: '/mission-control', redirect: '/dashboard' }
These accept old marketing-link URLs.
1.7 Code splitting¶
Nuxt 2 with webpack 4 automatically code-splits per page (each route gets its own JS chunk). The build produces 254 chunks (see architecture-overview.md §6 and performance.md).
There is no manual code splitting (import('./Heavy.vue') patterns) observed inside components. Lazy-loading inside a page is route-only.
The biggest single chunk is 4.99 MB raw / 1.37 MB gzipped (db8106d.js, contains react-dom + Polotno-related code). This is the chunk loaded on any editor page — it dominates the editor-route LCP.
1.8 Deep linking¶
Routes work for deep links because the SPA serves the same index.html for every URL (200.html in dist/, served by Nuxt at runtime). Specific cases:
- Authenticated deep links survive a refresh because the redirect middleware re-runs on every navigation. Cookies are the source of truth (see
../03-auth-and-sessions.md). - State held only in component data is lost on refresh. No router-state-persistence library is in use.
- Editor in-progress designs persist in
localStoragekeyed bypathname + hash— this is a Polotno feature, not the main app's router.
1.9 Error boundaries¶
Vue 2 has no <ErrorBoundary> component primitive. Vue 2.5+ supports errorCaptured lifecycle and Vue.config.errorHandler global hook. [VERIFY-RT-3]: search for errorCaptured and Vue.config.errorHandler in the codebase.
(Run during Phase E verification.)
If neither is present, the app falls back to Nuxt's layouts/error.vue for top-level errors only — component-level errors will be silent or crash the page.
2. State management¶
2.1 Library¶
| Property | Value | Source |
|---|---|---|
| Library | Vuex 3 | package.json:104 |
| Strict mode | Off | store/index.js:1 |
| Module structure | Folder-per-module + single-file modules | store/ |
2.2 Module inventory¶
store/
├── index.js Root module (non-strict)
├── api.js Central caching layer (this is the most important state file)
├── onboarding.js Onboarding flow state
├── chat/{actions,getters,mutations,state}.js
├── common/{actions,getters,mutations,state}.js
├── dashboard/{actions,getters,mutations,state}.js
├── leaderboard/{actions,getters,mutations,state}.js
├── plan/{actions,getters,mutations,state}.js
├── post/{actions,getters,mutations,state}.js
├── reach/{actions,getters,mutations,state}.js
└── modules/sitemap.js Loaded specifically for sitemap generation
Existing project doc ../05-state-management.md goes deeper.
2.3 Global state shape (root)¶
From store/index.js:2-27:
user, socialdata, sociallinkdata, profileTab, companyLogo, userAccounts,
selectedAccount, userDb, pageTitle, pageDescription, sidebarVisible,
userBlockVisible, myPosts, myStatusPosts, plans, postDataSet, postArray,
isAccountSwitching, defaultCategoriess, userSetting, defaultAccount,
shouldSaveUserSettingModel, isSaveSettingLoaded, autodc
Notable observations:
useris rarely populated. The codebase reads identity from cookies (usedetail,accountId) andapp.$auth.user, not fromstate.user. See./authentication-client.md.- Spelling typo:
defaultCategoriess(extra 's'). Confirms the field is referenced as-is across the codebase — renaming would break. isAccountSwitchingis a UX flag forAccountSwitcher.vueto suppress flicker.autodc— meaning unclear. [VERIFY-RT-4]: what doesautodcrepresent?SET_AUTODCmutation is instore/index.js:29-31.
2.4 The api module — central caching layer¶
store/api.js wraps ~12 backend endpoints in cache-with-TTL actions. Default TTL: 5 minutes (CACHE_DURATION = 5 * 60 * 1000). Caches:
- Single-value:
subscriptionCache,userDetailsCache,languagesCache,toneofVoiceCache,settingsCache,diyPostCache,allReelIdeasCache,accountDetailsCache,approvedDatesCache,libraryCache - Paginated (per-page):
postsCache,favPostsCache,sharedImagesCache,imageLibraryCache
Each action returns cached data on error (silent failure). Force-refresh via { force: true }. Full invalidation via clearApiCache mutation.
fetchSubscription writes a subs cookie as a side effect — middleware/redirect.js reads that cookie. Bypassing the action by calling the endpoint directly skips the cookie write.
The onboarding module has its own cache with 3-minute TTL (ONBOARDING_DETAILS_CACHE_TTL), inconsistent with api.js's 5 minutes. See ../05-state-management.md.
2.5 Persistence¶
Vuex state itself is not persisted across reloads (no vuex-persistedstate or similar in deps). However:
- Cookies persist identity (
token,usedetail,accountId, ~20 others). See./authentication-client.md. - localStorage persists
accountId,loggedInUserId,profiletab, UTM params,fallback-path, and the Polotno editor's design JSON keyed by URL.
This split — Vuex transient + cookies/localStorage permanent — has multiple consequences. The most important: the user field in Vuex state is empty after refresh until something explicitly populates it. Code reading store.state.user will get null more often than not.
2.6 State management findings¶
Finding [WC-ST-1] (Severity: Medium): Vuex strict: false is set at the root (store/index.js:1). This silently allows direct state mutation from outside actions/mutations, defeating the main reason to use Vuex. Recommendation: flip to strict in dev mode (process.env.NODE_ENV !== 'production'); audit and fix violators.
Finding [WC-ST-2] (Severity: Medium): Caching TTL is inconsistent (api.js 5 min, onboarding.js 3 min). No central place defines TTLs. Recommendation: extract to a constant per resource type.
Finding [WC-ST-3] (Severity: Medium): The api.js cache silently returns stale data on error. Errors are not surfaced to the user or to error tracking (no Sentry — see ./observability.md). Network failures are invisible.
Finding [WC-ST-4] (Severity: High): There is no automatic cache invalidation after mutations. The pattern relies on the developer to dispatch a { force: true } re-fetch or clearApiCache. In a 154,779-line codebase, this is fragile — easy to miss in PRs. Existing project doc ../05-state-management.md calls this out as a maintenance burden.
3. Open questions¶
Tracked in ./verify-markers.md:
- [VERIFY-RT-1] Are template/sample routes (
/charts,/forms,/typography, etc.) reachable from the production app? - [VERIFY-RT-2] Which carousel-editor variant is canonical?
- [VERIFY-RT-3] Are
errorCaptured/Vue.config.errorHandlerset anywhere? - [VERIFY-RT-4] What does
autodcrepresent in root state?