permissions->allows($request->user(), Permission::UsersInvite), 403); $data = $request->validated(); $profile = collect($data)->only(['firstName', 'lastName', 'department', 'jobLevel', 'directManagerId', 'teamIds'])->all(); if (filled($profile['directManagerId'] ?? null)) { abort_unless(User::query()->where('organization_id', $this->tenant->id())->where('id', $profile['directManagerId'])->exists(), 422, 'مدیر مستقیم معتبر نیست.'); } if (($profile['teamIds'] ?? []) !== []) { abort_unless(count($profile['teamIds']) === DB::table('teams')->where('organization_id', $this->tenant->id())->whereIn('id', $profile['teamIds'])->count(), 422, 'یک یا چند تیم معتبر نیست.'); } $invitation = $this->invitations->invite( $this->tenant->organization(), $request->user(), $data['email'], UserRole::from($data['role']), $profile, ); return response()->json(['data' => [ 'id' => $invitation->getKey(), 'email' => $invitation->email, 'role' => $invitation->role->value, 'expiresAt' => $invitation->expires_at->toISOString(), 'status' => 'pending', ]], 201); } public function index(Request $request): JsonResponse { abort_unless($this->permissions->allows($request->user(), Permission::UsersInvite), 403); $items = UserInvitation::query()->where('organization_id', $this->tenant->id())->latest()->get()->map(fn (UserInvitation $item) => [ 'id' => $item->getKey(), 'email' => $item->email, 'role' => $item->role->value, 'status' => $item->accepted_at ? 'accepted' : ($item->revoked_at ? 'revoked' : ($item->expires_at->isPast() ? 'expired' : 'pending')), 'expiresAt' => $item->expires_at->toISOString(), ]); return response()->json(['data' => $items]); } public function revoke(Request $request, string $invitation): JsonResponse { abort_unless($this->permissions->allows($request->user(), Permission::UsersInvite), 403); $item = UserInvitation::query()->where('organization_id', $this->tenant->id())->findOrFail($invitation); if ($item->accepted_at) { throw ValidationException::withMessages(['invitation' => ['Accepted invitations cannot be revoked.']]); } $item->update(['revoked_at' => now()]); return response()->json(['data' => ['revoked' => true]]); } public function resend(Request $request, string $invitation): JsonResponse { abort_unless($this->permissions->allows($request->user(), Permission::UsersInvite), 403); $item = UserInvitation::query()->where('organization_id', $this->tenant->id())->findOrFail($invitation); if ($item->accepted_at) { throw ValidationException::withMessages(['invitation' => ['Accepted invitations cannot be resent.']]); } $refreshed = $this->invitations->invite( $this->tenant->organization(), $request->user(), $item->email, $item->role, $item->profile ?? [], ); return response()->json(['data' => [ 'id' => $refreshed->getKey(), 'email' => $refreshed->email, 'role' => $refreshed->role->value, 'expiresAt' => $refreshed->expires_at->toISOString(), 'status' => 'pending', ]]); } public function accept(AcceptInvitationRequest $request): JsonResponse { $data = $request->validated(); $user = $this->invitations->accept($data['token'], $data['name'], $data['password']); $token = $user->createToken('web')->plainTextToken; return response()->json(['data' => ['token' => $token, 'userId' => $user->getKey()]]); } }