mirror of
https://github.com/coollabsio/coolify.git
synced 2026-11-06 13:10:37 -04:00
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.
This commit is contained in:
@@ -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,
|
||||
),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
<section {{ $attributes->merge(['class' => 'application-settings-section', 'data-settings-section-title' => $title]) }}>
|
||||
<header>
|
||||
<div class="min-w-0 py-0.5">
|
||||
<div @class(['min-w-0 py-0.5', 'flex flex-wrap items-center gap-2' => isset($badges)])>
|
||||
@if (filled($description ?? $helper))
|
||||
<h3>
|
||||
<x-helper :helper="$description ?? $helper" :label="'More information about '.$title">
|
||||
@@ -19,6 +19,7 @@
|
||||
@else
|
||||
<h3>{{ $title }}</h3>
|
||||
@endif
|
||||
{{ $badges ?? '' }}
|
||||
</div>
|
||||
@isset($actions)
|
||||
<div class="flex min-w-0 max-w-full flex-wrap items-center gap-2">
|
||||
|
||||
@@ -13,11 +13,13 @@
|
||||
<x-application.settings-section id="server-docker-images-section" title="Images"
|
||||
helper="Docker images stored on this server. Images that no container uses can be deleted here."
|
||||
flush>
|
||||
<x-slot:actions>
|
||||
@if ($usage)
|
||||
@if ($usage)
|
||||
<x-slot:badges>
|
||||
<x-status-badge :status="'Total ' . $usage['size']" type="neutral" />
|
||||
<x-status-badge :status="'Reclaimable ' . $usage['reclaimable']" type="warning" />
|
||||
@endif
|
||||
</x-slot:badges>
|
||||
@endif
|
||||
<x-slot:actions>
|
||||
<x-forms.button wire:click="load">
|
||||
<x-reicon name="refresh" class="size-3.5" />
|
||||
Refresh
|
||||
@@ -104,6 +106,15 @@
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
<x-table-pagination :from="$visibleImages->firstItem()" :to="$visibleImages->lastItem()"
|
||||
:total="$visibleImages->total()" :current-page="$visibleImages->currentPage()"
|
||||
:last-page="$visibleImages->lastPage()" wire-target="previousPage,nextPage,perPage"
|
||||
previous-action="previousPage" next-action="nextPage">
|
||||
<x-slot:pageSize>
|
||||
<x-page-size-select model="perPage" livewire
|
||||
storage-key="coolify.page-size.server-docker-images" />
|
||||
</x-slot:pageSize>
|
||||
</x-table-pagination>
|
||||
@endif
|
||||
</x-application.settings-section>
|
||||
</div>
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user