mirror of
https://github.com/tiennm99/coolify.git
synced 2026-10-18 12:14:07 +00:00
fix(ui): polish mobile layouts, error pages, and volume backups
Keep error pages full-viewport, lift the unsaved bar above the mobile keyboard, make status/heading layouts stack cleanly on small screens, route service DB backups to volume-backups, and tighten volume backup grid/forms. Cover with feature tests.
This commit is contained in:
1 parent
262ad8981a
commit
e4fa2b6964
31 files changed
+172
-83
No files matched your search
@@ -5,6 +5,7 @@
|
||||
])
|
||||
|
||||
@php
|
||||
$serviceParameters = \Illuminate\Support\Arr::except($parameters, ['stack_service_uuid']);
|
||||
$items = [
|
||||
[
|
||||
'label' => 'General',
|
||||
@@ -20,7 +21,8 @@
|
||||
],
|
||||
[
|
||||
'label' => 'Backups',
|
||||
'route' => 'project.service.database.backups',
|
||||
'route' => 'project.service.volume-backups.index',
|
||||
'parameters' => $serviceParameters,
|
||||
'icon' => 'storages',
|
||||
'active' => request()->routeIs('project.service.database.backup*'),
|
||||
'visible' => $serviceDatabase?->isBackupSolutionAvailable() || $serviceDatabase?->is_migrated,
|
||||
@@ -54,7 +56,7 @@
|
||||
'menu-item-active' => $item['active'],
|
||||
])
|
||||
@if ($item['navigate'] ?? true) {{ wireNavigate() }} @endif
|
||||
href="{{ route($item['route'], $parameters) }}">
|
||||
href="{{ route($item['route'], $item['parameters'] ?? $parameters) }}">
|
||||
<x-reicon :name="$item['icon']" class="menu-item-icon" />
|
||||
<span class="menu-item-label">{{ $item['label'] }}</span>
|
||||
</a>
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
</x-status-badge>
|
||||
|
||||
<div x-cloak x-show="open" x-transition.origin.top.left
|
||||
class="listbox-panel top-8! right-auto! left-0! z-[90]! w-64! min-w-64!" role="menu">
|
||||
class="listbox-panel top-8! right-auto! left-0! z-[90]! w-[min(16rem,calc(100vw-1.5rem))]! min-w-0! sm:w-64! sm:min-w-64!" role="menu">
|
||||
<div class="px-3 py-2 text-[11px] font-medium text-neutral-400 dark:text-fg-faint">{{ $title }}</div>
|
||||
<div class="listbox-option cursor-default! gap-2.5!">
|
||||
<span @class([
|
||||
|
||||
@@ -19,7 +19,28 @@
|
||||
Mobile: stacked layout (full label, buttons on the next line) inset from
|
||||
the viewport edges so body overflow-x-clip cannot clip it.
|
||||
Desktop: compact single-row centered pill. --}}
|
||||
<div wire:dirty.class="is-dirty"
|
||||
<div x-data="{
|
||||
keyboardInset: 0,
|
||||
updateKeyboardInset: null,
|
||||
init() {
|
||||
this.updateKeyboardInset = () => {
|
||||
const viewport = window.visualViewport;
|
||||
this.keyboardInset = window.innerWidth < 640 && viewport
|
||||
? Math.max(0, window.innerHeight - viewport.height - viewport.offsetTop)
|
||||
: 0;
|
||||
};
|
||||
|
||||
this.updateKeyboardInset();
|
||||
window.visualViewport?.addEventListener('resize', this.updateKeyboardInset);
|
||||
window.visualViewport?.addEventListener('scroll', this.updateKeyboardInset);
|
||||
window.addEventListener('resize', this.updateKeyboardInset);
|
||||
},
|
||||
destroy() {
|
||||
window.visualViewport?.removeEventListener('resize', this.updateKeyboardInset);
|
||||
window.visualViewport?.removeEventListener('scroll', this.updateKeyboardInset);
|
||||
window.removeEventListener('resize', this.updateKeyboardInset);
|
||||
},
|
||||
}" x-bind:style="`--keyboard-inset: ${keyboardInset}px`" wire:dirty.class="is-dirty"
|
||||
wire:loading.class="!opacity-0 !translate-y-6 !pointer-events-none"
|
||||
@keydown.enter.window="
|
||||
if ($el.classList.contains('is-dirty') &&
|
||||
@@ -35,7 +56,7 @@
|
||||
}
|
||||
"
|
||||
@if ($targets) wire:target="{{ $targets }}" @endif
|
||||
class="pointer-events-none fixed inset-x-3 bottom-[max(1.5rem,env(safe-area-inset-bottom,0px)+0.75rem)] z-[1000] flex max-w-full translate-y-6 flex-col items-stretch gap-2 rounded-2xl border border-white/10 bg-surface py-2.5 pr-2.5 pl-4 opacity-0 shadow-modal transition-[opacity,transform] duration-200 ease-out delay-0 [&.is-dirty]:pointer-events-auto [&.is-dirty]:translate-y-0 [&.is-dirty]:opacity-100 [&.is-dirty]:delay-300 sm:inset-x-auto sm:left-1/2 sm:bottom-6 sm:w-max sm:max-w-none sm:-translate-x-1/2 sm:flex-row sm:items-center sm:gap-8 sm:py-2 sm:pl-5 sm:pr-2">
|
||||
class="pointer-events-none fixed inset-x-3 bottom-[calc(var(--keyboard-inset,0px)+max(1.5rem,env(safe-area-inset-bottom,0px)+0.75rem))] z-[1000] flex max-w-full translate-y-6 flex-col items-stretch gap-2 rounded-2xl border border-white/10 bg-surface py-2.5 pr-2.5 pl-4 opacity-0 shadow-modal transition-[opacity,transform] duration-200 ease-out delay-0 [&.is-dirty]:pointer-events-auto [&.is-dirty]:translate-y-0 [&.is-dirty]:opacity-100 [&.is-dirty]:delay-300 sm:inset-x-auto sm:left-1/2 sm:bottom-6 sm:w-max sm:max-w-none sm:-translate-x-1/2 sm:flex-row sm:items-center sm:gap-8 sm:py-2 sm:pl-5 sm:pr-2">
|
||||
<span class="text-[13px] font-semibold leading-snug text-fg sm:whitespace-nowrap">{{ $label }}</span>
|
||||
<div class="flex shrink-0 items-center justify-end gap-2">
|
||||
<button type="button" onclick="window.location.reload()"
|
||||
|
||||
Reference in new issue
Block a user