Stories 0.2-0.5: rename folders→declarations (backend+frontend), configure Redis for cache/queue/sessions, add foundation database migrations (permissions, archived_at), replace DeclarationStatus enum with architecture lifecycle values, create DeclarationObserver for status transition validation and auto-archive, fix controller status transitions to respect observer rules. 93 tests pass (240 assertions). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
178 lines
6.0 KiB
PHP
178 lines
6.0 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Enums\WorkspaceUserRole;
|
|
use App\Http\Requests\StoreWorkspaceRequest;
|
|
use App\Http\Requests\UpdateWorkspaceRequest;
|
|
use App\Models\User;
|
|
use App\Models\Workspace;
|
|
use Illuminate\Http\RedirectResponse;
|
|
use Illuminate\Http\Request;
|
|
use Inertia\Inertia;
|
|
use Inertia\Response;
|
|
|
|
class WorkspaceController extends Controller
|
|
{
|
|
/**
|
|
* Display a listing of the workspaces.
|
|
*/
|
|
public function index(Request $request): Response
|
|
{
|
|
$perPage = min(max((int) $request->input('per_page', 10), 10), 100);
|
|
|
|
$workspaces = Workspace::query()
|
|
->withCount('users')
|
|
->latest()
|
|
->paginate($perPage)
|
|
->through(fn (Workspace $workspace) => [
|
|
'id' => $workspace->id,
|
|
'name' => $workspace->name,
|
|
'slug' => $workspace->slug,
|
|
'users_count' => $workspace->users_count,
|
|
'showUrl' => route('workspaces.show', $workspace),
|
|
'editUrl' => route('workspaces.edit', $workspace),
|
|
'destroyUrl' => route('workspaces.destroy', $workspace),
|
|
]);
|
|
|
|
return Inertia::render('workspaces/Index', [
|
|
'workspaces' => $workspaces,
|
|
'createUrl' => route('workspaces.create'),
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Show the form for creating a new workspace.
|
|
*/
|
|
public function create(): Response
|
|
{
|
|
return Inertia::render('workspaces/Create', [
|
|
'indexUrl' => route('workspaces.index'),
|
|
'storeUrl' => route('workspaces.store'),
|
|
'users' => User::query()->orderBy('name')->get(['id', 'name', 'email']),
|
|
'workspaceUserRoles' => WorkspaceUserRole::asSelectArray(),
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Store a newly created workspace in storage.
|
|
*/
|
|
public function store(StoreWorkspaceRequest $request): RedirectResponse
|
|
{
|
|
$data = $request->validated();
|
|
$userIds = $data['user_ids'] ?? [];
|
|
$userRoles = $data['user_roles'] ?? [];
|
|
unset($data['user_ids'], $data['user_roles']);
|
|
|
|
$workspace = Workspace::query()->create($data);
|
|
$syncData = collect($userIds)->mapWithKeys(fn ($userId) => [
|
|
(int) $userId => ['role' => $userRoles[$userId] ?? WorkspaceUserRole::Member],
|
|
])->all();
|
|
$workspace->users()->sync($syncData);
|
|
|
|
return to_route('workspaces.index');
|
|
}
|
|
|
|
/**
|
|
* Display the specified workspace.
|
|
*/
|
|
public function show(Workspace $workspace): Response
|
|
{
|
|
$workspace->load('users');
|
|
|
|
$clientsCount = $workspace->clients()->count();
|
|
$declarationsCount = $workspace->declarations()->count();
|
|
$declarationsByStatus = $workspace->declarations()
|
|
->selectRaw('status, count(*) as count')
|
|
->groupBy('status')
|
|
->pluck('count', 'status')
|
|
->all();
|
|
$declarationsThisMonth = $workspace->declarations()
|
|
->whereMonth('created_at', now()->month)
|
|
->whereYear('created_at', now()->year)
|
|
->count();
|
|
$declarationsNeedingAttention = $workspace->declarations()
|
|
->whereIn('status', [
|
|
\App\Enums\DeclarationStatus::EnAttenteClient,
|
|
])
|
|
->count();
|
|
|
|
return Inertia::render('workspaces/Show', [
|
|
'workspace' => [
|
|
'id' => $workspace->id,
|
|
'name' => $workspace->name,
|
|
'slug' => $workspace->slug,
|
|
'users' => $workspace->users->map(fn ($user) => [
|
|
'id' => $user->id,
|
|
'name' => $user->name,
|
|
'email' => $user->email,
|
|
'role' => $user->pivot->role->value,
|
|
])->all(),
|
|
],
|
|
'stats' => [
|
|
'clients' => $clientsCount,
|
|
'declarations' => $declarationsCount,
|
|
'declarations_by_status' => $declarationsByStatus,
|
|
'declarations_this_month' => $declarationsThisMonth,
|
|
'declarations_needing_attention' => $declarationsNeedingAttention,
|
|
],
|
|
'indexUrl' => route('workspaces.index'),
|
|
'editUrl' => route('workspaces.edit', $workspace),
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Show the form for editing the specified workspace.
|
|
*/
|
|
public function edit(Workspace $workspace): Response
|
|
{
|
|
$workspace->load('users');
|
|
$userRoles = $workspace->users->mapWithKeys(
|
|
fn ($user) => [$user->id => $user->pivot->role->value],
|
|
)->all();
|
|
|
|
return Inertia::render('workspaces/Edit', [
|
|
'workspace' => [
|
|
'id' => $workspace->id,
|
|
'name' => $workspace->name,
|
|
'slug' => $workspace->slug,
|
|
'user_ids' => $workspace->users->pluck('id')->all(),
|
|
'user_roles' => $userRoles,
|
|
],
|
|
'indexUrl' => route('workspaces.index'),
|
|
'updateUrl' => route('workspaces.update', $workspace),
|
|
'users' => User::query()->orderBy('name')->get(['id', 'name', 'email']),
|
|
'workspaceUserRoles' => WorkspaceUserRole::asSelectArray(),
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Update the specified workspace in storage.
|
|
*/
|
|
public function update(UpdateWorkspaceRequest $request, Workspace $workspace): RedirectResponse
|
|
{
|
|
$data = $request->validated();
|
|
$userIds = $data['user_ids'] ?? [];
|
|
$userRoles = $data['user_roles'] ?? [];
|
|
unset($data['user_ids'], $data['user_roles']);
|
|
|
|
$workspace->update($data);
|
|
$syncData = collect($userIds)->mapWithKeys(fn ($userId) => [
|
|
(int) $userId => ['role' => $userRoles[$userId] ?? WorkspaceUserRole::Member],
|
|
])->all();
|
|
$workspace->users()->sync($syncData);
|
|
|
|
return to_route('workspaces.index');
|
|
}
|
|
|
|
/**
|
|
* Remove the specified workspace from storage.
|
|
*/
|
|
public function destroy(Workspace $workspace): RedirectResponse
|
|
{
|
|
$workspace->delete();
|
|
|
|
return to_route('workspaces.index');
|
|
}
|
|
}
|