From 017cff54378ee0309932b9944a3e95df47bf4529 Mon Sep 17 00:00:00 2001 From: Eric Peters Date: Wed, 15 Apr 2026 18:09:08 +0200 Subject: [PATCH] Refactor Inertia responses to use resources --- .../Projects/BuildProjectBacklogPageProps.php | 49 ---- .../Projects/BuildProjectBoardPageProps.php | 37 --- .../BuildProjectTicketUpdateAttributes.php | 48 ---- app/Actions/Tickets/UpdateProjectTicket.php | 76 ++++++- .../Admin/UserManagementController.php | 14 +- .../Controllers/ProjectBoardController.php | 32 ++- .../ProjectBoardSettingsController.php | 29 ++- app/Http/Controllers/ProjectController.php | 96 +++++--- .../ProjectRepositorySettingsController.php | 37 ++- .../Controllers/ProjectTicketController.php | 211 +++++++----------- ...rojectTicketMetadataSettingsController.php | 23 +- .../Settings/GitConnectionController.php | 16 +- app/Http/Controllers/TeamController.php | 25 +-- .../AvailableGitRepositorySourceResource.php | 31 +++ app/Http/Resources/BoardColumnResource.php | 34 +++ .../Resources/FieldTypeOptionResource.php | 29 +++ .../Resources/GitRepositoryOptionResource.php | 27 +++ .../Resources/PermissionOptionResource.php | 23 ++ app/Http/Resources/ProjectResource.php | 27 +++ .../RepositoryConnectionResource.php | 51 +++++ app/Http/Resources/TeamResource.php | 26 +++ app/Http/Resources/TicketBranchResource.php | 22 ++ .../TicketFieldDefinitionResource.php | 27 +++ .../Resources/TicketFieldValueResource.php | 39 ++++ app/Http/Resources/TicketLinkResource.php | 48 ++++ app/Http/Resources/TicketLinkTypeResource.php | 26 +++ app/Http/Resources/TicketPriorityResource.php | 26 +++ .../Resources/TicketPullRequestResource.php | 26 +++ .../Resources/TicketReferenceResource.php | 25 +++ .../TicketRepositoryBranchResource.php | 26 +++ .../TicketRepositoryPullRequestResource.php | 28 +++ app/Http/Resources/TicketResource.php | 76 +++++++ app/Http/Resources/TicketStatusResource.php | 38 ++++ app/Http/Resources/TicketTreeNodeResource.php | 41 ++++ app/Http/Resources/TicketTypeResource.php | 28 +++ .../Resources/TicketWorklogEntryResource.php | 34 +++ .../Resources/UserGitConnectionResource.php | 30 +++ app/Http/Resources/UserResource.php | 26 +++ 38 files changed, 1125 insertions(+), 382 deletions(-) delete mode 100644 app/Actions/Projects/BuildProjectBacklogPageProps.php delete mode 100644 app/Actions/Projects/BuildProjectBoardPageProps.php delete mode 100644 app/Actions/Tickets/BuildProjectTicketUpdateAttributes.php create mode 100644 app/Http/Resources/AvailableGitRepositorySourceResource.php create mode 100644 app/Http/Resources/BoardColumnResource.php create mode 100644 app/Http/Resources/FieldTypeOptionResource.php create mode 100644 app/Http/Resources/GitRepositoryOptionResource.php create mode 100644 app/Http/Resources/PermissionOptionResource.php create mode 100644 app/Http/Resources/ProjectResource.php create mode 100644 app/Http/Resources/RepositoryConnectionResource.php create mode 100644 app/Http/Resources/TeamResource.php create mode 100644 app/Http/Resources/TicketBranchResource.php create mode 100644 app/Http/Resources/TicketFieldDefinitionResource.php create mode 100644 app/Http/Resources/TicketFieldValueResource.php create mode 100644 app/Http/Resources/TicketLinkResource.php create mode 100644 app/Http/Resources/TicketLinkTypeResource.php create mode 100644 app/Http/Resources/TicketPriorityResource.php create mode 100644 app/Http/Resources/TicketPullRequestResource.php create mode 100644 app/Http/Resources/TicketReferenceResource.php create mode 100644 app/Http/Resources/TicketRepositoryBranchResource.php create mode 100644 app/Http/Resources/TicketRepositoryPullRequestResource.php create mode 100644 app/Http/Resources/TicketResource.php create mode 100644 app/Http/Resources/TicketStatusResource.php create mode 100644 app/Http/Resources/TicketTreeNodeResource.php create mode 100644 app/Http/Resources/TicketTypeResource.php create mode 100644 app/Http/Resources/TicketWorklogEntryResource.php create mode 100644 app/Http/Resources/UserGitConnectionResource.php create mode 100644 app/Http/Resources/UserResource.php diff --git a/app/Actions/Projects/BuildProjectBacklogPageProps.php b/app/Actions/Projects/BuildProjectBacklogPageProps.php deleted file mode 100644 index ef714e7..0000000 --- a/app/Actions/Projects/BuildProjectBacklogPageProps.php +++ /dev/null @@ -1,49 +0,0 @@ - - */ - public function handle( - Project $project, - bool $canEditTickets, - bool $canCreateTickets, - ?string $status, - ): array { - return [ - 'backlogTicketStatuses' => $project->ticketStatuses() - ->whereNull('board_column_id') - ->get(), - 'canCreateTickets' => $canCreateTickets, - 'canEditTickets' => $canEditTickets, - 'project' => $project, - 'status' => $status, - 'ticketStatusOptions' => $project->ticketStatuses() - ->with('boardColumn') - ->get(), - 'ticketPriorities' => $project->ticketPriorities()->get(), - 'ticketTypes' => $project->ticketTypes()->get(), - 'tickets' => Ticket::query() - ->with([ - 'ticketPriority', - 'ticketStatus.allowedTargetStatuses.boardColumn', - 'ticketStatus.boardColumn', - 'ticketType', - ]) - ->where('project_id', $project->id) - ->whereHas('ticketStatus', fn ($query) => $query->whereNull('board_column_id')) - ->orderByDesc('sequence_number') - ->get(), - 'users' => User::query() - ->orderBy('name') - ->get(), - ]; - } -} diff --git a/app/Actions/Projects/BuildProjectBoardPageProps.php b/app/Actions/Projects/BuildProjectBoardPageProps.php deleted file mode 100644 index 8783aeb..0000000 --- a/app/Actions/Projects/BuildProjectBoardPageProps.php +++ /dev/null @@ -1,37 +0,0 @@ - - */ - public function handle( - Project $project, - bool $canEditTickets, - bool $canCreateTickets, - ?string $status, - ): array { - return [ - 'boardColumns' => $project->boardColumns() - ->with([ - 'ticketStatuses', - 'tickets' => fn ($query) => $query - ->with([ - 'ticketPriority', - 'ticketStatus.allowedTargetStatuses.boardColumn', - 'ticketStatus.boardColumn', - 'ticketType', - ]), - ]) - ->get(), - 'canCreateTickets' => $canCreateTickets, - 'canEditTickets' => $canEditTickets, - 'project' => $project, - 'status' => $status, - ]; - } -} diff --git a/app/Actions/Tickets/BuildProjectTicketUpdateAttributes.php b/app/Actions/Tickets/BuildProjectTicketUpdateAttributes.php deleted file mode 100644 index 0a049f8..0000000 --- a/app/Actions/Tickets/BuildProjectTicketUpdateAttributes.php +++ /dev/null @@ -1,48 +0,0 @@ - $validated - * @return array{ - * acceptance_criteria: string|null, - * assignee_user_id: int|null, - * author_user_id: int|null, - * blocked_comment: string|null, - * description: string|null, - * is_blocked: bool, - * parent_ticket_id: int|null, - * story_points: int|null, - * ticket_priority_id: int, - * ticket_type_id: int, - * title: string - * } - */ - public function handle(Ticket $ticket, array $validated): array - { - return [ - 'acceptance_criteria' => Arr::get($validated, 'acceptance_criteria'), - 'assignee_user_id' => Arr::exists($validated, 'assignee_user_id') - ? Arr::get($validated, 'assignee_user_id') - : $ticket->assignee_user_id, - 'author_user_id' => Arr::exists($validated, 'author_user_id') - ? Arr::get($validated, 'author_user_id') - : $ticket->author_user_id, - 'blocked_comment' => Arr::get($validated, 'blocked_comment'), - 'description' => Arr::get($validated, 'description'), - 'is_blocked' => Arr::get($validated, 'is_blocked', false), - 'parent_ticket_id' => Arr::get($validated, 'parent_ticket_id'), - 'story_points' => Arr::exists($validated, 'story_points') - ? Arr::get($validated, 'story_points') - : $ticket->story_points, - 'ticket_priority_id' => Arr::get($validated, 'ticket_priority_id'), - 'ticket_type_id' => Arr::get($validated, 'ticket_type_id'), - 'title' => Arr::get($validated, 'title'), - ]; - } -} diff --git a/app/Actions/Tickets/UpdateProjectTicket.php b/app/Actions/Tickets/UpdateProjectTicket.php index c87061c..6cd6f95 100644 --- a/app/Actions/Tickets/UpdateProjectTicket.php +++ b/app/Actions/Tickets/UpdateProjectTicket.php @@ -7,6 +7,7 @@ use App\Models\Ticket; use App\Models\TicketFieldDefinition; use App\Models\TicketFieldValue; use App\Models\User; +use Illuminate\Support\Arr; use Illuminate\Support\Collection; use Illuminate\Support\Facades\DB; @@ -20,29 +21,31 @@ class UpdateProjectTicket /** * @param array{ * acceptance_criteria: string|null, - * assignee_user_id: int|null, - * author_user_id: int|null, + * assignee_user_id?: int|null, + * author_user_id?: int|null, * blocked_comment: string|null, * description: string|null, + * field_values?: array, * is_blocked: bool, - * story_points: int|null, + * story_points?: int|null, * ticket_priority_id: int, * ticket_type_id: int, * parent_ticket_id: int|null, * title: string - * } $ticketAttributes - * @param array $fieldValueAttributes + * } $validated */ public function handle( Ticket $ticket, User $actor, - array $ticketAttributes, - array $fieldValueAttributes, + array $validated, ): bool { + $fieldValueAttributes = Arr::get($validated, 'field_values', []); + $ticketAttributes = $this->buildTicketAttributes($ticket, $validated); + return DB::transaction(function () use ($actor, $fieldValueAttributes, $ticket, $ticketAttributes): bool { if (! $ticketAttributes['is_blocked']) { $ticketAttributes['blocked_comment'] = null; @@ -212,6 +215,57 @@ class UpdateProjectTicket }); } + /** + * @param array{ + * acceptance_criteria: string|null, + * assignee_user_id?: int|null, + * author_user_id?: int|null, + * blocked_comment: string|null, + * description: string|null, + * is_blocked: bool, + * parent_ticket_id: int|null, + * story_points?: int|null, + * ticket_priority_id: int, + * ticket_type_id: int, + * title: string + * } $validated + * @return array{ + * acceptance_criteria: string|null, + * assignee_user_id: int|null, + * author_user_id: int|null, + * blocked_comment: string|null, + * description: string|null, + * is_blocked: bool, + * parent_ticket_id: int|null, + * story_points: int|null, + * ticket_priority_id: int, + * ticket_type_id: int, + * title: string + * } + */ + private function buildTicketAttributes(Ticket $ticket, array $validated): array + { + return [ + 'acceptance_criteria' => Arr::get($validated, 'acceptance_criteria'), + 'assignee_user_id' => Arr::exists($validated, 'assignee_user_id') + ? Arr::get($validated, 'assignee_user_id') + : $ticket->assignee_user_id, + 'author_user_id' => Arr::exists($validated, 'author_user_id') + ? Arr::get($validated, 'author_user_id') + : $ticket->author_user_id, + 'blocked_comment' => Arr::get($validated, 'blocked_comment'), + 'description' => Arr::get($validated, 'description'), + 'is_blocked' => Arr::get($validated, 'is_blocked', false), + 'parent_ticket_id' => Arr::get($validated, 'parent_ticket_id'), + 'story_points' => Arr::exists($validated, 'story_points') + ? Arr::get($validated, 'story_points') + : $ticket->story_points, + 'ticket_priority_id' => Arr::get($validated, 'ticket_priority_id'), + 'ticket_type_id' => Arr::get($validated, 'ticket_type_id'), + 'title' => Arr::get($validated, 'title'), + ]; + } + /** * @param array $changes * @param array $summaryLabels diff --git a/app/Http/Controllers/Admin/UserManagementController.php b/app/Http/Controllers/Admin/UserManagementController.php index d5fb660..87204ef 100644 --- a/app/Http/Controllers/Admin/UserManagementController.php +++ b/app/Http/Controllers/Admin/UserManagementController.php @@ -5,6 +5,8 @@ namespace App\Http\Controllers\Admin; use App\Actions\Permissions\PermissionService; use App\Http\Controllers\Controller; use App\Http\Requests\Admin\SyncUserGlobalPermissionsRequest; +use App\Http\Resources\PermissionOptionResource; +use App\Http\Resources\UserResource; use App\Models\User; use Illuminate\Http\RedirectResponse; use Illuminate\Support\Arr; @@ -21,11 +23,15 @@ class UserManagementController extends Controller { return Inertia::render('admin/users/Index', [ 'directPermissions' => $this->permissionService->assignmentMap(User::class), - 'permissionOptions' => $this->permissionService->permissionOptions(User::class, null), + 'permissionOptions' => PermissionOptionResource::collection( + $this->permissionService->permissionOptions(User::class, null), + )->resolve(), 'status' => session('status'), - 'users' => User::query() - ->orderBy('name') - ->get(), + 'users' => UserResource::collection( + User::query() + ->orderBy('name') + ->get(), + )->resolve(), ]); } diff --git a/app/Http/Controllers/ProjectBoardController.php b/app/Http/Controllers/ProjectBoardController.php index d9336a6..48297d1 100644 --- a/app/Http/Controllers/ProjectBoardController.php +++ b/app/Http/Controllers/ProjectBoardController.php @@ -3,11 +3,12 @@ namespace App\Http\Controllers; use App\Actions\Permissions\PermissionService; -use App\Actions\Projects\BuildProjectBoardPageProps; use App\Actions\Tickets\CreateTicketWorklogEntry; use App\Enums\Permission; use App\Enums\TicketWorklogEntryType; use App\Http\Requests\MoveProjectTicketRequest; +use App\Http\Resources\BoardColumnResource; +use App\Http\Resources\ProjectResource; use App\Models\Project; use App\Models\Ticket; use App\Models\TicketStatus; @@ -20,7 +21,6 @@ use Inertia\Response; class ProjectBoardController extends Controller { public function __construct( - private readonly BuildProjectBoardPageProps $buildProjectBoardPageProps, private readonly CreateTicketWorklogEntry $createTicketWorklogEntry, private readonly PermissionService $permissionService, ) {} @@ -34,12 +34,28 @@ class ProjectBoardController extends Controller 403, ); - return Inertia::render('projects/Board', $this->buildProjectBoardPageProps->handle( - $project, - $this->permissionService->allows($user, Permission::ProjectWrite, $project), - $this->permissionService->allows($user, Permission::ProjectWrite, $project), - session('status'), - )); + $canManageTickets = $this->permissionService->allows($user, Permission::ProjectWrite, $project); + + return Inertia::render('projects/Board', [ + 'boardColumns' => BoardColumnResource::collection( + $project->boardColumns() + ->with([ + 'ticketStatuses', + 'tickets' => fn ($query) => $query + ->with([ + 'ticketPriority', + 'ticketStatus.allowedTargetStatuses.boardColumn', + 'ticketStatus.boardColumn', + 'ticketType', + ]), + ]) + ->get(), + )->resolve(), + 'canCreateTickets' => $canManageTickets, + 'canEditTickets' => $canManageTickets, + 'project' => ProjectResource::make($project)->resolve(), + 'status' => session('status'), + ]); } public function move( diff --git a/app/Http/Controllers/ProjectBoardSettingsController.php b/app/Http/Controllers/ProjectBoardSettingsController.php index 129e7a4..30fcaff 100644 --- a/app/Http/Controllers/ProjectBoardSettingsController.php +++ b/app/Http/Controllers/ProjectBoardSettingsController.php @@ -9,6 +9,9 @@ use App\Http\Requests\StoreTicketStatusRequest; use App\Http\Requests\UpdateBoardColumnRequest; use App\Http\Requests\UpdateTicketStatusRequest; use App\Http\Requests\UpdateTicketStatusTransitionsRequest; +use App\Http\Resources\BoardColumnResource; +use App\Http\Resources\ProjectResource; +use App\Http\Resources\TicketStatusResource; use App\Models\BoardColumn; use App\Models\Project; use App\Models\TicketStatus; @@ -35,17 +38,23 @@ class ProjectBoardSettingsController extends Controller ); return Inertia::render('projects/settings/Board', [ - 'backlogTicketStatuses' => $project->ticketStatuses() - ->whereNull('board_column_id') - ->get(), - 'boardColumns' => $project->boardColumns() - ->with('ticketStatuses') - ->get(), - 'project' => $project, + 'backlogTicketStatuses' => TicketStatusResource::collection( + $project->ticketStatuses() + ->whereNull('board_column_id') + ->get(), + )->resolve(), + 'boardColumns' => BoardColumnResource::collection( + $project->boardColumns() + ->with('ticketStatuses') + ->get(), + )->resolve(), + 'project' => ProjectResource::make($project)->resolve(), 'status' => session('status'), - 'ticketStatuses' => $project->ticketStatuses() - ->with(['allowedTargetStatuses.boardColumn', 'boardColumn']) - ->get(), + 'ticketStatuses' => TicketStatusResource::collection( + $project->ticketStatuses() + ->with(['allowedTargetStatuses.boardColumn', 'boardColumn']) + ->get(), + )->resolve(), ]); } diff --git a/app/Http/Controllers/ProjectController.php b/app/Http/Controllers/ProjectController.php index 755a65a..edee943 100644 --- a/app/Http/Controllers/ProjectController.php +++ b/app/Http/Controllers/ProjectController.php @@ -3,14 +3,22 @@ namespace App\Http\Controllers; use App\Actions\Permissions\PermissionService; -use App\Actions\Projects\BuildProjectBacklogPageProps; use App\Actions\ProjectTicketMetadata\InitializeProjectTicketMetadata; use App\Enums\Permission; use App\Http\Requests\DeleteProjectRequest; use App\Http\Requests\StoreProjectRequest; use App\Http\Requests\SyncProjectPermissionsRequest; +use App\Http\Resources\PermissionOptionResource; +use App\Http\Resources\ProjectResource; +use App\Http\Resources\TeamResource; +use App\Http\Resources\TicketPriorityResource; +use App\Http\Resources\TicketResource; +use App\Http\Resources\TicketStatusResource; +use App\Http\Resources\TicketTypeResource; +use App\Http\Resources\UserResource; use App\Models\Project; use App\Models\Team; +use App\Models\Ticket; use App\Models\User; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; @@ -21,7 +29,6 @@ use Inertia\Response; class ProjectController extends Controller { public function __construct( - private readonly BuildProjectBacklogPageProps $buildProjectBacklogPageProps, private readonly InitializeProjectTicketMetadata $initializeProjectTicketMetadata, private readonly PermissionService $permissionService, ) {} @@ -38,7 +45,7 @@ class ProjectController extends Controller if ($viewableProjectIds === []) { return Inertia::render('projects/Index', [ - 'projects' => [], + 'projects' => ProjectResource::collection(collect())->resolve(), 'status' => session('status'), ]); } @@ -47,8 +54,7 @@ class ProjectController extends Controller } return Inertia::render('projects/Index', [ - 'projects' => $projectQuery - ->get(), + 'projects' => ProjectResource::collection($projectQuery->get())->resolve(), 'status' => session('status'), ]); } @@ -83,7 +89,7 @@ class ProjectController extends Controller return Inertia::render('projects/Show', [ 'canManageAccess' => $this->permissionService->allows($user, Permission::ProjectAdmin, $project), - 'project' => $project, + 'project' => ProjectResource::make($project)->resolve(), ]); } @@ -96,18 +102,50 @@ class ProjectController extends Controller 403, ); - return Inertia::render('projects/Backlog', $this->buildProjectBacklogPageProps->handle( - $project, - $this->permissionService->allows($user, Permission::ProjectWrite, $project), - $this->permissionService->allows($user, Permission::ProjectWrite, $project), - session('status'), - )); + $canManageTickets = $this->permissionService->allows($user, Permission::ProjectWrite, $project); + + return Inertia::render('projects/Backlog', [ + 'backlogTicketStatuses' => TicketStatusResource::collection( + $project->ticketStatuses() + ->whereNull('board_column_id') + ->get(), + )->resolve(), + 'canCreateTickets' => $canManageTickets, + 'canEditTickets' => $canManageTickets, + 'project' => ProjectResource::make($project)->resolve(), + 'status' => session('status'), + 'ticketStatusOptions' => TicketStatusResource::collection( + $project->ticketStatuses() + ->with('boardColumn') + ->get(), + )->resolve(), + 'ticketPriorities' => TicketPriorityResource::collection($project->ticketPriorities()->get())->resolve(), + 'ticketTypes' => TicketTypeResource::collection($project->ticketTypes()->get())->resolve(), + 'tickets' => TicketResource::collection( + Ticket::query() + ->with([ + 'ticketPriority', + 'ticketStatus.allowedTargetStatuses.boardColumn', + 'ticketStatus.boardColumn', + 'ticketType', + ]) + ->where('project_id', $project->id) + ->whereHas('ticketStatus', fn ($query) => $query->whereNull('board_column_id')) + ->orderByDesc('sequence_number') + ->get(), + )->resolve(), + 'users' => UserResource::collection( + User::query() + ->orderBy('name') + ->get(), + )->resolve(), + ]); } public function general(Request $request, Project $project): Response { return Inertia::render('projects/settings/General', [ - 'project' => $project, + 'project' => ProjectResource::make($project)->resolve(), 'status' => session('status'), ]); } @@ -122,25 +160,23 @@ class ProjectController extends Controller return Inertia::render('projects/Access', [ 'directPermissions' => $this->permissionService->assignmentMap(User::class, $project), - 'permissionOptions' => $this->permissionService->permissionOptions(User::class, Project::class), - 'project' => $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' => Team::query() - ->with('users') - ->orderBy('name') - ->get() - ->map(fn (Team $team): array => [ - 'id' => $team->id, - 'name' => $team->name, - 'created_at' => $team->created_at?->toISOString(), - 'updated_at' => $team->updated_at?->toISOString(), - 'user_ids' => $team->users->modelKeys(), - 'users' => $team->users->values(), - ]), - 'users' => User::query() - ->orderBy('name') - ->get(), + 'teams' => TeamResource::collection( + Team::query() + ->with('users') + ->orderBy('name') + ->get(), + )->resolve(), + 'users' => UserResource::collection( + User::query() + ->orderBy('name') + ->get(), + )->resolve(), ]); } diff --git a/app/Http/Controllers/ProjectRepositorySettingsController.php b/app/Http/Controllers/ProjectRepositorySettingsController.php index 33c858c..01e0e81 100644 --- a/app/Http/Controllers/ProjectRepositorySettingsController.php +++ b/app/Http/Controllers/ProjectRepositorySettingsController.php @@ -8,6 +8,9 @@ use App\Actions\Permissions\PermissionService; use App\Enums\Permission; use App\Http\Requests\StoreRepositoryConnectionRequest; use App\Http\Requests\UpdateRepositoryConnectionRequest; +use App\Http\Resources\AvailableGitRepositorySourceResource; +use App\Http\Resources\ProjectResource; +use App\Http\Resources\RepositoryConnectionResource; use App\Models\Project; use App\Models\RepositoryConnection; use App\Models\User; @@ -36,27 +39,21 @@ class ProjectRepositorySettingsController extends Controller 403, ); + $repositoryConnections = $project->repositoryConnections() + ->get() + ->each(function (RepositoryConnection $repositoryConnection) use ($user): void { + $repositoryConnection->setAttribute( + 'has_personal_access', + $this->hasPersonalAccess($user, $repositoryConnection), + ); + }); + return Inertia::render('projects/settings/Repositories', [ - 'availableRepositorySources' => $this->availableRepositorySources($user), - 'project' => $project, - 'repositoryConnections' => $project->repositoryConnections() - ->get() - ->map(fn (RepositoryConnection $repositoryConnection): array => [ - 'base_url' => $repositoryConnection->base_url, - 'created_at' => $repositoryConnection->created_at?->toISOString(), - 'has_personal_access' => $this->hasPersonalAccess($user, $repositoryConnection), - 'id' => $repositoryConnection->id, - 'owner_name' => $repositoryConnection->owner_name, - 'project_id' => $repositoryConnection->project_id, - 'provider' => $repositoryConnection->provider->value, - 'provider_label' => $repositoryConnection->provider->label(), - 'provider_repository_id' => $repositoryConnection->provider_repository_id, - 'repository_full_name' => $repositoryConnection->repository_full_name, - 'repository_name' => $repositoryConnection->repository_name, - 'repository_url' => $repositoryConnection->repository_url, - 'updated_at' => $repositoryConnection->updated_at?->toISOString(), - ]) - ->values(), + 'availableRepositorySources' => AvailableGitRepositorySourceResource::collection( + $this->availableRepositorySources($user), + )->resolve(), + 'project' => ProjectResource::make($project)->resolve(), + 'repositoryConnections' => RepositoryConnectionResource::collection($repositoryConnections)->resolve(), 'status' => session('status'), ]); } diff --git a/app/Http/Controllers/ProjectTicketController.php b/app/Http/Controllers/ProjectTicketController.php index 288f3c0..9c3bb21 100644 --- a/app/Http/Controllers/ProjectTicketController.php +++ b/app/Http/Controllers/ProjectTicketController.php @@ -6,12 +6,24 @@ use App\Actions\Git\BuildSuggestedTicketBranchName; use App\Actions\Git\ResolveTicketBranchMatches; use App\Actions\Git\ResolveTicketPullRequestMatches; use App\Actions\Permissions\PermissionService; -use App\Actions\Tickets\BuildProjectTicketUpdateAttributes; use App\Actions\Tickets\CreateProjectTicket; use App\Actions\Tickets\UpdateProjectTicket; use App\Enums\Permission; use App\Http\Requests\StoreProjectTicketRequest; use App\Http\Requests\UpdateProjectTicketRequest; +use App\Http\Resources\ProjectResource; +use App\Http\Resources\TicketFieldDefinitionResource; +use App\Http\Resources\TicketLinkResource; +use App\Http\Resources\TicketLinkTypeResource; +use App\Http\Resources\TicketPriorityResource; +use App\Http\Resources\TicketReferenceResource; +use App\Http\Resources\TicketRepositoryBranchResource; +use App\Http\Resources\TicketRepositoryPullRequestResource; +use App\Http\Resources\TicketResource; +use App\Http\Resources\TicketStatusResource; +use App\Http\Resources\TicketTreeNodeResource; +use App\Http\Resources\TicketTypeResource; +use App\Http\Resources\UserResource; use App\Models\Project; use App\Models\Ticket; use App\Models\TicketLink; @@ -25,7 +37,6 @@ use Inertia\Response; class ProjectTicketController extends Controller { public function __construct( - private readonly BuildProjectTicketUpdateAttributes $buildProjectTicketUpdateAttributes, private readonly BuildSuggestedTicketBranchName $buildSuggestedTicketBranchName, private readonly CreateProjectTicket $createProjectTicket, private readonly PermissionService $permissionService, @@ -54,17 +65,21 @@ class ProjectTicketController extends Controller return Inertia::render('projects/TicketCreate', [ 'defaultTicketStatusId' => $defaultTicketStatus?->id, - 'project' => $project, + 'project' => ProjectResource::make($project)->resolve(), 'source' => $source, 'status' => session('status'), - 'ticketPriorities' => $project->ticketPriorities()->get(), - 'ticketStatusOptions' => $project->ticketStatuses() - ->with('boardColumn') - ->get(), - 'ticketTypes' => $project->ticketTypes()->get(), - 'users' => User::query() - ->orderBy('name') - ->get(), + 'ticketPriorities' => TicketPriorityResource::collection($project->ticketPriorities()->get())->resolve(), + 'ticketStatusOptions' => TicketStatusResource::collection( + $project->ticketStatuses() + ->with('boardColumn') + ->get(), + )->resolve(), + 'ticketTypes' => TicketTypeResource::collection($project->ticketTypes()->get())->resolve(), + 'users' => UserResource::collection( + User::query() + ->orderBy('name') + ->get(), + )->resolve(), ]); } @@ -118,50 +133,65 @@ class ProjectTicketController extends Controller return Inertia::render('projects/Ticket', [ 'canEditTicket' => $canEditTicket, 'canManageProject' => $canManageProject, - 'childTicketTree' => $this->buildChildTicketTree($ticket), - 'linkedTickets' => $this->linkedTickets($ticket), - 'parentTicketOptions' => Ticket::query() - ->where('project_id', $project->id) - ->whereKeyNot($ticket->id) - ->whereHas('ticketType', fn ($query) => $query->where('is_parent', true)) - ->orderByDesc('sequence_number') - ->get() - ->map(fn (Ticket $parentTicket): array => [ - 'id' => $parentTicket->id, - 'ticket_key' => $parentTicket->ticket_key, - 'title' => $parentTicket->title, - ]), - 'project' => $project, - 'repositoryBranches' => $this->resolveTicketBranchMatches->handle( - $user, - $project, - $ticket, - ), - 'repositoryPullRequests' => $this->resolveTicketPullRequestMatches->handle( - $user, - $project, - $ticket, - ), + 'childTicketTree' => TicketTreeNodeResource::collection( + $ticket->childTickets() + ->with('ticketType') + ->get(), + )->resolve(), + 'linkedTickets' => TicketLinkResource::collection( + TicketLink::query() + ->with(['sourceTicket', 'targetTicket', 'ticketLinkType']) + ->where(function ($query) use ($ticket): void { + $query + ->where('source_ticket_id', $ticket->id) + ->orWhere('target_ticket_id', $ticket->id); + }) + ->orderBy('id') + ->get() + ->each(fn (TicketLink $ticketLink) => $ticketLink->setAttribute('current_ticket_id', $ticket->id)), + )->resolve(), + 'parentTicketOptions' => TicketReferenceResource::collection( + Ticket::query() + ->where('project_id', $project->id) + ->whereKeyNot($ticket->id) + ->whereHas('ticketType', fn ($query) => $query->where('is_parent', true)) + ->orderByDesc('sequence_number') + ->get(), + )->resolve(), + 'project' => ProjectResource::make($project)->resolve(), + 'repositoryBranches' => TicketRepositoryBranchResource::collection( + $this->resolveTicketBranchMatches->handle( + $user, + $project, + $ticket, + ), + )->resolve(), + 'repositoryPullRequests' => TicketRepositoryPullRequestResource::collection( + $this->resolveTicketPullRequestMatches->handle( + $user, + $project, + $ticket, + ), + )->resolve(), 'status' => session('status'), 'suggestedBranchName' => $this->buildSuggestedTicketBranchName->handle($ticket), - 'ticket' => $displayTicket, - 'ticketFieldDefinitions' => $project->ticketFieldDefinitions()->get(), - 'ticketLinkOptions' => Ticket::query() - ->where('project_id', $project->id) - ->whereKeyNot($ticket->id) - ->orderByDesc('sequence_number') - ->get() - ->map(fn (Ticket $linkedTicket): array => [ - 'id' => $linkedTicket->id, - 'ticket_key' => $linkedTicket->ticket_key, - 'title' => $linkedTicket->title, - ]), - 'ticketLinkTypes' => $project->ticketLinkTypes()->get(), - 'ticketPriorities' => $project->ticketPriorities()->get(), - 'ticketTypes' => $project->ticketTypes()->get(), - 'users' => User::query() - ->orderBy('name') - ->get(), + 'ticket' => TicketResource::make($displayTicket)->resolve(), + 'ticketFieldDefinitions' => TicketFieldDefinitionResource::collection($project->ticketFieldDefinitions()->get())->resolve(), + 'ticketLinkOptions' => TicketReferenceResource::collection( + Ticket::query() + ->where('project_id', $project->id) + ->whereKeyNot($ticket->id) + ->orderByDesc('sequence_number') + ->get(), + )->resolve(), + 'ticketLinkTypes' => TicketLinkTypeResource::collection($project->ticketLinkTypes()->get())->resolve(), + 'ticketPriorities' => TicketPriorityResource::collection($project->ticketPriorities()->get())->resolve(), + 'ticketTypes' => TicketTypeResource::collection($project->ticketTypes()->get())->resolve(), + 'users' => UserResource::collection( + User::query() + ->orderBy('name') + ->get(), + )->resolve(), ]); } @@ -178,14 +208,11 @@ class ProjectTicketController extends Controller ); $validated = $request->validated(); - $fieldValues = Arr::get($validated, 'field_values', []); - $ticketAttributes = $this->buildProjectTicketUpdateAttributes->handle($ticket, $validated); $wasUpdated = $this->updateProjectTicket->handle( $ticket, $user, - $ticketAttributes, - $fieldValues, + $validated, ); $statusMessage = $wasUpdated @@ -195,74 +222,4 @@ class ProjectTicketController extends Controller return to_route('projects.tickets.show', [$project, $ticket]) ->with('status', $statusMessage); } - - /** - * @return array, - * id: int, - * ticket_key: string, - * ticket_type_name: string|null, - * title: string - * }> - */ - private function buildChildTicketTree(Ticket $ticket): array - { - return $ticket->childTickets() - ->with('ticketType') - ->get() - ->map(fn (Ticket $childTicket): array => [ - 'children' => $this->buildChildTicketTree($childTicket), - 'id' => $childTicket->id, - 'ticket_key' => $childTicket->ticket_key, - 'ticket_type_name' => $childTicket->ticketType?->name, - 'title' => $childTicket->title, - ]) - ->all(); - } - - /** - * @return array - */ - private function linkedTickets(Ticket $ticket): array - { - return TicketLink::query() - ->with(['sourceTicket', 'targetTicket', 'ticketLinkType']) - ->where(function ($query) use ($ticket): void { - $query - ->where('source_ticket_id', $ticket->id) - ->orWhere('target_ticket_id', $ticket->id); - }) - ->orderBy('id') - ->get() - ->map(function (TicketLink $ticketLink) use ($ticket): array { - $linkedTicket = $ticketLink->source_ticket_id === $ticket->id - ? $ticketLink->targetTicket - : $ticketLink->sourceTicket; - - return [ - 'created_at' => $ticketLink->created_at?->toISOString(), - 'id' => $ticketLink->id, - 'linked_ticket' => [ - 'id' => $linkedTicket->id, - 'ticket_key' => $linkedTicket->ticket_key, - 'title' => $linkedTicket->title, - ], - 'source_ticket_id' => $ticketLink->source_ticket_id, - 'target_ticket_id' => $ticketLink->target_ticket_id, - 'ticket_link_type' => $ticketLink->ticketLinkType, - 'ticket_link_type_id' => $ticketLink->ticket_link_type_id, - 'updated_at' => $ticketLink->updated_at?->toISOString(), - ]; - }) - ->all(); - } } diff --git a/app/Http/Controllers/ProjectTicketMetadataSettingsController.php b/app/Http/Controllers/ProjectTicketMetadataSettingsController.php index 407a522..d4d7eb0 100644 --- a/app/Http/Controllers/ProjectTicketMetadataSettingsController.php +++ b/app/Http/Controllers/ProjectTicketMetadataSettingsController.php @@ -14,6 +14,12 @@ use App\Http\Requests\UpdateProjectNamedSettingRequest; use App\Http\Requests\UpdateProjectTicketMetadataSettings; use App\Http\Requests\UpdateProjectTicketTypeRequest; use App\Http\Requests\UpdateTicketFieldDefinitionRequest; +use App\Http\Resources\FieldTypeOptionResource; +use App\Http\Resources\ProjectResource; +use App\Http\Resources\TicketFieldDefinitionResource; +use App\Http\Resources\TicketLinkTypeResource; +use App\Http\Resources\TicketPriorityResource; +use App\Http\Resources\TicketTypeResource; use App\Models\Project; use App\Models\Ticket; use App\Models\TicketFieldDefinition; @@ -41,19 +47,14 @@ class ProjectTicketMetadataSettingsController extends Controller $this->abortUnlessCanManage($request, $project); return Inertia::render('projects/settings/Metadata', [ - 'fieldTypeOptions' => collect(TicketFieldDefinition::FIELD_TYPES) - ->map(fn (string $fieldType): array => [ - 'label' => ucfirst($fieldType), - 'value' => $fieldType, - ]) - ->all(), - 'project' => $project, + 'fieldTypeOptions' => FieldTypeOptionResource::collection(collect(TicketFieldDefinition::FIELD_TYPES))->resolve(), + 'project' => ProjectResource::make($project)->resolve(), 'error' => session('error'), 'status' => session('status'), - 'ticketFieldDefinitions' => $project->ticketFieldDefinitions()->get(), - 'ticketLinkTypes' => $project->ticketLinkTypes()->get(), - 'ticketPriorities' => $project->ticketPriorities()->get(), - 'ticketTypes' => $project->ticketTypes()->withCount('tickets')->get(), + 'ticketFieldDefinitions' => TicketFieldDefinitionResource::collection($project->ticketFieldDefinitions()->get())->resolve(), + 'ticketLinkTypes' => TicketLinkTypeResource::collection($project->ticketLinkTypes()->get())->resolve(), + 'ticketPriorities' => TicketPriorityResource::collection($project->ticketPriorities()->get())->resolve(), + 'ticketTypes' => TicketTypeResource::collection($project->ticketTypes()->withCount('tickets')->get())->resolve(), ]); } diff --git a/app/Http/Controllers/Settings/GitConnectionController.php b/app/Http/Controllers/Settings/GitConnectionController.php index a57c7df..7e4d0a2 100644 --- a/app/Http/Controllers/Settings/GitConnectionController.php +++ b/app/Http/Controllers/Settings/GitConnectionController.php @@ -7,6 +7,7 @@ use App\Actions\Git\GitProviderManager; use App\Http\Controllers\Controller; use App\Http\Requests\Settings\StoreUserGitConnectionRequest; use App\Http\Requests\Settings\UpdateUserGitConnectionRequest; +use App\Http\Resources\UserGitConnectionResource; use App\Models\UserGitConnection; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; @@ -25,20 +26,7 @@ class GitConnectionController extends Controller { return Inertia::render('settings/GitConnections', [ - 'gitConnections' => $request->user()->gitConnections - ->map(fn (UserGitConnection $gitConnection): array => [ - 'account_login' => $gitConnection->account_login, - 'account_name' => $gitConnection->account_name, - 'base_url' => $gitConnection->base_url, - 'created_at' => $gitConnection->created_at?->toISOString(), - 'id' => $gitConnection->id, - 'provider' => $gitConnection->provider->value, - 'provider_label' => $gitConnection->provider->label(), - 'provider_user_id' => $gitConnection->provider_user_id, - 'updated_at' => $gitConnection->updated_at?->toISOString(), - 'user_id' => $gitConnection->user_id, - ]) - ->values(), + 'gitConnections' => UserGitConnectionResource::collection($request->user()->gitConnections)->resolve(), 'status' => $request->session()->get('status'), ]); } diff --git a/app/Http/Controllers/TeamController.php b/app/Http/Controllers/TeamController.php index 21424cb..fac61f5 100644 --- a/app/Http/Controllers/TeamController.php +++ b/app/Http/Controllers/TeamController.php @@ -7,6 +7,9 @@ use App\Enums\Permission; use App\Http\Requests\StoreTeamRequest; use App\Http\Requests\SyncTeamUserPermissionsRequest; use App\Http\Requests\UpdateTeamMembersRequest; +use App\Http\Resources\PermissionOptionResource; +use App\Http\Resources\TeamResource; +use App\Http\Resources\UserResource; use App\Models\Team; use App\Models\User; use Illuminate\Http\RedirectResponse; @@ -37,25 +40,21 @@ class TeamController extends Controller return Inertia::render('teams/Index', [ 'canCreateTeams' => $canCreateTeams, - 'permissionOptions' => $this->permissionService->permissionOptions(User::class, Team::class), + 'permissionOptions' => PermissionOptionResource::collection( + $this->permissionService->permissionOptions(User::class, Team::class), + )->resolve(), 'status' => session('status'), 'teamPermissions' => $teams ->mapWithKeys(fn (Team $team): array => [ $team->id => $this->permissionService->assignmentMap(User::class, $team), ]) ->all(), - 'teams' => $teams - ->map(fn (Team $team): array => [ - 'id' => $team->id, - 'name' => $team->name, - 'created_at' => $team->created_at?->toISOString(), - 'updated_at' => $team->updated_at?->toISOString(), - 'user_ids' => $team->users->modelKeys(), - 'users' => $team->users->values(), - ]), - 'users' => User::query() - ->orderBy('name') - ->get(), + 'teams' => TeamResource::collection($teams)->resolve(), + 'users' => UserResource::collection( + User::query() + ->orderBy('name') + ->get(), + )->resolve(), ]); } diff --git a/app/Http/Resources/AvailableGitRepositorySourceResource.php b/app/Http/Resources/AvailableGitRepositorySourceResource.php new file mode 100644 index 0000000..00a1e4f --- /dev/null +++ b/app/Http/Resources/AvailableGitRepositorySourceResource.php @@ -0,0 +1,31 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'user_git_connection_id' => Arr::get($this->resource, 'user_git_connection_id'), + 'provider' => Arr::get($this->resource, 'provider'), + 'provider_label' => Arr::get($this->resource, 'provider_label'), + 'base_url' => Arr::get($this->resource, 'base_url'), + 'account_login' => Arr::get($this->resource, 'account_login'), + 'account_name' => Arr::get($this->resource, 'account_name'), + 'error' => Arr::get($this->resource, 'error'), + 'repositories' => GitRepositoryOptionResource::collection( + collect(Arr::get($this->resource, 'repositories', [])), + )->resolve(), + ]; + } +} diff --git a/app/Http/Resources/BoardColumnResource.php b/app/Http/Resources/BoardColumnResource.php new file mode 100644 index 0000000..7fb45c0 --- /dev/null +++ b/app/Http/Resources/BoardColumnResource.php @@ -0,0 +1,34 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'id' => $this->id, + 'name' => $this->name, + 'position' => $this->position, + 'project_id' => $this->project_id, + 'ticket_statuses' => $this->whenLoaded( + 'ticketStatuses', + fn (): array => TicketStatusResource::collection($this->ticketStatuses)->resolve(), + ), + 'tickets' => $this->whenLoaded( + 'tickets', + fn (): array => TicketResource::collection($this->tickets)->resolve(), + ), + 'created_at' => $this->created_at?->toISOString(), + 'updated_at' => $this->updated_at?->toISOString(), + ]; + } +} diff --git a/app/Http/Resources/FieldTypeOptionResource.php b/app/Http/Resources/FieldTypeOptionResource.php new file mode 100644 index 0000000..14db90c --- /dev/null +++ b/app/Http/Resources/FieldTypeOptionResource.php @@ -0,0 +1,29 @@ + + */ + public function toArray(Request $request): array + { + $fieldType = is_string($this->resource) + ? $this->resource + : Arr::get($this->resource, 'value'); + + return [ + 'label' => is_string($this->resource) + ? ucfirst($fieldType) + : Arr::get($this->resource, 'label'), + 'value' => $fieldType, + ]; + } +} diff --git a/app/Http/Resources/GitRepositoryOptionResource.php b/app/Http/Resources/GitRepositoryOptionResource.php new file mode 100644 index 0000000..6cfb487 --- /dev/null +++ b/app/Http/Resources/GitRepositoryOptionResource.php @@ -0,0 +1,27 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'provider_repository_id' => Arr::get($this->resource, 'provider_repository_id'), + 'owner_name' => Arr::get($this->resource, 'owner_name'), + 'repository_name' => Arr::get($this->resource, 'repository_name'), + 'full_name' => Arr::get($this->resource, 'full_name'), + 'repository_url' => Arr::get($this->resource, 'repository_url'), + 'visibility_label' => Arr::get($this->resource, 'visibility_label'), + ]; + } +} diff --git a/app/Http/Resources/PermissionOptionResource.php b/app/Http/Resources/PermissionOptionResource.php new file mode 100644 index 0000000..c29bab3 --- /dev/null +++ b/app/Http/Resources/PermissionOptionResource.php @@ -0,0 +1,23 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'label' => Arr::get($this->resource, 'label'), + 'value' => Arr::get($this->resource, 'value'), + ]; + } +} diff --git a/app/Http/Resources/ProjectResource.php b/app/Http/Resources/ProjectResource.php new file mode 100644 index 0000000..bc34640 --- /dev/null +++ b/app/Http/Resources/ProjectResource.php @@ -0,0 +1,27 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'id' => $this->id, + 'name' => $this->name, + 'key' => $this->key, + 'show_author' => $this->show_author, + 'show_story_points' => $this->show_story_points, + 'created_at' => $this->created_at?->toISOString(), + 'updated_at' => $this->updated_at?->toISOString(), + ]; + } +} diff --git a/app/Http/Resources/RepositoryConnectionResource.php b/app/Http/Resources/RepositoryConnectionResource.php new file mode 100644 index 0000000..1c74c39 --- /dev/null +++ b/app/Http/Resources/RepositoryConnectionResource.php @@ -0,0 +1,51 @@ + + */ + public function toArray(Request $request): array + { + $provider = $this->resource instanceof RepositoryConnection + ? $this->provider + : null; + + return [ + 'id' => $this->resource instanceof RepositoryConnection ? $this->id : Arr::get($this->resource, 'id'), + 'project_id' => $this->resource instanceof RepositoryConnection ? $this->project_id : Arr::get($this->resource, 'project_id'), + 'provider' => $provider?->value ?? Arr::get($this->resource, 'provider'), + 'provider_label' => $provider?->label() ?? Arr::get($this->resource, 'provider_label'), + 'base_url' => $this->resource instanceof RepositoryConnection ? $this->base_url : Arr::get($this->resource, 'base_url'), + 'provider_repository_id' => $this->resource instanceof RepositoryConnection + ? $this->provider_repository_id + : Arr::get($this->resource, 'provider_repository_id'), + 'owner_name' => $this->resource instanceof RepositoryConnection ? $this->owner_name : Arr::get($this->resource, 'owner_name'), + 'repository_name' => $this->resource instanceof RepositoryConnection + ? $this->repository_name + : Arr::get($this->resource, 'repository_name'), + 'repository_full_name' => $this->resource instanceof RepositoryConnection + ? $this->repository_full_name + : Arr::get($this->resource, 'repository_full_name'), + 'repository_url' => $this->resource instanceof RepositoryConnection ? $this->repository_url : Arr::get($this->resource, 'repository_url'), + 'has_personal_access' => $this->resource instanceof RepositoryConnection + ? (bool) ($this->resource->getAttribute('has_personal_access') ?? false) + : (bool) Arr::get($this->resource, 'has_personal_access', false), + 'created_at' => $this->resource instanceof RepositoryConnection + ? $this->created_at?->toISOString() + : Arr::get($this->resource, 'created_at'), + 'updated_at' => $this->resource instanceof RepositoryConnection + ? $this->updated_at?->toISOString() + : Arr::get($this->resource, 'updated_at'), + ]; + } +} diff --git a/app/Http/Resources/TeamResource.php b/app/Http/Resources/TeamResource.php new file mode 100644 index 0000000..b7a2d39 --- /dev/null +++ b/app/Http/Resources/TeamResource.php @@ -0,0 +1,26 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'id' => $this->id, + 'name' => $this->name, + 'user_ids' => $this->whenLoaded('users', fn (): array => $this->users->modelKeys()), + 'users' => $this->whenLoaded('users', fn (): array => UserResource::collection($this->users)->resolve()), + 'created_at' => $this->created_at?->toISOString(), + 'updated_at' => $this->updated_at?->toISOString(), + ]; + } +} diff --git a/app/Http/Resources/TicketBranchResource.php b/app/Http/Resources/TicketBranchResource.php new file mode 100644 index 0000000..6a59e52 --- /dev/null +++ b/app/Http/Resources/TicketBranchResource.php @@ -0,0 +1,22 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'name' => Arr::get($this->resource, 'name'), + ]; + } +} diff --git a/app/Http/Resources/TicketFieldDefinitionResource.php b/app/Http/Resources/TicketFieldDefinitionResource.php new file mode 100644 index 0000000..ed8596f --- /dev/null +++ b/app/Http/Resources/TicketFieldDefinitionResource.php @@ -0,0 +1,27 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'id' => $this->id, + 'field_type' => $this->field_type, + 'label' => $this->label, + 'position' => $this->position, + 'project_id' => $this->project_id, + 'created_at' => $this->created_at?->toISOString(), + 'updated_at' => $this->updated_at?->toISOString(), + ]; + } +} diff --git a/app/Http/Resources/TicketFieldValueResource.php b/app/Http/Resources/TicketFieldValueResource.php new file mode 100644 index 0000000..193c80a --- /dev/null +++ b/app/Http/Resources/TicketFieldValueResource.php @@ -0,0 +1,39 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'id' => $this->id, + 'ticket_field_definition_id' => $this->ticket_field_definition_id, + 'ticket_field_definition' => $this->whenLoaded( + 'ticketFieldDefinition', + fn (): ?array => $this->ticketFieldDefinition === null + ? null + : TicketFieldDefinitionResource::make($this->ticketFieldDefinition)->resolve(), + ), + 'ticket_id' => $this->ticket_id, + 'user_value' => $this->whenLoaded( + 'userValue', + fn (): ?array => $this->userValue === null + ? null + : UserResource::make($this->userValue)->resolve(), + ), + 'value_number' => $this->value_number, + 'value_user_id' => $this->value_user_id, + 'created_at' => $this->created_at?->toISOString(), + 'updated_at' => $this->updated_at?->toISOString(), + ]; + } +} diff --git a/app/Http/Resources/TicketLinkResource.php b/app/Http/Resources/TicketLinkResource.php new file mode 100644 index 0000000..15c2630 --- /dev/null +++ b/app/Http/Resources/TicketLinkResource.php @@ -0,0 +1,48 @@ + + */ + public function toArray(Request $request): array + { + if ($this->resource instanceof TicketLink) { + $currentTicketId = (int) ($this->resource->getAttribute('current_ticket_id') ?? 0); + $linkedTicket = $this->source_ticket_id === $currentTicketId + ? $this->targetTicket + : $this->sourceTicket; + + return [ + 'id' => $this->id, + 'linked_ticket' => TicketReferenceResource::make($linkedTicket)->resolve(), + 'source_ticket_id' => $this->source_ticket_id, + 'target_ticket_id' => $this->target_ticket_id, + 'ticket_link_type' => TicketLinkTypeResource::make($this->ticketLinkType)->resolve(), + 'ticket_link_type_id' => $this->ticket_link_type_id, + 'created_at' => $this->created_at?->toISOString(), + 'updated_at' => $this->updated_at?->toISOString(), + ]; + } + + return [ + 'id' => Arr::get($this->resource, 'id'), + 'linked_ticket' => TicketReferenceResource::make(Arr::get($this->resource, 'linked_ticket'))->resolve(), + 'source_ticket_id' => Arr::get($this->resource, 'source_ticket_id'), + 'target_ticket_id' => Arr::get($this->resource, 'target_ticket_id'), + 'ticket_link_type' => TicketLinkTypeResource::make(Arr::get($this->resource, 'ticket_link_type'))->resolve(), + 'ticket_link_type_id' => Arr::get($this->resource, 'ticket_link_type_id'), + 'created_at' => Arr::get($this->resource, 'created_at'), + 'updated_at' => Arr::get($this->resource, 'updated_at'), + ]; + } +} diff --git a/app/Http/Resources/TicketLinkTypeResource.php b/app/Http/Resources/TicketLinkTypeResource.php new file mode 100644 index 0000000..4411b36 --- /dev/null +++ b/app/Http/Resources/TicketLinkTypeResource.php @@ -0,0 +1,26 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'id' => $this->id, + 'label' => $this->label, + 'position' => $this->position, + 'project_id' => $this->project_id, + 'created_at' => $this->created_at?->toISOString(), + 'updated_at' => $this->updated_at?->toISOString(), + ]; + } +} diff --git a/app/Http/Resources/TicketPriorityResource.php b/app/Http/Resources/TicketPriorityResource.php new file mode 100644 index 0000000..adb160c --- /dev/null +++ b/app/Http/Resources/TicketPriorityResource.php @@ -0,0 +1,26 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'id' => $this->id, + 'name' => $this->name, + 'position' => $this->position, + 'project_id' => $this->project_id, + 'created_at' => $this->created_at?->toISOString(), + 'updated_at' => $this->updated_at?->toISOString(), + ]; + } +} diff --git a/app/Http/Resources/TicketPullRequestResource.php b/app/Http/Resources/TicketPullRequestResource.php new file mode 100644 index 0000000..29e7f3c --- /dev/null +++ b/app/Http/Resources/TicketPullRequestResource.php @@ -0,0 +1,26 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'html_url' => Arr::get($this->resource, 'html_url'), + 'number' => Arr::get($this->resource, 'number'), + 'source_branch' => Arr::get($this->resource, 'source_branch'), + 'status' => Arr::get($this->resource, 'status'), + 'title' => Arr::get($this->resource, 'title'), + ]; + } +} diff --git a/app/Http/Resources/TicketReferenceResource.php b/app/Http/Resources/TicketReferenceResource.php new file mode 100644 index 0000000..3c820ff --- /dev/null +++ b/app/Http/Resources/TicketReferenceResource.php @@ -0,0 +1,25 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'id' => $this->resource instanceof Ticket ? $this->id : Arr::get($this->resource, 'id'), + 'ticket_key' => $this->resource instanceof Ticket ? $this->ticket_key : Arr::get($this->resource, 'ticket_key'), + 'title' => $this->resource instanceof Ticket ? $this->title : Arr::get($this->resource, 'title'), + ]; + } +} diff --git a/app/Http/Resources/TicketRepositoryBranchResource.php b/app/Http/Resources/TicketRepositoryBranchResource.php new file mode 100644 index 0000000..631886b --- /dev/null +++ b/app/Http/Resources/TicketRepositoryBranchResource.php @@ -0,0 +1,26 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'branches' => TicketBranchResource::collection(collect(Arr::get($this->resource, 'branches', [])))->resolve(), + 'error' => Arr::get($this->resource, 'error'), + 'repository_connection' => RepositoryConnectionResource::make( + Arr::get($this->resource, 'repository_connection'), + )->resolve(), + ]; + } +} diff --git a/app/Http/Resources/TicketRepositoryPullRequestResource.php b/app/Http/Resources/TicketRepositoryPullRequestResource.php new file mode 100644 index 0000000..9ae9948 --- /dev/null +++ b/app/Http/Resources/TicketRepositoryPullRequestResource.php @@ -0,0 +1,28 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'error' => Arr::get($this->resource, 'error'), + 'pull_requests' => TicketPullRequestResource::collection( + collect(Arr::get($this->resource, 'pull_requests', [])), + )->resolve(), + 'repository_connection' => RepositoryConnectionResource::make( + Arr::get($this->resource, 'repository_connection'), + )->resolve(), + ]; + } +} diff --git a/app/Http/Resources/TicketResource.php b/app/Http/Resources/TicketResource.php new file mode 100644 index 0000000..ff7be1b --- /dev/null +++ b/app/Http/Resources/TicketResource.php @@ -0,0 +1,76 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'id' => $this->id, + 'acceptance_criteria' => $this->acceptance_criteria, + 'assignee' => $this->whenLoaded( + 'assignee', + fn (): ?array => $this->assignee === null + ? null + : UserResource::make($this->assignee)->resolve(), + ), + 'assignee_user_id' => $this->assignee_user_id, + 'author' => $this->whenLoaded( + 'author', + fn (): ?array => $this->author === null + ? null + : UserResource::make($this->author)->resolve(), + ), + 'author_user_id' => $this->author_user_id, + 'blocked_comment' => $this->blocked_comment, + 'description' => $this->description, + 'field_values' => $this->whenLoaded( + 'fieldValues', + fn (): array => TicketFieldValueResource::collection($this->fieldValues)->resolve(), + ), + 'is_blocked' => $this->is_blocked, + 'parent_ticket_id' => $this->parent_ticket_id, + 'project_id' => $this->project_id, + 'sequence_number' => $this->sequence_number, + 'story_points' => $this->story_points, + 'ticket_key' => $this->ticket_key, + 'ticket_priority' => $this->whenLoaded( + 'ticketPriority', + fn (): ?array => $this->ticketPriority === null + ? null + : TicketPriorityResource::make($this->ticketPriority)->resolve(), + ), + 'ticket_priority_id' => $this->ticket_priority_id, + 'ticket_status' => $this->whenLoaded( + 'ticketStatus', + fn (): ?array => $this->ticketStatus === null + ? null + : TicketStatusResource::make($this->ticketStatus)->resolve(), + ), + 'ticket_status_id' => $this->ticket_status_id, + 'ticket_type' => $this->whenLoaded( + 'ticketType', + fn (): ?array => $this->ticketType === null + ? null + : TicketTypeResource::make($this->ticketType)->resolve(), + ), + 'ticket_type_id' => $this->ticket_type_id, + 'title' => $this->title, + 'worklog_entries' => $this->whenLoaded( + 'worklogEntries', + fn (): array => TicketWorklogEntryResource::collection($this->worklogEntries)->resolve(), + ), + 'created_at' => $this->created_at?->toISOString(), + 'updated_at' => $this->updated_at?->toISOString(), + ]; + } +} diff --git a/app/Http/Resources/TicketStatusResource.php b/app/Http/Resources/TicketStatusResource.php new file mode 100644 index 0000000..f4f27d1 --- /dev/null +++ b/app/Http/Resources/TicketStatusResource.php @@ -0,0 +1,38 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'id' => $this->id, + 'allowed_target_statuses' => $this->whenLoaded( + 'allowedTargetStatuses', + fn (): array => TicketStatusResource::collection($this->allowedTargetStatuses)->resolve(), + ), + 'board_column' => $this->whenLoaded( + 'boardColumn', + fn (): ?array => $this->boardColumn === null + ? null + : BoardColumnResource::make($this->boardColumn)->resolve(), + ), + 'board_column_id' => $this->board_column_id, + 'name' => $this->name, + 'position' => $this->position, + 'project_id' => $this->project_id, + 'slug' => $this->slug, + 'created_at' => $this->created_at?->toISOString(), + 'updated_at' => $this->updated_at?->toISOString(), + ]; + } +} diff --git a/app/Http/Resources/TicketTreeNodeResource.php b/app/Http/Resources/TicketTreeNodeResource.php new file mode 100644 index 0000000..a888deb --- /dev/null +++ b/app/Http/Resources/TicketTreeNodeResource.php @@ -0,0 +1,41 @@ + + */ + public function toArray(Request $request): array + { + if ($this->resource instanceof Ticket) { + return [ + 'children' => TicketTreeNodeResource::collection( + $this->resource->childTickets() + ->with('ticketType') + ->get(), + )->resolve(), + 'id' => $this->id, + 'ticket_key' => $this->ticket_key, + 'ticket_type_name' => $this->ticketType?->name, + 'title' => $this->title, + ]; + } + + return [ + 'children' => TicketTreeNodeResource::collection(collect(Arr::get($this->resource, 'children', [])))->resolve(), + 'id' => Arr::get($this->resource, 'id'), + 'ticket_key' => Arr::get($this->resource, 'ticket_key'), + 'ticket_type_name' => Arr::get($this->resource, 'ticket_type_name'), + 'title' => Arr::get($this->resource, 'title'), + ]; + } +} diff --git a/app/Http/Resources/TicketTypeResource.php b/app/Http/Resources/TicketTypeResource.php new file mode 100644 index 0000000..ab2fa3b --- /dev/null +++ b/app/Http/Resources/TicketTypeResource.php @@ -0,0 +1,28 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'id' => $this->id, + 'is_parent' => $this->is_parent, + 'name' => $this->name, + 'position' => $this->position, + 'project_id' => $this->project_id, + 'tickets_count' => $this->when(isset($this->tickets_count), (int) $this->tickets_count), + 'created_at' => $this->created_at?->toISOString(), + 'updated_at' => $this->updated_at?->toISOString(), + ]; + } +} diff --git a/app/Http/Resources/TicketWorklogEntryResource.php b/app/Http/Resources/TicketWorklogEntryResource.php new file mode 100644 index 0000000..d3e67a2 --- /dev/null +++ b/app/Http/Resources/TicketWorklogEntryResource.php @@ -0,0 +1,34 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'id' => $this->id, + 'actor' => $this->whenLoaded( + 'actor', + fn (): ?array => $this->actor === null + ? null + : UserResource::make($this->actor)->resolve(), + ), + 'actor_id' => $this->actor_id, + 'changes' => $this->changes, + 'created_at' => $this->created_at?->toISOString(), + 'entry_type' => $this->entry_type?->value, + 'summary' => $this->summary, + 'ticket_id' => $this->ticket_id, + 'updated_at' => $this->updated_at?->toISOString(), + ]; + } +} diff --git a/app/Http/Resources/UserGitConnectionResource.php b/app/Http/Resources/UserGitConnectionResource.php new file mode 100644 index 0000000..2e43274 --- /dev/null +++ b/app/Http/Resources/UserGitConnectionResource.php @@ -0,0 +1,30 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'id' => $this->id, + 'user_id' => $this->user_id, + 'provider' => $this->provider->value, + 'provider_label' => $this->provider->label(), + 'base_url' => $this->base_url, + 'provider_user_id' => $this->provider_user_id, + 'account_login' => $this->account_login, + 'account_name' => $this->account_name, + 'created_at' => $this->created_at?->toISOString(), + 'updated_at' => $this->updated_at?->toISOString(), + ]; + } +} diff --git a/app/Http/Resources/UserResource.php b/app/Http/Resources/UserResource.php new file mode 100644 index 0000000..317fc44 --- /dev/null +++ b/app/Http/Resources/UserResource.php @@ -0,0 +1,26 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'id' => $this->id, + 'name' => $this->name, + 'email' => $this->email, + 'email_verified_at' => $this->email_verified_at?->toISOString(), + 'created_at' => $this->created_at?->toISOString(), + 'updated_at' => $this->updated_at?->toISOString(), + ]; + } +}