From dc6777a71d70df54c30ebf8aeaefef19dc87cbbf Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Tue, 13 Oct 2026 10:02:19 +0200 Subject: [PATCH] feat(server): paginate Docker images with adjustable page size Reset pagination when search or page size changes and clamp page bounds. Move disk usage badges beside the Images heading. --- app/Livewire/Server/DockerImages.php | 30 ++++++++++++++++++- .../application/settings-section.blade.php | 3 +- .../livewire/server/docker-images.blade.php | 17 +++++++++-- tests/Feature/ServerDockerImagesTest.php | 29 ++++++++++++++++++ 4 files changed, 74 insertions(+), 5 deletions(-) diff --git a/app/Livewire/Server/DockerImages.php b/app/Livewire/Server/DockerImages.php index c552002219..641a686851 100644 --- a/app/Livewire/Server/DockerImages.php +++ b/app/Livewire/Server/DockerImages.php @@ -4,12 +4,15 @@ namespace App\Livewire\Server; use App\Models\Server; use Illuminate\Foundation\Auth\Access\AuthorizesRequests; +use Illuminate\Pagination\LengthAwarePaginator; use Livewire\Attributes\Locked; use Livewire\Component; +use Livewire\WithPagination; class DockerImages extends Component { use AuthorizesRequests; + use WithPagination; public Server $server; @@ -27,6 +30,19 @@ class DockerImages extends Component public bool $loaded = false; + public int $perPage = 10; + + public function updatedSearch(): void + { + $this->resetPage(); + } + + public function updatedPerPage(): void + { + $this->perPage = max(1, min(100, $this->perPage)); + $this->resetPage(); + } + public function mount(string $server_uuid) { try { @@ -159,10 +175,22 @@ class DockerImages extends Component ->contains($search, ignoreCase: true)); } + $this->perPage = max(1, min(100, $this->perPage)); + $lastPage = max(1, (int) ceil($images->count() / $this->perPage)); + $page = max(1, min((int) $this->getPage(), $lastPage)); + if ($page !== $this->getPage()) { + $this->setPage($page); + } + return view('livewire.server.docker-images', [ // Not "images": Livewire shares public properties with the view, so that name is // already taken by the unfiltered array. - 'visibleImages' => $images->values(), + 'visibleImages' => new LengthAwarePaginator( + $images->forPage($page, $this->perPage)->values(), + $images->count(), + $this->perPage, + $page, + ), ]); } } diff --git a/resources/views/components/application/settings-section.blade.php b/resources/views/components/application/settings-section.blade.php index d1da325acf..1b1b93cb31 100644 --- a/resources/views/components/application/settings-section.blade.php +++ b/resources/views/components/application/settings-section.blade.php @@ -7,7 +7,7 @@
merge(['class' => 'application-settings-section', 'data-settings-section-title' => $title]) }}>
-
+
isset($badges)])> @if (filled($description ?? $helper))

@@ -19,6 +19,7 @@ @else

{{ $title }}

@endif + {{ $badges ?? '' }}

@isset($actions)
diff --git a/resources/views/livewire/server/docker-images.blade.php b/resources/views/livewire/server/docker-images.blade.php index 0e3a036bc0..ac7eef69e1 100644 --- a/resources/views/livewire/server/docker-images.blade.php +++ b/resources/views/livewire/server/docker-images.blade.php @@ -13,11 +13,13 @@ - - @if ($usage) + @if ($usage) + - @endif + + @endif + Refresh @@ -104,6 +106,15 @@
@endforeach
+ + + + + @endif diff --git a/tests/Feature/ServerDockerImagesTest.php b/tests/Feature/ServerDockerImagesTest.php index 51f139930b..d056d8c9e4 100644 --- a/tests/Feature/ServerDockerImagesTest.php +++ b/tests/Feature/ServerDockerImagesTest.php @@ -101,3 +101,32 @@ it('filters images by the search term', function () use ($fakeDocker, $nginxId, ->assertSee('1.27') ->assertDontSee(str_repeat('b', 12)); }); + +it('paginates images and returns to the first page when the search changes', function () { + Process::fake([ + '*docker image ls*' => Process::result(output: collect(range(1, 12)) + ->map(fn (int $i) => json_encode([ + 'Repository' => sprintf('app-%02d', $i), 'Tag' => 'latest', 'ID' => 'sha256:'.str_repeat(dechex($i), 64), + 'Size' => '1MB', 'CreatedSince' => '1 day ago', + ])) + ->implode("\n")), + '*docker ps -a*' => Process::result(output: ''), + '*docker system df*' => Process::result(output: ''), + ]); + + Livewire::test(DockerImages::class, ['server_uuid' => $this->server->uuid]) + ->call('load') + ->assertSee('app-10') + ->assertDontSee('app-11') + ->assertSee('1–10 of 12') + ->call('nextPage') + ->assertSee('app-12') + ->assertDontSee('app-01') + ->assertSee('11–12 of 12') + ->set('search', 'app-0') + ->assertSee('app-01') + ->assertSee('1–9 of 9') + ->set('perPage', 5) + ->assertSee('1–5 of 9') + ->assertDontSee('app-06'); +});