Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions MIGRATIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,77 @@ Breaking changes and upgrade notes for downstream projects.

---

## Admin tabs flattened to a single routed row (2026-04-14)

**Breaking change (URL-level).** The admin section now exposes its four
built-in sections (Users, Organizations, Readiness, Activity) as routed
siblings of the downstream `config.admin.tabs` extras. There is no more
nested "General" tab with an internal `v-window` — every tab is a real
URL and renders through the same `<router-view>`.

### What changed in the stack

- **REMOVED:** `src/modules/admin/views/admin.content.vue` — the inner
nested tab bar is gone. Its four window items are now dedicated views.
- **NEW:**
- `src/modules/admin/views/admin.users.view.vue`
- `src/modules/admin/views/admin.organizations.view.vue`
- `src/modules/admin/views/admin.readiness.view.vue`
- `src/modules/admin/views/admin.activity.view.vue`
- **CHANGED:** `src/modules/admin/router/admin.router.js` — the parent
`/admin` route now has dedicated children:
- `''` → redirect to `{ name: 'Admin Users' }`
- `users` → `Admin Users`
- `users/:id` → `Admin User`
- `organizations` → `Admin Organizations`
- `organizations/:organizationId` → `Admin Organization`
- `readiness` → `Admin Readiness`
- `activity` → `Admin Activity`
- **CHANGED:** `src/modules/admin/views/admin.layout.vue` — the tab bar
now renders built-in tabs + `config.admin.tabs` extras in one flat row.
The global error banner and the mailer warning were moved from the old
`admin.content.vue` into the layout so they stay visible across every
admin tab (including downstream extras).
- Readiness and Activity now fetch their data on `mounted()` (previously
via a `watch: { tab }` inside the old nested window).

### Action for downstream projects

**No config change is required.** The mechanism for contributing extra
admin tabs via `config.admin.tabs` + `injectAdminChildren` is unchanged,
and extras continue to render inline inside the admin layout.

1. Run `/update-stack` to pull the changes.
2. Verify hard-coded links in your project. The old URL `/admin` used to
land on the nested "General" tab; it now redirects to `/admin/users`
(client-side via vue-router), so existing links keep working. If you
want to point somewhere else, use one of the new stable URLs:
- `/admin/users`
- `/admin/organizations`
- `/admin/readiness`
- `/admin/activity`
Optional check: `grep -r "/admin" src/`.
3. Downstream projects that override `admin.content.vue` must delete the
override — the file no longer exists. Replace any such override with
a dedicated override of `admin.users.view.vue` /
`admin.organizations.view.vue` / `admin.readiness.view.vue` /
`admin.activity.view.vue`, or attach a custom tab via
`config.admin.tabs` + `injectAdminChildren`.
4. No route-name breakage for downstream extras — `injectAdminChildren`
still mounts your routes as children of the same `/admin` parent.

### Why

Two stacked tab bars on `*/admin` (top-level General + extras, nested
Users / Organizations / Readiness / Activity) was visually noisy and
duplicated navigation. The original intent of `config.admin.tabs` was
for downstream extras to live **alongside** the built-ins — not above
them. Flattening gives every admin section a real URL, preserves
deep-linking and browser back/forward, and keeps downstream extras
config-driven with zero migration work.

---

## Admin extra tabs are now nested routes (2026-04-12)

**Breaking change.** Downstream projects that added admin tabs via
Expand Down
2 changes: 1 addition & 1 deletion src/lib/helpers/tests/router.unit.tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const makeAdminRoutes = () => [
path: '/admin',
component: { name: 'AdminLayout' },
children: [
{ path: '', name: 'Admin', component: { name: 'AdminContent' } },
{ path: 'users', name: 'Admin Users', component: { name: 'AdminUsers' } },
{ path: 'users/:id', name: 'Admin User', component: { name: 'AdminUser' } },
],
},
Expand Down
54 changes: 44 additions & 10 deletions src/modules/admin/router/admin.router.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,28 @@
* Module dependencies.
*/
import adminLayout from '../views/admin.layout.vue';
import adminContent from '../views/admin.content.vue';
import adminUsers from '../views/admin.users.view.vue';
import adminOrganizations from '../views/admin.organizations.view.vue';
import adminReadiness from '../views/admin.readiness.view.vue';
import adminActivity from '../views/admin.activity.view.vue';
import adminUser from '../views/admin.user.view.vue';
import adminOrganization from '../views/admin.organization.view.vue';

/**
* Router configuration.
*
* The admin module exports a **parent route** (`/admin`) whose component
* is the admin layout (page header + tab bar + `<router-view>`). Built-in
* views (general content, user/organization detail) and any downstream
* injected child routes are rendered inside that `<router-view>`.
* is the admin layout (page header + tab bar + `<router-view>`). Each
* built-in section (Users, Organizations, Readiness, Activity) is a
* routed child so that the tab bar and the downstream-contributed extras
* (`config.admin.tabs`) live in one flat navigation row.
*
* Downstream modules should **not** add sibling routes like
* `/admin/knowledge` anymore. Instead, register them via the
* `injectAdminChildren` helper in `@/lib/helpers/router` so they become
* children of this parent route.
* Detail views (`users/:id`, `organizations/:organizationId`) are also
* children of the same parent — they deep-link inside the layout.
*
* Downstream modules that contribute an "admin tab" must register their
* routes via the `injectAdminChildren` helper in `@/lib/helpers/router`
* with **relative** paths (e.g. `'knowledge'`, not `'/admin/knowledge'`).
*/
export default [
{
Expand All @@ -33,8 +39,12 @@ export default [
children: [
{
path: '',
name: 'Admin General',
component: adminContent,
redirect: { name: 'Admin Users' },
},
{
path: 'users',
name: 'Admin Users',
component: adminUsers,
meta: {
action: 'manage', subject: 'UserAdmin',
},
Expand All @@ -48,6 +58,14 @@ export default [
action: 'manage', subject: 'UserAdmin',
},
},
{
path: 'organizations',
name: 'Admin Organizations',
component: adminOrganizations,
meta: {
action: 'manage', subject: 'UserAdmin',
},
},
{
path: 'organizations/:organizationId',
name: 'Admin Organization',
Expand All @@ -57,6 +75,22 @@ export default [
action: 'manage', subject: 'UserAdmin',
},
},
{
path: 'readiness',
name: 'Admin Readiness',
component: adminReadiness,
meta: {
action: 'manage', subject: 'UserAdmin',
},
},
{
path: 'activity',
name: 'Admin Activity',
component: adminActivity,
meta: {
action: 'manage', subject: 'UserAdmin',
},
},
],
},
];
Expand Down
Loading
Loading