mirror of
https://github.com/tiennm99/coolify.git
synced 2026-08-20 02:27:57 +00:00
fix(ui): align GitLab App create modal with GitHub
Match the GitHub create modal layout: intro copy, name/group row, system-wide warning, self-hosted accordion (URL/API/SSH), and a bottom Continue button instead of a duplicate header Save.
This commit is contained in:
@@ -6,6 +6,7 @@ use App\Models\GitlabApp;
|
||||
use App\Rules\SafeExternalUrl;
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Livewire\Component;
|
||||
|
||||
class Create extends Component
|
||||
@@ -16,34 +17,62 @@ class Create extends Component
|
||||
|
||||
public string $html_url = 'https://gitlab.com';
|
||||
|
||||
public string $api_url = 'https://gitlab.com/api/v4';
|
||||
|
||||
public string $custom_user = 'git';
|
||||
|
||||
public int $custom_port = 22;
|
||||
|
||||
public bool $is_system_wide = false;
|
||||
|
||||
public ?string $group_name = null;
|
||||
|
||||
private bool $shouldDeriveApiUrlAfterHtmlUrlUpdate = false;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->name = substr(generate_random_name(), 0, 30);
|
||||
}
|
||||
|
||||
public function updatingHtmlUrl(): void
|
||||
{
|
||||
$this->shouldDeriveApiUrlAfterHtmlUrlUpdate = blank($this->api_url)
|
||||
|| $this->api_url === $this->gitlabApiUrlFromHtmlUrl($this->html_url);
|
||||
}
|
||||
|
||||
public function updatedHtmlUrl(): void
|
||||
{
|
||||
if ($this->shouldDeriveApiUrlAfterHtmlUrlUpdate) {
|
||||
$this->api_url = $this->gitlabApiUrlFromHtmlUrl($this->html_url);
|
||||
}
|
||||
}
|
||||
|
||||
public function createGitLabApp()
|
||||
{
|
||||
try {
|
||||
$this->authorize('createAnyResource');
|
||||
|
||||
$this->html_url = rtrim($this->html_url, '/');
|
||||
$this->api_url = filled($this->api_url)
|
||||
? rtrim($this->api_url, '/')
|
||||
: $this->gitlabApiUrlFromHtmlUrl($this->html_url);
|
||||
|
||||
$this->validate([
|
||||
'name' => 'required|string',
|
||||
'html_url' => ['required', 'string', 'url', new SafeExternalUrl],
|
||||
'api_url' => ['required', 'string', 'url', new SafeExternalUrl],
|
||||
'custom_user' => 'required|string',
|
||||
'custom_port' => 'required|int',
|
||||
'is_system_wide' => 'required|bool',
|
||||
'group_name' => 'nullable|string',
|
||||
]);
|
||||
|
||||
$htmlUrl = rtrim($this->html_url, '/');
|
||||
$apiUrl = $htmlUrl.'/api/v4';
|
||||
|
||||
$gitlab_app = GitlabApp::create([
|
||||
'name' => $this->name,
|
||||
'api_url' => $apiUrl,
|
||||
'html_url' => $htmlUrl,
|
||||
'api_url' => $this->api_url,
|
||||
'html_url' => $this->html_url,
|
||||
'custom_user' => $this->custom_user,
|
||||
'custom_port' => $this->custom_port,
|
||||
'is_system_wide' => $this->is_system_wide,
|
||||
'group_name' => $this->group_name,
|
||||
'webhook_token' => Str::random(32),
|
||||
@@ -55,8 +84,15 @@ class Create extends Component
|
||||
}
|
||||
|
||||
return redirectRoute($this, 'source.gitlab.show', ['gitlab_app_uuid' => $gitlab_app->uuid]);
|
||||
} catch (ValidationException $e) {
|
||||
throw $e;
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
private function gitlabApiUrlFromHtmlUrl(string $htmlUrl): string
|
||||
{
|
||||
return rtrim($htmlUrl, '/').'/api/v4';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,70 @@
|
||||
<div>
|
||||
<form class="flex flex-col gap-2" wire:submit='createGitLabApp'>
|
||||
@can('createAnyResource')
|
||||
<form wire:submit='createGitLabApp' class="flex flex-col w-full gap-2">
|
||||
<div class="pb-2">This is required if you would like to get full integration (deployments from
|
||||
private repositories, webhooks, etc) with GitLab.</div>
|
||||
<div class="flex gap-2">
|
||||
<h2>New GitLab App</h2>
|
||||
<x-forms.button type="submit">Save</x-forms.button>
|
||||
<x-forms.input id="name" label="Name" required />
|
||||
<x-forms.input id="group_name" label="Group Name"
|
||||
helper="Optional. Comma-separated group names to filter repositories (e.g., myorg,myteam)."
|
||||
placeholder="If empty, all accessible repositories are listed." />
|
||||
</div>
|
||||
<div class="subtitle">Add a self-hosted or GitLab.com instance as a source for your applications.</div>
|
||||
<x-forms.input id="name" label="Name" required />
|
||||
<x-forms.input id="html_url" label="GitLab URL" required
|
||||
helper="For self-hosted GitLab, enter your instance URL (e.g., https://gitlab.example.com)." />
|
||||
<x-forms.input id="group_name" label="Group Name"
|
||||
helper="Optional. Comma-separated group names to filter repositories (e.g., myorg,myteam)." />
|
||||
@if (!isCloud())
|
||||
<x-forms.checkbox label="System Wide?" id="is_system_wide"
|
||||
helper="If checked, this GitLab App will be available for everyone in this Coolify instance." />
|
||||
<div x-data="{ showWarning: @entangle('is_system_wide') }">
|
||||
<div class="w-48">
|
||||
<x-forms.checkbox id="is_system_wide" label="System Wide"
|
||||
helper="If checked, this GitLab App will be available for everyone in this Coolify instance." />
|
||||
</div>
|
||||
<div x-show="showWarning" x-transition x-cloak class="w-full max-w-2xl mx-auto pt-2">
|
||||
<x-callout type="warning" title="Not Recommended">
|
||||
<div class="whitespace-normal break-words">
|
||||
System-wide GitLab Apps are shared across all teams on this Coolify instance. This means any team
|
||||
can use this GitLab App to deploy applications from your repositories. For better security and
|
||||
isolation, it's recommended to create team-specific GitLab Apps instead.
|
||||
</div>
|
||||
</x-callout>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
<div x-data="{
|
||||
activeAccordion: '',
|
||||
setActiveAccordion(id) {
|
||||
this.activeAccordion = (this.activeAccordion == id) ? '' : id
|
||||
}
|
||||
}" class="relative w-full py-2 mx-auto overflow-hidden text-sm font-normal rounded-md">
|
||||
<div x-data="{ id: $id('accordion') }" class="cursor-pointer">
|
||||
<button @click="setActiveAccordion(id)"
|
||||
class="flex items-center justify-between w-full px-1 py-2 text-left select-none dark:hover:text-white hover:bg-white/5"
|
||||
type="button">
|
||||
<h4>Self-hosted GitLab</h4>
|
||||
<svg class="w-4 h-4 duration-200 ease-out" :class="{ 'rotate-180': activeAccordion == id }"
|
||||
viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor"
|
||||
stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="6 9 12 15 18 9"></polyline>
|
||||
</svg>
|
||||
</button>
|
||||
<div x-show="activeAccordion==id" x-collapse x-cloak class="px-2">
|
||||
<div class="flex flex-col gap-2 pt-0 opacity-70">
|
||||
<div class="flex gap-2">
|
||||
<x-forms.input id="html_url" label="GitLab URL" required
|
||||
helper="For self-hosted GitLab, enter your instance URL (e.g., https://gitlab.example.com)." />
|
||||
<x-forms.input id="api_url" label="API URL" required
|
||||
helper="Usually your GitLab URL with /api/v4 appended." />
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<x-forms.input id="custom_user" label="Custom Git User" required />
|
||||
<x-forms.input id="custom_port" type="number" label="Custom Git Port" required />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<x-forms.button class="mt-4" type="submit">
|
||||
Continue
|
||||
</x-forms.button>
|
||||
</form>
|
||||
</div>
|
||||
@else
|
||||
<x-callout type="danger" title="Insufficient Permissions">
|
||||
You don't have permission to create new GitLab Apps. Please contact your team administrator for access.
|
||||
</x-callout>
|
||||
@endcan
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
use App\Livewire\Source\Gitlab\Create;
|
||||
use App\Models\GitlabApp;
|
||||
use App\Models\Team;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Livewire\Livewire;
|
||||
|
||||
uses(RefreshDatabase::class);
|
||||
|
||||
beforeEach(function () {
|
||||
$this->team = Team::factory()->create();
|
||||
$this->user = User::factory()->create();
|
||||
$this->team->members()->attach($this->user->id, ['role' => 'owner']);
|
||||
|
||||
$this->actingAs($this->user);
|
||||
session(['currentTeam' => $this->team]);
|
||||
});
|
||||
|
||||
describe('GitLab source create modal', function () {
|
||||
test('matches github create modal structure', function () {
|
||||
Livewire::test(Create::class)
|
||||
->assertSee('This is required if you would like to get full integration')
|
||||
->assertSee('Self-hosted GitLab')
|
||||
->assertSee('Continue')
|
||||
->assertDontSee('>Save</', false)
|
||||
->assertDontSeeHtml('<h2>New GitLab App</h2>');
|
||||
});
|
||||
|
||||
test('creates a gitlab app with defaults for gitlab.com', function () {
|
||||
Livewire::test(Create::class)
|
||||
->set('name', 'my-gitlab')
|
||||
->call('createGitLabApp')
|
||||
->assertRedirect();
|
||||
|
||||
$app = GitlabApp::where('name', 'my-gitlab')->first();
|
||||
expect($app)->not->toBeNull()
|
||||
->and($app->html_url)->toBe('https://gitlab.com')
|
||||
->and($app->api_url)->toBe('https://gitlab.com/api/v4')
|
||||
->and($app->custom_user)->toBe('git')
|
||||
->and($app->custom_port)->toBe(22);
|
||||
});
|
||||
|
||||
test('derives api url when html url changes', function () {
|
||||
Livewire::test(Create::class)
|
||||
->set('html_url', 'https://gitlab.example.com')
|
||||
->assertSet('api_url', 'https://gitlab.example.com/api/v4');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user