262 lines
8.6 KiB
PHP
262 lines
8.6 KiB
PHP
|
|
<?php
|
||
|
|
|
||
|
|
namespace App\Http\Controllers;
|
||
|
|
|
||
|
|
use App\Concerns\AuthorizesPermissions;
|
||
|
|
use App\Concerns\HasWorkspaceScope;
|
||
|
|
use App\Enums\Permission;
|
||
|
|
use App\Enums\WorkspaceUserRole;
|
||
|
|
use App\Http\Requests\InviteTeamMemberRequest;
|
||
|
|
use App\Http\Requests\UpdatePermissionsRequest;
|
||
|
|
use App\Http\Requests\UpdateTeamMemberRoleRequest;
|
||
|
|
use App\Mail\TeamInvitationMail;
|
||
|
|
use App\Models\TeamInvitation;
|
||
|
|
use App\Models\User;
|
||
|
|
use App\Models\WorkspaceUser;
|
||
|
|
use Illuminate\Http\RedirectResponse;
|
||
|
|
use Illuminate\Support\Facades\DB;
|
||
|
|
use Illuminate\Support\Facades\Mail;
|
||
|
|
use Inertia\Inertia;
|
||
|
|
use Inertia\Response;
|
||
|
|
|
||
|
|
class TeamController extends Controller
|
||
|
|
{
|
||
|
|
use AuthorizesPermissions;
|
||
|
|
use HasWorkspaceScope;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Display the team management page.
|
||
|
|
*/
|
||
|
|
public function index(): Response
|
||
|
|
{
|
||
|
|
$workspaceUser = auth()->user()->currentWorkspaceUser();
|
||
|
|
|
||
|
|
// Block Workers entirely — team page is Owner/Manager only
|
||
|
|
if ($workspaceUser->role->is(WorkspaceUserRole::Worker)) {
|
||
|
|
abort(404);
|
||
|
|
}
|
||
|
|
|
||
|
|
$workspace = $this->currentWorkspace();
|
||
|
|
|
||
|
|
$isOwner = $workspaceUser->role->is(WorkspaceUserRole::Owner);
|
||
|
|
|
||
|
|
// Load members with pivot data
|
||
|
|
$members = $workspace->users()
|
||
|
|
->withPivot('id', 'role', 'permissions', 'created_at')
|
||
|
|
->get()
|
||
|
|
->map(function ($user) use ($isOwner) {
|
||
|
|
$pivotRole = $user->pivot->role;
|
||
|
|
$role = $pivotRole?->value ?? $pivotRole;
|
||
|
|
$data = [
|
||
|
|
'id' => $user->id,
|
||
|
|
'name' => $user->name,
|
||
|
|
'email' => $user->email,
|
||
|
|
'role' => $role,
|
||
|
|
'joined_at' => $user->pivot->created_at,
|
||
|
|
'status' => 'active',
|
||
|
|
'workspace_user_id' => $user->pivot->id,
|
||
|
|
'updateRoleUrl' => route('team.updateRole', $user->pivot->id),
|
||
|
|
'removeUrl' => route('team.remove', $user->pivot->id),
|
||
|
|
];
|
||
|
|
|
||
|
|
// Add permissions data for Manager members (only visible to Owners)
|
||
|
|
if ($pivotRole instanceof WorkspaceUserRole && $pivotRole->is(WorkspaceUserRole::Manager) && $isOwner) {
|
||
|
|
$data['permissions'] = $user->pivot->permissions ?? [];
|
||
|
|
$data['permissionsUrl'] = route('team.updatePermissions', $user->pivot->id);
|
||
|
|
}
|
||
|
|
|
||
|
|
return $data;
|
||
|
|
});
|
||
|
|
|
||
|
|
// Load pending invitations
|
||
|
|
$pendingInvitations = TeamInvitation::where('workspace_id', $workspace->id)
|
||
|
|
->whereNull('accepted_at')
|
||
|
|
->where('expires_at', '>', now())
|
||
|
|
->get()
|
||
|
|
->map(fn ($inv) => [
|
||
|
|
'id' => $inv->id,
|
||
|
|
'email' => $inv->email,
|
||
|
|
'role' => $inv->role,
|
||
|
|
'invited_at' => $inv->created_at,
|
||
|
|
'status' => 'pending',
|
||
|
|
]);
|
||
|
|
|
||
|
|
// Can manage team: Owner always, Manager checks permission
|
||
|
|
$canManageTeam = $workspaceUser->role->is(WorkspaceUserRole::Owner)
|
||
|
|
|| ($workspaceUser->permissions[Permission::CanManageTeam] ?? false);
|
||
|
|
|
||
|
|
return Inertia::render('team/Index', [
|
||
|
|
'members' => $members,
|
||
|
|
'pendingInvitations' => $pendingInvitations,
|
||
|
|
'canManageTeam' => $canManageTeam,
|
||
|
|
'isOwner' => $isOwner,
|
||
|
|
'availablePermissions' => $isOwner ? $this->permissionLabels() : [],
|
||
|
|
'authUserId' => auth()->id(),
|
||
|
|
'inviteUrl' => route('team.invite'),
|
||
|
|
'roles' => $this->roleLabels(),
|
||
|
|
]);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Invite a new team member.
|
||
|
|
*/
|
||
|
|
public function invite(InviteTeamMemberRequest $request): RedirectResponse
|
||
|
|
{
|
||
|
|
$workspace = $this->currentWorkspace();
|
||
|
|
|
||
|
|
$invitation = TeamInvitation::create([
|
||
|
|
'workspace_id' => $workspace->id,
|
||
|
|
'email' => $request->validated('email'),
|
||
|
|
'role' => $request->validated('role'),
|
||
|
|
'invited_by' => auth()->id(),
|
||
|
|
'expires_at' => now()->addDays(7),
|
||
|
|
]);
|
||
|
|
|
||
|
|
Mail::to($invitation->email)->send(new TeamInvitationMail($workspace, $invitation));
|
||
|
|
|
||
|
|
return redirect()->back()->with('success', 'Invitation envoyée');
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Update a team member's role.
|
||
|
|
*/
|
||
|
|
public function updateRole(UpdateTeamMemberRoleRequest $request, int $workspaceUserId): RedirectResponse
|
||
|
|
{
|
||
|
|
$this->authorizePermission(Permission::CanManageTeam);
|
||
|
|
|
||
|
|
$workspaceUser = WorkspaceUser::where('id', $workspaceUserId)
|
||
|
|
->where('workspace_id', session('current_workspace_id'))
|
||
|
|
->firstOrFail();
|
||
|
|
|
||
|
|
if ($workspaceUser->role->is(WorkspaceUserRole::Owner)) {
|
||
|
|
abort(404);
|
||
|
|
}
|
||
|
|
|
||
|
|
if ($workspaceUser->user_id === auth()->id()) {
|
||
|
|
abort(404);
|
||
|
|
}
|
||
|
|
|
||
|
|
$oldRole = $workspaceUser->role->value;
|
||
|
|
$newRole = $request->validated('role');
|
||
|
|
|
||
|
|
DB::transaction(function () use ($workspaceUser, $oldRole, $newRole) {
|
||
|
|
$workspaceUser->update([
|
||
|
|
'role' => $newRole,
|
||
|
|
'permissions' => config("permissions.defaults.{$newRole}", []),
|
||
|
|
]);
|
||
|
|
|
||
|
|
$targetUser = User::findOrFail($workspaceUser->user_id);
|
||
|
|
activity()
|
||
|
|
->performedOn($workspaceUser)
|
||
|
|
->withProperties([
|
||
|
|
'target_user' => $targetUser->name,
|
||
|
|
'old_role' => $oldRole,
|
||
|
|
'new_role' => $newRole,
|
||
|
|
])
|
||
|
|
->log('role_changed');
|
||
|
|
});
|
||
|
|
|
||
|
|
return redirect()->back()->with('success', 'Rôle mis à jour');
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Update a Manager's individual permissions.
|
||
|
|
*/
|
||
|
|
public function updatePermissions(UpdatePermissionsRequest $request, int $workspaceUserId): RedirectResponse
|
||
|
|
{
|
||
|
|
$workspaceUser = WorkspaceUser::where('id', $workspaceUserId)
|
||
|
|
->where('workspace_id', session('current_workspace_id'))
|
||
|
|
->firstOrFail();
|
||
|
|
|
||
|
|
// Only Manager permissions can be toggled
|
||
|
|
if (! $workspaceUser->role->is(WorkspaceUserRole::Manager)) {
|
||
|
|
abort(404);
|
||
|
|
}
|
||
|
|
|
||
|
|
$newPermissions = $request->validated('permissions');
|
||
|
|
|
||
|
|
DB::transaction(function () use ($workspaceUser, $newPermissions) {
|
||
|
|
$oldPermissions = $workspaceUser->permissions ?? [];
|
||
|
|
|
||
|
|
$workspaceUser->update([
|
||
|
|
'permissions' => $newPermissions,
|
||
|
|
]);
|
||
|
|
|
||
|
|
$targetUser = User::findOrFail($workspaceUser->user_id);
|
||
|
|
activity()
|
||
|
|
->performedOn($workspaceUser)
|
||
|
|
->withProperties([
|
||
|
|
'target_user' => $targetUser->name,
|
||
|
|
'old_permissions' => $oldPermissions,
|
||
|
|
'new_permissions' => $newPermissions,
|
||
|
|
])
|
||
|
|
->log('permissions_updated');
|
||
|
|
});
|
||
|
|
|
||
|
|
return redirect()->back()->with('success', 'Permissions mises à jour');
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Remove a team member from the workspace.
|
||
|
|
*/
|
||
|
|
public function remove(int $workspaceUserId): RedirectResponse
|
||
|
|
{
|
||
|
|
$this->authorizePermission(Permission::CanManageTeam);
|
||
|
|
|
||
|
|
$workspaceUser = WorkspaceUser::where('id', $workspaceUserId)
|
||
|
|
->where('workspace_id', session('current_workspace_id'))
|
||
|
|
->firstOrFail();
|
||
|
|
|
||
|
|
if ($workspaceUser->role->is(WorkspaceUserRole::Owner)) {
|
||
|
|
abort(404);
|
||
|
|
}
|
||
|
|
|
||
|
|
if ($workspaceUser->user_id === auth()->id()) {
|
||
|
|
abort(404);
|
||
|
|
}
|
||
|
|
|
||
|
|
$targetUser = User::findOrFail($workspaceUser->user_id);
|
||
|
|
|
||
|
|
DB::transaction(function () use ($workspaceUser, $targetUser) {
|
||
|
|
$workspaceUser->delete();
|
||
|
|
|
||
|
|
activity()
|
||
|
|
->withProperties([
|
||
|
|
'target_user' => $targetUser->name,
|
||
|
|
'target_email' => $targetUser->email,
|
||
|
|
'role' => $workspaceUser->role->value,
|
||
|
|
])
|
||
|
|
->log('member_removed');
|
||
|
|
});
|
||
|
|
|
||
|
|
return redirect()->back()->with('success', 'Membre retiré');
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Get role labels for the frontend.
|
||
|
|
*
|
||
|
|
* @return array<string, string>
|
||
|
|
*/
|
||
|
|
protected function roleLabels(): array
|
||
|
|
{
|
||
|
|
return [
|
||
|
|
'manager' => 'Gestionnaire',
|
||
|
|
'worker' => 'Collaborateur',
|
||
|
|
];
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Get permission labels (French) for the frontend.
|
||
|
|
*
|
||
|
|
* @return array<string, string>
|
||
|
|
*/
|
||
|
|
protected function permissionLabels(): array
|
||
|
|
{
|
||
|
|
return [
|
||
|
|
Permission::CanManageTeam => "Gérer l'équipe",
|
||
|
|
Permission::CanViewActivityLogs => "Voir les journaux d'activité",
|
||
|
|
Permission::CanConfigurePortal => 'Configurer le portail client',
|
||
|
|
];
|
||
|
|
}
|
||
|
|
}
|