mirror of
https://github.com/coollabsio/coolify.git
synced 2026-11-06 13:10:37 -04:00
fix(ui): align service backup tables and sort executions by column
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
70adcc2b0e
commit
7fbc5d7797
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace App\Livewire\Project\Service;
|
||||
|
||||
use App\Models\LocalFileVolume;
|
||||
use App\Models\LocalPersistentVolume;
|
||||
use App\Models\ScheduledDatabaseBackup;
|
||||
use App\Models\ScheduledDatabaseBackupExecution;
|
||||
use App\Models\ScheduledVolumeBackup;
|
||||
@@ -14,6 +16,7 @@ use Illuminate\Contracts\View\View;
|
||||
use Illuminate\Database\Query\Builder;
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
use Illuminate\Support\Collection;
|
||||
use Livewire\Attributes\Locked;
|
||||
use Livewire\Component;
|
||||
use Livewire\WithPagination;
|
||||
|
||||
@@ -26,8 +29,20 @@ class BackupExecutions extends Component
|
||||
|
||||
public Service $service;
|
||||
|
||||
private const SORT_COLUMNS = [
|
||||
'target' => 'sort_target',
|
||||
'type' => 'sort_type',
|
||||
'schedule' => 'sort_schedule',
|
||||
'status' => 'status',
|
||||
'started' => 'created_at',
|
||||
'size' => 'sort_size',
|
||||
];
|
||||
|
||||
public int $perPage = 10;
|
||||
|
||||
#[Locked]
|
||||
public string $sortBy = 'started_desc';
|
||||
|
||||
public bool $executionModalOpen = false;
|
||||
|
||||
public ?array $selectedExecution = null;
|
||||
@@ -55,6 +70,16 @@ class BackupExecutions extends Component
|
||||
$this->resetPage('executionsPage');
|
||||
}
|
||||
|
||||
public function sortExecutions(string $sortBy): void
|
||||
{
|
||||
[$column, $direction] = array_pad(explode('_', $sortBy, 2), 2, null);
|
||||
if (! array_key_exists($column, self::SORT_COLUMNS) || ! in_array($direction, ['asc', 'desc'], true)) {
|
||||
return;
|
||||
}
|
||||
$this->sortBy = $sortBy;
|
||||
$this->resetPage('executionsPage');
|
||||
}
|
||||
|
||||
public function openExecution(string $executionUuid): void
|
||||
{
|
||||
$this->authorize('view', $this->service);
|
||||
@@ -119,19 +144,33 @@ class BackupExecutions extends Component
|
||||
->forService($this->service)
|
||||
->select('id');
|
||||
|
||||
$fileVolumeType = (new LocalFileVolume)->getMorphClass();
|
||||
$persistentVolumeType = (new LocalPersistentVolume)->getMorphClass();
|
||||
|
||||
$databaseExecutions = ScheduledDatabaseBackupExecution::query()
|
||||
->select('id', 'uuid', 'created_at')
|
||||
->select('id', 'uuid', 'created_at', 'status')
|
||||
->selectRaw("'database' as type")
|
||||
->selectRaw("CAST(NULLIF(size, '') AS BIGINT) as sort_size")
|
||||
->selectRaw("(SELECT LOWER(COALESCE(NULLIF(service_databases.human_name, ''), service_databases.name)) FROM scheduled_database_backups JOIN service_databases ON service_databases.id = scheduled_database_backups.database_id WHERE scheduled_database_backups.id = scheduled_database_backup_executions.scheduled_database_backup_id) as sort_target")
|
||||
->selectRaw("'Database' as sort_type")
|
||||
->selectRaw('(SELECT frequency FROM scheduled_database_backups WHERE scheduled_database_backups.id = scheduled_database_backup_executions.scheduled_database_backup_id) as sort_schedule')
|
||||
->whereIn('scheduled_database_backup_id', $databaseScheduleIds)
|
||||
->when($uuid !== null, fn ($query) => $query->where('uuid', $uuid));
|
||||
$volumeExecutions = ScheduledVolumeBackupExecution::query()
|
||||
->select('id', 'uuid', 'created_at')
|
||||
->select('id', 'uuid', 'created_at', 'status')
|
||||
->selectRaw("'storage' as type")
|
||||
->selectRaw('size as sort_size')
|
||||
->selectRaw('(SELECT LOWER(COALESCE((SELECT fs_path FROM local_file_volumes WHERE local_file_volumes.id = scheduled_volume_backups.backupable_id AND scheduled_volume_backups.backupable_type = ?), (SELECT name FROM local_persistent_volumes WHERE local_persistent_volumes.id = scheduled_volume_backups.backupable_id AND scheduled_volume_backups.backupable_type = ?))) FROM scheduled_volume_backups WHERE scheduled_volume_backups.id = scheduled_volume_backup_executions.scheduled_volume_backup_id) as sort_target', [$fileVolumeType, $persistentVolumeType])
|
||||
->selectRaw("(SELECT CASE WHEN backupable_type = ? THEN 'Directory' ELSE 'Volume' END FROM scheduled_volume_backups WHERE scheduled_volume_backups.id = scheduled_volume_backup_executions.scheduled_volume_backup_id) as sort_type", [$fileVolumeType])
|
||||
->selectRaw('(SELECT frequency FROM scheduled_volume_backups WHERE scheduled_volume_backups.id = scheduled_volume_backup_executions.scheduled_volume_backup_id) as sort_schedule')
|
||||
->whereIn('scheduled_volume_backup_id', $volumeScheduleIds)
|
||||
->when($uuid !== null, fn ($query) => $query->where('uuid', $uuid));
|
||||
|
||||
[$sortColumn, $sortDirection] = explode('_', $this->sortBy, 2);
|
||||
|
||||
return $databaseExecutions->toBase()
|
||||
->unionAll($volumeExecutions->toBase())
|
||||
->orderBy(self::SORT_COLUMNS[$sortColumn], $sortDirection)
|
||||
->orderByDesc('created_at')
|
||||
->orderByDesc('id')
|
||||
->orderBy('type');
|
||||
|
||||
+2
-10
@@ -3540,8 +3540,9 @@ input[type="search"]::-webkit-search-results-decoration {
|
||||
min-width: 50rem;
|
||||
}
|
||||
|
||||
/* Shared by the Schedules and Executions tables of a service, so their columns line up. */
|
||||
.service-backup-table-grid {
|
||||
grid-template-columns: minmax(10rem, 1.7fr) 6rem minmax(7rem, 0.8fr) 7.5rem 6.5rem minmax(8rem, 1fr) 4.5rem;
|
||||
grid-template-columns: minmax(10rem, 1.5fr) 6rem minmax(7rem, 0.8fr) 7.5rem minmax(11rem, 1.2fr) minmax(6rem, 0.8fr) 4.5rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@@ -3551,18 +3552,9 @@ input[type="search"]::-webkit-search-results-decoration {
|
||||
|
||||
/* Rows are reordered with CSS order, so dividers must not depend on DOM position. */
|
||||
.data-table-row.service-backup-table-grid {
|
||||
background: var(--coollabs-base);
|
||||
border-top: 1px solid var(--coollabs-fill);
|
||||
}
|
||||
|
||||
.data-table-row.service-backup-table-grid:hover {
|
||||
background: color-mix(in srgb, var(--coollabs-base) 98%, black);
|
||||
}
|
||||
|
||||
.dark .data-table-row.service-backup-table-grid:hover {
|
||||
background: color-mix(in srgb, var(--coollabs-base) 98%, white);
|
||||
}
|
||||
|
||||
/* Persistent storage volumes: Name | [Source] | Destination | [PR suffix] | Backup | [Actions] */
|
||||
.volumes-table-grid-readonly {
|
||||
grid-template-columns: minmax(10rem, 1.4fr) minmax(8rem, 1fr) 5rem;
|
||||
|
||||
@@ -40,9 +40,16 @@
|
||||
description="Execution history appears here after a backup schedule runs." icon-name="browser-terminal" />
|
||||
@else
|
||||
<div class="data-table relative w-full overflow-x-auto">
|
||||
<x-table.loading target="previousPage,nextPage,setPage,perPage" text="Loading executions..." />
|
||||
<div class="data-table-header grid min-w-[900px] grid-cols-[minmax(150px,1.4fr)_100px_100px_110px_190px_90px_4.5rem]">
|
||||
<span>Target</span><span>Type</span><span>Schedule</span><span>Status</span><span>Started</span><span>Size</span><span class="text-right">Actions</span>
|
||||
<x-table.loading target="previousPage,nextPage,setPage,perPage,sortExecutions" text="Loading executions..." />
|
||||
<div class="flex min-w-[64rem] flex-col">
|
||||
<div class="data-table-header service-backup-table-grid">
|
||||
<x-table.sort-header label="Target" asc="target_asc" desc="target_desc" action="sortExecutions" :current="$sortBy" />
|
||||
<x-table.sort-header label="Type" asc="type_asc" desc="type_desc" action="sortExecutions" :current="$sortBy" />
|
||||
<x-table.sort-header label="Schedule" asc="schedule_asc" desc="schedule_desc" action="sortExecutions" :current="$sortBy" />
|
||||
<x-table.sort-header label="Status" asc="status_asc" desc="status_desc" action="sortExecutions" :current="$sortBy" />
|
||||
<x-table.sort-header label="Started" asc="started_asc" desc="started_desc" first="desc" action="sortExecutions" :current="$sortBy" />
|
||||
<x-table.sort-header label="Size" asc="size_asc" desc="size_desc" first="desc" action="sortExecutions" :current="$sortBy" />
|
||||
<span class="text-right">Actions</span>
|
||||
</div>
|
||||
@foreach ($executions as $execution)
|
||||
@php
|
||||
@@ -56,7 +63,7 @@
|
||||
<div wire:key="service-backup-execution-{{ $execution['id'] }}"
|
||||
wire:click="openExecution('{{ $execution['uuid'] }}')"
|
||||
wire:keydown.enter="openExecution('{{ $execution['uuid'] }}')" role="button" tabindex="0"
|
||||
class="data-table-row grid min-w-[900px] cursor-pointer grid-cols-[minmax(150px,1.4fr)_100px_100px_110px_190px_90px_4.5rem] text-left text-[13px] text-neutral-700 dark:text-fg-dim">
|
||||
class="data-table-row service-backup-table-grid cursor-pointer text-left text-[13px] text-neutral-700 dark:text-fg-dim">
|
||||
<span class="flex min-w-0 items-center gap-2 font-medium text-neutral-950 dark:text-fg">
|
||||
<span class="truncate" title="{{ $execution['target'] }}">{{ $execution['target'] }}</span>
|
||||
<span tabindex="0" data-tooltip="{{ $execution['s3_tooltip'] }}"
|
||||
@@ -87,6 +94,7 @@
|
||||
</span>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
@if ($executions->hasPages())
|
||||
<x-table-pagination :from="$executions->firstItem()" :to="$executions->lastItem()"
|
||||
:total="$executions->total()" :current-page="$executions->currentPage()" :last-page="$executions->lastPage()"
|
||||
|
||||
Reference in New Issue
Block a user