# Jetstream Teams (platform)

## Purpose

Multi-team accounts, memberships, invitations, and related auth/profile flows provided by Laravel Jetstream. This is **platform**, not a removable product domain.

## Boundaries

- **Owns:** `User`, `Team`, memberships, invitations, Fortify/Jetstream actions, profile/API token UI scaffolding
- **Does not own:** Product CRM/scheduling features
- **Location:** Default Laravel/Jetstream paths under `laravel/app/`, `laravel/resources/js/`, `laravel/tests/Feature/`
- **Removable?** No — product domains may depend on this layer

## Models

| Model | Table | Notes |
|-------|-------|-------|
| `App\Models\User` | `users` | Jetstream user |
| `App\Models\Team` | `teams` | Team account |
| `App\Models\Membership` | `team_user` | Pivot membership |
| `App\Models\TeamInvitation` | `team_invitations` | Invitations |

## Routes

Managed by Jetstream/Fortify (web). Sanctum supports API token auth for mobile clients. See [Web and API](../architecture/web-and-api.md).

## Permissions

Jetstream team roles/abilities via `TeamPolicy` and related Jetstream configuration.

| Role key | Label | Abilities |
|----------|-------|-----------|
| `admin` | Administrator | `create`, `read`, `update`, `delete` |
| `editor` | Editor | `read`, `create`, `update` |
| `staff` | Staff | `read` |
| `customer` | Customer | `read`, `create`, `update` |
| `provider` | Provider | `read` |

Team **owners** retain full team-management gates (`addTeamMember`, etc.) regardless of role.

The `customer` role is for CRM portal logins linked via `customer_portal_users` (see [Customers](customers.md) and [Jobs](jobs.md)). Portal users do not manage company settings or the customers CRM.

The `provider` role is for third-party portal logins linked via `provider_portal_users` (see [Providers](providers.md) and [Jobs](jobs.md)). Providers see only appointments assigned to them (limited fields) and cannot access providers CRM or payments.

### Team Settings roster

On **Team Settings** (`GET /teams/{team}`), only **owners**, **administrators**, and **editors** receive the full member list, pending invitations, and team owner details. `staff`, `customer`, and `provider` members only receive their own membership in the Inertia payload — other members, invitations, and the owner’s name/email are omitted. Superadmins see the full roster.

### Create member with password (no invite)

Owners can create a user and attach them to the team without sending an invitation:

- Web: `POST /teams/{team}/members/create` (`team-members.create`) — flashes one-time `generated_password` (and `created_member_id`). **No email is sent automatically.**
- Web send: `POST /teams/{team}/members/{user}/credentials-email` (`team-members.credentials-email`) — owner must click **Email login details**. Body includes the still-valid one-time password (must `Hash::check` against the account). Plaintext is not stored.
- API: `POST /api/teams/{team}/members` (`api.team-members.store`) — JSON includes one-time `password`; still no automatic email

Public **signup** (`POST /register`) sends `WelcomeMail` automatically, then redirects to the [Billing](billing.md) subscribe page. Accounts created with a generated password do not fire that welcome mail.

Existing invite flow (`POST /teams/{team}/members`) is unchanged.

## Tests

Existing Jetstream Feature tests under `laravel/tests/Feature/` (registration, teams, API tokens, profile, etc.), plus `CreateTeamMemberWithPasswordTest` (create does not email; send credentials does) and `TeamSettingsMemberVisibilityTest` (only owners/admins/editors receive other members). Terms and privacy: `LegalDocumentsTest` and registration `terms` acceptance — see [Legal documents](legal.md).

## Notes for agents

- Do not move this into `app/Domains/`.
- Product features reference `User` / `Team` as platform dependencies and document that on their feature pages.
