feat: overhaul project workspace navigation
linter / quality (pull_request) Successful in 1m28s
tests / ci (8.4) (pull_request) Successful in 1m47s

This commit is contained in:
2026-07-18 17:52:25 +02:00
parent a46695c2eb
commit ff796aa10a
45 changed files with 1790 additions and 995 deletions
+22 -63
View File
@@ -8,19 +8,16 @@ use App\Actions\Projects\CreateProject;
use App\Actions\Projects\DeleteProject;
use App\Actions\Projects\DuplicateProjectConfiguration;
use App\Actions\Projects\ListVisibleProjects;
use App\Actions\Projects\ResolveProjectTemplateState;
use App\Actions\Projects\UpdateProjectTemplateLink;
use App\Enums\Permission;
use App\Enums\ProjectSettingsSection;
use App\Http\Requests\DeleteProjectRequest;
use App\Http\Requests\ResetProjectTemplateRequest;
use App\Http\Requests\StoreProjectRequest;
use App\Http\Requests\SwitchProjectTemplateRequest;
use App\Http\Requests\SyncProjectPermissionsRequest;
use App\Http\Resources\PermissionOptionResource;
use App\Http\Resources\ProjectResource;
use App\Http\Resources\ProjectTemplateResource;
use App\Http\Resources\TeamResource;
use App\Http\Resources\UserResource;
use App\Models\Project;
use App\Models\ProjectTemplate;
use App\Models\Team;
@@ -39,7 +36,6 @@ class ProjectController extends Controller
private readonly DeleteProject $deleteProject,
private readonly ListVisibleProjects $listVisibleProjects,
private readonly PermissionService $permissionService,
private readonly ResolveProjectTemplateState $resolveProjectTemplateState,
private readonly UpdateProjectTemplateLink $updateProjectTemplateLink,
) {}
@@ -83,10 +79,10 @@ class ProjectController extends Controller
{
$project = $this->createProject->handle($request->validated(), $request->user());
return to_route('projects.show', $project);
return to_route('projects.board.index', $project);
}
public function show(Request $request, Project $project): Response
public function show(Request $request, Project $project): RedirectResponse
{
$user = $request->user();
@@ -95,11 +91,7 @@ class ProjectController extends Controller
403,
);
return Inertia::render('projects/Show', [
'canManageAccess' => $this->permissionService->allows($user, Permission::ProjectAdmin, $project),
'project' => ProjectResource::make($project)->resolve(),
'templateState' => $this->resolveProjectTemplateState->handle($project),
]);
return to_route('projects.board.index', $project);
}
public function backlog(Request $request, Project $project): Response
@@ -117,31 +109,19 @@ class ProjectController extends Controller
);
}
public function general(Request $request, Project $project): Response
{
return Inertia::render('projects/settings/General', [
'project' => ProjectResource::make($project)->resolve(),
'sourceTemplates' => ProjectTemplateResource::collection(
ProjectTemplate::query()
->published()
->with('sourceProject')
->orderBy('name')
->get(),
)->resolve(),
'status' => session('status'),
'templateState' => $this->resolveProjectTemplateState->handle($project),
]);
}
public function switchTemplate(SwitchProjectTemplateRequest $request, Project $project): RedirectResponse
{
$validated = $request->validated();
$template = ProjectTemplate::query()
->published()
->findOrFail($request->integer('source_template_id'));
->findOrFail((int) Arr::get($validated, 'source_template_id'));
$this->updateProjectTemplateLink->switch($project, $template);
return to_route('projects.settings.general', $project)
return to_route('projects.settings.index', [
'project' => $project,
'section' => ProjectSettingsSection::General->value,
])
->with('status', "{$project->name} now uses the {$template->name} template.");
}
@@ -155,40 +135,13 @@ class ProjectController extends Controller
$this->updateProjectTemplateLink->reset($project->load('projectTemplate'));
return to_route('projects.settings.general', $project)
return to_route('projects.settings.index', [
'project' => $project,
'section' => ProjectSettingsSection::General->value,
])
->with('status', "{$project->name} was reset to the {$template->name} template baseline.");
}
public function access(Request $request, Project $project): Response
{
$user = $request->user();
abort_unless(
$this->permissionService->allows($user, Permission::ProjectAdmin, $project),
403,
);
return Inertia::render('projects/Access', [
'directPermissions' => $this->permissionService->assignmentMap(User::class, $project),
'permissionOptions' => PermissionOptionResource::collection(
$this->permissionService->permissionOptions(User::class, Project::class),
)->resolve(),
'project' => ProjectResource::make($project)->resolve(),
'status' => session('status'),
'teamPermissions' => $this->permissionService->assignmentMap(Team::class, $project),
'teams' => TeamResource::collection(
Team::query()
->with('users')
->orderBy('name')
->get(),
)->resolve(),
'users' => UserResource::collection(
User::query()
->orderBy('name')
->get(),
)->resolve(),
]);
}
public function updateUserPermissions(
SyncProjectPermissionsRequest $request,
Project $project,
@@ -205,7 +158,10 @@ class ProjectController extends Controller
$this->permissionService->sync($user, $project, $permissions);
return to_route('projects.access', $project)
return to_route('projects.settings.index', [
'project' => $project,
'section' => ProjectSettingsSection::Access->value,
])
->with('status', "{$user->name} permissions were updated.");
}
@@ -224,7 +180,10 @@ class ProjectController extends Controller
$this->permissionService->sync($team, $project, $permissions);
return to_route('projects.access', $project)
return to_route('projects.settings.index', [
'project' => $project,
'section' => ProjectSettingsSection::Access->value,
])
->with('status', "{$team->name} permissions were updated.");
}