# Configuration

`config/packstub-teams.php` holds app-wide settings; the plugin overrides any of them per panel.

| Key | Default | Purpose |
|---|---|---|
| `team_model` | `App\Models\Team` | the tenant model |
| `membership.table` / `team_foreign_key` / `user_foreign_key` / `role_column` | `team_user` / `team_id` / `user_id` / `role` | the pivot |
| `membership.team_relationship` / `user_relationship` | `members` / `joinedTeams` | relation names to prefer when your models define them |
| `invitations_table` | `team_invitations` | |
| `connection` | `null` | connection for the pivot + invitations (central DB in database-per-tenant apps) |
| `roles.provider` | `null` (auto) | `simple`, `spatie`, or a `RoleProvider` class |
| `roles.owner` / `roles.default` / `roles.managers` | `owner` / `member` / `['admin']` | reserved owner role, role for new invites, roles allowed to manage |
| `roles.roles` | owner/admin/member | `name => label` or `name => ['label', 'description', 'permissions']` |
| `invitations.expire_after_days` | 7 | |
| `invitations.require_email_match` | true | only the invited address may accept an e-mail invitation |
| `invitations.count_pending_toward_seats` | true | |
| `invitations.resend_cooldown_minutes` | 5 | |
| `invitations.max_batch` | 10 | addresses per Invite action |
| `invitations.links` | true | offer shareable links in the UI |
| `invitations.notify_existing_users` | true | database notification for invitees with an account |
| `invitations.auto_accept_on_login` | false | accept every pending invitation for the e-mail on login |
| `invitations.prune_accepted_after_days` | 30 | retention for `teams:prune-invitations` |
| `invitations.mail.queue` | false | |
| `personal_teams.enabled` / `name` | false / `:name's Team` | see [Installation](https://packstub.dev/docs/filament-teams/installation) |
| `seat_limit` | null | global default |

## The plugin API

```php
TeamsPlugin::make()
    ->roles([...], default: 'member', managers: ['admin'])
    ->roleProvider('spatie')                        // or 'simple' / a class
    ->seatLimit(fn (Team $team): ?int => $team->plan?->seats)
    ->canManageUsing(fn (User $user, Team $team): bool => ...)
    ->canInviteUsing(fn (User $user, Team $team): bool => ...)
    ->personalTeams(fn (User $user): Team => Team::create([...]), name: ":name's Team")
    ->navigationGroup('Team')->navigationSort(90)
    ->tenantMenuItem()                              // "Members" in the tenant switcher menu
    ->membersPage(MyMembers::class)                 // or null to skip
    ->invitationsPage(null)
    ->acceptRoute()                                 // /{panel}/invitations/{token}
    ->config(['seat_limit' => 10])                  // any packstub-teams.* key, per panel
    ->useFilamentTenancy();                         // preset for packstub/filament-tenancy
```

## The service

`Packstub\Teams\Facades\Teams` — `invite`, `inviteMany`, `createInviteLink`, `resend`, `revoke`, `accept`, `acceptAllFor`, `addMember`, `changeRole`, `removeMember`, `transferOwnership`, `roles`, `assignableRoles`, `defaultRoleFor`, `userCan`, `canManage`, `canInvite`, `seatLimit`, `seatsUsed`, `hasAvailableSeats`, `membersOf`, `teamsOf`, `invitationsOf`, `ownersOf`, `isMember`, `roleOf`, `pruneInvitations`, `createPersonalTeam`.

Rule violations throw a `Packstub\Teams\Exceptions\TeamsException` subclass: `SeatLimitReached`, `LastOwnerException`, `InvalidInvitation`, `InvalidRole`, `NotAMember`, `AlreadyAMember`, `ResendTooSoon`. The Filament pages turn them into notifications.
