183 خطوط
7.4 KiB
PHP
183 خطوط
7.4 KiB
PHP
<?php
|
|
|
|
namespace App\Modules\Teams\Http;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use App\Models\User;
|
|
use App\Modules\Assignments\Application\AssignmentResolver;
|
|
use App\Modules\Identity\Application\RolePermissions;
|
|
use App\Modules\Identity\Domain\Enums\Permission;
|
|
use App\Modules\Identity\Domain\Enums\UserRole;
|
|
use App\Modules\Teams\Domain\Team;
|
|
use App\Modules\Teams\Http\Requests\AttachTeamUserRequest;
|
|
use App\Modules\Teams\Http\Requests\StoreTeamRequest;
|
|
use App\Modules\Teams\Http\Requests\UpdateTeamRequest;
|
|
use App\Modules\Tenancy\Application\TenantContext;
|
|
use Illuminate\Http\JsonResponse;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Validation\ValidationException;
|
|
|
|
final class TeamController extends Controller
|
|
{
|
|
public function __construct(private readonly TenantContext $tenant, private readonly RolePermissions $permissions, private readonly AssignmentResolver $assignmentResolver) {}
|
|
|
|
public function index(Request $request): JsonResponse
|
|
{
|
|
abort_unless($this->permissions->allows($request->user(), Permission::TeamsView), 403);
|
|
$query = Team::query()->where('organization_id', $this->tenant->id());
|
|
|
|
if ($request->user()->role === UserRole::Manager) {
|
|
$query->whereHas('managers', fn ($manager) => $manager->whereKey($request->user()->getKey()));
|
|
}
|
|
|
|
$teams = $query->withCount(['members', 'managers'])->orderBy('name')->get()->map(fn (Team $team) => $this->payload($team));
|
|
|
|
return response()->json(['data' => $teams]);
|
|
}
|
|
|
|
public function store(StoreTeamRequest $request): JsonResponse
|
|
{
|
|
$this->authorizeDesigner($request);
|
|
$team = DB::transaction(function () use ($request): Team {
|
|
$team = Team::query()->create([
|
|
'organization_id' => $this->tenant->id(),
|
|
'name' => $request->validated('name'),
|
|
'description' => $request->validated('description'),
|
|
'status' => 'active',
|
|
'created_by' => $request->user()->getKey(),
|
|
]);
|
|
if ($request->filled('managerId')) {
|
|
$this->attachManagerAndReports($team, $this->manager($request->validated('managerId')));
|
|
}
|
|
|
|
return $team->loadCount(['members', 'managers']);
|
|
});
|
|
|
|
return response()->json(['data' => $this->payload($team)], 201);
|
|
}
|
|
|
|
public function show(Request $request, string $team): JsonResponse
|
|
{
|
|
abort_unless($this->permissions->allows($request->user(), Permission::TeamsView), 403);
|
|
$query = Team::query()->where('organization_id', $this->tenant->id());
|
|
if ($request->user()->role === UserRole::Manager) {
|
|
$query->whereHas('managers', fn ($manager) => $manager->whereKey($request->user()->getKey()));
|
|
}
|
|
$model = $query
|
|
->withCount(['members', 'managers'])
|
|
->with(['members:id,name,email,role,status,department,job_level,direct_manager_id', 'managers:id,name,email,role,status,department,job_level,direct_manager_id'])
|
|
->findOrFail($team);
|
|
|
|
return response()->json(['data' => array_merge($this->payload($model), [
|
|
'members' => $model->members->map(fn (User $user) => $this->userPayload($user)),
|
|
'managers' => $model->managers->map(fn (User $user) => $this->userPayload($user)),
|
|
])]);
|
|
}
|
|
|
|
public function update(UpdateTeamRequest $request, string $team): JsonResponse
|
|
{
|
|
$this->authorizeDesigner($request);
|
|
$model = Team::query()->where('organization_id', $this->tenant->id())->findOrFail($team);
|
|
$model->update($request->safe()->only(['name', 'description']));
|
|
|
|
return response()->json(['data' => $this->payload($model)]);
|
|
}
|
|
|
|
public function attachMember(AttachTeamUserRequest $request, string $team): JsonResponse
|
|
{
|
|
$this->authorizeDesigner($request);
|
|
[$targetTeam, $user] = $this->resolveTeamAndUser($team, $request->validated('userId'));
|
|
$targetTeam->members()->syncWithoutDetaching([$user->getKey()]);
|
|
$this->assignmentResolver->syncOrganization($this->tenant->id());
|
|
|
|
return response()->json(['data' => ['attached' => true]]);
|
|
}
|
|
|
|
public function attachManager(AttachTeamUserRequest $request, string $team): JsonResponse
|
|
{
|
|
$this->authorizeDesigner($request);
|
|
$targetTeam = Team::query()->where('organization_id', $this->tenant->id())->findOrFail($team);
|
|
$this->attachManagerAndReports($targetTeam, $this->manager($request->validated('userId')));
|
|
$this->assignmentResolver->syncOrganization($this->tenant->id());
|
|
|
|
return response()->json(['data' => ['attached' => true]]);
|
|
}
|
|
|
|
public function detachMember(Request $request, string $team, string $user): JsonResponse
|
|
{
|
|
$this->authorizeDesigner($request);
|
|
[$targetTeam, $targetUser] = $this->resolveTeamAndUser($team, $user);
|
|
$targetTeam->members()->detach($targetUser);
|
|
|
|
return response()->json(['data' => ['detached' => true]]);
|
|
}
|
|
|
|
public function detachManager(Request $request, string $team, string $user): JsonResponse
|
|
{
|
|
$this->authorizeDesigner($request);
|
|
[$targetTeam, $targetUser] = $this->resolveTeamAndUser($team, $user);
|
|
$targetTeam->managers()->detach($targetUser);
|
|
|
|
return response()->json(['data' => ['detached' => true]]);
|
|
}
|
|
|
|
private function authorizeDesigner(Request $request): void
|
|
{
|
|
abort_unless($this->permissions->allows($request->user(), Permission::TeamsManage), 403);
|
|
}
|
|
|
|
private function resolveTeamAndUser(string $team, string $user): array
|
|
{
|
|
$targetTeam = Team::query()->where('organization_id', $this->tenant->id())->findOrFail($team);
|
|
$targetUser = User::query()->where('organization_id', $this->tenant->id())->findOrFail($user);
|
|
|
|
return [$targetTeam, $targetUser];
|
|
}
|
|
|
|
private function manager(string $user): User
|
|
{
|
|
$manager = User::query()->where('organization_id', $this->tenant->id())->findOrFail($user);
|
|
if ($manager->role !== UserRole::Manager) {
|
|
throw ValidationException::withMessages(['userId' => ['Only a Manager may manage a team.']]);
|
|
}
|
|
|
|
return $manager;
|
|
}
|
|
|
|
private function attachManagerAndReports(Team $team, User $manager): void
|
|
{
|
|
$team->managers()->syncWithoutDetaching([$manager->getKey()]);
|
|
$reports = User::query()
|
|
->where('organization_id', $this->tenant->id())
|
|
->where('direct_manager_id', $manager->getKey())
|
|
->where('status', 'active')
|
|
->pluck('id')
|
|
->all();
|
|
$team->members()->syncWithoutDetaching($reports);
|
|
}
|
|
|
|
private function payload(Team $team): array
|
|
{
|
|
return [
|
|
'id' => $team->getKey(), 'name' => $team->name, 'description' => $team->description,
|
|
'status' => $team->status, 'memberCount' => $team->members_count ?? 0,
|
|
'managerCount' => $team->managers_count ?? 0,
|
|
];
|
|
}
|
|
|
|
private function userPayload(User $user): array
|
|
{
|
|
return [
|
|
'id' => $user->getKey(),
|
|
'name' => $user->name,
|
|
'email' => $user->email,
|
|
'role' => $user->role->value,
|
|
'status' => $user->status->value,
|
|
'department' => $user->department,
|
|
'jobLevel' => $user->job_level,
|
|
'directManagerId' => $user->direct_manager_id,
|
|
];
|
|
}
|
|
}
|