fixed missing features
This commit is contained in:
@@ -30,20 +30,16 @@ interface GitProviderClient
|
||||
* @return array<int, GitBranch>
|
||||
*/
|
||||
public function listBranches(
|
||||
string $baseUrl,
|
||||
string $token,
|
||||
string $owner,
|
||||
string $repository,
|
||||
GitRepository $repository,
|
||||
): array;
|
||||
|
||||
/**
|
||||
* @return array<int, GitPullRequest>
|
||||
*/
|
||||
public function listPullRequests(
|
||||
string $baseUrl,
|
||||
string $token,
|
||||
string $owner,
|
||||
string $repository,
|
||||
GitRepository $repository,
|
||||
): array;
|
||||
|
||||
public function createBranch(
|
||||
|
||||
@@ -8,6 +8,7 @@ namespace App\Actions\Git\Data;
|
||||
final readonly class GitRepository
|
||||
{
|
||||
public function __construct(
|
||||
public string $baseUrl,
|
||||
public string $providerRepositoryId,
|
||||
public string $owner,
|
||||
public string $name,
|
||||
|
||||
@@ -55,7 +55,7 @@ class GiteaProviderClient implements GitProviderClient
|
||||
->throw()
|
||||
->json();
|
||||
|
||||
return $this->mapRepository($payload);
|
||||
return $this->mapRepository($payload, $baseUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -85,7 +85,7 @@ class GiteaProviderClient implements GitProviderClient
|
||||
->json();
|
||||
|
||||
foreach ($payload as $repository) {
|
||||
$repositories[] = $this->mapRepository($repository);
|
||||
$repositories[] = $this->mapRepository($repository, $baseUrl);
|
||||
}
|
||||
|
||||
$page++;
|
||||
@@ -98,19 +98,17 @@ class GiteaProviderClient implements GitProviderClient
|
||||
* @return array<int, GitBranch>
|
||||
*/
|
||||
public function listBranches(
|
||||
string $baseUrl,
|
||||
string $token,
|
||||
string $owner,
|
||||
string $repository,
|
||||
GitRepository $repository,
|
||||
): array {
|
||||
$page = 1;
|
||||
$branches = [];
|
||||
|
||||
do {
|
||||
/** @var array<int, array{name: string}> $payload */
|
||||
$payload = $this->request($baseUrl, $token)
|
||||
$payload = $this->request($repository->baseUrl, $token)
|
||||
->get(
|
||||
'/repos/'.rawurlencode($owner).'/'.rawurlencode($repository).'/branches',
|
||||
'/repos/'.rawurlencode($repository->owner).'/'.rawurlencode($repository->name).'/branches',
|
||||
[
|
||||
'limit' => 100,
|
||||
'page' => $page,
|
||||
@@ -135,10 +133,8 @@ class GiteaProviderClient implements GitProviderClient
|
||||
* @return array<int, GitPullRequest>
|
||||
*/
|
||||
public function listPullRequests(
|
||||
string $baseUrl,
|
||||
string $token,
|
||||
string $owner,
|
||||
string $repository,
|
||||
GitRepository $repository,
|
||||
): array {
|
||||
$page = 1;
|
||||
$pullRequests = [];
|
||||
@@ -153,9 +149,9 @@ class GiteaProviderClient implements GitProviderClient
|
||||
* head: array{ref: string}
|
||||
* }> $payload
|
||||
*/
|
||||
$payload = $this->request($baseUrl, $token)
|
||||
$payload = $this->request($repository->baseUrl, $token)
|
||||
->get(
|
||||
'/repos/'.rawurlencode($owner).'/'.rawurlencode($repository).'/pulls',
|
||||
'/repos/'.rawurlencode($repository->owner).'/'.rawurlencode($repository->name).'/pulls',
|
||||
[
|
||||
'limit' => 100,
|
||||
'page' => $page,
|
||||
@@ -248,9 +244,10 @@ class GiteaProviderClient implements GitProviderClient
|
||||
* private: bool
|
||||
* } $repository
|
||||
*/
|
||||
private function mapRepository(array $repository): GitRepository
|
||||
private function mapRepository(array $repository, string $baseUrl): GitRepository
|
||||
{
|
||||
return new GitRepository(
|
||||
baseUrl: $baseUrl,
|
||||
providerRepositoryId: (string) $repository['id'],
|
||||
owner: $repository['owner']['login'],
|
||||
name: $repository['name'],
|
||||
|
||||
@@ -66,10 +66,8 @@ class ResolveTicketBranchMatches
|
||||
$this->gitProviderManager
|
||||
->client($userGitConnection->provider)
|
||||
->listBranches(
|
||||
$userGitConnection->base_url,
|
||||
$userGitConnection->api_token,
|
||||
$repositoryConnection->owner_name,
|
||||
$repositoryConnection->repository_name,
|
||||
$repositoryConnection->toGitRepository(),
|
||||
),
|
||||
)
|
||||
->filter(fn (GitBranch $branch): bool => $this->ticketKeyReferenceMatcher->matches(
|
||||
@@ -85,7 +83,7 @@ class ResolveTicketBranchMatches
|
||||
$error = null;
|
||||
} catch (Throwable) {
|
||||
$branches = [];
|
||||
$error = 'KanbanGit could not load branches from this repository right now.';
|
||||
$error = ((string) config('app.name')).' could not load branches from this repository right now.';
|
||||
}
|
||||
|
||||
return [
|
||||
|
||||
@@ -72,10 +72,8 @@ class ResolveTicketPullRequestMatches
|
||||
$this->gitProviderManager
|
||||
->client($userGitConnection->provider)
|
||||
->listPullRequests(
|
||||
$userGitConnection->base_url,
|
||||
$userGitConnection->api_token,
|
||||
$repositoryConnection->owner_name,
|
||||
$repositoryConnection->repository_name,
|
||||
$repositoryConnection->toGitRepository(),
|
||||
),
|
||||
)
|
||||
->filter(fn (GitPullRequest $pullRequest): bool => $this->ticketKeyReferenceMatcher->matches(
|
||||
@@ -94,7 +92,7 @@ class ResolveTicketPullRequestMatches
|
||||
|
||||
$error = null;
|
||||
} catch (Throwable) {
|
||||
$error = 'KanbanGit could not load pull requests from this repository right now.';
|
||||
$error = ((string) config('app.name')).' could not load pull requests from this repository right now.';
|
||||
$pullRequests = [];
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
<?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(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?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,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?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'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Actions\Tickets;
|
||||
|
||||
use App\Enums\TicketWorklogEntryType;
|
||||
use App\Models\Project;
|
||||
use App\Models\Ticket;
|
||||
use App\Models\TicketStatus;
|
||||
@@ -56,7 +57,7 @@ class CreateProjectTicket
|
||||
$this->createTicketWorklogEntry->handle(
|
||||
$ticket,
|
||||
$actor,
|
||||
'created',
|
||||
TicketWorklogEntryType::Created,
|
||||
"Ticket was created in {$ticketStatus->name}.",
|
||||
[
|
||||
'from' => null,
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Actions\Tickets;
|
||||
|
||||
use App\Enums\TicketWorklogEntryType;
|
||||
use App\Models\Ticket;
|
||||
use App\Models\TicketWorklogEntry;
|
||||
use App\Models\User;
|
||||
@@ -14,7 +15,7 @@ class CreateTicketWorklogEntry
|
||||
public function handle(
|
||||
Ticket $ticket,
|
||||
?User $actor,
|
||||
string $entryType,
|
||||
TicketWorklogEntryType $entryType,
|
||||
string $summary,
|
||||
?array $changes = null,
|
||||
): TicketWorklogEntry {
|
||||
|
||||
@@ -0,0 +1,173 @@
|
||||
<?php
|
||||
|
||||
namespace App\Actions\Tickets;
|
||||
|
||||
use App\Models\Ticket;
|
||||
use App\Models\TicketFieldDefinition;
|
||||
use App\Models\TicketFieldValue;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
class SyncProjectTicketFieldValues
|
||||
{
|
||||
/**
|
||||
* @param Collection<int, TicketFieldDefinition> $fieldDefinitions
|
||||
* @param Collection<int, TicketFieldValue> $existingFieldValues
|
||||
* @param array<int, array{
|
||||
* ticket_field_definition_id: int,
|
||||
* value_number?: float|int|string|null,
|
||||
* value_user_id?: int|string|null
|
||||
* }> $fieldValueAttributes
|
||||
* @return array{
|
||||
* changes: array<int, array<string, mixed>>,
|
||||
* summary_labels: array<int, string>
|
||||
* }
|
||||
*/
|
||||
public function handle(
|
||||
Ticket $ticket,
|
||||
Collection $fieldDefinitions,
|
||||
Collection $existingFieldValues,
|
||||
array $fieldValueAttributes,
|
||||
): array {
|
||||
$changes = [];
|
||||
$summaryLabels = [];
|
||||
|
||||
foreach ($fieldValueAttributes as $fieldValueAttribute) {
|
||||
$fieldDefinition = $fieldDefinitions->get((int) $fieldValueAttribute['ticket_field_definition_id']);
|
||||
|
||||
if ($fieldDefinition === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$existingFieldValue = $existingFieldValues->get($fieldDefinition->id);
|
||||
|
||||
if ($fieldDefinition->field_type === 'user') {
|
||||
$this->syncUserFieldValue(
|
||||
$changes,
|
||||
$existingFieldValue,
|
||||
$fieldDefinition,
|
||||
$fieldValueAttribute,
|
||||
$summaryLabels,
|
||||
$ticket,
|
||||
);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->syncNumberFieldValue(
|
||||
$changes,
|
||||
$existingFieldValue,
|
||||
$fieldDefinition,
|
||||
$fieldValueAttribute,
|
||||
$summaryLabels,
|
||||
$ticket,
|
||||
);
|
||||
}
|
||||
|
||||
return [
|
||||
'changes' => $changes,
|
||||
'summary_labels' => $summaryLabels,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, array<string, mixed>> $changes
|
||||
* @param array{value_user_id?: int|string|null} $fieldValueAttribute
|
||||
* @param array<int, string> $summaryLabels
|
||||
*/
|
||||
private function syncUserFieldValue(
|
||||
array &$changes,
|
||||
?TicketFieldValue $existingFieldValue,
|
||||
TicketFieldDefinition $fieldDefinition,
|
||||
array $fieldValueAttribute,
|
||||
array &$summaryLabels,
|
||||
Ticket $ticket,
|
||||
): void {
|
||||
$newUserId = $fieldValueAttribute['value_user_id'] ?? null;
|
||||
$newUserId = $newUserId === null || $newUserId === '' ? null : (int) $newUserId;
|
||||
|
||||
if ($existingFieldValue?->value_user_id === $newUserId) {
|
||||
return;
|
||||
}
|
||||
|
||||
$oldUserName = $existingFieldValue?->userValue?->name;
|
||||
$newUserName = $newUserId === null ? null : User::query()->find($newUserId)?->name;
|
||||
|
||||
$changes[$fieldDefinition->id] = [
|
||||
'field' => $fieldDefinition->label,
|
||||
'from' => $oldUserName,
|
||||
'to' => $newUserName,
|
||||
];
|
||||
$summaryLabels[] = "{$fieldDefinition->label} field";
|
||||
|
||||
if ($newUserId === null) {
|
||||
$existingFieldValue?->delete();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$ticket->fieldValues()->updateOrCreate(
|
||||
[
|
||||
'ticket_field_definition_id' => $fieldDefinition->id,
|
||||
],
|
||||
[
|
||||
'value_number' => null,
|
||||
'value_user_id' => $newUserId,
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, array<string, mixed>> $changes
|
||||
* @param array{value_number?: float|int|string|null} $fieldValueAttribute
|
||||
* @param array<int, string> $summaryLabels
|
||||
*/
|
||||
private function syncNumberFieldValue(
|
||||
array &$changes,
|
||||
?TicketFieldValue $existingFieldValue,
|
||||
TicketFieldDefinition $fieldDefinition,
|
||||
array $fieldValueAttribute,
|
||||
array &$summaryLabels,
|
||||
Ticket $ticket,
|
||||
): void {
|
||||
$newNumber = $fieldValueAttribute['value_number'] ?? null;
|
||||
$newNumber = $newNumber === null || $newNumber === '' ? null : (float) $newNumber;
|
||||
$oldNumber = $existingFieldValue?->value_number;
|
||||
|
||||
if ($oldNumber === $newNumber) {
|
||||
return;
|
||||
}
|
||||
|
||||
$changes[$fieldDefinition->id] = [
|
||||
'field' => $fieldDefinition->label,
|
||||
'from' => $this->formatNumber($oldNumber),
|
||||
'to' => $this->formatNumber($newNumber),
|
||||
];
|
||||
$summaryLabels[] = "{$fieldDefinition->label} field";
|
||||
|
||||
if ($newNumber === null) {
|
||||
$existingFieldValue?->delete();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$ticket->fieldValues()->updateOrCreate(
|
||||
[
|
||||
'ticket_field_definition_id' => $fieldDefinition->id,
|
||||
],
|
||||
[
|
||||
'value_number' => $newNumber,
|
||||
'value_user_id' => null,
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
private function formatNumber(?float $value): ?string
|
||||
{
|
||||
if ($value === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return rtrim(rtrim(number_format($value, 2, '.', ''), '0'), '.');
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Actions\Tickets;
|
||||
|
||||
use App\Enums\TicketWorklogEntryType;
|
||||
use App\Models\Ticket;
|
||||
use App\Models\TicketFieldDefinition;
|
||||
use App\Models\TicketFieldValue;
|
||||
@@ -13,6 +14,7 @@ class UpdateProjectTicket
|
||||
{
|
||||
public function __construct(
|
||||
private readonly CreateTicketWorklogEntry $createTicketWorklogEntry,
|
||||
private readonly SyncProjectTicketFieldValues $syncProjectTicketFieldValues,
|
||||
) {}
|
||||
|
||||
/**
|
||||
@@ -179,36 +181,19 @@ class UpdateProjectTicket
|
||||
$ticket->save();
|
||||
}
|
||||
|
||||
foreach ($fieldValueAttributes as $fieldValueAttribute) {
|
||||
$fieldDefinition = $fieldDefinitions->get((int) $fieldValueAttribute['ticket_field_definition_id']);
|
||||
$fieldValueChangeSet = $this->syncProjectTicketFieldValues->handle(
|
||||
$ticket,
|
||||
$fieldDefinitions,
|
||||
$existingFieldValues,
|
||||
$fieldValueAttributes,
|
||||
);
|
||||
|
||||
if ($fieldDefinition === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$existingFieldValue = $existingFieldValues->get($fieldDefinition->id);
|
||||
|
||||
if ($fieldDefinition->field_type === 'user') {
|
||||
$this->syncUserFieldValue(
|
||||
$changes,
|
||||
$existingFieldValue,
|
||||
$fieldDefinition,
|
||||
$fieldValueAttribute,
|
||||
$summaryLabels,
|
||||
$ticket,
|
||||
);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->syncNumberFieldValue(
|
||||
$changes,
|
||||
$existingFieldValue,
|
||||
$fieldDefinition,
|
||||
$fieldValueAttribute,
|
||||
$summaryLabels,
|
||||
$ticket,
|
||||
);
|
||||
if ($fieldValueChangeSet['changes'] !== []) {
|
||||
$changes['field_values'] = $fieldValueChangeSet['changes'];
|
||||
$summaryLabels = [
|
||||
...$summaryLabels,
|
||||
...$fieldValueChangeSet['summary_labels'],
|
||||
];
|
||||
}
|
||||
|
||||
if ($summaryLabels === []) {
|
||||
@@ -218,7 +203,7 @@ class UpdateProjectTicket
|
||||
$this->createTicketWorklogEntry->handle(
|
||||
$ticket->fresh(),
|
||||
$actor,
|
||||
'updated',
|
||||
TicketWorklogEntryType::Updated,
|
||||
$this->buildSummary($summaryLabels),
|
||||
$changes,
|
||||
);
|
||||
@@ -273,98 +258,6 @@ class UpdateProjectTicket
|
||||
$summaryLabels[] = $summaryLabel;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $changes
|
||||
* @param array{value_user_id?: int|string|null} $fieldValueAttribute
|
||||
* @param array<int, string> $summaryLabels
|
||||
*/
|
||||
private function syncUserFieldValue(
|
||||
array &$changes,
|
||||
?TicketFieldValue $existingFieldValue,
|
||||
TicketFieldDefinition $fieldDefinition,
|
||||
array $fieldValueAttribute,
|
||||
array &$summaryLabels,
|
||||
Ticket $ticket,
|
||||
): void {
|
||||
$newUserId = $fieldValueAttribute['value_user_id'] ?? null;
|
||||
$newUserId = $newUserId === null || $newUserId === '' ? null : (int) $newUserId;
|
||||
|
||||
if ($existingFieldValue?->value_user_id === $newUserId) {
|
||||
return;
|
||||
}
|
||||
|
||||
$oldUserName = $existingFieldValue?->userValue?->name;
|
||||
$newUserName = $newUserId === null ? null : User::query()->find($newUserId)?->name;
|
||||
|
||||
$changes['field_values'][$fieldDefinition->id] = [
|
||||
'field' => $fieldDefinition->label,
|
||||
'from' => $oldUserName,
|
||||
'to' => $newUserName,
|
||||
];
|
||||
$summaryLabels[] = "{$fieldDefinition->label} field";
|
||||
|
||||
if ($newUserId === null) {
|
||||
$existingFieldValue?->delete();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$ticket->fieldValues()->updateOrCreate(
|
||||
[
|
||||
'ticket_field_definition_id' => $fieldDefinition->id,
|
||||
],
|
||||
[
|
||||
'value_number' => null,
|
||||
'value_user_id' => $newUserId,
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $changes
|
||||
* @param array{value_number?: float|int|string|null} $fieldValueAttribute
|
||||
* @param array<int, string> $summaryLabels
|
||||
*/
|
||||
private function syncNumberFieldValue(
|
||||
array &$changes,
|
||||
?TicketFieldValue $existingFieldValue,
|
||||
TicketFieldDefinition $fieldDefinition,
|
||||
array $fieldValueAttribute,
|
||||
array &$summaryLabels,
|
||||
Ticket $ticket,
|
||||
): void {
|
||||
$newNumber = $fieldValueAttribute['value_number'] ?? null;
|
||||
$newNumber = $newNumber === null || $newNumber === '' ? null : (float) $newNumber;
|
||||
$oldNumber = $existingFieldValue?->value_number;
|
||||
|
||||
if ($oldNumber === $newNumber) {
|
||||
return;
|
||||
}
|
||||
|
||||
$changes['field_values'][$fieldDefinition->id] = [
|
||||
'field' => $fieldDefinition->label,
|
||||
'from' => $this->formatNumber($oldNumber),
|
||||
'to' => $this->formatNumber($newNumber),
|
||||
];
|
||||
$summaryLabels[] = "{$fieldDefinition->label} field";
|
||||
|
||||
if ($newNumber === null) {
|
||||
$existingFieldValue?->delete();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$ticket->fieldValues()->updateOrCreate(
|
||||
[
|
||||
'ticket_field_definition_id' => $fieldDefinition->id,
|
||||
],
|
||||
[
|
||||
'value_number' => $newNumber,
|
||||
'value_user_id' => null,
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, string> $summaryLabels
|
||||
*/
|
||||
@@ -386,13 +279,4 @@ class UpdateProjectTicket
|
||||
|
||||
return 'Updated '.$uniqueLabels->implode(', ').', and '.$lastLabel.'.';
|
||||
}
|
||||
|
||||
private function formatNumber(?float $value): ?string
|
||||
{
|
||||
if ($value === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return rtrim(rtrim(number_format($value, 2, '.', ''), '0'), '.');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
enum TicketWorklogEntryType: string
|
||||
{
|
||||
case Created = 'created';
|
||||
case LinkRemoved = 'link_removed';
|
||||
case LinkUpdated = 'link_updated';
|
||||
case StatusChanged = 'status_changed';
|
||||
case Updated = 'updated';
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Actions\Permissions\PermissionService;
|
||||
use App\Actions\Tickets\CreateProjectTicket;
|
||||
use App\Enums\Permission;
|
||||
use App\Http\Requests\StoreProjectTicketRequest;
|
||||
use App\Models\Project;
|
||||
use App\Models\Ticket;
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Inertia\Inertia;
|
||||
use Inertia\Response;
|
||||
|
||||
class ProjectBacklogController extends Controller
|
||||
{
|
||||
public function __construct(
|
||||
private readonly CreateProjectTicket $createProjectTicket,
|
||||
private readonly PermissionService $permissionService,
|
||||
) {}
|
||||
|
||||
public function index(Request $request, Project $project): Response
|
||||
{
|
||||
$user = $request->user();
|
||||
|
||||
abort_unless(
|
||||
$this->permissionService->allows($user, Permission::ProjectRead, $project),
|
||||
403,
|
||||
);
|
||||
|
||||
return Inertia::render('projects/Backlog', [
|
||||
'backlogTicketStatuses' => $project->ticketStatuses()
|
||||
->whereNull('board_column_id')
|
||||
->get(),
|
||||
'canEditTickets' => $this->permissionService->allows($user, Permission::ProjectWrite, $project),
|
||||
'canCreateTickets' => $this->permissionService->allows($user, Permission::ProjectWrite, $project),
|
||||
'project' => $project,
|
||||
'status' => session('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(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function store(StoreProjectTicketRequest $request, Project $project): RedirectResponse
|
||||
{
|
||||
$user = $request->user();
|
||||
$validated = $request->validated();
|
||||
|
||||
abort_unless(
|
||||
$this->permissionService->allows($user, Permission::ProjectWrite, $project),
|
||||
403,
|
||||
);
|
||||
|
||||
$ticket = $this->createProjectTicket->handle($project, $validated, $user);
|
||||
|
||||
return to_route('projects.backlog.index', $project)
|
||||
->with('status', "{$ticket->ticket_key} was added to the backlog.");
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,10 @@
|
||||
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\Models\Project;
|
||||
use App\Models\Ticket;
|
||||
@@ -18,6 +20,7 @@ use Inertia\Response;
|
||||
class ProjectBoardController extends Controller
|
||||
{
|
||||
public function __construct(
|
||||
private readonly BuildProjectBoardPageProps $buildProjectBoardPageProps,
|
||||
private readonly CreateTicketWorklogEntry $createTicketWorklogEntry,
|
||||
private readonly PermissionService $permissionService,
|
||||
) {}
|
||||
@@ -31,23 +34,12 @@ class ProjectBoardController extends Controller
|
||||
403,
|
||||
);
|
||||
|
||||
return Inertia::render('projects/Board', [
|
||||
'boardColumns' => $project->boardColumns()
|
||||
->with([
|
||||
'ticketStatuses',
|
||||
'tickets' => fn ($query) => $query
|
||||
->with([
|
||||
'ticketPriority',
|
||||
'ticketStatus.allowedTargetStatuses.boardColumn',
|
||||
'ticketStatus.boardColumn',
|
||||
'ticketType',
|
||||
]),
|
||||
])
|
||||
->get(),
|
||||
'canEditTickets' => $this->permissionService->allows($user, Permission::ProjectWrite, $project),
|
||||
'project' => $project,
|
||||
'status' => session('status'),
|
||||
]);
|
||||
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'),
|
||||
));
|
||||
}
|
||||
|
||||
public function move(
|
||||
@@ -90,7 +82,7 @@ class ProjectBoardController extends Controller
|
||||
$this->createTicketWorklogEntry->handle(
|
||||
$ticket->fresh(),
|
||||
$user,
|
||||
'status_changed',
|
||||
TicketWorklogEntryType::StatusChanged,
|
||||
"Moved from {$originLabel} to {$destinationLabel}.",
|
||||
[
|
||||
'from' => [
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
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;
|
||||
@@ -20,6 +21,7 @@ use Inertia\Response;
|
||||
class ProjectController extends Controller
|
||||
{
|
||||
public function __construct(
|
||||
private readonly BuildProjectBacklogPageProps $buildProjectBacklogPageProps,
|
||||
private readonly InitializeProjectTicketMetadata $initializeProjectTicketMetadata,
|
||||
private readonly PermissionService $permissionService,
|
||||
) {}
|
||||
@@ -85,6 +87,23 @@ class ProjectController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
public function backlog(Request $request, Project $project): Response
|
||||
{
|
||||
$user = $request->user();
|
||||
|
||||
abort_unless(
|
||||
$this->permissionService->allows($user, Permission::ProjectRead, $project),
|
||||
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'),
|
||||
));
|
||||
}
|
||||
|
||||
public function general(Request $request, Project $project): Response
|
||||
{
|
||||
return Inertia::render('projects/settings/General', [
|
||||
|
||||
@@ -189,7 +189,7 @@ class ProjectRepositorySettingsController extends Controller
|
||||
$error = null;
|
||||
} catch (Throwable) {
|
||||
$repositories = [];
|
||||
$error = 'KanbanGit could not load repositories from this Git connection right now.';
|
||||
$error = ((string) config('app.name')).' could not load repositories from this Git connection right now.';
|
||||
}
|
||||
|
||||
return [
|
||||
|
||||
@@ -73,10 +73,8 @@ class ProjectTicketBranchController extends Controller
|
||||
$this->gitProviderManager
|
||||
->client($userGitConnection->provider)
|
||||
->listBranches(
|
||||
$userGitConnection->base_url,
|
||||
$userGitConnection->api_token,
|
||||
$repositoryConnection->owner_name,
|
||||
$repositoryConnection->repository_name,
|
||||
$repositoryConnection->toGitRepository(),
|
||||
),
|
||||
)
|
||||
->contains(
|
||||
@@ -85,7 +83,7 @@ class ProjectTicketBranchController extends Controller
|
||||
} catch (Throwable) {
|
||||
return response()->json([
|
||||
'is_valid' => false,
|
||||
'message' => 'KanbanGit could not validate this branch name right now.',
|
||||
'message' => ((string) config('app.name')).' could not validate this branch name right now.',
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -147,10 +145,8 @@ class ProjectTicketBranchController extends Controller
|
||||
try {
|
||||
$branchAlreadyExists = collect(
|
||||
$gitProvider->listBranches(
|
||||
$userGitConnection->base_url,
|
||||
$userGitConnection->api_token,
|
||||
$repositoryConnection->owner_name,
|
||||
$repositoryConnection->repository_name,
|
||||
$repositoryConnection->toGitRepository(),
|
||||
),
|
||||
)
|
||||
->contains(
|
||||
@@ -158,7 +154,7 @@ class ProjectTicketBranchController extends Controller
|
||||
);
|
||||
} catch (Throwable) {
|
||||
throw ValidationException::withMessages([
|
||||
'branch_name' => 'KanbanGit could not validate this branch name right now.',
|
||||
'branch_name' => ((string) config('app.name')).' could not validate this branch name right now.',
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -186,7 +182,7 @@ class ProjectTicketBranchController extends Controller
|
||||
);
|
||||
} catch (Throwable) {
|
||||
throw ValidationException::withMessages([
|
||||
'branch_name' => 'KanbanGit could not create this branch right now.',
|
||||
'branch_name' => ((string) config('app.name')).' could not create this branch right now.',
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,8 +6,11 @@ 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\Models\Project;
|
||||
use App\Models\Ticket;
|
||||
@@ -22,13 +25,68 @@ 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,
|
||||
private readonly ResolveTicketBranchMatches $resolveTicketBranchMatches,
|
||||
private readonly ResolveTicketPullRequestMatches $resolveTicketPullRequestMatches,
|
||||
private readonly UpdateProjectTicket $updateProjectTicket,
|
||||
) {}
|
||||
|
||||
public function create(Request $request, Project $project): Response
|
||||
{
|
||||
$user = $request->user();
|
||||
|
||||
abort_unless(
|
||||
$this->permissionService->allows($user, Permission::ProjectWrite, $project),
|
||||
403,
|
||||
);
|
||||
|
||||
$source = Arr::get($request->query(), 'source') === 'board'
|
||||
? 'board'
|
||||
: 'backlog';
|
||||
$defaultTicketStatus = $source === 'board'
|
||||
? $project->ticketStatuses()->whereNotNull('board_column_id')->first()
|
||||
: $project->ticketStatuses()->whereNull('board_column_id')->first();
|
||||
|
||||
$defaultTicketStatus ??= $project->ticketStatuses()->first();
|
||||
|
||||
return Inertia::render('projects/TicketCreate', [
|
||||
'defaultTicketStatusId' => $defaultTicketStatus?->id,
|
||||
'project' => $project,
|
||||
'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(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function store(StoreProjectTicketRequest $request, Project $project): RedirectResponse
|
||||
{
|
||||
$user = $request->user();
|
||||
|
||||
abort_unless(
|
||||
$this->permissionService->allows($user, Permission::ProjectWrite, $project),
|
||||
403,
|
||||
);
|
||||
|
||||
$ticket = $this->createProjectTicket->handle(
|
||||
$project,
|
||||
$request->validated(),
|
||||
$user,
|
||||
);
|
||||
|
||||
return to_route('projects.tickets.show', [$project, $ticket])
|
||||
->with('status', "{$ticket->ticket_key} was created.");
|
||||
}
|
||||
|
||||
public function show(Request $request, Project $project, Ticket $ticket): Response
|
||||
{
|
||||
$user = $request->user();
|
||||
@@ -40,7 +98,6 @@ class ProjectTicketController extends Controller
|
||||
|
||||
$canEditTicket = $this->permissionService->allows($user, Permission::ProjectWrite, $project);
|
||||
$canManageProject = $this->permissionService->allows($user, Permission::ProjectAdmin, $project);
|
||||
$excludedParentIds = [$ticket->id, ...$this->descendantIds($ticket)];
|
||||
$displayTicket = $ticket->load([
|
||||
'assignee',
|
||||
'author',
|
||||
@@ -65,7 +122,7 @@ class ProjectTicketController extends Controller
|
||||
'linkedTickets' => $this->linkedTickets($ticket),
|
||||
'parentTicketOptions' => Ticket::query()
|
||||
->where('project_id', $project->id)
|
||||
->whereNotIn('id', $excludedParentIds)
|
||||
->whereKeyNot($ticket->id)
|
||||
->whereHas('ticketType', fn ($query) => $query->where('is_parent', true))
|
||||
->orderByDesc('sequence_number')
|
||||
->get()
|
||||
@@ -122,25 +179,7 @@ class ProjectTicketController extends Controller
|
||||
|
||||
$validated = $request->validated();
|
||||
$fieldValues = Arr::get($validated, 'field_values', []);
|
||||
$ticketAttributes = [
|
||||
'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'),
|
||||
];
|
||||
$ticketAttributes = $this->buildProjectTicketUpdateAttributes->handle($ticket, $validated);
|
||||
|
||||
$wasUpdated = $this->updateProjectTicket->handle(
|
||||
$ticket,
|
||||
@@ -181,20 +220,6 @@ class ProjectTicketController extends Controller
|
||||
->all();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, int>
|
||||
*/
|
||||
private function descendantIds(Ticket $ticket): array
|
||||
{
|
||||
return $ticket->childTickets()
|
||||
->get()
|
||||
->flatMap(function (Ticket $childTicket): array {
|
||||
return [$childTicket->id, ...$this->descendantIds($childTicket)];
|
||||
})
|
||||
->values()
|
||||
->all();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array{
|
||||
* created_at: string|null,
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace App\Http\Controllers;
|
||||
use App\Actions\Permissions\PermissionService;
|
||||
use App\Actions\Tickets\CreateTicketWorklogEntry;
|
||||
use App\Enums\Permission;
|
||||
use App\Enums\TicketWorklogEntryType;
|
||||
use App\Http\Requests\StoreTicketLinkRequest;
|
||||
use App\Models\Project;
|
||||
use App\Models\Ticket;
|
||||
@@ -59,7 +60,7 @@ class ProjectTicketLinkController extends Controller
|
||||
$this->createTicketWorklogEntry->handle(
|
||||
$ticket,
|
||||
$user,
|
||||
'link_updated',
|
||||
TicketWorklogEntryType::LinkUpdated,
|
||||
"Linked {$linkedTicket->ticket_key} as {$ticketLink->ticketLinkType->label}.",
|
||||
[
|
||||
'linked_ticket_key' => $linkedTicket->ticket_key,
|
||||
@@ -70,7 +71,7 @@ class ProjectTicketLinkController extends Controller
|
||||
$this->createTicketWorklogEntry->handle(
|
||||
$linkedTicket,
|
||||
$user,
|
||||
'link_updated',
|
||||
TicketWorklogEntryType::LinkUpdated,
|
||||
"Linked {$ticket->ticket_key} as {$ticketLink->ticketLinkType->label}.",
|
||||
[
|
||||
'linked_ticket_key' => $ticket->ticket_key,
|
||||
@@ -117,7 +118,7 @@ class ProjectTicketLinkController extends Controller
|
||||
$this->createTicketWorklogEntry->handle(
|
||||
$ticket,
|
||||
$user,
|
||||
'link_removed',
|
||||
TicketWorklogEntryType::LinkRemoved,
|
||||
"Removed {$label} link to {$linkedTicket->ticket_key}.",
|
||||
[
|
||||
'linked_ticket_key' => $linkedTicket->ticket_key,
|
||||
@@ -128,7 +129,7 @@ class ProjectTicketLinkController extends Controller
|
||||
$this->createTicketWorklogEntry->handle(
|
||||
$linkedTicket,
|
||||
$user,
|
||||
'link_removed',
|
||||
TicketWorklogEntryType::LinkRemoved,
|
||||
"Removed {$label} link to {$ticket->ticket_key}.",
|
||||
[
|
||||
'linked_ticket_key' => $ticket->ticket_key,
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace App\Http\Controllers;
|
||||
use App\Actions\Permissions\PermissionService;
|
||||
use App\Actions\Tickets\CreateTicketWorklogEntry;
|
||||
use App\Enums\Permission;
|
||||
use App\Enums\TicketWorklogEntryType;
|
||||
use App\Http\Requests\DestroyProjectTicketTypeRequest;
|
||||
use App\Http\Requests\StoreProjectNamedSettingRequest;
|
||||
use App\Http\Requests\StoreProjectTicketTypeRequest;
|
||||
@@ -158,7 +159,7 @@ class ProjectTicketMetadataSettingsController extends Controller
|
||||
$this->createTicketWorklogEntry->handle(
|
||||
$ticket->fresh(),
|
||||
$request->user(),
|
||||
'updated',
|
||||
TicketWorklogEntryType::Updated,
|
||||
"Updated type because {$deletedName} type was deleted.",
|
||||
[
|
||||
'ticket_type' => [
|
||||
|
||||
@@ -61,7 +61,7 @@ class GitConnectionController extends Controller
|
||||
} catch (Throwable) {
|
||||
return back()
|
||||
->withErrors([
|
||||
'api_token' => 'KanbanGit could not authenticate against Gitea with this base URL and token.',
|
||||
'api_token' => ((string) config('app.name')).' could not authenticate against Gitea with this base URL and token.',
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ class GitConnectionController extends Controller
|
||||
} catch (Throwable) {
|
||||
return back()
|
||||
->withErrors([
|
||||
'api_token' => 'KanbanGit could not authenticate against Gitea with this base URL and token.',
|
||||
'api_token' => ((string) config('app.name')).' could not authenticate against Gitea with this base URL and token.',
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -39,10 +39,7 @@ class StoreProjectTicketRequest extends FormRequest
|
||||
'ticket_status_id' => [
|
||||
'required',
|
||||
'integer',
|
||||
Rule::exists('ticket_statuses', 'id')
|
||||
->where(fn ($query) => $query
|
||||
->where('project_id', $project->id)
|
||||
->whereNull('board_column_id')),
|
||||
Rule::exists('ticket_statuses', 'id')->where('project_id', $project->id),
|
||||
],
|
||||
'ticket_type_id' => [
|
||||
'required',
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Actions\Git\Data\GitRepository;
|
||||
use App\Actions\Git\GitProviderKey;
|
||||
use Database\Factories\RepositoryConnectionFactory;
|
||||
use Illuminate\Database\Eloquent\Attributes\Fillable;
|
||||
@@ -38,4 +39,18 @@ class RepositoryConnection extends Model
|
||||
{
|
||||
return $this->belongsTo(Project::class);
|
||||
}
|
||||
|
||||
public function toGitRepository(): GitRepository
|
||||
{
|
||||
return new GitRepository(
|
||||
baseUrl: $this->base_url,
|
||||
providerRepositoryId: $this->provider_repository_id,
|
||||
owner: $this->owner_name,
|
||||
name: $this->repository_name,
|
||||
fullName: $this->repository_full_name,
|
||||
htmlUrl: $this->repository_url,
|
||||
defaultBranch: null,
|
||||
isPrivate: false,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\TicketWorklogEntryType;
|
||||
use Database\Factories\TicketWorklogEntryFactory;
|
||||
use Illuminate\Database\Eloquent\Attributes\Fillable;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
@@ -27,6 +28,7 @@ class TicketWorklogEntry extends Model
|
||||
{
|
||||
return [
|
||||
'changes' => 'array',
|
||||
'entry_type' => TicketWorklogEntryType::class,
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -57,6 +57,9 @@ class AppServiceProvider extends ServiceProvider
|
||||
Gate::define('access-admin', fn (User $user): bool => $permissionService->allows($user, Permission::GlobalAdmin));
|
||||
Gate::define('create-project', fn (User $user): bool => $permissionService->allows($user, Permission::ProjectCreate));
|
||||
Gate::define('create-team', fn (User $user): bool => $permissionService->allows($user, Permission::TeamCreate));
|
||||
Gate::define('admin-project', fn (User $user, Project $project): bool => $permissionService->allows($user, Permission::ProjectAdmin, $project));
|
||||
Gate::define('view-project', fn (User $user, Project $project): bool => $permissionService->allows($user, Permission::ProjectRead, $project));
|
||||
Gate::define('write-project', fn (User $user, Project $project): bool => $permissionService->allows($user, Permission::ProjectWrite, $project));
|
||||
Gate::define('manage-teams', fn (User $user): bool => $permissionService->canManageAnyTeam($user));
|
||||
Gate::define('view-teams', fn (User $user): bool => Gate::forUser($user)->any(['create-team', 'manage-teams']));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user