The sort select above the admin dashboard's shares list was capped at 20rem by the admin-shares-sort wrapper. The wrapper and its rule in app.css are gone, so the select stretches with the card's stack like the other fields. CHANGELOG (Unreleased) notes it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
75 lines
4.9 KiB
PHP
75 lines
4.9 KiB
PHP
<x-page :title="__('Admin Dashboard')" :description="__('Shares, files and storage at a glance')">
|
|
<x-grid :columns="2" gap="space200">
|
|
<x-stat :title="__('Total Shares')" :value="$totalShares" icon="link" />
|
|
<x-stat :title="__('Active Shares')" :value="$activeShares" icon="schedule" />
|
|
<x-stat :title="__('Total Files')" :value="$totalFiles" icon="description" />
|
|
<x-stat :title="__('Disk Usage')" :value="Number::fileSize($usedSpace)" icon="hard_drive" :description="Number::fileSize($usedSpace).' / '.Number::fileSize($maxQuota)">
|
|
<x-progress :value="$maxQuota > 0 ? min(100, ($usedSpace / $maxQuota) * 100) : 0" :label="__('Disk Usage')" />
|
|
</x-stat>
|
|
</x-grid>
|
|
|
|
{{-- The shares as a list, not a table: a table's columns need more than the page's 40rem, and
|
|
every page keeps that one width. The sort is a full-width select above the list instead of column headers. --}}
|
|
<x-card :title="__('All Shares')" heading="h2" variant="outlined">
|
|
<x-stack gap="space200">
|
|
@if ($shares->total() === 0)
|
|
<x-empty-state icon="link_off" :title="__('No shares yet')" :description="__('Shares appear here once someone uploads files.')" />
|
|
@else
|
|
<x-select
|
|
wire:model.live="sort"
|
|
:label="__('Sort by')"
|
|
:options="[
|
|
['id' => 'newest', 'name' => __('Newest first')],
|
|
['id' => 'oldest', 'name' => __('Oldest first')],
|
|
['id' => 'expiring', 'name' => __('Expiring soonest')],
|
|
['id' => 'largest', 'name' => __('Largest')],
|
|
['id' => 'most-downloaded', 'name' => __('Most downloads')],
|
|
['id' => 'most-files', 'name' => __('Most files')],
|
|
]"
|
|
data-test="shares-sort"
|
|
/>
|
|
|
|
{{-- Each share fits the column on a phone: the token opens it, so delete is the one button;
|
|
the details are two short lines that never clip (admin-shares, app.css). --}}
|
|
<x-list dividers :label="__('All Shares')" class="admin-shares">
|
|
@foreach ($shares as $share)
|
|
<x-list-item :overline="__('Created :time', ['time' => $share->created_at->diffForHumans()])" wire:key="share-{{ $share->id }}" data-test="share-row">
|
|
<a href="{{ route('share.download', $share) }}" target="_blank" rel="noopener" class="md-link"><code>{{ $share->token }}</code></a>
|
|
|
|
<x-slot:description>
|
|
<span class="admin-share-detail md-tabular">{{ trans_choice(':count file|:count files', $share->files_count) }} · {{ Number::fileSize($share->total_size) }} · {{ $share->max_downloads ? trans_choice(':count of :max download|:count of :max downloads', $share->max_downloads, ['count' => $share->download_count, 'max' => $share->max_downloads]) : trans_choice(':count download|:count downloads', $share->download_count) }}</span>
|
|
|
|
{{-- A share at its limit is closed; the cleanup deletes it a day after its last download. --}}
|
|
@if ($share->hasReachedDownloadLimit())
|
|
<span class="admin-share-detail md-ink-error">{{ __('Download limit reached') }}</span>
|
|
@elseif (! $share->expires_at)
|
|
<span class="admin-share-detail">{{ __('Never expires') }}</span>
|
|
@elseif ($share->isExpired())
|
|
<span class="admin-share-detail md-ink-error">{{ __('Expired :time', ['time' => $share->expires_at->diffForHumans()]) }}</span>
|
|
@else
|
|
<span class="admin-share-detail">{{ __('Expires :time', ['time' => $share->expires_at->diffForHumans()]) }}</span>
|
|
@endif
|
|
</x-slot:description>
|
|
|
|
<x-slot:end>
|
|
<x-button icon="delete" :tooltip="__('Delete')" color="error" wire:click="$set('deletingShareId', {{ $share->id }})" data-test="delete-share-{{ $share->id }}" />
|
|
</x-slot:end>
|
|
</x-list-item>
|
|
@endforeach
|
|
</x-list>
|
|
|
|
{{ $shares->links() }}
|
|
@endif
|
|
</x-stack>
|
|
</x-card>
|
|
|
|
<x-modal wire:model="deletingShareId" :title="__('Delete this share?')" icon="delete">
|
|
{{ __('Are you sure you want to delete this share?') }}
|
|
|
|
<x-slot:actions>
|
|
<x-button :label="__('Cancel')" x-on:click="close()" />
|
|
<x-button :label="__('Delete')" danger x-on:click="$wire.deleteShare($wire.deletingShareId)" data-test="confirm-delete-share" />
|
|
</x-slot:actions>
|
|
</x-modal>
|
|
</x-page>
|