You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(ui): config-overridable loader, plan-badge set, and copy strings (#4475)
* feat(ui): config-overridable loader, plan-badge set, and copy strings
Three hardcoded UI spots become config-overridable so a downstream project
can rebrand/reconfigure them without editing the stack view (avoids byte-
drift on every /update-stack). Every key defaults to today's exact behavior.
- ui.loader.component: new CoreAppSpinner component renders the built-in
v-progress-circular by default, or a project loader SFC resolved via an
import.meta.glob filename convention when configured. Wired into the 6
hardcoded loading-state usages (auth token view, docs article/home/
reference views, billing subscriptions + account view).
- billing.planBadge.plans / billing.planBadge.fallbackColor: the plan
badge's hardcoded free/starter/pro/enterprise ids + colors become the
devkit default only, fully replaceable via an ordered plans array.
- billing.meterPeriodWord, billing.freePlanBlurb, app.itemNoun: three
hardcoded copy strings now read `this.config?.X ?? currentDefault`.
Closes#4474
* fix(ui): reword loader comment to avoid leak-guard word match
Non-substantive wording fix — 'becomes' contained a flagged substring.
* ci(e2e): poll /api/health not /api/home (404 catch-all exposed the non-route)
* test(billing): add JSDoc header to mountSubscriptions helper
Three previously-hardcoded UI spots became config-overridable so a downstream project can rebrand/reconfigure them without editing the stack view (which caused byte-drift on every `/update-stack`). All three are no-ops for downstreams that don't opt in — every key is absent/null by default and every consumer falls back to the exact current behavior.
10
+
11
+
### What changed (this repo)
12
+
13
+
-**Loader** (new `config.ui.loader.component`, default `null`): new `CoreAppSpinner` component (`src/modules/core/components/core.appSpinner.component.vue`) renders the built-in `v-progress-circular` by default, or a project-provided loader SFC resolved via an `import.meta.glob` filename convention (`/src/modules/*/components/**/*.loader.component.vue`) when the config key is set — config values can only be strings, so the override is a Vite path, not a component reference. The 6 hardcoded `v-progress-circular` loading-state usages (auth token view, docs article/home/reference views, billing subscriptions + account view) now render `<AppSpinner>`, passing color/size/data-test through via Vue's automatic attribute fallthrough.
14
+
-**Plan badge** (new `config.billing.planBadge.plans` / `config.billing.planBadge.fallbackColor`, both absent by default): `billing.planBadge.component.vue`'s hardcoded allowed-ids + color map (`free/starter/pro/enterprise`) is now the devkit default only, overridable by an ordered `{ id, color }[]` array (a downstream override fully replaces the set — `generateConfig`'s deepMerge replaces arrays wholesale, it does not union them).
15
+
-**Copy** (new `config.billing.meterPeriodWord`, `config.billing.freePlanBlurb`, `config.app.itemNoun`, all absent by default): three hardcoded strings (the meter-period word in the upgrade-prompt copy, the free-plan upgrade blurb, and the "projects" noun in the workspace-setup helper text) now read `this.config?.X ?? currentDefault`. None of the three keys are seeded in any config fragment, so the generated `src/config/index.js` stays byte-identical.
16
+
17
+
### Action required for downstream projects (`/update-stack`)
18
+
19
+
1. All files are devkit-owned → arrive via `/update-stack`. No action required — every key defaults to today's exact behavior.
20
+
2. To customize: add any of `ui.loader.component`, `billing.planBadge.{plans,fallbackColor}`, `billing.meterPeriodWord`, `billing.freePlanBlurb`, `app.itemNoun` to your `src/config/defaults/<project>.config.js` (or a per-module `<module>.<project>.config.js` fragment). A custom loader SFC ships inside your own project module (e.g. `src/modules/<project>/components/<project>.loader.component.vue`) and is referenced by its literal absolute-from-root Vite path.
The docs module's in-theme OpenAPI reference (`/docs/api`) renders the merged spec at `GET /api/spec.json` natively — it is now the **only** reference surface. The dead "Open in Redoc" affordance (a header button + an empty-state fallback link, both pointing at the decommissioned backend `/api/docs` Redoc UI) has been removed.
component: null,// Vite path of a custom loader SFC matching /src/modules/*/components/**/*.loader.component.vue; null = built-in v-progress-circular (see CoreAppSpinner)
107
+
},
108
+
},
109
+
// Config-driven copy — intentionally absent here (each consumer falls back to its
110
+
// current hardcoded default via `config.X ?? default`), so the generated config stays
111
+
// byte-identical until a downstream opts in via `src/config/defaults/<project>.config.js`:
112
+
// app.itemNoun — noun for the items a workspace groups, default 'projects'
113
+
// billing.meterPeriodWord — billing period word in meter-mode upgrade copy, default 'monthly'
114
+
// billing.freePlanBlurb — full free-plan upgrade paragraph (see billing.subscriptions.component.vue)
115
+
// billing.planBadge.plans / billing.planBadge.fallbackColor — see billing.planBadge.component.vue
0 commit comments