From c1fc9466b28eb73d22b08a7e05189881d23db704 Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Fri, 30 Oct 2026 15:16:41 +0100 Subject: [PATCH] fix(ui): unify dropdown menu styles and drop sort buttons that duplicate table headers Co-Authored-By: Claude Opus 5.5 --- .../application/backup/index.blade.php | 33 +----------- .../application/deployment/index.blade.php | 43 ++++++++-------- .../project/application/previews.blade.php | 7 ++- .../views/livewire/project/index.blade.php | 48 +++++++++--------- .../livewire/project/resource/index.blade.php | 48 +++++++++--------- .../service/volume-backup/index.blade.php | 34 +------------ .../shared/environment-variable/all.blade.php | 19 ++++--- .../views/livewire/project/show.blade.php | 50 ++++++++++--------- .../livewire/security/api-tokens.blade.php | 9 ++-- resources/views/livewire/tags/show.blade.php | 50 ++++++++++--------- tests/v4/Browser/BackupSortTest.php | 23 ++------- 11 files changed, 153 insertions(+), 211 deletions(-) diff --git a/resources/views/livewire/project/application/backup/index.blade.php b/resources/views/livewire/project/application/backup/index.blade.php index 727a647f7d..9c441491e6 100644 --- a/resources/views/livewire/project/application/backup/index.blade.php +++ b/resources/views/livewire/project/application/backup/index.blade.php @@ -18,7 +18,6 @@ 's3' => $backup->save_s3 ? ($backup->s3Storages->isNotEmpty() ? $backup->s3Storages : collect([$backup->s3])->filter())->count() : -1, 'lastRunAt' => $backup->latestExecution?->finished_at?->timestamp ?? ($backup->latestExecution?->status === 'running' ? now()->timestamp : 0), 'executions' => $backup->executions_count, - 'createdAt' => $backup->created_at?->timestamp ?? 0, ])->values()), filterOptions: @js(collect([['value' => 'all', 'label' => 'All targets']])->merge( $backups->map(fn ($backup) => [ @@ -26,12 +25,6 @@ 'label' => $backup->targetType(), ])->unique('value')->values() )->values()), - sortOptions: [ - { value: 'target_asc', label: 'Target A–Z' }, - { value: 'target_desc', label: 'Target Z–A' }, - { value: 'newest', label: 'Newest first' }, - { value: 'oldest', label: 'Oldest first' }, - ], get filteredBackups() { const query = this.search.toLowerCase(); const filtered = this.backups.filter((backup) => { @@ -52,8 +45,6 @@ const sortValue = sortValues[column] || sortValues.target; const order = direction === 'desc' ? -1 : 1; return filtered.sort((left, right) => { - if (this.sortBy === 'newest') return right.createdAt - left.createdAt; - if (this.sortBy === 'oldest') return left.createdAt - right.createdAt; const leftValue = sortValue(left); const rightValue = sortValue(right); return order * (typeof leftValue === 'number' ? leftValue - rightValue : leftValue.localeCompare(rightValue)) || @@ -144,7 +135,7 @@ - - - - - diff --git a/resources/views/livewire/project/application/deployment/index.blade.php b/resources/views/livewire/project/application/deployment/index.blade.php index c5fe02f54b..11d868ad90 100644 --- a/resources/views/livewire/project/application/deployment/index.blade.php +++ b/resources/views/livewire/project/application/deployment/index.blade.php @@ -158,26 +158,29 @@ @endforeach @endif - - @foreach ([ - 'newest' => 'Newest first', - 'oldest' => 'Oldest first', - ] as $sortValue => $sortLabel) - - @endforeach - + {{-- The Started header sorts the full table; the embedded history has no such header. --}} + @if ($embedded) + + @foreach ([ + 'newest' => 'Newest first', + 'oldest' => 'Oldest first', + ] as $sortValue => $sortLabel) + + @endforeach + + @endif @if ($deployments->isNotEmpty()) diff --git a/resources/views/livewire/project/application/previews.blade.php b/resources/views/livewire/project/application/previews.blade.php index 01df79cdfe..2a3acd419a 100644 --- a/resources/views/livewire/project/application/previews.blade.php +++ b/resources/views/livewire/project/application/previews.blade.php @@ -231,9 +231,12 @@ {{ $previewIsStopped ? 'Deploy' : 'Redeploy' }} + @if (!$previewIsStopped || auth()->user()->can('delete', $application)) +
+ @endif @if (!$previewIsStopped) - - - + + + + diff --git a/resources/views/livewire/project/resource/index.blade.php b/resources/views/livewire/project/resource/index.blade.php index 21f030f647..38b03a3599 100644 --- a/resources/views/livewire/project/resource/index.blade.php +++ b/resources/views/livewire/project/resource/index.blade.php @@ -132,30 +132,32 @@ - - - - - - + + + + $backup->save_s3 ? ($backup->s3Storages->isNotEmpty() ? $backup->s3Storages : collect([$backup->s3])->filter())->where('team_id', $serviceTeamId)->count() : -1, 'lastRunAt' => $backup->latestExecution?->finished_at?->timestamp ?? ($backup->latestExecution?->status === 'running' ? now()->timestamp : 0), - 'createdAt' => $backup->created_at?->timestamp ?? 0, ])->concat($databaseBackups->map(fn ($backup) => [ 'id' => 'database:'.$backup->id, 'name' => strtolower($backup->database->human_name ?: $backup->database->name), @@ -31,7 +30,6 @@ }, 's3' => $backup->save_s3 ? ($backup->s3Storages->isNotEmpty() ? $backup->s3Storages : collect([$backup->s3])->filter())->where('team_id', $serviceTeamId)->count() : -1, 'lastRunAt' => $backup->latest_log?->finished_at?->timestamp ?? ($backup->latest_log?->status === 'running' ? now()->timestamp : 0), - 'createdAt' => $backup->created_at?->timestamp ?? 0, ]))->values()), filterOptions: @js(collect([['value' => 'all', 'label' => 'All targets']])->merge( $backups->map(fn ($backup) => [ @@ -39,12 +37,6 @@ 'label' => $backup->targetType(), ])->push(['value' => 'database', 'label' => 'Database'])->unique('value')->values() )->values()), - sortOptions: [ - { value: 'target_asc', label: 'Target A–Z' }, - { value: 'target_desc', label: 'Target Z–A' }, - { value: 'newest', label: 'Newest first' }, - { value: 'oldest', label: 'Oldest first' }, - ], get filteredBackups() { const query = this.search.toLowerCase(); const filtered = this.backups.filter((backup) => { @@ -64,8 +56,6 @@ const sortValue = sortValues[column] || sortValues.target; const order = direction === 'desc' ? -1 : 1; return filtered.sort((left, right) => { - if (this.sortBy === 'newest') return right.createdAt - left.createdAt; - if (this.sortBy === 'oldest') return left.createdAt - right.createdAt; const leftValue = sortValue(left); const rightValue = sortValue(right); return order * (typeof leftValue === 'number' ? leftValue - rightValue : leftValue.localeCompare(rightValue)) || @@ -220,7 +210,7 @@ - - - - - diff --git a/resources/views/livewire/project/shared/environment-variable/all.blade.php b/resources/views/livewire/project/shared/environment-variable/all.blade.php index f667f44ae3..55b614a968 100644 --- a/resources/views/livewire/project/shared/environment-variable/all.blade.php +++ b/resources/views/livewire/project/shared/environment-variable/all.blade.php @@ -170,14 +170,17 @@ @endforeach @endif - - @foreach (['default' => 'Default order', 'name_asc' => 'Name A–Z', 'name_desc' => 'Name Z–A'] as $value => $label) - - @endforeach - + {{-- The table header, with its Name sort, is hidden on small screens (app.css, max-width: 768px). --}} +
+ + @foreach (['default' => 'Default order', 'name_asc' => 'Name A–Z', 'name_desc' => 'Name Z–A'] as $value => $label) + + @endforeach + +
@can('manageEnvironment', $resource) {{-- Do not disable Add based on readyToLoad: modal-input uses wire:ignore, so a disabled attribute painted on first load would never re-enable. --}} diff --git a/resources/views/livewire/project/show.blade.php b/resources/views/livewire/project/show.blade.php index 30e9a432d6..95c072b35c 100644 --- a/resources/views/livewire/project/show.blade.php +++ b/resources/views/livewire/project/show.blade.php @@ -61,31 +61,33 @@
- - - - - - + + + +
+
diff --git a/resources/views/livewire/tags/show.blade.php b/resources/views/livewire/tags/show.blade.php index 41f0db49a8..6f9093cecc 100644 --- a/resources/views/livewire/tags/show.blade.php +++ b/resources/views/livewire/tags/show.blade.php @@ -57,31 +57,33 @@
- - - - - - + + + +
diff --git a/tests/v4/Browser/BackupSortTest.php b/tests/v4/Browser/BackupSortTest.php index 4d9b637145..c8386dbe97 100644 --- a/tests/v4/Browser/BackupSortTest.php +++ b/tests/v4/Browser/BackupSortTest.php @@ -13,11 +13,11 @@ beforeEach(function () { }); /** - * Creates volume backups for bravo, alpha, and charlie, in that order from oldest to newest. + * Creates volume backups for bravo, alpha, and charlie. */ function createSortableVolumeBackups(Model $resource): void { - foreach (['bravo', 'alpha', 'charlie'] as $index => $name) { + foreach (['bravo', 'alpha', 'charlie'] as $name) { $volume = LocalPersistentVolume::create([ 'uuid' => (string) new Cuid2, 'name' => $name.'-data', @@ -26,15 +26,13 @@ function createSortableVolumeBackups(Model $resource): void 'resource_type' => $resource->getMorphClass(), ]); - $backup = ScheduledVolumeBackup::create([ + ScheduledVolumeBackup::create([ 'backupable_type' => $volume->getMorphClass(), 'backupable_id' => $volume->id, 'team_id' => 0, 'frequency' => '0 0 * * *', 'enabled' => true, ]); - - ScheduledVolumeBackup::whereKey($backup->id)->update(['created_at' => now()->subDays(10 - $index)]); } } @@ -55,24 +53,13 @@ function visibleBackupOrderScript(): string function assertBackupSortOrders(mixed $page, string $screenshot): void { $page->assertSee('alpha-data') - ->assertScript(visibleBackupOrderScript(), 'alpha-data,bravo-data,charlie-data') - ->click('Sort') - ->click('Target Z–A') - ->assertScript(visibleBackupOrderScript(), 'charlie-data,bravo-data,alpha-data') - ->click('Sort') - ->click('Oldest first') - ->assertScript(visibleBackupOrderScript(), 'bravo-data,alpha-data,charlie-data') - ->click('Sort') - ->click('Newest first') - ->assertScript(visibleBackupOrderScript(), 'charlie-data,alpha-data,bravo-data') - ->click('[aria-label^="Sort by target"]') ->assertScript(visibleBackupOrderScript(), 'alpha-data,bravo-data,charlie-data') ->click('[aria-label^="Sort by target"]') ->assertScript(visibleBackupOrderScript(), 'charlie-data,bravo-data,alpha-data') ->screenshot(filename: $screenshot); } -it('reorders application storage backups when the sort option changes', function () { +it('reorders application storage backups when the target header is clicked', function () { $application = createBrowserApplication($this->stack, [ 'uuid' => 'app-browser-backup-sort', 'name' => 'Backup Sort App', @@ -86,7 +73,7 @@ it('reorders application storage backups when the sort option changes', function assertBackupSortOrders($page, 'application-backup-sort'); }); -it('reorders service storage backups when the sort option changes', function () { +it('reorders service storage backups when the target header is clicked', function () { ['service' => $service, 'serviceApplication' => $serviceApplication] = createBrowserService($this->stack, [ 'uuid' => 'svc-browser-backup-sort', ]);