Implement pagination for network analysis, statistics history, and task history in Proxmox statistics view

This commit is contained in:
2025-12-08 19:35:21 -05:00
parent f37084bdb2
commit 1cdae8b693
2 changed files with 623 additions and 14 deletions

View File

@@ -897,11 +897,31 @@
<template id="statistics-history-export" type="application/json">
@json($statisticsExportRows)
</template>
@if($this->statistics->hasPages())
<div class="p-4 border-t border-neutral">
{{ $this->statistics->links() }}
<!-- Statistics History Pagination -->
<div class="p-4 border-t border-neutral">
<div class="flex flex-col sm:flex-row items-center justify-between gap-4">
<!-- Per Page Selector -->
<div class="flex items-center gap-2 text-sm">
<span class="text-base/70">Show</span>
<select wire:model.live="statisticsPerPage"
class="bg-background border border-neutral rounded-md px-2 py-1 text-sm focus:outline-none focus:ring-2 focus:ring-primary/50">
<option value="10">10</option>
<option value="25">25</option>
<option value="50">50</option>
<option value="100">100</option>
</select>
<span class="text-base/70">entries</span>
<span class="text-base/50 ml-2">({{ $this->statistics->total() }} total)</span>
</div>
<!-- Pagination Links -->
@if($this->statistics->hasPages())
<div class="flex items-center gap-1">
{{ $this->statistics->links() }}
</div>
@endif
</div>
@endif
</div>
</div>
<!-- Task History Section -->
@@ -2160,12 +2180,12 @@
</tr>
</thead>
<tbody class="divide-y divide-neutral/40">
@foreach($networkAnalysis['connections'] as $index => $connection)
@foreach($this->paginatedConnections as $index => $connection)
@php
// Use raw bytes value from backend for reliable numeric sorting
$bytesForSort = (int)($connection['bytes'] ?? 0);
@endphp
<tr wire:key="conn-{{ $index }}"
<tr wire:key="conn-{{ $connectionsCurrentPage }}-{{ $index }}"
class="hover:bg-background-secondary/50 transition-colors table-row"
data-direction="{{ strtolower($connection['direction'] ?? '') }}"
data-protocol="{{ strtolower($connection['protocol'] ?? '') }}"
@@ -2561,6 +2581,98 @@
</tbody>
</table>
</div>
{{-- Connections Pagination Controls --}}
@if(count($networkAnalysis['connections']) > 0)
<div class="p-4 border-t border-neutral flex flex-col sm:flex-row items-center justify-between gap-4">
{{-- Records per page selector --}}
<div class="flex items-center gap-3">
<label class="text-sm text-base/70">Show:</label>
<select wire:model.live="connectionsPerPage" class="px-3 py-1.5 border border-neutral rounded-lg bg-background text-sm">
<option value="10">10</option>
<option value="25">25</option>
<option value="50">50</option>
<option value="100">100</option>
<option value="250">250</option>
</select>
<span class="text-sm text-base/60">per page</span>
</div>
{{-- Page info and navigation --}}
<div class="flex items-center gap-4">
<span class="text-sm text-base/60">
Showing {{ (($connectionsCurrentPage - 1) * $connectionsPerPage) + 1 }} - {{ min($connectionsCurrentPage * $connectionsPerPage, count($networkAnalysis['connections'])) }} of {{ count($networkAnalysis['connections']) }} connections
</span>
<div class="flex items-center gap-1">
{{-- First page --}}
<button type="button"
wire:click="goToConnectionsPage(1)"
@if($connectionsCurrentPage === 1) disabled @endif
class="px-2 py-1 border border-neutral rounded hover:bg-background-secondary transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
title="First page">
<x-ri-arrow-left-double-line class="size-4" />
</button>
{{-- Previous page --}}
<button type="button"
wire:click="previousConnectionsPage"
@if($connectionsCurrentPage === 1) disabled @endif
class="px-2 py-1 border border-neutral rounded hover:bg-background-secondary transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
title="Previous page">
<x-ri-arrow-left-s-line class="size-4" />
</button>
{{-- Page numbers --}}
@php
$connectionsTotalPages = $this->connectionsTotalPages;
$startPage = max(1, $connectionsCurrentPage - 2);
$endPage = min($connectionsTotalPages, $connectionsCurrentPage + 2);
@endphp
@if($startPage > 1)
<button type="button" wire:click="goToConnectionsPage(1)" class="px-3 py-1 border border-neutral rounded hover:bg-background-secondary transition-colors text-sm">1</button>
@if($startPage > 2)
<span class="px-2 text-base/60">...</span>
@endif
@endif
@for($i = $startPage; $i <= $endPage; $i++)
<button type="button"
wire:click="goToConnectionsPage({{ $i }})"
class="px-3 py-1 border rounded text-sm transition-colors {{ $connectionsCurrentPage === $i ? 'bg-primary text-white border-primary' : 'border-neutral hover:bg-background-secondary' }}">
{{ $i }}
</button>
@endfor
@if($endPage < $connectionsTotalPages)
@if($endPage < $connectionsTotalPages - 1)
<span class="px-2 text-base/60">...</span>
@endif
<button type="button" wire:click="goToConnectionsPage({{ $connectionsTotalPages }})" class="px-3 py-1 border border-neutral rounded hover:bg-background-secondary transition-colors text-sm">{{ $connectionsTotalPages }}</button>
@endif
{{-- Next page --}}
<button type="button"
wire:click="nextConnectionsPage"
@if($connectionsCurrentPage === $connectionsTotalPages) disabled @endif
class="px-2 py-1 border border-neutral rounded hover:bg-background-secondary transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
title="Next page">
<x-ri-arrow-right-s-line class="size-4" />
</button>
{{-- Last page --}}
<button type="button"
wire:click="goToConnectionsPage({{ $connectionsTotalPages }})"
@if($connectionsCurrentPage === $connectionsTotalPages) disabled @endif
class="px-2 py-1 border border-neutral rounded hover:bg-background-secondary transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
title="Last page">
<x-ri-arrow-right-double-line class="size-4" />
</button>
</div>
</div>
</div>
@endif
</div>
@endif
</div>
@@ -2617,7 +2729,7 @@
</tr>
</thead>
<tbody class="divide-y divide-neutral/30">
@foreach($networkAnalysis['topConnections'] as $index => $row)
@foreach($this->paginatedTopConnections as $index => $row)
@php
$endpointIP = $row['endpoint'] ?? '—';
$port = $row['port'] ?? 0;
@@ -2625,7 +2737,7 @@
$bytes = $row['bytes'] ?? 0;
$bytesForSort = $bytes;
@endphp
<tr class="hover:bg-background-secondary/60 table-row"
<tr wire:key="top-conn-{{ $topConnectionsCurrentPage }}-{{ $index }}" class="hover:bg-background-secondary/60 table-row"
data-endpoint="{{ strtolower($endpointIP) }}"
data-port="{{ $port }}"
data-connections="{{ $connections }}"
@@ -2792,6 +2904,98 @@
</tbody>
</table>
</div>
{{-- Top Connections Pagination Controls --}}
@if(count($networkAnalysis['topConnections']) > 0)
<div class="p-4 border-t border-neutral flex flex-col sm:flex-row items-center justify-between gap-4">
{{-- Records per page selector --}}
<div class="flex items-center gap-3">
<label class="text-sm text-base/70">Show:</label>
<select wire:model.live="topConnectionsPerPage" class="px-3 py-1.5 border border-neutral rounded-lg bg-background text-sm">
<option value="10">10</option>
<option value="25">25</option>
<option value="50">50</option>
<option value="100">100</option>
<option value="250">250</option>
</select>
<span class="text-sm text-base/60">per page</span>
</div>
{{-- Page info and navigation --}}
<div class="flex items-center gap-4">
<span class="text-sm text-base/60">
Showing {{ (($topConnectionsCurrentPage - 1) * $topConnectionsPerPage) + 1 }} - {{ min($topConnectionsCurrentPage * $topConnectionsPerPage, count($networkAnalysis['topConnections'])) }} of {{ count($networkAnalysis['topConnections']) }} peers
</span>
<div class="flex items-center gap-1">
{{-- First page --}}
<button type="button"
wire:click="goToTopConnectionsPage(1)"
@if($topConnectionsCurrentPage === 1) disabled @endif
class="px-2 py-1 border border-neutral rounded hover:bg-background-secondary transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
title="First page">
<x-ri-arrow-left-double-line class="size-4" />
</button>
{{-- Previous page --}}
<button type="button"
wire:click="previousTopConnectionsPage"
@if($topConnectionsCurrentPage === 1) disabled @endif
class="px-2 py-1 border border-neutral rounded hover:bg-background-secondary transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
title="Previous page">
<x-ri-arrow-left-s-line class="size-4" />
</button>
{{-- Page numbers --}}
@php
$topConnectionsTotalPages = $this->topConnectionsTotalPages;
$startPage = max(1, $topConnectionsCurrentPage - 2);
$endPage = min($topConnectionsTotalPages, $topConnectionsCurrentPage + 2);
@endphp
@if($startPage > 1)
<button type="button" wire:click="goToTopConnectionsPage(1)" class="px-3 py-1 border border-neutral rounded hover:bg-background-secondary transition-colors text-sm">1</button>
@if($startPage > 2)
<span class="px-2 text-base/60">...</span>
@endif
@endif
@for($i = $startPage; $i <= $endPage; $i++)
<button type="button"
wire:click="goToTopConnectionsPage({{ $i }})"
class="px-3 py-1 border rounded text-sm transition-colors {{ $topConnectionsCurrentPage === $i ? 'bg-primary text-white border-primary' : 'border-neutral hover:bg-background-secondary' }}">
{{ $i }}
</button>
@endfor
@if($endPage < $topConnectionsTotalPages)
@if($endPage < $topConnectionsTotalPages - 1)
<span class="px-2 text-base/60">...</span>
@endif
<button type="button" wire:click="goToTopConnectionsPage({{ $topConnectionsTotalPages }})" class="px-3 py-1 border border-neutral rounded hover:bg-background-secondary transition-colors text-sm">{{ $topConnectionsTotalPages }}</button>
@endif
{{-- Next page --}}
<button type="button"
wire:click="nextTopConnectionsPage"
@if($topConnectionsCurrentPage === $topConnectionsTotalPages) disabled @endif
class="px-2 py-1 border border-neutral rounded hover:bg-background-secondary transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
title="Next page">
<x-ri-arrow-right-s-line class="size-4" />
</button>
{{-- Last page --}}
<button type="button"
wire:click="goToTopConnectionsPage({{ $topConnectionsTotalPages }})"
@if($topConnectionsCurrentPage === $topConnectionsTotalPages) disabled @endif
class="px-2 py-1 border border-neutral rounded hover:bg-background-secondary transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
title="Last page">
<x-ri-arrow-right-double-line class="size-4" />
</button>
</div>
</div>
</div>
@endif
</div>
@endif
</div>
@@ -2842,14 +3046,14 @@
</tr>
</thead>
<tbody class="divide-y divide-neutral/30">
@foreach($networkAnalysis['topPorts'] as $index => $row)
@foreach($this->paginatedTopPorts as $index => $row)
@php
$port = $row['port'] ?? 0;
$connections = $row['connections'] ?? 0;
$bytes = $row['bytes'] ?? 0;
$bytesForSort = $bytes;
@endphp
<tr class="hover:bg-background-secondary/60 table-row"
<tr wire:key="top-port-{{ $topPortsCurrentPage }}-{{ $index }}" class="hover:bg-background-secondary/60 table-row"
data-port="{{ $port }}"
data-connections="{{ $connections }}"
data-traffic="{{ $bytesForSort }}">
@@ -2913,6 +3117,98 @@
</tbody>
</table>
</div>
{{-- Top Ports Pagination Controls --}}
@if(count($networkAnalysis['topPorts']) > 0)
<div class="p-4 border-t border-neutral flex flex-col sm:flex-row items-center justify-between gap-4">
{{-- Records per page selector --}}
<div class="flex items-center gap-3">
<label class="text-sm text-base/70">Show:</label>
<select wire:model.live="topPortsPerPage" class="px-3 py-1.5 border border-neutral rounded-lg bg-background text-sm">
<option value="10">10</option>
<option value="25">25</option>
<option value="50">50</option>
<option value="100">100</option>
<option value="250">250</option>
</select>
<span class="text-sm text-base/60">per page</span>
</div>
{{-- Page info and navigation --}}
<div class="flex items-center gap-4">
<span class="text-sm text-base/60">
Showing {{ (($topPortsCurrentPage - 1) * $topPortsPerPage) + 1 }} - {{ min($topPortsCurrentPage * $topPortsPerPage, count($networkAnalysis['topPorts'])) }} of {{ count($networkAnalysis['topPorts']) }} ports
</span>
<div class="flex items-center gap-1">
{{-- First page --}}
<button type="button"
wire:click="goToTopPortsPage(1)"
@if($topPortsCurrentPage === 1) disabled @endif
class="px-2 py-1 border border-neutral rounded hover:bg-background-secondary transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
title="First page">
<x-ri-arrow-left-double-line class="size-4" />
</button>
{{-- Previous page --}}
<button type="button"
wire:click="previousTopPortsPage"
@if($topPortsCurrentPage === 1) disabled @endif
class="px-2 py-1 border border-neutral rounded hover:bg-background-secondary transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
title="Previous page">
<x-ri-arrow-left-s-line class="size-4" />
</button>
{{-- Page numbers --}}
@php
$topPortsTotalPages = $this->topPortsTotalPages;
$startPage = max(1, $topPortsCurrentPage - 2);
$endPage = min($topPortsTotalPages, $topPortsCurrentPage + 2);
@endphp
@if($startPage > 1)
<button type="button" wire:click="goToTopPortsPage(1)" class="px-3 py-1 border border-neutral rounded hover:bg-background-secondary transition-colors text-sm">1</button>
@if($startPage > 2)
<span class="px-2 text-base/60">...</span>
@endif
@endif
@for($i = $startPage; $i <= $endPage; $i++)
<button type="button"
wire:click="goToTopPortsPage({{ $i }})"
class="px-3 py-1 border rounded text-sm transition-colors {{ $topPortsCurrentPage === $i ? 'bg-primary text-white border-primary' : 'border-neutral hover:bg-background-secondary' }}">
{{ $i }}
</button>
@endfor
@if($endPage < $topPortsTotalPages)
@if($endPage < $topPortsTotalPages - 1)
<span class="px-2 text-base/60">...</span>
@endif
<button type="button" wire:click="goToTopPortsPage({{ $topPortsTotalPages }})" class="px-3 py-1 border border-neutral rounded hover:bg-background-secondary transition-colors text-sm">{{ $topPortsTotalPages }}</button>
@endif
{{-- Next page --}}
<button type="button"
wire:click="nextTopPortsPage"
@if($topPortsCurrentPage === $topPortsTotalPages) disabled @endif
class="px-2 py-1 border border-neutral rounded hover:bg-background-secondary transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
title="Next page">
<x-ri-arrow-right-s-line class="size-4" />
</button>
{{-- Last page --}}
<button type="button"
wire:click="goToTopPortsPage({{ $topPortsTotalPages }})"
@if($topPortsCurrentPage === $topPortsTotalPages) disabled @endif
class="px-2 py-1 border border-neutral rounded hover:bg-background-secondary transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
title="Last page">
<x-ri-arrow-right-double-line class="size-4" />
</button>
</div>
</div>
</div>
@endif
</div>
@endif
</div>