Refactor Inertia responses to use resources
linter / quality (pull_request) Successful in 1m15s
tests / ci (8.4) (pull_request) Successful in 1m29s

This commit is contained in:
Eric Peters
2026-04-15 18:09:08 +02:00
parent 5a7972df7c
commit 017cff5437
38 changed files with 1125 additions and 382 deletions
@@ -1,49 +0,0 @@
<?php
namespace App\Actions\Projects;
use App\Models\Project;
use App\Models\Ticket;
use App\Models\User;
class BuildProjectBacklogPageProps
{
/**
* @return array<string, mixed>
*/
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(),
];
}
}
@@ -1,37 +0,0 @@
<?php
namespace App\Actions\Projects;
use App\Models\Project;
class BuildProjectBoardPageProps
{
/**
* @return array<string, mixed>
*/
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,
];
}
}
@@ -1,48 +0,0 @@
<?php
namespace App\Actions\Tickets;
use App\Models\Ticket;
use Illuminate\Support\Arr;
class BuildProjectTicketUpdateAttributes
{
/**
* @param array<string, mixed> $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'),
];
}
}
+65 -11
View File
@@ -7,6 +7,7 @@ use App\Models\Ticket;
use App\Models\TicketFieldDefinition; use App\Models\TicketFieldDefinition;
use App\Models\TicketFieldValue; use App\Models\TicketFieldValue;
use App\Models\User; use App\Models\User;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
@@ -20,29 +21,31 @@ class UpdateProjectTicket
/** /**
* @param array{ * @param array{
* acceptance_criteria: string|null, * acceptance_criteria: string|null,
* assignee_user_id: int|null, * assignee_user_id?: int|null,
* author_user_id: int|null, * author_user_id?: int|null,
* blocked_comment: string|null, * blocked_comment: string|null,
* description: string|null, * description: string|null,
* field_values?: array<int, array{
* ticket_field_definition_id: int,
* value_number?: float|int|string|null,
* value_user_id?: int|string|null
* }>,
* is_blocked: bool, * is_blocked: bool,
* story_points: int|null, * story_points?: int|null,
* ticket_priority_id: int, * ticket_priority_id: int,
* ticket_type_id: int, * ticket_type_id: int,
* parent_ticket_id: int|null, * parent_ticket_id: int|null,
* title: string * title: string
* } $ticketAttributes * } $validated
* @param array<int, array{
* ticket_field_definition_id: int,
* value_number?: float|int|string|null,
* value_user_id?: int|string|null
* }> $fieldValueAttributes
*/ */
public function handle( public function handle(
Ticket $ticket, Ticket $ticket,
User $actor, User $actor,
array $ticketAttributes, array $validated,
array $fieldValueAttributes,
): bool { ): bool {
$fieldValueAttributes = Arr::get($validated, 'field_values', []);
$ticketAttributes = $this->buildTicketAttributes($ticket, $validated);
return DB::transaction(function () use ($actor, $fieldValueAttributes, $ticket, $ticketAttributes): bool { return DB::transaction(function () use ($actor, $fieldValueAttributes, $ticket, $ticketAttributes): bool {
if (! $ticketAttributes['is_blocked']) { if (! $ticketAttributes['is_blocked']) {
$ticketAttributes['blocked_comment'] = null; $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<string, mixed> $changes * @param array<string, mixed> $changes
* @param array<int, string> $summaryLabels * @param array<int, string> $summaryLabels
@@ -5,6 +5,8 @@ namespace App\Http\Controllers\Admin;
use App\Actions\Permissions\PermissionService; use App\Actions\Permissions\PermissionService;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Http\Requests\Admin\SyncUserGlobalPermissionsRequest; use App\Http\Requests\Admin\SyncUserGlobalPermissionsRequest;
use App\Http\Resources\PermissionOptionResource;
use App\Http\Resources\UserResource;
use App\Models\User; use App\Models\User;
use Illuminate\Http\RedirectResponse; use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Arr; use Illuminate\Support\Arr;
@@ -21,11 +23,15 @@ class UserManagementController extends Controller
{ {
return Inertia::render('admin/users/Index', [ return Inertia::render('admin/users/Index', [
'directPermissions' => $this->permissionService->assignmentMap(User::class), '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'), 'status' => session('status'),
'users' => User::query() 'users' => UserResource::collection(
->orderBy('name') User::query()
->get(), ->orderBy('name')
->get(),
)->resolve(),
]); ]);
} }
@@ -3,11 +3,12 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
use App\Actions\Permissions\PermissionService; use App\Actions\Permissions\PermissionService;
use App\Actions\Projects\BuildProjectBoardPageProps;
use App\Actions\Tickets\CreateTicketWorklogEntry; use App\Actions\Tickets\CreateTicketWorklogEntry;
use App\Enums\Permission; use App\Enums\Permission;
use App\Enums\TicketWorklogEntryType; use App\Enums\TicketWorklogEntryType;
use App\Http\Requests\MoveProjectTicketRequest; use App\Http\Requests\MoveProjectTicketRequest;
use App\Http\Resources\BoardColumnResource;
use App\Http\Resources\ProjectResource;
use App\Models\Project; use App\Models\Project;
use App\Models\Ticket; use App\Models\Ticket;
use App\Models\TicketStatus; use App\Models\TicketStatus;
@@ -20,7 +21,6 @@ use Inertia\Response;
class ProjectBoardController extends Controller class ProjectBoardController extends Controller
{ {
public function __construct( public function __construct(
private readonly BuildProjectBoardPageProps $buildProjectBoardPageProps,
private readonly CreateTicketWorklogEntry $createTicketWorklogEntry, private readonly CreateTicketWorklogEntry $createTicketWorklogEntry,
private readonly PermissionService $permissionService, private readonly PermissionService $permissionService,
) {} ) {}
@@ -34,12 +34,28 @@ class ProjectBoardController extends Controller
403, 403,
); );
return Inertia::render('projects/Board', $this->buildProjectBoardPageProps->handle( $canManageTickets = $this->permissionService->allows($user, Permission::ProjectWrite, $project);
$project,
$this->permissionService->allows($user, Permission::ProjectWrite, $project), return Inertia::render('projects/Board', [
$this->permissionService->allows($user, Permission::ProjectWrite, $project), 'boardColumns' => BoardColumnResource::collection(
session('status'), $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( public function move(
@@ -9,6 +9,9 @@ use App\Http\Requests\StoreTicketStatusRequest;
use App\Http\Requests\UpdateBoardColumnRequest; use App\Http\Requests\UpdateBoardColumnRequest;
use App\Http\Requests\UpdateTicketStatusRequest; use App\Http\Requests\UpdateTicketStatusRequest;
use App\Http\Requests\UpdateTicketStatusTransitionsRequest; 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\BoardColumn;
use App\Models\Project; use App\Models\Project;
use App\Models\TicketStatus; use App\Models\TicketStatus;
@@ -35,17 +38,23 @@ class ProjectBoardSettingsController extends Controller
); );
return Inertia::render('projects/settings/Board', [ return Inertia::render('projects/settings/Board', [
'backlogTicketStatuses' => $project->ticketStatuses() 'backlogTicketStatuses' => TicketStatusResource::collection(
->whereNull('board_column_id') $project->ticketStatuses()
->get(), ->whereNull('board_column_id')
'boardColumns' => $project->boardColumns() ->get(),
->with('ticketStatuses') )->resolve(),
->get(), 'boardColumns' => BoardColumnResource::collection(
'project' => $project, $project->boardColumns()
->with('ticketStatuses')
->get(),
)->resolve(),
'project' => ProjectResource::make($project)->resolve(),
'status' => session('status'), 'status' => session('status'),
'ticketStatuses' => $project->ticketStatuses() 'ticketStatuses' => TicketStatusResource::collection(
->with(['allowedTargetStatuses.boardColumn', 'boardColumn']) $project->ticketStatuses()
->get(), ->with(['allowedTargetStatuses.boardColumn', 'boardColumn'])
->get(),
)->resolve(),
]); ]);
} }
+66 -30
View File
@@ -3,14 +3,22 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
use App\Actions\Permissions\PermissionService; use App\Actions\Permissions\PermissionService;
use App\Actions\Projects\BuildProjectBacklogPageProps;
use App\Actions\ProjectTicketMetadata\InitializeProjectTicketMetadata; use App\Actions\ProjectTicketMetadata\InitializeProjectTicketMetadata;
use App\Enums\Permission; use App\Enums\Permission;
use App\Http\Requests\DeleteProjectRequest; use App\Http\Requests\DeleteProjectRequest;
use App\Http\Requests\StoreProjectRequest; use App\Http\Requests\StoreProjectRequest;
use App\Http\Requests\SyncProjectPermissionsRequest; 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\Project;
use App\Models\Team; use App\Models\Team;
use App\Models\Ticket;
use App\Models\User; use App\Models\User;
use Illuminate\Http\RedirectResponse; use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
@@ -21,7 +29,6 @@ use Inertia\Response;
class ProjectController extends Controller class ProjectController extends Controller
{ {
public function __construct( public function __construct(
private readonly BuildProjectBacklogPageProps $buildProjectBacklogPageProps,
private readonly InitializeProjectTicketMetadata $initializeProjectTicketMetadata, private readonly InitializeProjectTicketMetadata $initializeProjectTicketMetadata,
private readonly PermissionService $permissionService, private readonly PermissionService $permissionService,
) {} ) {}
@@ -38,7 +45,7 @@ class ProjectController extends Controller
if ($viewableProjectIds === []) { if ($viewableProjectIds === []) {
return Inertia::render('projects/Index', [ return Inertia::render('projects/Index', [
'projects' => [], 'projects' => ProjectResource::collection(collect())->resolve(),
'status' => session('status'), 'status' => session('status'),
]); ]);
} }
@@ -47,8 +54,7 @@ class ProjectController extends Controller
} }
return Inertia::render('projects/Index', [ return Inertia::render('projects/Index', [
'projects' => $projectQuery 'projects' => ProjectResource::collection($projectQuery->get())->resolve(),
->get(),
'status' => session('status'), 'status' => session('status'),
]); ]);
} }
@@ -83,7 +89,7 @@ class ProjectController extends Controller
return Inertia::render('projects/Show', [ return Inertia::render('projects/Show', [
'canManageAccess' => $this->permissionService->allows($user, Permission::ProjectAdmin, $project), 'canManageAccess' => $this->permissionService->allows($user, Permission::ProjectAdmin, $project),
'project' => $project, 'project' => ProjectResource::make($project)->resolve(),
]); ]);
} }
@@ -96,18 +102,50 @@ class ProjectController extends Controller
403, 403,
); );
return Inertia::render('projects/Backlog', $this->buildProjectBacklogPageProps->handle( $canManageTickets = $this->permissionService->allows($user, Permission::ProjectWrite, $project);
$project,
$this->permissionService->allows($user, Permission::ProjectWrite, $project), return Inertia::render('projects/Backlog', [
$this->permissionService->allows($user, Permission::ProjectWrite, $project), 'backlogTicketStatuses' => TicketStatusResource::collection(
session('status'), $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 public function general(Request $request, Project $project): Response
{ {
return Inertia::render('projects/settings/General', [ return Inertia::render('projects/settings/General', [
'project' => $project, 'project' => ProjectResource::make($project)->resolve(),
'status' => session('status'), 'status' => session('status'),
]); ]);
} }
@@ -122,25 +160,23 @@ class ProjectController extends Controller
return Inertia::render('projects/Access', [ return Inertia::render('projects/Access', [
'directPermissions' => $this->permissionService->assignmentMap(User::class, $project), 'directPermissions' => $this->permissionService->assignmentMap(User::class, $project),
'permissionOptions' => $this->permissionService->permissionOptions(User::class, Project::class), 'permissionOptions' => PermissionOptionResource::collection(
'project' => $project, $this->permissionService->permissionOptions(User::class, Project::class),
)->resolve(),
'project' => ProjectResource::make($project)->resolve(),
'status' => session('status'), 'status' => session('status'),
'teamPermissions' => $this->permissionService->assignmentMap(Team::class, $project), 'teamPermissions' => $this->permissionService->assignmentMap(Team::class, $project),
'teams' => Team::query() 'teams' => TeamResource::collection(
->with('users') Team::query()
->orderBy('name') ->with('users')
->get() ->orderBy('name')
->map(fn (Team $team): array => [ ->get(),
'id' => $team->id, )->resolve(),
'name' => $team->name, 'users' => UserResource::collection(
'created_at' => $team->created_at?->toISOString(), User::query()
'updated_at' => $team->updated_at?->toISOString(), ->orderBy('name')
'user_ids' => $team->users->modelKeys(), ->get(),
'users' => $team->users->values(), )->resolve(),
]),
'users' => User::query()
->orderBy('name')
->get(),
]); ]);
} }
@@ -8,6 +8,9 @@ use App\Actions\Permissions\PermissionService;
use App\Enums\Permission; use App\Enums\Permission;
use App\Http\Requests\StoreRepositoryConnectionRequest; use App\Http\Requests\StoreRepositoryConnectionRequest;
use App\Http\Requests\UpdateRepositoryConnectionRequest; 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\Project;
use App\Models\RepositoryConnection; use App\Models\RepositoryConnection;
use App\Models\User; use App\Models\User;
@@ -36,27 +39,21 @@ class ProjectRepositorySettingsController extends Controller
403, 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', [ return Inertia::render('projects/settings/Repositories', [
'availableRepositorySources' => $this->availableRepositorySources($user), 'availableRepositorySources' => AvailableGitRepositorySourceResource::collection(
'project' => $project, $this->availableRepositorySources($user),
'repositoryConnections' => $project->repositoryConnections() )->resolve(),
->get() 'project' => ProjectResource::make($project)->resolve(),
->map(fn (RepositoryConnection $repositoryConnection): array => [ 'repositoryConnections' => RepositoryConnectionResource::collection($repositoryConnections)->resolve(),
'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(),
'status' => session('status'), 'status' => session('status'),
]); ]);
} }
+84 -127
View File
@@ -6,12 +6,24 @@ use App\Actions\Git\BuildSuggestedTicketBranchName;
use App\Actions\Git\ResolveTicketBranchMatches; use App\Actions\Git\ResolveTicketBranchMatches;
use App\Actions\Git\ResolveTicketPullRequestMatches; use App\Actions\Git\ResolveTicketPullRequestMatches;
use App\Actions\Permissions\PermissionService; use App\Actions\Permissions\PermissionService;
use App\Actions\Tickets\BuildProjectTicketUpdateAttributes;
use App\Actions\Tickets\CreateProjectTicket; use App\Actions\Tickets\CreateProjectTicket;
use App\Actions\Tickets\UpdateProjectTicket; use App\Actions\Tickets\UpdateProjectTicket;
use App\Enums\Permission; use App\Enums\Permission;
use App\Http\Requests\StoreProjectTicketRequest; use App\Http\Requests\StoreProjectTicketRequest;
use App\Http\Requests\UpdateProjectTicketRequest; 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\Project;
use App\Models\Ticket; use App\Models\Ticket;
use App\Models\TicketLink; use App\Models\TicketLink;
@@ -25,7 +37,6 @@ use Inertia\Response;
class ProjectTicketController extends Controller class ProjectTicketController extends Controller
{ {
public function __construct( public function __construct(
private readonly BuildProjectTicketUpdateAttributes $buildProjectTicketUpdateAttributes,
private readonly BuildSuggestedTicketBranchName $buildSuggestedTicketBranchName, private readonly BuildSuggestedTicketBranchName $buildSuggestedTicketBranchName,
private readonly CreateProjectTicket $createProjectTicket, private readonly CreateProjectTicket $createProjectTicket,
private readonly PermissionService $permissionService, private readonly PermissionService $permissionService,
@@ -54,17 +65,21 @@ class ProjectTicketController extends Controller
return Inertia::render('projects/TicketCreate', [ return Inertia::render('projects/TicketCreate', [
'defaultTicketStatusId' => $defaultTicketStatus?->id, 'defaultTicketStatusId' => $defaultTicketStatus?->id,
'project' => $project, 'project' => ProjectResource::make($project)->resolve(),
'source' => $source, 'source' => $source,
'status' => session('status'), 'status' => session('status'),
'ticketPriorities' => $project->ticketPriorities()->get(), 'ticketPriorities' => TicketPriorityResource::collection($project->ticketPriorities()->get())->resolve(),
'ticketStatusOptions' => $project->ticketStatuses() 'ticketStatusOptions' => TicketStatusResource::collection(
->with('boardColumn') $project->ticketStatuses()
->get(), ->with('boardColumn')
'ticketTypes' => $project->ticketTypes()->get(), ->get(),
'users' => User::query() )->resolve(),
->orderBy('name') 'ticketTypes' => TicketTypeResource::collection($project->ticketTypes()->get())->resolve(),
->get(), 'users' => UserResource::collection(
User::query()
->orderBy('name')
->get(),
)->resolve(),
]); ]);
} }
@@ -118,50 +133,65 @@ class ProjectTicketController extends Controller
return Inertia::render('projects/Ticket', [ return Inertia::render('projects/Ticket', [
'canEditTicket' => $canEditTicket, 'canEditTicket' => $canEditTicket,
'canManageProject' => $canManageProject, 'canManageProject' => $canManageProject,
'childTicketTree' => $this->buildChildTicketTree($ticket), 'childTicketTree' => TicketTreeNodeResource::collection(
'linkedTickets' => $this->linkedTickets($ticket), $ticket->childTickets()
'parentTicketOptions' => Ticket::query() ->with('ticketType')
->where('project_id', $project->id) ->get(),
->whereKeyNot($ticket->id) )->resolve(),
->whereHas('ticketType', fn ($query) => $query->where('is_parent', true)) 'linkedTickets' => TicketLinkResource::collection(
->orderByDesc('sequence_number') TicketLink::query()
->get() ->with(['sourceTicket', 'targetTicket', 'ticketLinkType'])
->map(fn (Ticket $parentTicket): array => [ ->where(function ($query) use ($ticket): void {
'id' => $parentTicket->id, $query
'ticket_key' => $parentTicket->ticket_key, ->where('source_ticket_id', $ticket->id)
'title' => $parentTicket->title, ->orWhere('target_ticket_id', $ticket->id);
]), })
'project' => $project, ->orderBy('id')
'repositoryBranches' => $this->resolveTicketBranchMatches->handle( ->get()
$user, ->each(fn (TicketLink $ticketLink) => $ticketLink->setAttribute('current_ticket_id', $ticket->id)),
$project, )->resolve(),
$ticket, 'parentTicketOptions' => TicketReferenceResource::collection(
), Ticket::query()
'repositoryPullRequests' => $this->resolveTicketPullRequestMatches->handle( ->where('project_id', $project->id)
$user, ->whereKeyNot($ticket->id)
$project, ->whereHas('ticketType', fn ($query) => $query->where('is_parent', true))
$ticket, ->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'), 'status' => session('status'),
'suggestedBranchName' => $this->buildSuggestedTicketBranchName->handle($ticket), 'suggestedBranchName' => $this->buildSuggestedTicketBranchName->handle($ticket),
'ticket' => $displayTicket, 'ticket' => TicketResource::make($displayTicket)->resolve(),
'ticketFieldDefinitions' => $project->ticketFieldDefinitions()->get(), 'ticketFieldDefinitions' => TicketFieldDefinitionResource::collection($project->ticketFieldDefinitions()->get())->resolve(),
'ticketLinkOptions' => Ticket::query() 'ticketLinkOptions' => TicketReferenceResource::collection(
->where('project_id', $project->id) Ticket::query()
->whereKeyNot($ticket->id) ->where('project_id', $project->id)
->orderByDesc('sequence_number') ->whereKeyNot($ticket->id)
->get() ->orderByDesc('sequence_number')
->map(fn (Ticket $linkedTicket): array => [ ->get(),
'id' => $linkedTicket->id, )->resolve(),
'ticket_key' => $linkedTicket->ticket_key, 'ticketLinkTypes' => TicketLinkTypeResource::collection($project->ticketLinkTypes()->get())->resolve(),
'title' => $linkedTicket->title, 'ticketPriorities' => TicketPriorityResource::collection($project->ticketPriorities()->get())->resolve(),
]), 'ticketTypes' => TicketTypeResource::collection($project->ticketTypes()->get())->resolve(),
'ticketLinkTypes' => $project->ticketLinkTypes()->get(), 'users' => UserResource::collection(
'ticketPriorities' => $project->ticketPriorities()->get(), User::query()
'ticketTypes' => $project->ticketTypes()->get(), ->orderBy('name')
'users' => User::query() ->get(),
->orderBy('name') )->resolve(),
->get(),
]); ]);
} }
@@ -178,14 +208,11 @@ class ProjectTicketController extends Controller
); );
$validated = $request->validated(); $validated = $request->validated();
$fieldValues = Arr::get($validated, 'field_values', []);
$ticketAttributes = $this->buildProjectTicketUpdateAttributes->handle($ticket, $validated);
$wasUpdated = $this->updateProjectTicket->handle( $wasUpdated = $this->updateProjectTicket->handle(
$ticket, $ticket,
$user, $user,
$ticketAttributes, $validated,
$fieldValues,
); );
$statusMessage = $wasUpdated $statusMessage = $wasUpdated
@@ -195,74 +222,4 @@ class ProjectTicketController extends Controller
return to_route('projects.tickets.show', [$project, $ticket]) return to_route('projects.tickets.show', [$project, $ticket])
->with('status', $statusMessage); ->with('status', $statusMessage);
} }
/**
* @return array<int, array{
* children: array<int, mixed>,
* 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<int, array{
* created_at: string|null,
* id: int,
* linked_ticket: array{id: int, ticket_key: string, title: string},
* source_ticket_id: int,
* target_ticket_id: int,
* ticket_link_type: mixed,
* ticket_link_type_id: int,
* updated_at: string|null
* }>
*/
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();
}
} }
@@ -14,6 +14,12 @@ use App\Http\Requests\UpdateProjectNamedSettingRequest;
use App\Http\Requests\UpdateProjectTicketMetadataSettings; use App\Http\Requests\UpdateProjectTicketMetadataSettings;
use App\Http\Requests\UpdateProjectTicketTypeRequest; use App\Http\Requests\UpdateProjectTicketTypeRequest;
use App\Http\Requests\UpdateTicketFieldDefinitionRequest; 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\Project;
use App\Models\Ticket; use App\Models\Ticket;
use App\Models\TicketFieldDefinition; use App\Models\TicketFieldDefinition;
@@ -41,19 +47,14 @@ class ProjectTicketMetadataSettingsController extends Controller
$this->abortUnlessCanManage($request, $project); $this->abortUnlessCanManage($request, $project);
return Inertia::render('projects/settings/Metadata', [ return Inertia::render('projects/settings/Metadata', [
'fieldTypeOptions' => collect(TicketFieldDefinition::FIELD_TYPES) 'fieldTypeOptions' => FieldTypeOptionResource::collection(collect(TicketFieldDefinition::FIELD_TYPES))->resolve(),
->map(fn (string $fieldType): array => [ 'project' => ProjectResource::make($project)->resolve(),
'label' => ucfirst($fieldType),
'value' => $fieldType,
])
->all(),
'project' => $project,
'error' => session('error'), 'error' => session('error'),
'status' => session('status'), 'status' => session('status'),
'ticketFieldDefinitions' => $project->ticketFieldDefinitions()->get(), 'ticketFieldDefinitions' => TicketFieldDefinitionResource::collection($project->ticketFieldDefinitions()->get())->resolve(),
'ticketLinkTypes' => $project->ticketLinkTypes()->get(), 'ticketLinkTypes' => TicketLinkTypeResource::collection($project->ticketLinkTypes()->get())->resolve(),
'ticketPriorities' => $project->ticketPriorities()->get(), 'ticketPriorities' => TicketPriorityResource::collection($project->ticketPriorities()->get())->resolve(),
'ticketTypes' => $project->ticketTypes()->withCount('tickets')->get(), 'ticketTypes' => TicketTypeResource::collection($project->ticketTypes()->withCount('tickets')->get())->resolve(),
]); ]);
} }
@@ -7,6 +7,7 @@ use App\Actions\Git\GitProviderManager;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Http\Requests\Settings\StoreUserGitConnectionRequest; use App\Http\Requests\Settings\StoreUserGitConnectionRequest;
use App\Http\Requests\Settings\UpdateUserGitConnectionRequest; use App\Http\Requests\Settings\UpdateUserGitConnectionRequest;
use App\Http\Resources\UserGitConnectionResource;
use App\Models\UserGitConnection; use App\Models\UserGitConnection;
use Illuminate\Http\RedirectResponse; use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
@@ -25,20 +26,7 @@ class GitConnectionController extends Controller
{ {
return Inertia::render('settings/GitConnections', [ return Inertia::render('settings/GitConnections', [
'gitConnections' => $request->user()->gitConnections 'gitConnections' => UserGitConnectionResource::collection($request->user()->gitConnections)->resolve(),
->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(),
'status' => $request->session()->get('status'), 'status' => $request->session()->get('status'),
]); ]);
} }
+12 -13
View File
@@ -7,6 +7,9 @@ use App\Enums\Permission;
use App\Http\Requests\StoreTeamRequest; use App\Http\Requests\StoreTeamRequest;
use App\Http\Requests\SyncTeamUserPermissionsRequest; use App\Http\Requests\SyncTeamUserPermissionsRequest;
use App\Http\Requests\UpdateTeamMembersRequest; 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\Team;
use App\Models\User; use App\Models\User;
use Illuminate\Http\RedirectResponse; use Illuminate\Http\RedirectResponse;
@@ -37,25 +40,21 @@ class TeamController extends Controller
return Inertia::render('teams/Index', [ return Inertia::render('teams/Index', [
'canCreateTeams' => $canCreateTeams, 'canCreateTeams' => $canCreateTeams,
'permissionOptions' => $this->permissionService->permissionOptions(User::class, Team::class), 'permissionOptions' => PermissionOptionResource::collection(
$this->permissionService->permissionOptions(User::class, Team::class),
)->resolve(),
'status' => session('status'), 'status' => session('status'),
'teamPermissions' => $teams 'teamPermissions' => $teams
->mapWithKeys(fn (Team $team): array => [ ->mapWithKeys(fn (Team $team): array => [
$team->id => $this->permissionService->assignmentMap(User::class, $team), $team->id => $this->permissionService->assignmentMap(User::class, $team),
]) ])
->all(), ->all(),
'teams' => $teams 'teams' => TeamResource::collection($teams)->resolve(),
->map(fn (Team $team): array => [ 'users' => UserResource::collection(
'id' => $team->id, User::query()
'name' => $team->name, ->orderBy('name')
'created_at' => $team->created_at?->toISOString(), ->get(),
'updated_at' => $team->updated_at?->toISOString(), )->resolve(),
'user_ids' => $team->users->modelKeys(),
'users' => $team->users->values(),
]),
'users' => User::query()
->orderBy('name')
->get(),
]); ]);
} }
@@ -0,0 +1,31 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Support\Arr;
class AvailableGitRepositorySourceResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @return array<string, mixed>
*/
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(),
];
}
}
@@ -0,0 +1,34 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class BoardColumnResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @return array<string, mixed>
*/
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(),
];
}
}
@@ -0,0 +1,29 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Support\Arr;
class FieldTypeOptionResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @return array<string, mixed>
*/
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,
];
}
}
@@ -0,0 +1,27 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Support\Arr;
class GitRepositoryOptionResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @return array<string, mixed>
*/
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'),
];
}
}
@@ -0,0 +1,23 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Support\Arr;
class PermissionOptionResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @return array<string, mixed>
*/
public function toArray(Request $request): array
{
return [
'label' => Arr::get($this->resource, 'label'),
'value' => Arr::get($this->resource, 'value'),
];
}
}
+27
View File
@@ -0,0 +1,27 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class ProjectResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @return array<string, mixed>
*/
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(),
];
}
}
@@ -0,0 +1,51 @@
<?php
namespace App\Http\Resources;
use App\Models\RepositoryConnection;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Support\Arr;
class RepositoryConnectionResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @return array<string, mixed>
*/
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'),
];
}
}
+26
View File
@@ -0,0 +1,26 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class TeamResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @return array<string, mixed>
*/
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(),
];
}
}
@@ -0,0 +1,22 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Support\Arr;
class TicketBranchResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @return array<string, mixed>
*/
public function toArray(Request $request): array
{
return [
'name' => Arr::get($this->resource, 'name'),
];
}
}
@@ -0,0 +1,27 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class TicketFieldDefinitionResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @return array<string, mixed>
*/
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(),
];
}
}
@@ -0,0 +1,39 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class TicketFieldValueResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @return array<string, mixed>
*/
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(),
];
}
}
+48
View File
@@ -0,0 +1,48 @@
<?php
namespace App\Http\Resources;
use App\Models\TicketLink;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Support\Arr;
class TicketLinkResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @return array<string, mixed>
*/
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'),
];
}
}
@@ -0,0 +1,26 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class TicketLinkTypeResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @return array<string, mixed>
*/
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(),
];
}
}
@@ -0,0 +1,26 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class TicketPriorityResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @return array<string, mixed>
*/
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(),
];
}
}
@@ -0,0 +1,26 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Support\Arr;
class TicketPullRequestResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @return array<string, mixed>
*/
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'),
];
}
}
@@ -0,0 +1,25 @@
<?php
namespace App\Http\Resources;
use App\Models\Ticket;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Support\Arr;
class TicketReferenceResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @return array<string, mixed>
*/
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'),
];
}
}
@@ -0,0 +1,26 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Support\Arr;
class TicketRepositoryBranchResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @return array<string, mixed>
*/
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(),
];
}
}
@@ -0,0 +1,28 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Support\Arr;
class TicketRepositoryPullRequestResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @return array<string, mixed>
*/
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(),
];
}
}
+76
View File
@@ -0,0 +1,76 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class TicketResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @return array<string, mixed>
*/
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(),
];
}
}
@@ -0,0 +1,38 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class TicketStatusResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @return array<string, mixed>
*/
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(),
];
}
}
@@ -0,0 +1,41 @@
<?php
namespace App\Http\Resources;
use App\Models\Ticket;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Support\Arr;
class TicketTreeNodeResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @return array<string, mixed>
*/
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'),
];
}
}
+28
View File
@@ -0,0 +1,28 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class TicketTypeResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @return array<string, mixed>
*/
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(),
];
}
}
@@ -0,0 +1,34 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class TicketWorklogEntryResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @return array<string, mixed>
*/
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(),
];
}
}
@@ -0,0 +1,30 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class UserGitConnectionResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @return array<string, mixed>
*/
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(),
];
}
}
+26
View File
@@ -0,0 +1,26 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class UserResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @return array<string, mixed>
*/
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(),
];
}
}