mirror of
https://github.com/coollabsio/coolify.git
synced 2026-09-25 07:50:35 -05:00
Discussion #11833: since v4.3.19 the resource settings sidebar expands every group by default, so reaching Backups etc. means scrolling past every section. Restore the v4.3.18 behaviour across all grouped resource sidebars (application, database, service, server): each group header is now a collapsible toggle, and only the group containing the active page is open by default. Manual expand/ collapse is remembered per resource type in localStorage; an explicit collapse sticks even for the active group. Desktop (xl) only — the mobile grid is unchanged (display:contents wrapper + xl-scoped collapse). - new shared Alpine provider settingsSidebarAccordion (resources/js) + app.js - nav-section-toggle utility (chevron header) - wired into the four grouped sidebars - static regression test
114 lines
6.9 KiB
PHP
114 lines
6.9 KiB
PHP
@props(['database', 'currentRoute'])
|
|
|
|
@php
|
|
$databaseRouteParameters = [
|
|
'project_uuid' => $database->environment->project->uuid,
|
|
'environment_uuid' => $database->environment->uuid,
|
|
'database_uuid' => $database->uuid,
|
|
];
|
|
|
|
$configurationItems = collect([
|
|
['label' => 'General', 'route' => 'project.database.configuration', 'icon' => 'settings'],
|
|
['label' => 'Environment Variables', 'route' => 'project.database.environment-variables', 'icon' => 'variables'],
|
|
['label' => 'Persistent Storage', 'route' => 'project.database.persistent-storage', 'icon' => 'storages'],
|
|
['label' => 'Backups', 'route' => 'project.database.backup.index', 'icon' => 'database', 'visible' => $database->isBackupSolutionAvailable()],
|
|
['label' => 'Import Backup', 'route' => 'project.database.import-backup', 'icon' => 'upload', 'navigate' => false, 'visible' => auth()->user()?->can('update', $database)],
|
|
['label' => 'Servers', 'route' => 'project.database.servers', 'icon' => 'servers'],
|
|
['label' => 'Runtime Logs', 'route' => 'project.database.logs', 'icon' => 'unordered-list', 'navigate' => false],
|
|
['label' => 'Terminal', 'route' => 'project.database.command', 'icon' => 'browser-terminal', 'navigate' => false, 'visible' => auth()->user()?->can('canAccessTerminal')],
|
|
['label' => 'Webhooks', 'route' => 'project.database.webhooks', 'icon' => 'notifications'],
|
|
['label' => 'Healthcheck', 'route' => 'project.database.healthcheck', 'icon' => 'feedback'],
|
|
['label' => 'Resource Limits', 'route' => 'project.database.resource-limits', 'icon' => 'cpu'],
|
|
['label' => 'Resource Operations', 'route' => 'project.database.resource-operations', 'icon' => 'server-update'],
|
|
['label' => 'Metrics', 'route' => 'project.database.metrics', 'icon' => 'graph'],
|
|
['label' => 'Tags', 'route' => 'project.database.tags', 'icon' => 'tags'],
|
|
['label' => 'Danger Zone', 'route' => 'project.database.danger', 'icon' => 'shield-alert'],
|
|
])->filter(fn (array $item): bool => $item['visible'] ?? true)
|
|
->map(fn (array $item): array => [
|
|
...$item,
|
|
'active' => $currentRoute === $item['route']
|
|
|| ($item['route'] === 'project.database.backup.index'
|
|
&& str($currentRoute)->startsWith('project.database.backup')),
|
|
]);
|
|
|
|
$menuGroups = [
|
|
'Settings' => ['General', 'Environment Variables', 'Persistent Storage', 'Healthcheck'],
|
|
'Observe & troubleshoot' => ['Runtime Logs', 'Terminal', 'Metrics'],
|
|
'Deploy' => ['Servers'],
|
|
'Automation' => ['Webhooks', 'Backups', 'Import Backup'],
|
|
'Operations' => ['Resource Operations', 'Resource Limits', 'Tags', 'Danger Zone'],
|
|
];
|
|
|
|
$groupedItems = collect($menuGroups)
|
|
->map(fn (array $labels) => collect($labels)
|
|
->map(fn (string $label) => $configurationItems->firstWhere('label', $label))
|
|
->filter()
|
|
->values())
|
|
->filter(fn ($items) => $items->isNotEmpty());
|
|
|
|
// Group that holds the current page — the only one expanded by default.
|
|
$activeGroup = (string) $groupedItems->search(fn ($items) => $items->contains(fn ($item) => $item['active'] ?? false));
|
|
|
|
$pageSections = $database->type() === 'standalone-postgresql'
|
|
? [
|
|
['id' => 'database-details-section', 'label' => 'Database details'],
|
|
['id' => 'credentials-section', 'label' => 'Credentials'],
|
|
['id' => 'initialization-section', 'label' => 'Initialization'],
|
|
['id' => 'runtime-network-section', 'label' => 'Runtime and network'],
|
|
['id' => 'public-access-section', 'label' => 'Public access'],
|
|
['id' => 'configuration-section', 'label' => 'Configuration'],
|
|
['id' => 'log-delivery-section', 'label' => 'Log delivery'],
|
|
['id' => 'initialization-scripts-section', 'label' => 'Initialization scripts'],
|
|
]
|
|
: [];
|
|
@endphp
|
|
|
|
<aside class="application-settings-navigation min-w-0 xl:self-start">
|
|
<nav aria-label="Database settings"
|
|
x-data="settingsSidebarAccordion({ activeGroup: @js($activeGroup), storageKey: 'coolify.settings-sidebar.database' })"
|
|
class="grid grid-cols-2 gap-0.5 border-y border-neutral-200 py-3 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-1 xl:border-y-0 xl:py-0 dark:border-white/[0.06]">
|
|
@foreach ($groupedItems as $groupLabel => $groupItems)
|
|
@unless ($loop->first)
|
|
<div class="my-2 hidden border-t border-neutral-200 xl:block dark:border-white/[0.06]" aria-hidden="true"></div>
|
|
@endunless
|
|
<button type="button" class="nav-section-toggle hidden xl:flex" @click="toggle(@js($groupLabel))"
|
|
:aria-expanded="isOpen(@js($groupLabel))">
|
|
<span>{{ $groupLabel }}</span>
|
|
<svg class="size-3 shrink-0 opacity-60 transition-transform"
|
|
:class="!isOpen(@js($groupLabel)) && '-rotate-90'" viewBox="0 0 24 24" fill="none"
|
|
stroke="currentColor" stroke-width="2" aria-hidden="true">
|
|
<path stroke-linecap="round" stroke-linejoin="round" d="m6 9 6 6 6-6" />
|
|
</svg>
|
|
</button>
|
|
<div class="contents" :class="isOpen(@js($groupLabel)) ? 'xl:block' : 'xl:hidden'">
|
|
@foreach ($groupItems as $menuItem)
|
|
<a @class(['menu-item', 'menu-item-active' => $menuItem['active']])
|
|
@if ($menuItem['navigate'] ?? true) {{ wireNavigate() }} @endif
|
|
href="{{ route($menuItem['route'], $databaseRouteParameters) }}">
|
|
<x-reicon :name="$menuItem['icon']" class="menu-item-icon" />
|
|
<span class="menu-item-label">{{ $menuItem['label'] }}</span>
|
|
</a>
|
|
@if ($menuItem['active'] && $menuItem['route'] === 'project.database.configuration' && $pageSections !== [])
|
|
<div class="nav-children hidden flex-col gap-0.5 py-1 xl:flex"
|
|
x-data="{
|
|
activeSection: '',
|
|
scrollToSection(id) {
|
|
this.activeSection = id;
|
|
window.scrollToSettingsSection?.(id);
|
|
},
|
|
}">
|
|
@foreach ($pageSections as $section)
|
|
<button type="button" class="menu-subitem"
|
|
:class="activeSection === '{{ $section['id'] }}' && 'menu-subitem-active'"
|
|
@click="scrollToSection('{{ $section['id'] }}')">
|
|
<span class="menu-item-label text-left">{{ $section['label'] }}</span>
|
|
</button>
|
|
@endforeach
|
|
</div>
|
|
@endif
|
|
@endforeach
|
|
</div>
|
|
@endforeach
|
|
</nav>
|
|
</aside>
|