mirror of
https://github.com/coollabsio/coolify.git
synced 2026-11-12 15:53:46 -04:00
fix(breadcrumb): link breadcrumb names to their page
The section and resource names in the top breadcrumb open their page. The arrow next to a name still opens the switch list. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
dc7a5946e9
commit
b81cc4116d
@@ -0,0 +1,8 @@
|
||||
---
|
||||
type: changed
|
||||
---
|
||||
Names in the top breadcrumb, which now link to their page
|
||||
|
||||
- The section name, such as `Servers`, opens the list page of the section
|
||||
- The project, environment, resource, server, storage, source, destination, and tag names open their own page
|
||||
- The arrow next to a name still opens the switch list
|
||||
@@ -1,20 +1,28 @@
|
||||
@props(['label', 'items', 'title', 'uuid' => null])
|
||||
|
||||
@php
|
||||
$href = collect($items)->firstWhere('active', true)['href'] ?? null;
|
||||
@endphp
|
||||
|
||||
<span class="shrink-0 px-0.5 text-neutral-300 dark:text-fg-faint">/</span>
|
||||
<div class="relative min-w-0 shrink" x-data="{ open: false, search: '' }" @keydown.escape.window="open = false"
|
||||
@click.outside="open = false">
|
||||
<div class="flex h-8 min-w-0 items-center gap-1">
|
||||
<button type="button"
|
||||
@click="open = !open; if (open) { search = ''; $nextTick(() => $refs.search.focus()) }"
|
||||
title="Switch resource"
|
||||
class="flex h-8 min-w-0 items-center gap-1.5 rounded-md px-2 opacity-70 transition-[background-color,opacity] hover:bg-neutral-100 hover:opacity-100 dark:hover:bg-white/[0.05]">
|
||||
<span class="min-w-0 truncate font-semibold text-black dark:text-fg"
|
||||
x-text="nameOf(@js($uuid), @js($label))">{{ $label }}</span>
|
||||
<svg class="size-4 shrink-0 text-neutral-400 dark:text-fg-faint" viewBox="0 0 24 24" fill="none">
|
||||
<path d="M8 9l4-4 4 4M8 15l4 4 4-4" stroke="currentColor" stroke-width="1.6"
|
||||
stroke-linecap="round" stroke-linejoin="round" />
|
||||
</svg>
|
||||
</button>
|
||||
<div class="flex h-8 min-w-0 items-center rounded-md opacity-70 transition-opacity hover:opacity-100">
|
||||
<a href="{{ $href }}" {{ wireNavigate() }} title="Go to {{ strtolower(str($title)->singular()) }}"
|
||||
class="flex h-8 min-w-0 items-center rounded-l-md pr-1 pl-2 transition-colors hover:bg-neutral-100 dark:hover:bg-white/[0.05]">
|
||||
<span class="min-w-0 truncate font-semibold text-black dark:text-fg"
|
||||
x-text="nameOf(@js($uuid), @js($label))">{{ $label }}</span>
|
||||
</a>
|
||||
<button type="button"
|
||||
@click="open = !open; if (open) { search = ''; $nextTick(() => $refs.search.focus()) }"
|
||||
title="Switch resource" class="flex h-8 shrink-0 items-center rounded-r-md px-0.5 transition-colors hover:bg-neutral-100 dark:hover:bg-white/[0.05]">
|
||||
<svg class="size-4 shrink-0 text-neutral-400 dark:text-fg-faint" viewBox="0 0 24 24" fill="none">
|
||||
<path d="M8 9l4-4 4 4M8 15l4 4 4-4" stroke="currentColor" stroke-width="1.6"
|
||||
stroke-linecap="round" stroke-linejoin="round" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
@isset($meta)
|
||||
{{ $meta }}
|
||||
@endisset
|
||||
|
||||
@@ -85,24 +85,24 @@
|
||||
? \App\Models\Tag::ownedByCurrentTeam()->orderBy('name')->get()->unique('name')->values()
|
||||
: collect();
|
||||
$currentTag = $tags->firstWhere('name', $tagName);
|
||||
$dashboardContext = match (true) {
|
||||
request()->routeIs('dashboard') => 'Dashboard',
|
||||
request()->routeIs('project.index', 'project.canvas.index') => 'Projects',
|
||||
request()->routeIs('terminal') => 'Terminal',
|
||||
request()->routeIs('server.*') => 'Servers',
|
||||
request()->routeIs('source.*') => 'Sources',
|
||||
request()->routeIs('destination.*') => 'Destinations',
|
||||
request()->routeIs('registries.*') => 'Registries',
|
||||
request()->routeIs('storage.*') => 'Storage',
|
||||
request()->routeIs('shared-variables.*') => 'Shared Variables',
|
||||
request()->routeIs('team.*') => 'Team',
|
||||
request()->routeIs('notifications.*') => 'Notifications',
|
||||
request()->routeIs('security.*') => 'Keys & Tokens',
|
||||
request()->routeIs('tags.*') => 'Tags',
|
||||
request()->routeIs('settings.*') => 'Settings',
|
||||
request()->routeIs('profile*') => 'Profile',
|
||||
request()->routeIs('admin.*') => 'Admin',
|
||||
default => null,
|
||||
[$dashboardContext, $dashboardContextRoute] = match (true) {
|
||||
request()->routeIs('dashboard') => ['Dashboard', 'dashboard'],
|
||||
request()->routeIs('project.index', 'project.canvas.index') => ['Projects', 'project.index'],
|
||||
request()->routeIs('terminal') => ['Terminal', 'terminal'],
|
||||
request()->routeIs('server.*') => ['Servers', 'server.index'],
|
||||
request()->routeIs('source.*') => ['Sources', 'source.all'],
|
||||
request()->routeIs('destination.*') => ['Destinations', 'destination.index'],
|
||||
request()->routeIs('registries.*') => ['Registries', 'registries.index'],
|
||||
request()->routeIs('storage.*') => ['Storage', 'storage.index'],
|
||||
request()->routeIs('shared-variables.*') => ['Shared Variables', 'shared-variables.index'],
|
||||
request()->routeIs('team.*') => ['Team', 'team.index'],
|
||||
request()->routeIs('notifications.*') => ['Notifications', 'notifications.email'],
|
||||
request()->routeIs('security.*') => ['Keys & Tokens', 'security.private-key.index'],
|
||||
request()->routeIs('tags.*') => ['Tags', 'tags.show'],
|
||||
request()->routeIs('settings.*') => ['Settings', 'settings.index'],
|
||||
request()->routeIs('profile*') => ['Profile', 'profile'],
|
||||
request()->routeIs('admin.*') => ['Admin', 'admin.index'],
|
||||
default => [null, null],
|
||||
};
|
||||
@endphp
|
||||
<div class="flex min-w-0 items-center gap-0.5 text-[13px]" data-page-context="{{ $dashboardContext }}"
|
||||
@@ -130,8 +130,8 @@
|
||||
|
||||
@if (!$currentProject && $dashboardContext)
|
||||
<span x-show="!titleRepeats" x-cloak class="shrink-0 px-0.5 text-neutral-300 dark:text-fg-faint">/</span>
|
||||
<span x-show="!titleRepeats" x-cloak
|
||||
class="flex h-8 min-w-0 items-center truncate px-2 font-semibold text-black opacity-70 dark:text-fg">{{ $dashboardContext }}</span>
|
||||
<a href="{{ route($dashboardContextRoute) }}" {{ wireNavigate() }} x-show="!titleRepeats" x-cloak
|
||||
class="flex h-8 min-w-0 items-center truncate rounded-md px-2 font-semibold text-black opacity-70 transition-[background-color,opacity] hover:bg-neutral-100 hover:opacity-100 dark:text-fg dark:hover:bg-white/[0.05]">{{ $dashboardContext }}</a>
|
||||
@endif
|
||||
|
||||
@if ($currentStorage)
|
||||
@@ -217,14 +217,19 @@
|
||||
<span class="shrink-0 text-neutral-300 dark:text-fg-faint px-0.5">/</span>
|
||||
{{-- Project switcher --}}
|
||||
<div class="relative min-w-0 shrink" x-data="{ open: false }" @keydown.escape.window="open = false">
|
||||
<button type="button" @click="open = !open" @click.outside="open = false" title="Switch project"
|
||||
class="flex items-center gap-1.5 min-w-0 max-w-full h-8 px-2 rounded-md opacity-70 transition-[background-color,opacity] hover:opacity-100 hover:bg-neutral-100 dark:hover:bg-white/[0.05]">
|
||||
<span class="min-w-0 truncate font-semibold text-black dark:text-fg"
|
||||
x-text="nameOf(@js($currentProject->uuid), @js($currentProject->name))">{{ $currentProject->name }}</span>
|
||||
<svg class="size-4 shrink-0 text-neutral-400 dark:text-fg-faint" viewBox="0 0 24 24" fill="none">
|
||||
<path d="M8 9l4-4 4 4M8 15l4 4 4-4" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" />
|
||||
</svg>
|
||||
</button>
|
||||
<div class="flex h-8 min-w-0 items-center rounded-md opacity-70 transition-opacity hover:opacity-100">
|
||||
<a href="{{ route($projectDestinationRoute, ['project_uuid' => $currentProject->uuid]) }}" {{ wireNavigate() }} title="Go to project"
|
||||
class="flex h-8 min-w-0 items-center rounded-l-md pr-1 pl-2 transition-colors hover:bg-neutral-100 dark:hover:bg-white/[0.05]">
|
||||
<span class="min-w-0 truncate font-semibold text-black dark:text-fg"
|
||||
x-text="nameOf(@js($currentProject->uuid), @js($currentProject->name))">{{ $currentProject->name }}</span>
|
||||
</a>
|
||||
<button type="button" @click="open = !open" @click.outside="open = false" title="Switch project"
|
||||
class="flex h-8 shrink-0 items-center rounded-r-md px-0.5 transition-colors hover:bg-neutral-100 dark:hover:bg-white/[0.05]">
|
||||
<svg class="size-4 shrink-0 text-neutral-400 dark:text-fg-faint" viewBox="0 0 24 24" fill="none">
|
||||
<path d="M8 9l4-4 4 4M8 15l4 4 4-4" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div x-show="open" x-cloak x-transition.opacity.duration.120ms
|
||||
class="listbox-panel scrollbar left-0! z-[90]! max-h-80! min-w-56 max-w-72">
|
||||
<div class="px-2 py-1 text-[10px] font-semibold uppercase tracking-wide text-neutral-400 dark:text-fg-faint">
|
||||
@@ -244,13 +249,18 @@
|
||||
<span class="shrink-0 text-neutral-300 dark:text-fg-faint px-0.5">/</span>
|
||||
{{-- Environment switcher --}}
|
||||
<div class="relative min-w-0 shrink" x-data="{ open: false }" @keydown.escape.window="open = false">
|
||||
<button type="button" @click="open = !open" @click.outside="open = false" title="Switch environment"
|
||||
class="flex items-center gap-1.5 min-w-0 max-w-full h-8 px-2 rounded-md opacity-70 transition-[background-color,opacity] hover:opacity-100 hover:bg-neutral-100 dark:hover:bg-white/[0.05]">
|
||||
<span class="min-w-0 truncate font-semibold text-black dark:text-fg">{{ $currentEnvironment->name }}</span>
|
||||
<svg class="size-4 shrink-0 text-neutral-400 dark:text-fg-faint" viewBox="0 0 24 24" fill="none">
|
||||
<path d="M8 9l4-4 4 4M8 15l4 4 4-4" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" />
|
||||
</svg>
|
||||
</button>
|
||||
<div class="flex h-8 min-w-0 items-center rounded-md opacity-70 transition-opacity hover:opacity-100">
|
||||
<a href="{{ route('project.resource.index', ['project_uuid' => $currentProject->uuid, 'environment_uuid' => $currentEnvironment->uuid]) }}" {{ wireNavigate() }} title="Go to environment"
|
||||
class="flex h-8 min-w-0 items-center rounded-l-md pr-1 pl-2 transition-colors hover:bg-neutral-100 dark:hover:bg-white/[0.05]">
|
||||
<span class="min-w-0 truncate font-semibold text-black dark:text-fg">{{ $currentEnvironment->name }}</span>
|
||||
</a>
|
||||
<button type="button" @click="open = !open" @click.outside="open = false" title="Switch environment"
|
||||
class="flex h-8 shrink-0 items-center rounded-r-md px-0.5 transition-colors hover:bg-neutral-100 dark:hover:bg-white/[0.05]">
|
||||
<svg class="size-4 shrink-0 text-neutral-400 dark:text-fg-faint" viewBox="0 0 24 24" fill="none">
|
||||
<path d="M8 9l4-4 4 4M8 15l4 4 4-4" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div x-show="open" x-cloak x-transition.opacity.duration.120ms
|
||||
class="listbox-panel scrollbar left-0! z-[90]! max-h-80! min-w-52 max-w-72">
|
||||
<div class="px-2 py-1 text-[10px] font-semibold uppercase tracking-wide text-neutral-400 dark:text-fg-faint">
|
||||
|
||||
@@ -118,18 +118,22 @@
|
||||
<span class="relative flex min-w-0 shrink items-center gap-2 px-1"
|
||||
x-data="{ open: false, search: '', servers: @js($serverSwitcherOptions) }"
|
||||
@click.outside="open = false" @keydown.escape.window="open = false">
|
||||
<button type="button"
|
||||
class="flex h-8 max-w-56 min-w-0 items-center gap-1.5 rounded-md px-2 transition-colors hover:bg-neutral-100 dark:hover:bg-white/[0.05] xl:max-w-72"
|
||||
@click="open = !open" :aria-expanded="open" aria-label="Switch server">
|
||||
<span class="min-w-0 truncate font-semibold text-black dark:text-fg">
|
||||
{{ $server->name }}
|
||||
</span>
|
||||
<svg class="size-4 shrink-0 text-neutral-400 dark:text-fg-faint" viewBox="0 0 24 24"
|
||||
fill="none" aria-hidden="true">
|
||||
<path d="M8 9l4-4 4 4M8 15l4 4 4-4" stroke="currentColor" stroke-width="1.6"
|
||||
stroke-linecap="round" stroke-linejoin="round" />
|
||||
</svg>
|
||||
</button>
|
||||
<div class="flex h-8 max-w-56 min-w-0 items-center rounded-md xl:max-w-72">
|
||||
<a href="{{ route('server.show', ['server_uuid' => $server->uuid]) }}" {{ wireNavigate() }} title="Go to server"
|
||||
class="flex h-8 min-w-0 items-center rounded-l-md pr-1 pl-2 transition-colors hover:bg-neutral-100 dark:hover:bg-white/[0.05]">
|
||||
<span class="min-w-0 truncate font-semibold text-black dark:text-fg">
|
||||
{{ $server->name }}
|
||||
</span>
|
||||
</a>
|
||||
<button type="button" class="flex h-8 shrink-0 items-center rounded-r-md px-0.5 transition-colors hover:bg-neutral-100 dark:hover:bg-white/[0.05]"
|
||||
@click="open = !open" :aria-expanded="open" aria-label="Switch server">
|
||||
<svg class="size-4 shrink-0 text-neutral-400 dark:text-fg-faint" viewBox="0 0 24 24"
|
||||
fill="none" aria-hidden="true">
|
||||
<path d="M8 9l4-4 4 4M8 15l4 4 4-4" stroke="currentColor" stroke-width="1.6"
|
||||
stroke-linecap="round" stroke-linejoin="round" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div x-cloak x-show="open" x-transition.origin.top.left
|
||||
class="listbox-panel top-9! left-1! z-[90]! w-64! min-w-0!">
|
||||
<div class="border-b border-neutral-200 p-1.5 dark:border-white/[0.08]">
|
||||
|
||||
@@ -13,12 +13,12 @@ it('updates the project breadcrumb after the project is renamed', function () {
|
||||
loginAndSkipBoarding();
|
||||
|
||||
$page = visit(route('project.edit', ['project_uuid' => $this->stack['project']->uuid]));
|
||||
$page->assertSeeIn('button[title="Switch project"]', 'Browser Project')
|
||||
$page->assertSeeIn('a[title="Go to project"]', 'Browser Project')
|
||||
->fill('name', 'Renamed Project');
|
||||
|
||||
submitLivewireForm($page);
|
||||
|
||||
$page->assertSeeIn('button[title="Switch project"]', 'Renamed Project')
|
||||
$page->assertSeeIn('a[title="Go to project"]', 'Renamed Project')
|
||||
->screenshot(filename: 'breadcrumb-project-renamed');
|
||||
});
|
||||
|
||||
@@ -27,11 +27,11 @@ it('updates the resource breadcrumb after the database is renamed', function ()
|
||||
loginAndSkipBoarding();
|
||||
|
||||
$page = visit(databaseConfigurationUrl($this->stack['project'], $this->stack['environment'], $postgres));
|
||||
$page->assertSeeIn('button[title="Switch resource"]', 'Old Postgres')
|
||||
$page->assertSeeIn('a[title="Go to resource"]', 'Old Postgres')
|
||||
->fill('name', 'Renamed Postgres');
|
||||
|
||||
submitLivewireForm($page);
|
||||
|
||||
$page->assertSeeIn('button[title="Switch resource"]', 'Renamed Postgres')
|
||||
$page->assertSeeIn('a[title="Go to resource"]', 'Renamed Postgres')
|
||||
->screenshot(filename: 'breadcrumb-database-renamed');
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user