Features: shared vs role pages
Default: one shared feature page, gated by permissions. Split into role-specific pages only when the workflow itself is different — not when a button should be hidden.
Related: Domains · Permissions · Shared pages.
Decision
| Choose | When |
|---|---|
| Shared page | Multiple roles use the same workflow; only actions / fields differ |
| Role page | Semantics or layout are genuinely unique to one role (e.g. a role dashboard) |
Use shared pages when
- The same list/detail/create flow serves several roles
- Differences are “can approve” vs “can only view” — permission IDs, not separate routes
- You would otherwise copy-paste three near-identical pages
Use role pages when
- The primary job of the screen is role-specific (admin ops dashboard vs employee home)
- Shared CRUD would force awkward conditionals that obscure the UX
Current state
| Kind | Examples |
|---|---|
| Shared | Users, maintenance, departments, system (monitor / logs / audit / settings), shared account surfaces |
| Role-specific | Role dashboards (admin, hod, developer, employee, …) |
Shared page structure
Under domains/shared/pages/<feature>/:
text
list/index.tsx
list/skeleton.tsx
detail/index.tsx
detail/skeleton.tsx
create/index.tsx
create/skeleton.tsx- Keep skeletons local so loading UI matches that page and stays permission-aware
- Data: TanStack Query hooks +
lib/services/*— not ad-hoc fetch in the page - Actions:
hasPermission('…')before showing privileged controls
Anti-patterns
/admin/usersand/hod/usersas two copies of the same list- Hiding a whole shared feature behind a role folder “for convenience”
- Checking
user.role === 'administrator'in the page when a permission ID exists