# Feature: Super Admin (platform)

## Purpose

Platform-wide Super Admin portal: manage users and teams, read-only browse of product data across all teams, and virtual Jetstream administrator access so superadmins can enter any team and operate the normal app.

## Boundaries

- **Owns:** `super_admins` table / `SuperAdmin` model, `/super-admin` routes and Inertia pages, `EnsureSuperAdmin` middleware, `super-admin` Gate, grant/revoke Artisan commands, `SuperAdminResourceRegistry` + resource contract, User Jetstream helper overrides for act-as-team
- **Does not own:** Product domain CRUD (editing product data is done by entering a team and using domain UI)
- **Depends on (platform only):** User / Team / Jetstream / Inertia Admin UI chrome
- **Depends on (other domains):** Domains optionally register read-only browse adapters; removing a domain removes its browse section
- **Removable?** No — shared platform privilege layer

## Models

| Model | Table | Notes |
|-------|-------|-------|
| `App\Models\SuperAdmin` | `super_admins` | `user_id` unique FK → `users`, cascade on delete |

Privilege is membership in `super_admins` only — not Jetstream team role `admin`.

## Act as any team administrator

When `User::isSuperAdmin()` is true:

| Helper | Behavior |
|--------|----------|
| `belongsToTeam` | true for any team |
| `hasTeamPermission` / `teamPermissions` | full (`*`) |
| `teamRole` | Jetstream `admin` |
| `hasTeamRole` | true only for `admin` (avoids portal/staff restricted paths) |
| `switchTeam` / `allTeams` | any / all teams |

`TeamPolicy` owner-only gates also allow superadmins. No `team_user` rows are inserted for this access.

**Enter team:** `POST /super-admin/teams/{team}/enter` switches `current_team_id` and redirects to the dashboard.

## Routes

### Web

| Method | URI | Name | Controller |
|--------|-----|------|------------|
| GET | `/super-admin` | `super-admin.dashboard` | `DashboardController` |
| GET/POST | `/super-admin/users` … | `super-admin.users.*` | `UserController` |
| GET/POST/PUT/DELETE | `/super-admin/teams` … | `super-admin.teams.*` | `TeamController` |
| POST | `/super-admin/teams/{team}/enter` | `super-admin.teams.enter` | `TeamController@enter` |
| GET | `/super-admin/browse/{resource}` | `super-admin.browse.index` | `BrowseController` |
| GET | `/super-admin/browse/{resource}/{id}` | `super-admin.browse.show` | `BrowseController` |

Middleware: `web` (session, cookies, Inertia), then `auth:sanctum`, Jetstream session, `verified`, `super-admin`.

`routes/super-admin.php` is registered as a `withRouting(web: …)` file. Do not load it from the `then` callback — that skips the `web` group, so the browser has no session and `/super-admin` bounces guests (including logged-in users) back to the app dashboard.

### Artisan

- `php artisan super-admin:grant {email}`
- `php artisan super-admin:revoke {email}`

## Browse registry

Platform: `App\Support\SuperAdmin\SuperAdminResource` + `SuperAdminResourceRegistry`.

Domains register adapters in `DomainServiceProvider::boot()`:

| Domain | Key(s) |
|--------|--------|
| Customers | `customers` |
| Jobs | `jobs` |
| Providers | `providers` |
| Products | `sundry-products` |
| Messaging | `conversations` |
| CompanySettings | `company-settings` |

## Permissions

- Portal: must be in `super_admins`
- Cannot self-delete or self-revoke superadmin from the portal UI
- Personal teams cannot be deleted from Super Admin

## Tests

- Path: `laravel/tests/Feature/SuperAdmin/`
- Cover: 403 for non-superadmin, grant/revoke commands, user/team management, browse, act-as-team

## Notes for agents

- Do not move this into `app/Domains/`.
- Do not use a blanket `Gate::before` for superadmins.
- Prefer Enter team over adding product write routes under `/super-admin`.
- Keep Super Admin web routes inside the `web` middleware group (`bootstrap/app.php` `withRouting(web: […])`).
