# Story 1.2: Team Management Page — View & Invite Members Status: done ## Story As a firm owner, I want to view all team members in my workspace and invite new members via email, So that I can build my team and see who has access to my firm's data. ## Acceptance Criteria 1. **Team index page** — A route `GET /team` renders a team page displaying all workspace members in a table with columns: name, email, role (as Badge), join date, and status (active/pending). 2. **Pending invitations** — Invited users who have not yet accepted show a "Pending" StatusBadge in the status column. 3. **Invite Member button** — An "Inviter un membre" button opens a Dialog/Sheet form with: - Email input (required, valid email format) - Role selection dropdown (Manager or Worker — Owner is never assignable) 4. **Invitation email** — Submitting the invite form sends an invitation email to the specified address with a link to register/join the workspace. 5. **Immediate feedback** — The invitation appears in the team list as "Pending" immediately after submission. A success toast confirms: "Invitation envoyée". 6. **Workspace scoping** — The `TeamController` uses the `HasWorkspaceScope` trait to scope members to the current workspace. 7. **Worker access denied** — Workers cannot access the Team page (`abort(404)` via `AuthorizesPermissions`). 8. **Manager conditional access** — Managers can view the team list but only see the "Inviter un membre" button if they have `can_manage_team` permission. 9. **Layout & breadcrumbs** — The page uses `AppLayout` with breadcrumbs: `Dashboard / Équipe`. 10. **EmptyState** — When the workspace has only the owner, an EmptyState is shown: "Invitez votre premier membre d'équipe" with the invite action button. ## Tasks / Subtasks - [x] Task 1: Create `TeamController` with `index` and `invite` methods (AC: #1, #6, #7, #8) - [x] 1.1 Create `app/Http/Controllers/TeamController.php` using `HasWorkspaceScope` and `AuthorizesPermissions` traits - [x] 1.2 `index()` method: block Workers (`authorizePermission` with early abort for Worker role), load workspace members with pivot data, load pending invitations, render Inertia page `team/Index` - [x] 1.3 `invite()` method: validate with `InviteTeamMemberRequest`, create invitation record, send invitation email, redirect back with success toast - [x] 1.4 Pass `canManageTeam` boolean prop to frontend (true for Owner, check permission for Manager) - [x] Task 2: Create `InviteTeamMemberRequest` form request (AC: #3) - [x] 2.1 Create `app/Http/Requests/InviteTeamMemberRequest.php` - [x] 2.2 `authorize()`: verify user is Owner OR Manager with `can_manage_team` permission - [x] 2.3 `rules()`: validate `email` (required, email format, not already in workspace), `role` (required, in: manager, worker) - [x] Task 3: Create `TeamInvitation` model and migration (AC: #2, #4) - [x] 3.1 Create migration `create_team_invitations_table`: `id`, `workspace_id` (FK), `email`, `role` (string), `token` (uuid, unique), `invited_by` (FK to users), `accepted_at` (nullable datetime), `expires_at` (datetime), timestamps - [x] 3.2 Create `app/Models/TeamInvitation.php` with fillable, casts, relationships (`workspace`, `invitedBy`), `isValid()` method, auto-generate token on creating - [x] 3.3 Add `teamInvitations(): HasMany` relationship to `Workspace` model - [x] Task 4: Create `TeamInvitationMail` mailable (AC: #4) - [x] 4.1 Create `app/Mail/TeamInvitationMail.php` following existing mailable pattern (envelope + content + markdown) - [x] 4.2 Create markdown email template `resources/views/emails/team-invitation.blade.php` with workspace name, role, and registration/accept link - [x] 4.3 Queue the mail dispatch (use `ShouldQueue` or dispatch via queue) - [x] Task 5: Create routes (AC: #1) - [x] 5.1 Add to `routes/web.php` inside the workspace middleware group: - `Route::get('team', [TeamController::class, 'index'])->name('team.index')` - `Route::post('team/invite', [TeamController::class, 'invite'])->name('team.invite')` - [x] Task 6: Create `team/Index.vue` page (AC: #1, #2, #3, #5, #8, #9, #10) - [x] 6.1 Create `resources/js/pages/team/Index.vue` with `