Docs

Dashboard

Dashboard information architecture, navigation, role-aware homes, and shared shell behavior.

Overview

The dashboard is the authenticated product shell. It is not a single generic home page; it is a set of role-aware operational surfaces rooted under:

  • /dashboard

  • /dashboard/admin

  • /dashboard/content

  • /dashboard/organization

  • /dashboard/settings

  • /dashboard/test

All dashboard routes are locale-prefixed at runtime, for example /en/dashboard/content/pages.

Route Groups

Dashboard routes are organized by user intent:

SectionRoute rootPurpose
Home/dashboardPermission-aware directory of every authorized dashboard destination.
Admin/dashboard/adminPlatform health and superadmin controls.
Content/dashboard/contentCMS, media, pages, sections, components, data, design tokens, and transactional emails.
Organization/dashboard/organizationOrganization billing, plans, code access, members, and organization settings.
Settings/dashboard/settingsPersonal account, security, preferences, organizations, and developer API keys.
Test/dashboard/testInternal authorization and route diagnostics.

Sidebar parent items are navigable section dashboards. A parent route must never point at a hidden or missing page. This keeps keyboard command navigation, breadcrumbs, and direct links predictable.

Shared Shell

The dashboard shell provides:

  • authenticated layout and active-organization checks

  • sidebar navigation

  • app bar actions

  • breadcrumbs

  • command menu

  • active organization switcher

  • user menu

  • focus overlay for guarded dashboard content

Server-side dashboard reads use an effective active organization resolved from the Better Auth session and the user's organization memberships. When the stored Better Auth active organization is missing or no longer belongs to the user, the server falls back to the user's default organization and then to the first membership. Dashboard view models, navigation filtering, and server actions must rely on this server session context rather than waiting for client-side organization hydration.

Key files:

  • src/app/[locale]/dashboard/layout.tsx

  • src/blocks/dashboard/navigation/*

  • src/blocks/dashboard/auth-checker.server.tsx

  • src/blocks/dashboard/auth-checker.client.tsx

  • src/blocks/dashboard/section-navigation.tsx

Dashboard routes should use PageDashboard or the local dashboard block patterns rather than creating unrelated page chrome.

Section Dashboards

Section dashboards are compact route-root pages that summarize useful health signals for the current section. They prevent sidebar parents from behaving like non-clickable labels without repeating the page title already owned by the dashboard app bar.

The dashboard shell renders a desktop shared section navigation bar in a sticky subheader directly under the dashboard app bar, with breadcrumbs above it when the breadcrumb flag is active. It is derived from the filtered sidebar tree and only lists child routes for the active section, so it stays compact, permission-aware, and focused on local subsection navigation instead of duplicating the top-level dashboard sections. The breadcrumb trail and section navigation bar are controlled independently by the dashboard-breadcrumbs-enabled and dashboard-section-navigation-enabled managed feature flags.

Current section dashboards:

  • /dashboard/admin

  • /dashboard/content

  • /dashboard/organization

  • /dashboard/settings

  • /dashboard/test

/dashboard/admin, /dashboard/content, /dashboard/organization and /dashboard/settings are screens (see Screens); /dashboard/test is a page written in code (see Test Dashboard).

Each section dashboard should:

  • rely on the shell-level section navigation for visible child links

  • respect authz visibility

  • show operational cards, charts, and action prompts that are specific to that section

  • avoid duplicating hidden admin, billing, or top-level navigation actions through standalone shortcut cards

Screens

The home (/dashboard), the content dashboard (/dashboard/content), the media library (/dashboard/content/media) and its trash, the pages list (/dashboard/content/pages), the email templates list (/dashboard/content/email-templates), the CMS data and data models lists (/dashboard/content/data, /dashboard/content/data-models), the organization overview (/dashboard/organization), the quotes and signatures page (/dashboard/organization/signatures), the admin overview (/dashboard/admin), the authorization page (/dashboard/admin/authorization), the registry approvals (/dashboard/admin/registry-templates), the billing catalog (/dashboard/admin/billing-products), the dynamic data model catalog (/dashboard/admin/dynamic-data), the settings overview (/dashboard/settings), the screen management pages (/dashboard/organization/screens, /dashboard/admin/screens) and each dynamic data model's records page (/dashboard/<section>/dynamic-data/<scope>/<model>, see Model screens) are screens: YaYaw Table dashboard documents made of sections, widgets and filters. A screen's default lives in the code and is versioned. An organization's own copy is stored in the database only once it customizes the screen, and the default can always be restored. The test dashboard stays a page written in code (see Test Dashboard), as do the documentation catalog, the sections and components catalogs, and the users, members and API keys pages. An organization can also create screens of its own (see Custom screens).

Widgets read their rows and numbers from sources the document names by id, such as system:pages or a deployed dynamic data model. A source checks can() on every read. People who neither edit nor publish a screen get it without the widgets whose source they cannot list.

Customization is off until the screen-customization feature flag is on; until then every screen shows its default. With the flag on, organization admins (the Organization Admin and Super Admin roles, through the screen:manage permission) manage the screens of their organization; everyone else sees the published screen. Organization admins can also give other people View, Edit or Publish on one screen, or on every screen of a menu section (see Access to a screen).

A global screen belongs to no organization: it has one copy for the whole platform (the admin overview, the email templates list, the authorization page, the registry approvals and the billing catalog are). Only Super Admins manage it, through screen:manage held globally: an Organization Admin's permission holds in their own organization only. Its status bar speaks of the platform's copy. A Super Admin's assistant reaches it with scope: "global": it lists the global screens, reads them and saves their drafts, and a Super Admin publishes them.

A screen's editors and publishers see a status bar above the screen. Its editors review what is prepared; its publishers (and organization admins) also publish, discard, reset and keep:

  • Customized, with Reset to default, when the organization published its own copy;

  • History, when the screen keeps published versions, even back on its default (see History);

  • A draft of this screen is waiting: prepared in the dashboard or by an assistant, by whom, when and why (and which version it restores), with Review (the draft shows in place of the screen and nothing is saved), Publish and Discard. A draft with problems lists them and cannot be published;

  • a warning when the draft was built on an older default;

  • The default of this screen was updated, when the code default changed since the organization's copy, with View default, Reset to default and Keep mine.

Publishing, discarding and resetting ask for a confirmation first.

A screen's editors also edit it in place: Edit opens YaYaw Table's screen editor on the screen as everyone sees it, or on the draft they are reviewing (a pending draft is edited while it is reviewed, so it is never replaced unseen). They add and move sections, add widgets from the sources they may read and the blocks the screen may place, edit a widget's view in the live table, and change the filters. Save draft saves a draft from the dashboard, checked like any other: the problems the editor finds itself stop it at once, and those only the server knows (a block the screen keeps, its full-page table, a source the member cannot read, the number of widgets) are listed above the screen by widget. The draft then shows for review, and Publish in the status bar publishes it.

MCP prepares, humans publish: an assistant lists screens, reads them and saves drafts (yayaw_screens_list, yayaw_screen_get and yayaw_screen_save_draft), reads their published versions and restores one as a draft (yayaw_screen_revisions and yayaw_screen_revision_restore), all described in Control Plane. Publishing, discarding, resetting and keeping a copy only happen in the status bar or on the screen management pages, and a draft is checked again for the member who publishes it.

The screens live in src/lib/server/services/screens/ and src/blocks/dashboard/screens/; docs/dashboard/screens.md is the developer guide.

Screen management

Screens lists every screen with its state, so drafts do not wait unseen:

  • /dashboard/organization/screens, in the Organization section, lists the organization's screens to the members who hold screen:read there (Organization Admins and Super Admins, whose screen:manage allows it);

  • /dashboard/admin/screens, in the Admin section, lists the platform's global screens. Its route is gated like the admin overview (feature-flag:manage), and its rows need screen:read held globally: an Organization Admin's permission holds in their own organization only, so they never open it.

Each page shows how many screens are customized, how many drafts are pending and how many come from assistants, how many copies derive from an older default, the screens by status, then the screens' table. The page keeps its table: a customization can move it but never remove it. Each row gives the screen, its section and status (Default, Customized or Default updated), its pending draft (from the dashboard or from an assistant through MCP, with its reason, author and date, and whether it was built on an older default), when and by whom its copy was published, and the versions of the default. An author shows while they are a member of the organization (for a global screen, while their account exists). The page never reads the screens' documents.

A row's menu has Open, and Review draft and View default, which open the screen on its pending draft or on its new default, where its own status bar offers the rest, then History when the screen keeps published versions (see History). Members who manage screens also have Publish draft, Discard draft, Reset to default (for a copy) and Keep mine (when the default changed). Each change asks for a confirmation, then goes through the same checks as the status bar, at the revision the row showed: when the screen changed since, the page says so and loads the rows again. Nothing changes while an administrator impersonates the member, and with screen-customization off the page says customization is off instead of listing the screens.

History

Each publication of a screen is kept as a numbered version (1, 2, …): the newest 25 of each screen, deleted with the screen. Each copy published before the history existed became version 1. A version records the published screen, when and by whom it was published, the draft it published (prepared in the dashboard or by an assistant, by whom and why) and the version that draft restored. Versions are provenance: they grant nothing.

History opens from the status bar and from a row of the screen management pages. It lists the versions, the newest first, and marks the one the screen shows now. For each version:

  • Preview shows it in place of the screen (the page's address gets ?screen-preview=version:<n>, so a link keeps it), with Back to the screen, Compare and Restore as draft in the status bar. Its blocks show the data the page loaded for the screen;

  • Compare lists what the version changes from what the screen shows now (its published copy, else its default) or from another version: widgets, sections and filters added, removed or changed, each with its JSON path and the paths of the values that differ;

  • Restore as draft, once confirmed, makes the version the pending draft, replacing the one waiting if any. It is checked for the member like any draft (a version naming a source they cannot read is refused) and never published: the draft shows for review, says which version it restores, and Publish publishes it. The version the screen shows now has nothing to restore.

Everyone who may read the screen (screen:read on it: the readers of the screen management pages, and whoever was given access to it) lists and compares its versions, whole for its editors and publishers, without the widgets on sources they cannot read for the others. Previewing a version is for its editors and publishers; restoring one saves a draft, so it is for its editors: it asks screen:update afresh, and never happens while an administrator impersonates the member. An assistant reads the versions and restores one as a draft with yayaw_screen_revisions and yayaw_screen_revision_restore; someone who may publish the screen still publishes it.

Access to a screen

On the organization's page, a row's menu also has Manage access, for the members who manage screens. It opens the screen's access panel, which lists who has access and at which level, on this screen or on every screen of its section, removes an access, and gives access to:

  • a member of the organization, on this screen;

  • a team of the organization, on this screen or on every screen of its section;

  • everyone in the organization, on this screen or on every screen of its section.

LevelWhat it allows
ViewSee the screen.
EditAlso review its pending draft, save drafts in the editor and restore a version as a draft.
PublishAlso publish or discard the draft, and reset the screen to its default.

Each level includes the one before it, and giving another level replaces it. On a system screen, access says what someone may do with the screen, never whether they open its page: its route still decides that. Only organization admins manage access. Access is group membership, never a setting on a person: a member joins the screen's own group, a team or the organization's member group is bound to the screen or to its section (see Authorization). A person who leaves the organization loses these accesses. Nothing changes while an administrator impersonates the member, and an assistant never changes who has access. A screen grants no data: every widget still reads its source only when that source allows it.

Custom screens

With screen-customization on, organization admins create screens of their own (the screen:create permission, which their screen:manage includes). A custom screen is a dashboard like the others, made of sections, widgets and filters, with:

  • its own address, /dashboard/screens/<slug>: the slug can change (old links then stop working), the screen itself never does;

  • a title in English and in French;

  • a menu entry: in the Content or Organization section after their own pages, or in a Screens group, with an icon and an order.

A new screen starts as a draft that only its editors see, and publishing it shows it to the people who may view it. Unless its creator chooses nobody, everyone in the organization gets View on it; more access is given as on any screen, where its section is its menu placement (see Access to a screen). A screen an assistant prepares through MCP gets nobody until a person publishes it and gives access.

A custom screen has no default: it is never reset, and never marked customized. Its editors place widgets on the sources they may read, the blocks meant for custom screens (To do, the media storage and the organization's billing) and at most one full-page table, on any source they may read. Its history works as any screen's (see History).

Archiving a screen hides it from everyone but the organization admins and freezes it until it is restored, as it was. Only an archived screen can be deleted, with its history and every access given to it. Someone who may not view a screen gets the same "not found" as for a screen that does not exist. An organization keeps at most 50 custom screens, archived ones included, and at most 10 that assistants created and nobody published yet. The screen management page lists its custom screens after the others, with their state: Unpublished, Published or Archived.

In the menu, a published custom screen shows to the people who may view it: after the pages of its section (Content or Organization), or in the Screens group, right after the Organization section, whose own link opens its first screen. Entries go by their order, then by their title in the reader's language. A draft, an archived screen and another organization's screens never show there. The home's directory and the section's overview list them too, and the breadcrumbs and the page's title name the screen as its entry does.

On the organization's screen management page, New screen (for organization admins) asks for the screen's titles in English and in French, its address (taken from the English title until you change it), its place in the menu, its icon, its order, and whether everyone in the organization can view it once published (checked by default). The screen is created as a draft, which opens so you can build it with Edit, then publish it. A custom screen's row also offers:

  • Menu entry: its address, titles, place, icon and order. A new address breaks the links to the old one, and another place in the menu changes who has access through that section;

  • Archive and Restore, each confirmed;

  • Delete, for an archived screen only, once its address is typed: the screen, its versions and every access given to it are deleted for good.

Publishing a screen an assistant prepared, for the first time, offers everyone in the organization View, checked by default, on its row as in its status bar.

Model screens

Each dynamic data model's records page is a screen of the organization, global models included: every organization keeps its own copy of it. Its default is a single full-width table of the model's records, which keeps the model's own table settings, saved views and favorites. Around the table the page still shows what it did: the record opened from a row (its summary, its related records and its event timelines), and, for whoever may manage the model, creating, editing, deleting, bulk editing and importing records, the record form, File tree moves, the Gantt's plans, connect destinations, form links and the address search. Every write is checked again on the server.

  • Who sees it. Whoever may list the model (dynamic-data:list on the model, or over its organization, or globally for a global model); for anyone else the page does not exist. A section's page (/dashboard/content/dynamic-data, /dashboard/organization/dynamic-data, /dashboard/<section>/dynamic-data) shows its first model's screen. The admin model page (/dashboard/admin/dynamic-data/<scope>/<model>) shows the model's definition (contract, revisions, deployment plan) above its screen, to whoever manages the model.

  • Customizing it. With screen-customization on, its editors change it like any screen: add numbers, charts or other tables, give the table a view of its own, then publish. The screen's section, for access, is the model's menu section (Admin by default, Content, Organization or the model's own): access given on that section, or on the screen itself, covers it (see Access to a screen). The screen management page lists a model's screen once it has been drafted or published, to whoever lists its model over its scope.

  • Links. A link filtering a model's records now names the screen's table (dynamic-data:<scope>:<slug>-filters); a link saved with the records table's former keys (dynamic-data-records-table:<model id>-…) is translated once when it opens.

  • Assistants prepare drafts of these screens through MCP; a person publishes them.

Role-Aware Home

The main /dashboard route is the permission-aware navigation home, rendered from the home screen (dashboard.root). Its default is made of host blocks: the workspace summary, connecting an assistant, To do, favorites and recent destinations side by side, the directory of every destination (a customized home must keep it) and the applications. The navigation blocks are driven by a server view-model that reads the filtered dashboard sidebar tree and builds an exhaustive directory of the static destinations the current actor can access. A section can remain in the directory because it contains an authorized child route, but its overview/root link is rendered only when that root was authorized independently.

Because the home page derives from navigationConfig.nav.sidebar.groups after filterNavigationByPermissions(...), future dashboard pages can appear on the home page by being added to the normal sidebar model and passing the same authz filters. Do not maintain a second home-page shortcut registry or truncate the authorized static directory.

The directory has a local, accent-insensitive search. Favorites and recent destinations are client-side accelerators stored in a versioned localStorage payload scoped to the current user and active organization. Stored hrefs are always intersected with the latest server-filtered destination set before they are displayed or written back, so stale preferences cannot re-expose a route after an authorization change. They are navigation preferences only and never replace server-side route guards.

Deployed dynamic-data models can grow independently of the static navigation tree. They therefore live in a separate, searchable applications/data browser instead of expanding every section inline. Its entries still come from the same permission-filtered navigation model and remain subject to server-side dynamic-data authorization.

All authenticated users:

  • receive every authorized static destination grouped by section

  • receive a section overview link only when the section root is independently authorized

  • can search the directory and maintain permission-filtered favorites and recent destinations on their current browser

  • browse authorized dynamic applications and data in a separate searchable surface

  • see active organization context when one is available

  • do not receive duplicated organization health, billing, content, revenue, or provider analytics on the home page

Key services:

  • src/lib/server/services/dashboard/dashboard-home.ts (the home's navigation)

  • src/lib/server/services/screens/ (screens, their sources and blocks)

Screens read their sources in batches through server actions, and aggregates of the system sources are cached for 5 minutes per source, organization, access and request. Sources that read a provider (PostHog, Umami, Stripe) never break a screen: when the provider is not configured, fails or takes too long, their widgets say so instead of showing zeros, and the failure stays in the server logs.

Admin Dashboard

/dashboard/admin is the superadmin platform health surface. Its overview is the admin screen (dashboard.admin.root, see Screens), a global screen: the same for the whole platform, whatever organization is active. Its default shows:

  • a Period filter, the last 30 days by default, on what happens over time: revenue, the audience, new users and new organizations; the totals stay whole;

  • nine numbers: net revenue over the period, compared with the previous period, with a weekly trend; gross revenue and Stripe fees; active or recoverable subscriptions, past-due subscriptions and organizations; users, new users and events;

  • To do beside the numbers;

  • revenue by day (net revenue and fees) and the audience by day (events and daily users);

  • new users and new organizations by day;

  • subscriptions by status and the deployment (hosting, environment, version and URL of the running release);

  • the admin pages the viewer may open.

To do lists failed one-time billing webhooks, billing products out of sync with Stripe, Stripe without its secret key and an analytics provider selected without its API configuration. Each item is counted behind its own can() check, and items with nothing to report are left out.

Every figure comes from a global source that checks can() without any organization: users and the audience need user:manage, organizations organization:manage, subscriptions and revenue billing:manage, and the deployment system-settings:manage. An Organization Admin's permissions hold in their own organization only, so they never open these figures; a Super Admin's are global. The page itself keeps its gate (feature-flag:manage), and a viewer sees only the widgets whose source they may list.

Revenue is read from the platform's Stripe balance: payments and refunds per day of the viewer's time zone, over at most 90 days (the revenue widgets refuse a longer period), and at most 500 Stripe transactions per read (beyond, the numbers cover part of the period). The audience is read from PostHog or Umami. Users are counted day by day: someone active on several days counts on each, so the audience chart shows daily users, since a period's users cannot be added up from days. Refresh all reloads the screen, and a provider that is not configured or fails says so in its own widgets.

/dashboard/admin/users is the superadmin user-management surface. It is gated by Yayaw user:manage, not by Better Auth user.role. It lists Better Auth users, ban and 2FA status, default organizations, active sessions, and organization membership counts. The detail drawer lets superadmins add a user to another organization, change a Better Auth organization membership role, remove a membership, set the default organization, inspect the matching Yayaw groups, and start or stop RBAC-checked impersonation.

Treat /dashboard/admin as the canonical platform status URL in docs and navigation.

Content Dashboard

/dashboard/content is the content screen (dashboard.content.root, see Screens). Its default shows:

  • a Period filter on page views and top pages, the last 30 days by default;

  • four numbers: published pages; pages waiting for publication (never published, or published with unpublished changes; archived pages excluded); page views over the period, compared with the previous period, with a weekly trend; media storage, the trash excluded;

  • page views by day and the most viewed pages;

  • the pages edited last and To do;

  • media by kind and pages created by month.

To do lists pages never published, published pages with unpublished changes, unpublished sections, AI runs that failed in the last 7 days, media without a thumbnail, media storage at 80% of the plan or more, and an analytics provider selected without its configuration. Each item is counted behind its own can() check, and items with nothing to report are left out.

Every number comes from a screen source (system:pages, system:page-views, system:top-pages, system:media and system:attention) that checks can() on every read. The page numbers and to-do items count the pages the pages list shows the person: global pages with a global page read, list or manage policy, the organization's with page access there. Organization page views need page access in the active organization; global page views need global page:manage, because global page:read can represent public runtime access. Page views are the cms_page_viewed events of the CMS page runtime, read from PostHog or Umami. When analytics is off or not configured, or its provider fails, the page view widgets say so instead of showing zeros.

Content-specific docs:

Organization Dashboard

/dashboard/organization is the section root for active-organization operations: billing summary, plan selection, purchased code access, members and organization settings. Its overview is the organization screen (dashboard.organization.root, see Screens). Its default shows:

  • a Period filter, the last 30 days by default, on the new members and the activity by day only: the member total and the lists stay whole;

  • three numbers: members; new members over the period, compared with the previous period, with a weekly trend; pending invitations (an invitation past its expiry is no longer pending);

  • the activity by day (pages, sections, media and members added), the billing summary (plan, status, seats and the end of a grace period) and To do;

  • the recent activity and members by month;

  • the organization's pages the viewer may open.

To do lists billing restricted or canceled, a failed payment's grace period with the days left and every seat of the plan taken, for members who may read the organization's billing, and pending invitations that expire within a day, for members who manage invitations.

Every figure comes from a screen source or a block that checks can(): members need member:list, invitations invitation:manage, and each kind of activity its own page, section, media or member permission (pages and sections outside the archive, media outside the trash). The billing summary and its To do items need billing:read: a member's organization role (owner, admin, manager) never shows billing by itself, and the summary tells anyone else it is not available to them. Content counts are on the content dashboard.

Billing surfaces intentionally stay split:

  • /dashboard/organization/billing for current state and activity

  • /dashboard/organization/plans for checkout actions

  • /dashboard/organization/code-access for purchased source-code deliverables

This split keeps plan comparison, billing history, and post-purchase access flows from crowding into one page.

Quotes and signatures

/dashboard/organization/signatures is the quotes and signatures screen (dashboard.organization.signatures, see Screens). It opens to the organization's members who may read its quotes (signature:read, checked afresh on every read, and membership of the organization), never while an administrator impersonates someone. Its default shows:

  • four numbers: quotes ready to send, awaiting their signature, signed, and those that need attention (still being prepared, an invitation that was not delivered, or past their expiry), counted over every quote;

  • the Stripe connection: whether the organization's Stripe account is connected and whether sending for signature is configured on this instance, with Connect Stripe for members who manage quotes. The screen keeps it: it is where the account is connected;

  • the quotes, the newest first, 20 a page, with the list's work queues, a kanban by status and a calendar by expiry. New quote prepares a quote; a row opens its PDF, downloads and evidence, and the actions to send it, cancel it, resume its preparation or retry its invoice.

Amounts stay in each quote's currency and are never added up. Preparing, sending, cancelling and reconciling go through the signature server actions, which check the organization, membership and signature:manage again. The screen lists every quote of the organization.

Settings Dashboard

/dashboard/settings groups personal and developer settings:

  • account profile

  • security and MFA

  • preferences

  • joined organizations

  • developer API keys

Its overview is the settings screen (dashboard.settings.root, see Screens). Every signed-in person opens it, with or without an organization, and its blocks always show their own account. Its default shows:

  • Account security, which the screen always keeps: two-factor authentication and email verification, then how many passkeys, active sessions and personal API keys the account has, each line linking to the page that changes it. A session is active until it expires; an administrator's impersonation is not counted, as the Security page does not list it. An API key counts while it is enabled and not expired.

  • Your account: name, email, since when the account exists, theme and language, with links to the account and preferences pages.

  • The settings pages.

These figures are computed on the server for the signed-in person only, and the page receives booleans and counts: never a token, a key, an identifier or a session's details. Screen sources never read sessions, API keys, passkeys, linked accounts or two-factor secrets, so no widget, assistant draft or screen read can reach them.

The Developer settings page uses Yayaw's custom API key block, not the generic Better Auth API key UI, because control-plane permissions are server-managed and must be written through Yayaw's permission repair/issue flow.

Test Dashboard

/dashboard/test is an internal diagnostics overview, gated like its pages by feature-flag:manage. It lists, one line each:

  • the dashboard routes, and the public, protected and permissioned routes of the navigation config;

  • the managed feature flags present in the database out of those declared in code, the stored flags that are on, and the managed flags exposed to the browser;

  • the authorization resources, actions and resource/action pairs.

Then it links the test pages the viewer may open (Routes and Authorizations), taken from the same permission-filtered navigation as the section pages of the screens.

It is not a screen: it counts what the build declares, and the stored flags, for a few holders of feature-flag:manage, and nobody would customize it. It is a small server component (src/blocks/dashboard/test/overview/).

Navigation should use the shared route/navigation config and @/i18n/navigation helpers. Avoid raw next/link and raw path concatenation in dashboard navigation components unless there is a specific route-level reason.

Rules:

  • sidebar labels and command menu entries must resolve from the same route model

  • breadcrumbs should reflect real navigable parents

  • hidden routes should not appear in the sidebar or command menu

  • parent items should have a real page, not only expandable children

  • route visibility should match authz/resource gates

Authorization

Dashboard route access uses the Yayaw authorization service on top of Better Auth session and organization membership.

Important contracts:

  • route checks are server-side

  • generated DB actions still run can(...)

  • navigation visibility does not replace server authorization

  • platform user management and impersonation use user:manage

  • Yayaw never treats Better Auth user.role as application authorization

  • test routes under /dashboard/test are diagnostics only

The authorization admin page lives at:

  • /dashboard/admin/authorization

It supports group/role/policy inspection, quick evaluation, detail views, and audit-oriented diagnostics for the RBAC model. It is a global screen (see Authorization): the groups' numbers, the Groups directory and the quick evaluator, for holders of a global group:list; each group's page (groups/[groupId]) stays written in code.

Development Checklist

When adding a dashboard page:

  1. Add the route under the right section root.

  2. Add or update route metadata/navigation config.

  3. Ensure the parent section dashboard links to it when visible.

  4. Add server-side route access checks.

  5. Gate user-facing navigation with the same authz/resource intent.

  6. Add translations under src/messages/default/dashboard.json and src/messages/fr/dashboard.json.

  7. Update the relevant English docs.

  8. Add focused tests when the route changes navigation, authz, or shared dashboard view-model behavior.

Validation

Useful checks after dashboard navigation changes:

bun run check
bunx tsc --noEmit
bun test src/lib/server/authz/contracts.test.ts
bun run build

Table workspace behavior

Dashboard tables keep their saved views, favorites and table settings for the current user and organization. The list pages and each model's records are screens (below); the documentation catalog and the dynamic data model catalog use the same workspace wrapper. Pages and documentation keep their dedicated editors and offer selected-page SEO or documentation navigation changes as explicit bulk forms. Authorization groups allow selection for export, without group mutation batches. Dynamic records follow the model's permissions and opt-in inline editors.

The pages list is a screen too (see Pages Catalog): its table keeps the list's columns, work queues, saved views and favorites, opens each page in its editor, and offers the create dialog, publication after a review, archiving, deletion and the search engine settings of a selection to whoever may manage pages.

The email templates list is a global screen (see Transactional Email Templates): its table keeps the list's columns, work queues, saved views and favorites, opens each template in its editor, and offers the create form and the activation, deactivation and deletion of a selection to whoever may manage templates, without the Table bulk editor.

The CMS data and data models lists are screens of the organization (see CMS Data Models): their tables keep the lists' columns, work queues, saved views and favorites, list global models and the active organization's as the lists did, and offer whoever may change a model its form (on a click, which loads the model's or the entry's latest document first), the create forms, and the publication and archiving of a selection, without inline or bulk edits.

The authorization groups directory is a global screen too (see Authorization): its table keeps the directory's columns, queues, saved views and favorites, opens each group's page, and offers the create form and the export of a selection, without group mutation batches.

The dynamic data model catalog is a screen of the organization (see Control Plane): an operator readiness panel (what to fix first among the admin section's models, their counts, runtime routes and surfaces, and the target database's readiness) above the catalog's table, which keeps the columns, saved views and favorites the catalog kept and lists global models and the active organization's, sorted, searched and paged in SQL rather than loaded whole. Whoever may create models gets the model builder (a new model, or a new draft of one, in a wide form), and each model whoever may manage it gets its row's actions: publishing a ready draft, reviewing and opening a deployment, and archiving the model once confirmed. A row's click, Open and Review deployment all open the model's own page (/dashboard/admin/dynamic-data/<scope>/<model>): its definition (contract, deployment plan, revision history) above its own model screen (see Model screens).

The registry approvals are a global screen as well (see Architecture): their table keeps the list's columns, work queues, saved views and favorites, and offers whoever approves registries (a global registry-template:manage) the create form, each registry's edit form on a click, and its deletion, without selection or bulk edit. The page syncs the known registries each time it loads.

The billing catalog is a global screen too (see Billing Products): its numbers count products only, never a sum of prices; its plans' limits and its table keep the list's columns, work queues, saved views and favorites, and offer the edit form of a product (price, currency, on sale, sort order), without inline or bulk edits.

The quotes and signatures page is a screen of the organization (see Organization Dashboard): its table keeps the quotes list's columns, work queues, saved views and favorites, lists every quote of the organization, and opens a quote's panel on a click, without selection, edit or bulk action.

The media manager is a screen (see Media Library). It opens in Gallery and can switch to Table or File tree. Clicking the visual opens the rich media preview; clicking the description opens the read-only record drawer. Images and videos honor the selected Fill or Fit setting. Neutral native tags wrap compactly, and preview height S/M/L is independent of card width. Saved views preserve the preview height. The gallery retains the application's specialized font, PDF and video previews.

Managers can move a selection to a folder, duplicate files, or move them to the 30-day trash. Read-only users retain preview, information and selection without mutation actions. The trash, a page of its own (/dashboard/content/media/trash), exposes restoration, and prevents edit, duplication or repeated deletion. Ctrl/Cmd+A selects matching results across pages; Ctrl/Cmd+D duplicates permitted files; Ctrl/Cmd+Z restores a reversible deletion. Keyboard commands do not override typing in inputs or commands inside a modal.

All fixed catalogs explicitly enable private and organization-shared saved views. The wrapper honors an explicit sharing/save opt-out and disables organization sharing in personal workspaces. The active application locale also reaches Table's dates, keyboard feedback and view settings.

Each person's favorite view and order of the saved views (Move left and Move right in the view menu, … for the views that do not fit) are kept on the server, per organization and table, and never change a shared view. A screen's own view, such as the media library's, can be a favorite.