Files
SealShare/resources/views/livewire/admin/admin-dashboard.blade.php
T
surtic86andClaude Opus 5 82eeacea28 Show the SealShare version and links on the admin dashboard
A quiet line under the shares card reads "SealShare 2.1.0 · Release
notes · Website · Made by noNameWEB": the release notes link goes to the
installed version's Gitea release, the others to sealshare.nonameweb.ch
and nonameweb.ch, each in a new tab. It is page chrome, not content, so
it is a footer line rather than a card.

The image has neither .git nor CHANGELOG.md, so the version is a
constant in config/app.php, bumped with each release. AppVersionTest
fails while it differs from the newest released heading in
CHANGELOG.md, so a forgotten bump stops CI before the tag's image is
built. CHANGELOG (Unreleased) notes it.

The dashboard screenshots are unchanged: the line sits below their fold.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-18 21:13:19 +02:00

83 lines
5.6 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>
{{-- Page chrome, not content, so a quiet line instead of a card: the installed version, its release notes and who makes SealShare. --}}
<footer class="md-type-body-sm md-ink-variant md-text-center" data-test="dashboard-about">
SealShare {{ $version }}
· <a href="https://gitea.nonameweb.ch/noNameWEB/SealShare/releases/tag/v{{ $version }}" target="_blank" rel="noopener" class="md-link md-ink-primary">{{ __('Release notes') }}</a>
· <a href="https://sealshare.nonameweb.ch" target="_blank" rel="noopener" class="md-link md-ink-primary">{{ __('Website') }}</a>
· {{ __('Made by') }} <a href="https://nonameweb.ch" target="_blank" rel="noopener" class="md-link md-ink-primary">noNameWEB</a>
</footer>
<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>