Feature: Pagination für Quarantäne und Mail-Queue (25 pro Seite)
- Quarantäne und Queue zeigen je 25 Einträge pro Seite - Pagination-Bar mit Seitenanzeige (X-Y von Z) und Blätter-Buttons - Seite wird bei Filter- oder Suchwechsel auf 1 zurückgesetzt - Quarantäne: rows-Select entfernt (API holt intern 500, UI paginiert) - CSS-Klassen mq-pagination, mq-pag-btn passend zum Dark-Design Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>main v1.1.143
parent
b52ea46f22
commit
bc66870681
|
|
@ -18,12 +18,18 @@ class QuarantineList extends Component
|
|||
#[Url(as: 'q', keep: true)]
|
||||
public string $search = '';
|
||||
|
||||
#[Url(as: 'rows', keep: true)]
|
||||
public int $rows = 200;
|
||||
#[Url(as: 'page', keep: true)]
|
||||
public int $page = 1;
|
||||
|
||||
public int $perPage = 25;
|
||||
public int $rows = 500;
|
||||
|
||||
#[On('quarantine:updated')]
|
||||
public function refresh(): void {}
|
||||
|
||||
public function updatedFilter(): void { $this->page = 1; }
|
||||
public function updatedSearch(): void { $this->page = 1; }
|
||||
|
||||
public function openMessage(string $msgId): void
|
||||
{
|
||||
$this->dispatch('openModal',
|
||||
|
|
@ -61,7 +67,17 @@ class QuarantineList extends Component
|
|||
));
|
||||
}
|
||||
|
||||
return view('livewire.ui.nx.mail.quarantine-list', compact('messages', 'counts'));
|
||||
$total = count($messages);
|
||||
$totalPages = max(1, (int) ceil($total / $this->perPage));
|
||||
$this->page = max(1, min($this->page, $totalPages));
|
||||
$paged = array_slice($messages, ($this->page - 1) * $this->perPage, $this->perPage);
|
||||
|
||||
return view('livewire.ui.nx.mail.quarantine-list', [
|
||||
'messages' => $paged,
|
||||
'counts' => $counts,
|
||||
'total' => $total,
|
||||
'totalPages' => $totalPages,
|
||||
]);
|
||||
}
|
||||
|
||||
// ── helpers ──────────────────────────────────────────────────────────────
|
||||
|
|
|
|||
|
|
@ -18,12 +18,19 @@ class QueueList extends Component
|
|||
#[Url(as: 'q', keep: true)]
|
||||
public string $search = '';
|
||||
|
||||
public array $selected = [];
|
||||
public bool $selectAll = false;
|
||||
#[Url(as: 'page', keep: true)]
|
||||
public int $page = 1;
|
||||
|
||||
public int $perPage = 25;
|
||||
public array $selected = [];
|
||||
public bool $selectAll = false;
|
||||
|
||||
#[On('queue:updated')]
|
||||
public function refresh(): void {}
|
||||
|
||||
public function updatedFilter(): void { $this->page = 1; $this->selected = []; $this->selectAll = false; }
|
||||
public function updatedSearch(): void { $this->page = 1; }
|
||||
|
||||
public function updatedSelectAll(bool $val): void
|
||||
{
|
||||
$messages = $this->fetchQueue();
|
||||
|
|
@ -89,7 +96,17 @@ class QueueList extends Component
|
|||
));
|
||||
}
|
||||
|
||||
return view('livewire.ui.nx.mail.queue-list', compact('messages', 'counts'));
|
||||
$total = count($messages);
|
||||
$totalPages = max(1, (int) ceil($total / $this->perPage));
|
||||
$this->page = max(1, min($this->page, $totalPages));
|
||||
$paged = array_slice($messages, ($this->page - 1) * $this->perPage, $this->perPage);
|
||||
|
||||
return view('livewire.ui.nx.mail.queue-list', [
|
||||
'messages' => $paged,
|
||||
'counts' => $counts,
|
||||
'total' => $total,
|
||||
'totalPages' => $totalPages,
|
||||
]);
|
||||
}
|
||||
|
||||
// ── helpers ──────────────────────────────────────────────────────────────
|
||||
|
|
|
|||
|
|
@ -2168,3 +2168,12 @@ textarea.sec-input { height: auto; line-height: 1.55; padding: 8px 10px; resize:
|
|||
.audit-level.error { border-color: rgba(239,68,68,.3); background: rgba(239,68,68,.08); color: #fca5a5; }
|
||||
.audit-level.critical { border-color: rgba(239,68,68,.5); background: rgba(239,68,68,.15); color: #f87171; font-weight: 700; }
|
||||
.audit-msg { color: var(--mw-t2); min-width: 0; word-break: break-word; line-height: 1.55; font-family: ui-monospace,monospace; font-size: 11.5px; }
|
||||
|
||||
/* ── Pagination (Queue / Quarantine) ────────────────────────────── */
|
||||
.mq-pagination { display: flex; align-items: center; justify-content: space-between; padding: 10px 14px; border-top: 1px solid var(--mw-b2); }
|
||||
.mq-pag-info { font-size: 11.5px; color: var(--mw-t4); }
|
||||
.mq-pag-btns { display: flex; align-items: center; gap: 4px; }
|
||||
.mq-pag-btn { display: flex; align-items: center; justify-content: center; min-width: 28px; height: 26px; padding: 0 6px; border-radius: 5px; border: 1px solid var(--mw-b2); background: transparent; color: var(--mw-t3); font-size: 12px; cursor: pointer; transition: background .15s, border-color .15s, color .15s; }
|
||||
.mq-pag-btn:hover:not(:disabled) { background: var(--mw-bg3); color: var(--mw-t1); }
|
||||
.mq-pag-btn.active { background: rgba(124,58,237,.2); border-color: rgba(124,58,237,.5); color: #c4b5fd; }
|
||||
.mq-pag-btn:disabled { opacity: .35; cursor: default; }
|
||||
|
|
|
|||
|
|
@ -8,18 +8,13 @@
|
|||
<svg width="16" height="16" viewBox="0 0 14 14" fill="none"><circle cx="7" cy="7" r="5.5" stroke="currentColor" stroke-width="1.2"/><path d="M7 4v4l2.5 2" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/></svg>
|
||||
Quarantäne
|
||||
<span class="mq-total-badge">{{ $counts['all'] }}</span>
|
||||
<span class="mq-page-sub">Rspamd · letzte {{ count($messages) }} Einträge</span>
|
||||
<span class="mq-page-sub">Rspamd · {{ $total }} Einträge</span>
|
||||
</div>
|
||||
<div class="mq-page-actions">
|
||||
<div class="mq-search-wrap">
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none"><circle cx="5" cy="5" r="3.8" stroke="currentColor" stroke-width="1.2"/><path d="M8 8l2.5 2.5" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/></svg>
|
||||
<input type="text" wire:model.live.debounce.300ms="search" class="mq-search-input" placeholder="Von, An, Betreff …">
|
||||
</div>
|
||||
<select wire:model.live="rows" class="mq-select">
|
||||
<option value="100">100 Einträge</option>
|
||||
<option value="200">200 Einträge</option>
|
||||
<option value="500">500 Einträge</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -104,6 +99,22 @@
|
|||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@if($totalPages > 1)
|
||||
<div class="mq-pagination">
|
||||
<span class="mq-pag-info">{{ ($page - 1) * $perPage + 1 }}–{{ min($page * $perPage, $total) }} von {{ $total }}</span>
|
||||
<div class="mq-pag-btns">
|
||||
<button wire:click="$set('page', {{ max(1, $page - 1) }})" class="mq-pag-btn" @if($page <= 1) disabled @endif>
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none"><path d="M7.5 2L3.5 6l4 4" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||
</button>
|
||||
@for($i = max(1, $page - 2); $i <= min($totalPages, $page + 2); $i++)
|
||||
<button wire:click="$set('page', {{ $i }})" class="mq-pag-btn {{ $i === $page ? 'active' : '' }}">{{ $i }}</button>
|
||||
@endfor
|
||||
<button wire:click="$set('page', {{ min($totalPages, $page + 1) }})" class="mq-pag-btn" @if($page >= $totalPages) disabled @endif>
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none"><path d="M4.5 2L8.5 6l-4 4" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
@elseif($counts['all'] === 0)
|
||||
<div class="mq-empty">
|
||||
<svg width="36" height="36" viewBox="0 0 14 14" fill="none"><circle cx="7" cy="7" r="5.5" stroke="currentColor" stroke-width="1" opacity=".3"/><path d="M7 4v4l2.5 2" stroke="currentColor" stroke-width="1" stroke-linecap="round" opacity=".3"/></svg>
|
||||
|
|
|
|||
|
|
@ -92,6 +92,22 @@
|
|||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@if($totalPages > 1)
|
||||
<div class="mq-pagination">
|
||||
<span class="mq-pag-info">{{ ($page - 1) * $perPage + 1 }}–{{ min($page * $perPage, $total) }} von {{ $total }}</span>
|
||||
<div class="mq-pag-btns">
|
||||
<button wire:click="$set('page', {{ max(1, $page - 1) }})" class="mq-pag-btn" @if($page <= 1) disabled @endif>
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none"><path d="M7.5 2L3.5 6l4 4" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||
</button>
|
||||
@for($i = max(1, $page - 2); $i <= min($totalPages, $page + 2); $i++)
|
||||
<button wire:click="$set('page', {{ $i }})" class="mq-pag-btn {{ $i === $page ? 'active' : '' }}">{{ $i }}</button>
|
||||
@endfor
|
||||
<button wire:click="$set('page', {{ min($totalPages, $page + 1) }})" class="mq-pag-btn" @if($page >= $totalPages) disabled @endif>
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none"><path d="M4.5 2L8.5 6l-4 4" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
@else
|
||||
<div class="mq-empty">
|
||||
<svg width="36" height="36" viewBox="0 0 14 14" fill="none"><path d="M1 4h12M4 4V2h6v2M4 12V7m6 5V7" stroke="currentColor" stroke-width="1" stroke-linecap="round" opacity=".3"/></svg>
|
||||
|
|
|
|||
Loading…
Reference in New Issue