fix(ui): respect sidebar toggle and show application domains
Honor an explicit sidebarCollapsed preference instead of always auto-collapsing with a second nav. Show the primary domain on the application access card, use a persisted GitLab name in the page title after delete, and shrink the compose view switcher.
This commit is contained in:
parent
424dbd36ff
commit
fbb569a4b3
8 changed files with 75 additions and 24 deletions
|
|
@ -9,6 +9,7 @@
|
|||
@auth
|
||||
<div x-data="{
|
||||
open: false,
|
||||
hasSidebarPreference: localStorage.getItem('sidebarCollapsed') !== null,
|
||||
userCollapsed: localStorage.getItem('sidebarCollapsed') === 'true',
|
||||
autoCollapse: localStorage.getItem('sidebarAutoCollapse') !== 'false',
|
||||
hasSecondBar: false,
|
||||
|
|
@ -25,7 +26,7 @@
|
|||
},
|
||||
targetCollapsed() {
|
||||
this.hasSecondBar = !!document.querySelector('.application-settings-navigation');
|
||||
return this.userCollapsed || (this.autoCollapse && this.hasSecondBar);
|
||||
return this.hasSidebarPreference ? this.userCollapsed : (this.autoCollapse && this.hasSecondBar);
|
||||
},
|
||||
applyCollapsed(animate) {
|
||||
const target = this.targetCollapsed();
|
||||
|
|
@ -41,6 +42,7 @@
|
|||
},
|
||||
toggleSidebar() {
|
||||
this.collapsed = !this.collapsed;
|
||||
this.hasSidebarPreference = true;
|
||||
this.userCollapsed = this.collapsed;
|
||||
localStorage.setItem('sidebarCollapsed', this.userCollapsed);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@
|
|||
<h3 class="mb-3 text-sm font-semibold text-black dark:text-fg">Public access</h3>
|
||||
@php
|
||||
$domainCount = 0;
|
||||
$primaryDomain = null;
|
||||
if ($buildPack === 'dockercompose') {
|
||||
$composeDomains = $application->docker_compose_domains
|
||||
? json_decode($application->docker_compose_domains, true)
|
||||
|
|
@ -41,35 +42,57 @@
|
|||
$domainString = data_get($serviceDomain, 'domain');
|
||||
if (filled($domainString)) {
|
||||
$domainCount += countDomains($domainString);
|
||||
$primaryDomain ??= collect(explode(',', $domainString))
|
||||
->map(fn ($domain) => trim($domain))
|
||||
->first(fn ($domain) => filled($domain));
|
||||
}
|
||||
}
|
||||
}
|
||||
} elseif (filled($fqdn)) {
|
||||
$domainCount = countDomains($fqdn);
|
||||
$primaryDomain = collect(explode(',', $fqdn))
|
||||
->map(fn ($domain) => trim($domain))
|
||||
->first(fn ($domain) => filled($domain));
|
||||
}
|
||||
$additionalDomainCount = max(0, $domainCount - 1);
|
||||
@endphp
|
||||
<div class="flex items-center gap-3 rounded-lg border border-neutral-200 bg-neutral-50/60 px-4 py-3 dark:border-white/[0.07] dark:bg-white/[0.05]">
|
||||
<div class="flex min-w-0 flex-1 items-center gap-3">
|
||||
@php
|
||||
$applicationDomainsUrl = route('project.application.domains', [
|
||||
'project_uuid' => $application->environment->project->uuid,
|
||||
'environment_uuid' => $application->environment->uuid,
|
||||
'application_uuid' => $application->uuid,
|
||||
]);
|
||||
@endphp
|
||||
<div class="group relative flex items-center gap-3 rounded-lg border border-neutral-200 bg-neutral-50/60 px-4 py-3 transition-colors hover:bg-neutral-100 focus-within:ring-2 focus-within:ring-coollabs/40 dark:border-white/[0.07] dark:bg-white/[0.05] dark:hover:bg-white/[0.08] dark:focus-within:ring-warning/40">
|
||||
<a class="flex min-w-0 flex-1 items-center gap-3 after:absolute after:inset-0 after:content-[''] focus-visible:outline-none"
|
||||
aria-label="{{ $domainCount > 0 ? 'Manage application domains' : 'Add an application domain' }}"
|
||||
href="{{ $applicationDomainsUrl }}" {{ wireNavigate() }}>
|
||||
<div class="flex size-9 shrink-0 items-center justify-center rounded-md bg-neutral-200/70 text-neutral-600 dark:bg-white/[0.07] dark:text-fg-dim">
|
||||
<x-reicon name="globe" class="size-4" />
|
||||
</div>
|
||||
<div class="min-w-0">
|
||||
<p class="text-sm font-medium text-black dark:text-fg">
|
||||
{{ $domainCount }} configured {{ Str::plural('domain', $domainCount) }}
|
||||
@if ($primaryDomain)
|
||||
<span class="block truncate">{{ $primaryDomain }}</span>
|
||||
@else
|
||||
No public domain configured
|
||||
@endif
|
||||
</p>
|
||||
<p class="text-xs text-neutral-500 dark:text-fg-dim">
|
||||
Domains, DNS checks, and redirect settings
|
||||
@if ($additionalDomainCount > 0)
|
||||
+{{ $additionalDomainCount }} more {{ Str::plural('domain', $additionalDomainCount) }}
|
||||
@elseif ($domainCount === 0)
|
||||
Make this application available from a URL
|
||||
@else
|
||||
Manage DNS checks and redirect settings
|
||||
@endif
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<a class="icon-button ml-auto shrink-0" title="Manage domains"
|
||||
aria-label="Manage domains"
|
||||
href="{{ route('project.application.domains', [
|
||||
'project_uuid' => $application->environment->project->uuid,
|
||||
'environment_uuid' => $application->environment->uuid,
|
||||
'application_uuid' => $application->uuid,
|
||||
]) }}" {{ wireNavigate() }}>
|
||||
<x-reicon name="settings" class="size-4" />
|
||||
</a>
|
||||
<a class="button relative z-10 ml-auto shrink-0" aria-label="{{ $domainCount > 0 ? 'Manage application domains' : 'Add an application domain' }}"
|
||||
href="{{ $applicationDomainsUrl }}" {{ wireNavigate() }}>
|
||||
{{ $domainCount > 0 ? 'Manage domains' : 'Add domain' }}
|
||||
<x-reicon name="arrow-right" class="size-4" />
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
|
|
|
|||
|
|
@ -117,7 +117,7 @@ class="grid grid-cols-2 gap-0.5 border-y border-neutral-200 py-3 sm:grid-cols-3
|
|||
</div>
|
||||
<div class="flex w-full items-center justify-between gap-2 sm:w-auto sm:justify-start">
|
||||
<div
|
||||
class="flex h-9 items-center rounded-lg border border-neutral-200 bg-white p-0.5 dark:border-white/[0.08] dark:bg-white/[0.06]">
|
||||
class="flex h-8 items-center rounded-lg border border-neutral-200 bg-white dark:border-white/[0.08] dark:bg-white/[0.06]">
|
||||
<button type="button" x-on:click="setViewMode('table')"
|
||||
class="flex size-7.5 items-center justify-center rounded-md transition-colors"
|
||||
:class="viewMode === 'table'
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<div>
|
||||
<x-slot:title>
|
||||
{{ $gitlab_app->name ?: 'GitLab App' }} | Sources | Coolify
|
||||
{{ $name ?: 'GitLab App' }} | Sources | Coolify
|
||||
</x-slot>
|
||||
|
||||
@if ($isConnected)
|
||||
|
|
|
|||
|
|
@ -10,13 +10,18 @@
|
|||
expect($generalSettings)
|
||||
->toContain('id="access-section" title="Access"')
|
||||
->toContain('<h3 class="mb-3 text-sm font-semibold text-black dark:text-fg">Public access</h3>')
|
||||
->toContain("Str::plural('domain', \$domainCount)")
|
||||
->toContain('Domains, DNS checks, and redirect settings')
|
||||
->toContain('class="flex items-center gap-3 rounded-lg')
|
||||
->toContain('class="icon-button ml-auto shrink-0"')
|
||||
->toContain('aria-label="Manage domains"')
|
||||
->toContain('<x-reicon name="settings" class="size-4" />')
|
||||
->not->toContain('<x-reicon name="arrow-right" class="size-3.5" />')
|
||||
->toContain('$primaryDomain = null;')
|
||||
->toContain('$additionalDomainCount = max(0, $domainCount - 1);')
|
||||
->toContain("aria-label=\"{{ \$domainCount > 0 ? 'Manage application domains' : 'Add an application domain' }}\"")
|
||||
->toContain('{{ $primaryDomain }}')
|
||||
->toContain("+{{ \$additionalDomainCount }} more {{ Str::plural('domain', \$additionalDomainCount) }}")
|
||||
->toContain('No public domain configured')
|
||||
->toContain('Make this application available from a URL')
|
||||
->toContain("{{ \$domainCount > 0 ? 'Manage domains' : 'Add domain' }}")
|
||||
->toContain('class="button relative z-10 ml-auto shrink-0"')
|
||||
->toContain('aria-label="{{ $domainCount > 0 ? \'Manage application domains\' : \'Add an application domain\' }}"')
|
||||
->toContain('<x-reicon name="arrow-right" class="size-4" />')
|
||||
->not->toContain('<x-reicon name="settings" class="size-4" />')
|
||||
->toContain('<livewire:project.application.internal-access')
|
||||
->not->toContain('wire:init="loadCurrentInternalHostname"')
|
||||
->and($internalAccessSettings)
|
||||
|
|
|
|||
|
|
@ -57,6 +57,23 @@
|
|||
->assertSet('apiUrl', 'https://gitlab.example.com/api/v4');
|
||||
});
|
||||
|
||||
test('redirects without rendering an error after the gitlab app is deleted', function () {
|
||||
Livewire::withQueryParams(['gitlab_app_uuid' => $this->gitlabApp->uuid])
|
||||
->test(Change::class)
|
||||
->call('delete')
|
||||
->assertRedirect(route('source.all'));
|
||||
|
||||
$this->assertModelMissing($this->gitlabApp);
|
||||
});
|
||||
|
||||
test('uses the persisted name for the title during a post-delete update', function () {
|
||||
$view = file_get_contents(resource_path('views/livewire/source/gitlab/change.blade.php'));
|
||||
|
||||
expect($view)
|
||||
->toContain("{{ \$name ?: 'GitLab App' }} | Sources | Coolify")
|
||||
->not->toContain("{{ \$gitlab_app->name ?: 'GitLab App' }} | Sources | Coolify");
|
||||
});
|
||||
|
||||
test('saves and reloads the application secret after refresh', function () {
|
||||
Livewire::withQueryParams(['gitlab_app_uuid' => $this->gitlabApp->uuid])
|
||||
->test(Change::class)
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@
|
|||
->toContain('aria-label="Grid view"')
|
||||
->toContain('mb-3 flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between')
|
||||
->toContain('flex w-full items-center justify-between gap-2 sm:w-auto sm:justify-start')
|
||||
->toContain('flex h-8 items-center rounded-lg border border-neutral-200 bg-white')
|
||||
->not->toContain('flex h-9 items-center rounded-lg border border-neutral-200 bg-white')
|
||||
->toContain("localStorage.setItem('service-compose-resources-view', mode)")
|
||||
->not->toContain('>Sort</button>')
|
||||
->and($resourceCard)
|
||||
|
|
|
|||
|
|
@ -27,8 +27,10 @@
|
|||
$layout = file_get_contents(resource_path('views/layouts/app.blade.php'));
|
||||
|
||||
expect($layout)
|
||||
->toContain("hasSidebarPreference: localStorage.getItem('sidebarCollapsed') !== null")
|
||||
->toContain("userCollapsed: localStorage.getItem('sidebarCollapsed') === 'true'")
|
||||
->toContain('return this.userCollapsed || (this.autoCollapse && this.hasSecondBar);')
|
||||
->toContain('return this.hasSidebarPreference ? this.userCollapsed : (this.autoCollapse && this.hasSecondBar);')
|
||||
->toContain('this.hasSidebarPreference = true;')
|
||||
->toContain('this.userCollapsed = this.collapsed;')
|
||||
->toContain("localStorage.setItem('sidebarCollapsed', this.userCollapsed);")
|
||||
->toContain('sidebarReady: false')
|
||||
|
|
|
|||
Loading…
Reference in a new issue