Overview
This release introduces a full role-based access control system that governs who can access what across the entire platform. Five roles define distinct access levels — from customers who only see the public catalogue, to designers who manage the builder, sales reps who handle quotes, and admins who oversee everything. Super-admins retain unrestricted access through both /home and /admin.
Roles & Hierarchy
The system defines five roles ordered by privilege level (lower number = more authority):
| Level | Role | Description |
|---|---|---|
| 1 | Owner | System role with all 10 permissions |
| 2 | Admin | Full platform management — products, designs, assets, quotes, users, billing |
| 3 | Designer | Builder setup — products, designs, assets. No access to quotes |
| 4 | Sales Rep | Quote management — view and manage own quotes only |
| 5 | Member | Customer — public catalogue and uniform builder only. Cannot access /home |
A sixth access level, Super Admin, is not stored in the database but determined by the user's JWT (app_metadata.role = 'super-admin' + MFA). Super-admins bypass all route restrictions.
Permissions Matrix
Ten granular permissions control feature access:
| Permission | Owner | Admin | Designer | Sales Rep | Member |
|---|---|---|---|---|---|
| roles.manage | Yes | — | — | — | — |
| billing.manage | Yes | Yes | — | — | — |
| settings.manage | Yes | Yes | Yes | Yes | Yes |
| members.manage | Yes | Yes | — | — | — |
| invites.manage | Yes | Yes | — | — | — |
| products.manage | Yes | Yes | Yes | — | — |
| designs.manage | Yes | Yes | Yes | — | — |
| assets.manage | Yes | Yes | Yes | — | — |
| quotes.manage | Yes | Yes | — | Yes | — |
| rules.manage | Yes | Yes | — | — | — |
Route Access Control
| Route | Member | Sales Rep | Designer | Admin | Super Admin |
|---|---|---|---|---|---|
/ (catalogue) | Yes | Yes | Yes | Yes | Yes |
/builder/* | Yes | Yes | Yes | Yes | Yes |
/home | No | Yes | Yes | Yes | Yes |
/home/products | No | No | Yes | Yes | Yes |
/home/designs | No | No | Yes | Yes | Yes |
/home/assets/* | No | No | Yes | Yes | Yes |
/home/quotes | No | Yes | No | Yes | Yes |
/home/settings | No | Yes | No | Yes | Yes |
/admin | No | No | No | No | Yes |
Features
Middleware Route Protection
The Next.js middleware (proxy.ts) enforces access to /home/* at the edge:
- Checks authentication — unauthenticated users redirect to sign-in
- Checks MFA requirement — redirects to verification if needed
- Checks super-admin status — super-admins always pass
- Calls
get_account_role()RPC — staff roles (admin, designer, sales-rep) pass; customers (member) redirect to/
Customer-First Sign-In Flow
All post-login redirects now default to / (the product catalogue) instead of /home. This applies to:
- Email/password sign-in
- OAuth (Google) sign-in
- Sign-up
- Email confirmation
- MFA verification
Staff members navigate to /home manually or via direct links. The middleware validates their role before granting access.
Role-Aware Navigation
The /home sidebar dynamically shows only the routes relevant to the user's role:
- Admin sees: Home, Products, Designs, Assets (Colors, Palettes, Fonts, Graphics), Quotes, Settings
- Designer sees: Home, Products, Designs, Assets
- Sales Rep sees: Quotes, Settings
The navigation config accepts the user's role from the user_account_workspace database view and filters routes accordingly.
Admin Panel Enhancements
The /admin sidebar now includes a "Content" section with direct links to /home/products, /home/designs, /home/assets, and /home/quotes — giving super-admins quick access to content management.
The accounts table at /admin/accounts gains:
- A Role column displaying each personal account's current role
- A Change Role action in the account dropdown, allowing super-admins to promote or demote users between member, admin, designer, and sales-rep
Quotes Row-Level Security
Database-level RLS policies now scope quote visibility by role:
- Admin/Owner (hierarchy level 1–2): see all quotes in the account
- Sales Rep: see only quotes they personally created
- Member: see only their own submitted quotes (read-only)
- Designer: no quote access whatsoever
Two helper functions — can_view_quote() and can_update_quote() — encapsulate the logic, keeping RLS policies clean and maintainable.
Server Action Guards
A reusable requireTeamPermission() utility enables permission checks inside any server action. Quote mutation actions (delete, update fulfillment, update notes) now accept an optional accountId parameter — when provided, the caller's quotes.manage permission is verified before proceeding.
Technical Details
New Files
apps/web/ ├── lib/server/require-team-permission.ts # Reusable permission guard packages/features/admin/src/ ├── components/admin-change-role-dialog.tsx # Role assignment UI └── lib/server/schema/admin-actions.schema.ts # ChangeAccountRoleSchema
Modified Files (29 total)
Middleware & Auth (7 files): ├── proxy.ts # Role check in /home handler ├── auth/sign-in/page.tsx # Redirect default → / ├── auth/sign-up/page.tsx # Redirect default → / ├── auth/callback/route.ts # Redirect default → / ├── auth/confirm/route.ts # Redirect default → / ├── auth/verify/page.tsx # Redirect default → / └── sign-in-methods-container.tsx # Fallback redirect → / Navigation (8 files): ├── personal-account-navigation.config.tsx # Role-aware filtering ├── team-account-navigation.config.tsx # Permission-aware filtering ├── home-sidebar.tsx # Passes role to config ├── home-mobile-navigation.tsx # Passes role to config ├── home-menu-navigation.tsx # Passes role to config ├── team-account-layout-sidebar.tsx # Passes permissions to config ├── team-account-layout-mobile-navigation.tsx └── team-account-navigation-menu.tsx Admin (4 files): ├── admin-sidebar.tsx # Content links section ├── admin-accounts-table.tsx # Role column + Change Role action ├── admin-server-actions.ts # changeAccountRoleAction └── admin-actions.schema.ts # ChangeAccountRoleSchema Schema (4 files): ├── 01-enums.sql # Extended app_permissions ├── 03-accounts.sql # role column + trigger update ├── 15-account-views.sql # View includes role └── 17-roles-seed.sql # Full 5-role seed Other (4 files): ├── [account]/layout.tsx # Passes permissions to nav ├── quote.schema.ts # Optional accountId ├── quote server-actions.ts # Team permission guard └── database.types.ts # Updated TypeScript types
Data Flow
- User signs up →
kit.setup_new_user()trigger creates personal account withrole = 'member' - Super-admin visits
/admin/accounts→ changes role to designer, sales-rep, or admin via dropdown - User signs in → lands on
/(product catalogue) regardless of role - User navigates to
/home→ middleware callsget_account_role()→ member redirected to/, staff allowed through /homelayout loadsuser_account_workspaceview (includes role) → sidebar filtered by role- Quote operations check
can_view_quote()/can_update_quote()at the database level for row-scoped access