feat(ui): add volume backup storage controls and grouped navigation

This commit is contained in:
Andras Bacsai committed 2026-08-10 10:35:15 +02:00
1 parent 4ba6aa3500
commit 62b2fe2330
20 files changed
+373 -112

No files matched your search

@@ -63,7 +63,7 @@
class="absolute top-9 right-0 z-50 w-52 rounded-lg border border-neutral-200 bg-white p-1 shadow-modal dark:border-white/[0.1] dark:bg-raised">
<template x-for="option in sortOptions" :key="option.value">
<button type="button"
class="flex h-8 w-full items-center rounded-md px-2 text-left text-[12px] text-neutral-600 transition-colors hover:bg-neutral-100 hover:text-black dark:text-fg-dim dark:hover:bg-white/[0.06] dark:hover:text-fg"
class="flex h-9 w-full items-center rounded-md px-2 text-left text-[12px] text-neutral-600 transition-colors hover:bg-neutral-100 hover:text-black dark:text-fg-dim dark:hover:bg-white/[0.06] dark:hover:text-fg"
x-on:click="sortBy = option.value; sortOpen = false; page = 1">
<span class="flex-1" x-text="option.label"></span>
<svg x-show="sortBy === option.value" class="size-3.5 text-warning"
@@ -77,9 +77,9 @@
</div>
<div
class="flex h-8 items-center rounded-lg border border-neutral-200 bg-white p-0.5 dark:border-white/[0.08] dark:bg-white/[0.035]">
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.035]">
<button type="button" x-on:click="setViewMode('table')"
class="flex size-6.5 items-center justify-center rounded-md transition-colors"
class="flex size-7.5 items-center justify-center rounded-md transition-colors"
:class="viewMode === 'table'
?
'control-selected' :
@@ -88,7 +88,7 @@
<x-reicon name="unordered-list" class="size-3.5" />
</button>
<button type="button" x-on:click="setViewMode('grid')"
class="flex size-6.5 items-center justify-center rounded-md transition-colors"
class="flex size-7.5 items-center justify-center rounded-md transition-colors"
:class="viewMode === 'grid'
?
'control-selected' :
@@ -133,12 +133,12 @@
<div class="relative z-10 flex shrink-0 items-center gap-0.5">
<a x-show="project.addResourceHref" :href="project.addResourceHref"
{{ wireNavigate() }}
class="flex size-6.5 items-center justify-center rounded-md text-neutral-400 transition-colors hover:bg-neutral-100 hover:text-black dark:text-fg-faint dark:hover:bg-white/[0.06] dark:hover:text-fg"
class="flex size-7.5 items-center justify-center rounded-md text-neutral-400 transition-colors hover:bg-neutral-100 hover:text-black dark:text-fg-faint dark:hover:bg-white/[0.06] dark:hover:text-fg"
title="Add resource" :aria-label="`Add resource to ${project.name}`">
<x-reicon name="plus" class="size-3" />
</a>
<a x-show="project.settingsHref" :href="project.settingsHref" {{ wireNavigate() }}
class="flex size-6.5 items-center justify-center rounded-md text-neutral-400 transition-colors hover:bg-neutral-100 hover:text-black dark:text-fg-faint dark:hover:bg-white/[0.06] dark:hover:text-fg"
class="flex size-7.5 items-center justify-center rounded-md text-neutral-400 transition-colors hover:bg-neutral-100 hover:text-black dark:text-fg-faint dark:hover:bg-white/[0.06] dark:hover:text-fg"
title="Project settings" :aria-label="`Open settings for ${project.name}`">
<x-reicon name="settings" class="size-3" />
</a>
@@ -129,7 +129,7 @@
class="absolute top-9 right-0 z-50 w-48 rounded-lg border border-neutral-200 bg-white p-1 shadow-modal dark:border-white/[0.1] dark:bg-raised">
<template x-for="option in sortOptions" :key="option.value">
<button type="button"
class="flex h-8 w-full items-center rounded-md px-2 text-left text-[12px] text-neutral-600 transition-colors hover:bg-neutral-100 hover:text-black dark:text-fg-dim dark:hover:bg-white/[0.06] dark:hover:text-fg"
class="flex h-9 w-full items-center rounded-md px-2 text-left text-[12px] text-neutral-600 transition-colors hover:bg-neutral-100 hover:text-black dark:text-fg-dim dark:hover:bg-white/[0.06] dark:hover:text-fg"
x-on:click="sortBy = option.value; sortOpen = false; page = 1">
<span class="flex-1" x-text="option.label"></span>
<svg x-show="sortBy === option.value" class="size-3.5 text-warning"
@@ -143,9 +143,9 @@
</div>
<div
class="flex h-8 items-center rounded-lg border border-neutral-200 bg-white p-0.5 dark:border-white/[0.08] dark:bg-white/[0.035]">
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.035]">
<button type="button" x-on:click="setViewMode('table')"
class="flex size-6.5 items-center justify-center rounded-md transition-colors"
class="flex size-7.5 items-center justify-center rounded-md transition-colors"
:class="viewMode === 'table'
?
'control-selected' :
@@ -154,7 +154,7 @@
<x-reicon name="unordered-list" class="size-3.5" />
</button>
<button type="button" x-on:click="setViewMode('grid')"
class="flex size-6.5 items-center justify-center rounded-md transition-colors"
class="flex size-7.5 items-center justify-center rounded-md transition-colors"
:class="viewMode === 'grid'
?
'control-selected' :
@@ -18,7 +18,7 @@
['label' => 'Environment Variables', 'route' => 'project.service.environment-variables', 'icon' => 'variables', 'hasWarning' => ! $service->isDeployable],
['label' => 'Persistent Storage', 'route' => 'project.service.storages', 'icon' => 'storages'],
['label' => 'Backups', 'route' => 'project.service.volume-backups.index', 'icon' => 'database'],
['label' => 'Runtime', 'route' => 'project.service.logs', 'icon' => 'unordered-list', 'navigate' => false],
['label' => 'Runtime Logs', 'route' => 'project.service.logs', 'icon' => 'unordered-list', 'navigate' => false],
['label' => 'Terminal', 'route' => 'project.service.command', 'icon' => 'browser-terminal', 'navigate' => false, 'visible' => auth()->user()?->can('canAccessTerminal')],
['label' => 'Scheduled Tasks', 'route' => 'project.service.scheduled-tasks.show', 'icon' => 'calendar'],
['label' => 'Webhooks', 'route' => 'project.service.webhooks', 'icon' => 'notifications'],
@@ -33,14 +33,17 @@
]);
$menuGroups = [
'Settings' => ['General', 'Domains', 'Environment Variables', 'Persistent Storage', 'Backups'],
'Automation' => ['Scheduled Tasks', 'Webhooks'],
'Logs' => ['Runtime'],
'Operations' => ['Terminal', 'Resource Operations', 'Tags', 'Danger Zone'],
'Settings' => ['General', 'Domains', 'Environment Variables', 'Persistent Storage'],
'Observe & troubleshoot' => ['Runtime Logs', 'Terminal'],
'Automation' => ['Scheduled Tasks', 'Webhooks', 'Backups'],
'Operations' => ['Resource Operations', 'Tags', 'Danger Zone'],
];
$groupedItems = collect($menuGroups)
->map(fn (array $labels) => $configurationItems->whereIn('label', $labels)->values())
->map(fn (array $labels) => collect($labels)
->map(fn (string $label) => $configurationItems->firstWhere('label', $label))
->filter()
->values())
->filter(fn ($items) => $items->isNotEmpty());
$storageSections = $applications
@@ -113,9 +116,9 @@
</div>
<div class="flex w-full items-center justify-between gap-2 sm:w-auto sm:justify-start">
<div
class="flex h-8 items-center rounded-lg border border-neutral-200 bg-white p-0.5 dark:border-white/[0.08] dark:bg-white/[0.035]">
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.035]">
<button type="button" x-on:click="setViewMode('table')"
class="flex size-6.5 items-center justify-center rounded-md transition-colors"
class="flex size-7.5 items-center justify-center rounded-md transition-colors"
:class="viewMode === 'table'
? 'control-selected'
: 'text-neutral-400 hover:bg-neutral-100 hover:text-black dark:text-fg-faint dark:hover:bg-white/[0.06] dark:hover:text-fg'"
@@ -123,7 +126,7 @@
<x-reicon name="unordered-list" class="size-3.5" />
</button>
<button type="button" x-on:click="setViewMode('grid')"
class="flex size-6.5 items-center justify-center rounded-md transition-colors"
class="flex size-7.5 items-center justify-center rounded-md transition-colors"
:class="viewMode === 'grid'
? 'control-selected'
: 'text-neutral-400 hover:bg-neutral-100 hover:text-black dark:text-fg-faint dark:hover:bg-white/[0.06] dark:hover:text-fg'"
@@ -85,7 +85,7 @@
class="absolute top-9 right-0 z-50 w-48 rounded-lg border border-neutral-200 bg-white p-1 shadow-modal dark:border-white/[0.1] dark:bg-raised">
<template x-for="option in sortOptions" :key="option.value">
<button type="button"
class="flex h-8 w-full items-center rounded-md px-2 text-left text-[12px] text-neutral-600 transition-colors hover:bg-neutral-100 hover:text-black dark:text-fg-dim dark:hover:bg-white/[0.06] dark:hover:text-fg"
class="flex h-9 w-full items-center rounded-md px-2 text-left text-[12px] text-neutral-600 transition-colors hover:bg-neutral-100 hover:text-black dark:text-fg-dim dark:hover:bg-white/[0.06] dark:hover:text-fg"
x-on:click="sortBy = option.value; sortOpen = false; page = 1">
<span class="flex-1" x-text="option.label"></span>
<svg x-show="sortBy === option.value" class="size-3.5 text-warning"
@@ -99,9 +99,9 @@
</div>
<div
class="flex h-8 items-center rounded-lg border border-neutral-200 bg-white p-0.5 dark:border-white/[0.08] dark:bg-white/[0.035]">
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.035]">
<button type="button" x-on:click="setViewMode('table')"
class="flex size-6.5 items-center justify-center rounded-md transition-colors"
class="flex size-7.5 items-center justify-center rounded-md transition-colors"
:class="viewMode === 'table'
?
'control-selected' :
@@ -110,7 +110,7 @@
<x-reicon name="unordered-list" class="size-3.5" />
</button>
<button type="button" x-on:click="setViewMode('grid')"
class="flex size-6.5 items-center justify-center rounded-md transition-colors"
class="flex size-7.5 items-center justify-center rounded-md transition-colors"
:class="viewMode === 'grid'
?
'control-selected' :
@@ -152,13 +152,13 @@
<div class="relative z-10 flex shrink-0 items-center gap-0.5">
<a x-show="environment.addResourceHref" :href="environment.addResourceHref"
{{ wireNavigate() }}
class="flex size-6.5 items-center justify-center rounded-md text-neutral-400 transition-colors hover:bg-neutral-100 hover:text-black dark:text-fg-faint dark:hover:bg-white/[0.06] dark:hover:text-fg"
class="flex size-7.5 items-center justify-center rounded-md text-neutral-400 transition-colors hover:bg-neutral-100 hover:text-black dark:text-fg-faint dark:hover:bg-white/[0.06] dark:hover:text-fg"
title="Add resource" :aria-label="`Add resource to ${environment.name}`">
<x-reicon name="plus" class="size-3" />
</a>
<a x-show="environment.settingsHref" :href="environment.settingsHref"
{{ wireNavigate() }}
class="flex size-6.5 items-center justify-center rounded-md text-neutral-400 transition-colors hover:bg-neutral-100 hover:text-black dark:text-fg-faint dark:hover:bg-white/[0.06] dark:hover:text-fg"
class="flex size-7.5 items-center justify-center rounded-md text-neutral-400 transition-colors hover:bg-neutral-100 hover:text-black dark:text-fg-faint dark:hover:bg-white/[0.06] dark:hover:text-fg"
title="Environment settings"
:aria-label="`Open settings for ${environment.name}`">
<x-reicon name="settings" class="size-3" />