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:
Andras Bacsai committed 2026-08-06 21:29:26 +02:00
1 parent 262ad8981a
commit e4fa2b6964
31 files changed
+172 -83

No files matched your search

@@ -12,7 +12,8 @@ it('shows internal Docker access details in application general settings', funct
->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="icon-button shrink-0 self-start sm:self-auto"')
->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" />')
+1
View File
@@ -16,6 +16,7 @@ it('renders redesigned error pages with shared shell and actions', function (str
$html = view($view, ['exception' => $exception])->render();
expect($html)
->toContain('error-page-body')
->toContain('error-shell')
->toContain('error-code')
->toContain('error-title')
@@ -1,5 +1,18 @@
<?php
it('keeps the volume backup executions table horizontally scrollable on mobile', function () {
$view = file_get_contents(resource_path('views/livewire/project/shared/storages/volume-backups/executions.blade.php'));
$css = file_get_contents(resource_path('css/app.css'));
expect($view)
->toContain('volume-backup-executions-grid')
->toContain('data-table w-full overflow-x-auto')
->and($css)
->toContain('.volume-backup-executions-grid')
->toContain('min-width: 50rem;')
->not->toContain('.data-table-header.volume-backup-executions-grid');
});
use App\Livewire\Project\Service\VolumeBackup\Create as CreateServiceVolumeBackup;
use App\Livewire\Project\Shared\Storages\All;
use App\Models\Application;
+3 -1
View File
@@ -13,7 +13,9 @@ it('aggregates preview container and health check status', function () {
->toContain('Not configured')
->toContain('aria-label="About unconfigured health checks"')
->toContain('Traffic can still be routed to the container')
->toContain('aria-haspopup="menu"');
->toContain('aria-haspopup="menu"')
->toContain('right-auto! left-0!')
->toContain('w-[min(16rem,calc(100vw-1.5rem))]!');
});
it('uses the aggregated preview status in the previews list', function () {
@@ -33,6 +33,27 @@ it('uses a single unified navbar for application, service, database, and server
->not->toContain('border-l border-neutral-200 pl-1');
});
it('uses interactive status summaries in mobile resource headings', function () {
$headings = [
resource_path('views/livewire/project/application/heading.blade.php') => '<x-status-summary :status="$application->status" />',
resource_path('views/livewire/project/database/heading.blade.php') => '<x-status-summary :status="$database->status" title="Database status" />',
resource_path('views/livewire/project/service/heading.blade.php') => '<x-status-summary :status="$service->status" title="Service status" container-name="Containers" />',
];
foreach ($headings as $path => $statusSummary) {
$mobileHeading = str(file_get_contents($path))
->after('<div class="mb-3 w-full xl:hidden">')
->before('<div class="w-full xl:hidden">')
->toString();
expect($mobileHeading)
->toContain('flex min-w-0 flex-col items-start gap-2')
->toContain('min-w-0 max-w-full truncate')
->toContain($statusSummary)
->not->toContain('<x-status-badge');
}
});
it('docks desktop resource actions in the top bar instead of floating over content', function () {
$layout = file_get_contents(resource_path('views/layouts/app.blade.php'));
$files = [
@@ -56,6 +56,16 @@ test('unsaved bar stays above floating notifications so save actions remain acce
->and($popup)->toContain('z-999');
});
test('unsaved bar stays above the mobile keyboard', function () {
$contents = file_get_contents(resource_path('views/components/unsaved-bar.blade.php'));
expect($contents)
->toContain('window.visualViewport')
->toContain('keyboardInset')
->toContain('--keyboard-inset')
->toContain('removeEventListener');
});
test('settings general unsaved bar excludes auto-saving instance timezone', function () {
$path = resource_path('views/livewire/settings/index.blade.php');
$contents = file_get_contents($path);
@@ -36,8 +36,11 @@ it('aligns compose resource columns and uses icon actions', function () {
expect($configuration)->toContain($columns)
->and($resourceCard)->toContain($columns)
->toContain('flex flex-wrap items-center justify-end gap-1 sm:contents')
->toContain('aria-label="Resource settings"')
->toContain('aria-label="Database backups"')
->toContain('aria-label="Service backups"')
->toContain("route('project.service.volume-backups.index', \$parameters)")
->not->toContain("route('project.service.database.backups'")
->not->toContain('>Settings</a>')
->not->toContain('>Backups</a>');
});
@@ -74,6 +74,16 @@ it('shows the service sidebar on the storage backups page', function () {
->toContain('xl:grid-cols-[210px_minmax(0,1fr)]');
});
it('links compose database backups to the parent service backups page', function () {
$sidebar = file_get_contents(resource_path('views/components/service-database/sidebar.blade.php'));
expect($sidebar)
->toContain("'route' => 'project.service.volume-backups.index'")
->toContain("'parameters' => \$serviceParameters")
->toContain("\$item['parameters'] ?? \$parameters")
->not->toContain("'route' => 'project.service.database.backups'");
});
it('uses a distinct backup icon across resource sidebars', function () {
$sidebars = [
resource_path('views/components/application/configuration-sidebar.blade.php'),
+17
View File
@@ -826,6 +826,7 @@ it('enables and disables volume S3 backups from the S3 title action', function (
'section' => 's3',
])
->assertSee('Enable S3')
->assertDontSee('You do not have permission to perform this action.')
->call('toggleS3')
->assertSet('saveToS3', true)
->assertSee('Disable S3');
@@ -865,6 +866,22 @@ it('shows and saves volume S3 retention while S3 backups are disabled', function
->and($backup->retention_max_storage_s3)->toBe(4.5);
});
it('allows team owners to edit volume backup retention settings', function () {
$team = Team::factory()->create();
signInForVolumeBackups($this, $team);
[$application, $volume] = createVolumeBackupApplication($team);
$volume->scheduledBackups()->create([
'team_id' => $team->id,
'frequency' => 'daily',
]);
Livewire::test(VolumeBackups::class, [
'storage' => $volume,
'resource' => $application,
'section' => 'retention',
])->assertDontSee('You do not have permission to perform this action.');
});
it('only updates S3 fields when toggling volume S3 backups', function () {
$team = Team::factory()->create();
signInForVolumeBackups($this, $team);