feat(admin): make dashboard live and disk-aware
Wire dashboard panels to real alerts, activity, boxes, and users data instead of static mock rows. Enable working dashboard actions (close alerts, close low alerts, cleanup expired boxes, exports, and navigation). Update storage overview to use real filesystem free/total space from the uploads volume. Make top alert chip data-driven across admin pages.
This commit is contained in:
@@ -14,13 +14,12 @@
|
||||
<link rel="stylesheet" href="/static/css/admin.css">
|
||||
</head>
|
||||
<body>
|
||||
{{ $d := .Dashboard }}
|
||||
<div class="admin-shell">
|
||||
<div class="admin-frame">
|
||||
{{ template "admin/header.html" . }}
|
||||
|
||||
<!-- Dashboard Window -->
|
||||
<div class="win98-window admin-dashboard-window" role="main">
|
||||
<!-- Titlebar -->
|
||||
<div class="win98-titlebar">
|
||||
<div class="win98-titlebar-label">
|
||||
<img class="win98-titlebar-icon" src="/static/WarpBoxLogo.png" alt="" aria-hidden="true">
|
||||
@@ -33,7 +32,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Menu Bar -->
|
||||
<nav class="menu-bar" aria-label="Dashboard toolbar">
|
||||
<div class="menu-item">
|
||||
<button class="menu-button" type="button" aria-expanded="false">File</button>
|
||||
@@ -57,83 +55,74 @@
|
||||
<div class="menu-item">
|
||||
<button class="menu-button" type="button" aria-expanded="false">Boxes</button>
|
||||
<div class="menu-popup">
|
||||
<button class="menu-action" type="button" data-command="show-all-boxes"><span>B</span><span>Show all boxes</span><span></span></button>
|
||||
<button class="menu-action" type="button" data-command="export-boxes"><span>C</span><span>Export boxes CSV</span><span></span></button>
|
||||
<button class="menu-action" type="button" data-command="cleanup-dry-run"><span>D</span><span>Cleanup dry run</span><span></span></button>
|
||||
<button class="menu-action" type="button" data-command="show-all-boxes"><span>B</span><span>Open boxes page</span><span></span></button>
|
||||
<button class="menu-action" type="button" data-command="export-boxes"><span>C</span><span>Export visible boxes CSV</span><span></span></button>
|
||||
<button class="menu-action" type="button" data-command="cleanup-expired"><span>D</span><span>Cleanup expired boxes</span><span></span></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="menu-item">
|
||||
<button class="menu-button" type="button" aria-expanded="false">Alerts</button>
|
||||
<div class="menu-popup">
|
||||
<button class="menu-action" type="button" data-command="show-all-alerts"><span>!</span><span>Show all alerts</span><span></span></button>
|
||||
<button class="menu-action" type="button" data-command="dismiss-low-alerts"><span>L</span><span>Close all low alerts</span><span></span></button>
|
||||
<button class="menu-action" type="button" data-command="export-alerts"><span>J</span><span>Export alerts JSON</span><span></span></button>
|
||||
<button class="menu-action" type="button" data-command="show-all-alerts"><span>!</span><span>Open alerts page</span><span></span></button>
|
||||
<button class="menu-action" type="button" data-command="close-low-alerts"><span>L</span><span>Close low alerts</span><span></span></button>
|
||||
<button class="menu-action" type="button" data-command="export-alerts"><span>J</span><span>Export visible alerts JSON</span><span></span></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="menu-item">
|
||||
<button class="menu-button" type="button" aria-expanded="false">Admin</button>
|
||||
<div class="menu-popup">
|
||||
<button class="menu-action" type="button" data-command="config-snapshot"><span>S</span><span>Config snapshot</span><span></span></button>
|
||||
<button class="menu-action" type="button" data-command="support-summary"><span>?</span><span>Support summary</span><span></span></button>
|
||||
<button class="menu-action" type="button" data-command="thumbnail-rebuild"><span>I</span><span>Queue thumbnail rebuild</span><span></span></button>
|
||||
<div class="menu-separator"></div>
|
||||
<button class="menu-action" type="button" data-command="open-users"><span>U</span><span>Open user manager</span><span></span></button>
|
||||
<button class="menu-action" type="button" data-command="open-activity"><span>A</span><span>Open activity log</span><span></span></button>
|
||||
<button class="menu-action" type="button" data-command="open-settings"><span>G</span><span>Open settings</span><span></span></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="menu-item">
|
||||
<button class="menu-button" type="button" aria-expanded="false">Help</button>
|
||||
<div class="menu-popup">
|
||||
<button class="menu-action" type="button" data-command="alerts-help"><span>!</span><span>How alert tracing works</span><span></span></button>
|
||||
<button class="menu-action" type="button" data-command="shortcuts"><span>K</span><span>Keyboard shortcuts</span><span></span></button>
|
||||
<button class="menu-action" type="button" data-command="about"><span>W</span><span>About this mockup</span><span></span></button>
|
||||
<button class="menu-action" type="button" data-command="about"><span>W</span><span>About this dashboard</span><span></span></button>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- Dashboard Body -->
|
||||
<div class="dashboard-body">
|
||||
<!-- Hero -->
|
||||
<section class="dashboard-hero raised-panel" aria-labelledby="dashboardTitle">
|
||||
<div class="hero-copy">
|
||||
<h2 id="dashboardTitle">Dashboard</h2>
|
||||
<p>At-a-glance account and admin overview for boxes, alerts, storage, users, and recent activity.</p>
|
||||
<p>Live overview for boxes, alerts, storage, users, and recent account activity.</p>
|
||||
</div>
|
||||
<div class="hero-status" aria-label="System summary">
|
||||
<div class="hero-status-row"><span>Guest uploads</span><strong class="status-ok">enabled</strong></div>
|
||||
<div class="hero-status-row"><span>ZIP downloads</span><strong class="status-ok">enabled</strong></div>
|
||||
<div class="hero-status-row"><span>One-time boxes</span><strong class="status-warn">limited</strong></div>
|
||||
<div class="hero-status-row"><span>Guest uploads</span><strong class="{{ if eq $d.GuestUploadsLabel "enabled" }}status-ok{{ else }}status-danger{{ end }}">{{ $d.GuestUploadsLabel }}</strong></div>
|
||||
<div class="hero-status-row"><span>API uploads</span><strong class="{{ if eq $d.APIUploadsLabel "enabled" }}status-ok{{ else }}status-danger{{ end }}">{{ $d.APIUploadsLabel }}</strong></div>
|
||||
<div class="hero-status-row"><span>ZIP downloads</span><strong class="{{ if eq $d.ZipDownloadsLabel "enabled" }}status-ok{{ else }}status-warn{{ end }}">{{ $d.ZipDownloadsLabel }}</strong></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Stats -->
|
||||
<section class="stats-grid" aria-label="Dashboard statistics">
|
||||
<article class="stat-card sunken-panel is-info" id="activeBoxesCard">
|
||||
<p class="stat-label">Active boxes</p>
|
||||
<p class="stat-value">128</p>
|
||||
<p class="stat-note"><span class="stat-note-pill">+12 today</span><span class="stat-note-pill">42 passworded</span></p>
|
||||
<p class="stat-value">{{ $d.ActiveBoxes }}</p>
|
||||
<p class="stat-note"><span class="stat-note-pill">+{{ $d.BoxesCreatedToday }} today</span><span class="stat-note-pill">{{ $d.PasswordedBoxes }} passworded</span></p>
|
||||
</article>
|
||||
<article class="stat-card sunken-panel is-info" id="storageCard">
|
||||
<p class="stat-label">Storage available</p>
|
||||
<p class="stat-value">812 GiB</p>
|
||||
<p class="stat-note"><span class="stat-note-pill">188 GiB used</span><span class="stat-note-pill">1 TiB app cap</span><span class="stat-note-pill">local backend</span></p>
|
||||
<span class="meter-track" aria-hidden="true"><span class="meter-bar" style="--meter: 18.8%"></span></span>
|
||||
<p class="stat-value">{{ $d.StorageFreeLabel }}</p>
|
||||
<p class="stat-note"><span class="stat-note-pill">{{ $d.StorageUsedLabel }} used</span><span class="stat-note-pill">{{ $d.StorageCapLabel }} cap</span><span class="stat-note-pill">{{ $d.StorageBackend }} backend</span></p>
|
||||
<span class="meter-track" aria-hidden="true"><span class="meter-bar" style="--meter: {{ $d.StorageMeter }}"></span></span>
|
||||
</article>
|
||||
<article class="stat-card sunken-panel is-warning" id="alertsCard">
|
||||
<p class="stat-label">Alerts</p>
|
||||
<p class="stat-value"><span id="alertCountValue">15</span></p>
|
||||
<p class="stat-note" id="alertStatNote"><span class="stat-note-pill">2 high</span><span class="stat-note-pill">5 medium</span><span class="stat-note-pill">8 low</span></p>
|
||||
<p class="stat-value"><span id="alertCountValue">{{ $d.OpenAlerts }}</span></p>
|
||||
<p class="stat-note" id="alertStatNote"><span class="stat-note-pill">{{ $d.HighAlerts }} high</span><span class="stat-note-pill">{{ $d.MediumAlerts }} medium</span><span class="stat-note-pill">{{ $d.LowAlerts }} low</span></p>
|
||||
</article>
|
||||
<article class="stat-card sunken-panel is-ok" id="usersCard">
|
||||
<p class="stat-label">Users</p>
|
||||
<p class="stat-value">19</p>
|
||||
<p class="stat-note"><span class="stat-note-pill">15 active</span><span class="stat-note-pill">4 disabled</span><span class="stat-note-pill">admin-only</span></p>
|
||||
<p class="stat-value">{{ $d.TotalUsers }}</p>
|
||||
<p class="stat-note"><span class="stat-note-pill">{{ $d.ActiveUsers }} active</span><span class="stat-note-pill">{{ $d.DisabledUsers }} disabled</span><span class="stat-note-pill">{{ $d.APIKeyCount }} API keys</span></p>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
<!-- Main Grid: Alerts, Boxes, Activity -->
|
||||
<section class="dashboard-main-grid" aria-label="Dashboard panels">
|
||||
<!-- Alerts -->
|
||||
<article id="alerts" class="win98-window section-window">
|
||||
<div class="win98-titlebar">
|
||||
<div class="win98-titlebar-label">
|
||||
@@ -147,87 +136,29 @@
|
||||
<div class="section-body sunken-panel">
|
||||
<div class="scroll-panel alerts-scroll" aria-label="Scrollable alerts inbox">
|
||||
<div class="alert-list">
|
||||
<div class="alert-row" data-severity="high" data-alert-title="Storage backend is almost full" data-alert-code="421" data-alert-meta='{"backend":"local","used_bytes":1009317314560,"available_bytes":45097156608,"configured_cap_bytes":1099511627776,"recommended_action":"run cleanup dry run or raise app cap"}'>
|
||||
<span class="alert-severity">high</span>
|
||||
<div><p class="alert-title">Storage backend is almost full</p><p class="alert-desc">The active local storage backend has less than 5% free capacity under the configured app cap.</p><p class="alert-trace">code 421, trace storage.local.capacity.high</p></div>
|
||||
<div class="alert-actions"><button class="tiny-button" type="button" data-view-meta>Meta</button><button class="tiny-button" type="button" data-dismiss-alert>Close</button></div>
|
||||
</div>
|
||||
<div class="alert-row" data-severity="high" data-alert-title="Disabled user has active sessions" data-alert-code="181" data-alert-meta='{"user":"old-operator","active_sessions":2,"recommended_action":"revoke sessions"}'>
|
||||
<span class="alert-severity">high</span>
|
||||
<div><p class="alert-title">Disabled user has active sessions</p><p class="alert-desc">A disabled account still has active sessions that should be revoked.</p><p class="alert-trace">code 181, trace auth.sessions.disabled_user_active</p></div>
|
||||
<div class="alert-actions"><button class="tiny-button" type="button" data-view-meta>Meta</button><button class="tiny-button" type="button" data-dismiss-alert>Close</button></div>
|
||||
</div>
|
||||
<div class="alert-row" data-severity="medium" data-alert-title="Expired boxes waiting cleanup" data-alert-code="301" data-alert-meta='{"expired_boxes":17,"oldest_expired_at":"2026-04-29T22:18:00+03:00","recommended_action":"run cleanup"}'>
|
||||
<span class="alert-severity">medium</span>
|
||||
<div><p class="alert-title">Expired boxes waiting cleanup</p><p class="alert-desc">Expired boxes are still present on disk and are eligible for cleanup.</p><p class="alert-trace">code 301, trace boxes.expiry.cleanup_pending</p></div>
|
||||
<div class="alert-actions"><button class="tiny-button" type="button" data-view-meta>Meta</button><button class="tiny-button" type="button" data-dismiss-alert>Close</button></div>
|
||||
</div>
|
||||
<div class="alert-row" data-severity="medium" data-alert-title="API key UI enabled but key backend missing" data-alert-code="711" data-alert-meta='{"ui_surface":"upload.api_key_input","backend_model":"missing","recommended_action":"hide UI or implement API keys"}'>
|
||||
<span class="alert-severity">medium</span>
|
||||
<div><p class="alert-title">API key UI enabled but key backend missing</p><p class="alert-desc">The frontend advertises API key usage while server-side API key validation is not connected yet.</p><p class="alert-trace">code 711, trace api_keys.ui.backend_missing</p></div>
|
||||
<div class="alert-actions"><button class="tiny-button" type="button" data-view-meta>Meta</button><button class="tiny-button" type="button" data-dismiss-alert>Close</button></div>
|
||||
</div>
|
||||
<div class="alert-row" data-severity="medium" data-alert-title="Thumbnail queue is behind" data-alert-code="602" data-alert-meta='{"pending_thumbnails":44,"worker_interval_seconds":30,"recommended_action":"increase batch size or queue rebuild"}'>
|
||||
<span class="alert-severity">medium</span>
|
||||
<div><p class="alert-title">Thumbnail queue is behind</p><p class="alert-desc">The thumbnail worker has accumulated more pending previews than expected.</p><p class="alert-trace">code 602, trace thumbnails.worker.queue_lag</p></div>
|
||||
<div class="alert-actions"><button class="tiny-button" type="button" data-view-meta>Meta</button><button class="tiny-button" type="button" data-dismiss-alert>Close</button></div>
|
||||
</div>
|
||||
<div class="alert-row" data-severity="medium" data-alert-title="Large ZIP download failed" data-alert-code="502" data-alert-meta='{"box":"BX-7D20","zip_bytes":897300992,"attempt":1,"recommended_action":"retry manually or inspect files"}'>
|
||||
<span class="alert-severity">medium</span>
|
||||
<div><p class="alert-title">Large ZIP download failed</p><p class="alert-desc">A ZIP stream failed before the response finished.</p><p class="alert-trace">code 502, trace downloads.zip.stream_failed</p></div>
|
||||
<div class="alert-actions"><button class="tiny-button" type="button" data-view-meta>Meta</button><button class="tiny-button" type="button" data-dismiss-alert>Close</button></div>
|
||||
</div>
|
||||
<div class="alert-row" data-severity="medium" data-alert-title="Guest quota close to daily cap" data-alert-code="231" data-alert-meta='{"ip":"192.0.2.44","used_today_bytes":1795162112,"daily_cap_bytes":2147483648,"recommended_action":"none"}'>
|
||||
<span class="alert-severity">medium</span>
|
||||
<div><p class="alert-title">Guest quota close to daily cap</p><p class="alert-desc">A guest IP is close to its configured daily upload cap.</p><p class="alert-trace">code 231, trace quotas.guest.daily.near_cap</p></div>
|
||||
<div class="alert-actions"><button class="tiny-button" type="button" data-view-meta>Meta</button><button class="tiny-button" type="button" data-dismiss-alert>Close</button></div>
|
||||
</div>
|
||||
<div class="alert-row" data-severity="low" data-alert-title="Thumbnail generation skipped" data-alert-code="601" data-alert-meta='{"box":"BX-9F31","file":"mockup.webp","reason":"unsupported decoder","recommended_action":"none"}'>
|
||||
<span class="alert-severity">low</span>
|
||||
<div><p class="alert-title">Thumbnail generation skipped</p><p class="alert-desc">A preview could not be generated for one image file.</p><p class="alert-trace">code 601, trace thumbnails.generate.skipped</p></div>
|
||||
<div class="alert-actions"><button class="tiny-button" type="button" data-view-meta>Meta</button><button class="tiny-button" type="button" data-dismiss-alert>Close</button></div>
|
||||
</div>
|
||||
<div class="alert-row" data-severity="low" data-alert-title="One-time box downloaded" data-alert-code="511" data-alert-meta='{"box":"BX-440C","delete_after_success":true,"recommended_action":"none"}'>
|
||||
<span class="alert-severity">low</span>
|
||||
<div><p class="alert-title">One-time box downloaded</p><p class="alert-desc">A one-time ZIP handoff completed and the box was queued for deletion.</p><p class="alert-trace">code 511, trace downloads.one_time.completed</p></div>
|
||||
<div class="alert-actions"><button class="tiny-button" type="button" data-view-meta>Meta</button><button class="tiny-button" type="button" data-dismiss-alert>Close</button></div>
|
||||
</div>
|
||||
<div class="alert-row" data-severity="low" data-alert-title="Settings override changed" data-alert-code="801" data-alert-meta='{"setting":"box_poll_interval_ms","source":"admin_override","recommended_action":"audit when audit log exists"}'>
|
||||
<span class="alert-severity">low</span>
|
||||
<div><p class="alert-title">Settings override changed</p><p class="alert-desc">A runtime setting was changed through the settings UI.</p><p class="alert-trace">code 801, trace settings.override.changed</p></div>
|
||||
<div class="alert-actions"><button class="tiny-button" type="button" data-view-meta>Meta</button><button class="tiny-button" type="button" data-dismiss-alert>Close</button></div>
|
||||
</div>
|
||||
<div class="alert-row" data-severity="low" data-alert-title="Password protected box created" data-alert-code="121" data-alert-meta='{"box":"BX-C2A8","owner":"maya","recommended_action":"none"}'>
|
||||
<span class="alert-severity">low</span>
|
||||
<div><p class="alert-title">Password protected box created</p><p class="alert-desc">A user created a password protected upload box.</p><p class="alert-trace">code 121, trace boxes.create.passworded</p></div>
|
||||
<div class="alert-actions"><button class="tiny-button" type="button" data-view-meta>Meta</button><button class="tiny-button" type="button" data-dismiss-alert>Close</button></div>
|
||||
</div>
|
||||
<div class="alert-row" data-severity="low" data-alert-title="Upload completed slowly" data-alert-code="222" data-alert-meta='{"box":"BX-88B4","duration_seconds":731,"recommended_action":"none"}'>
|
||||
<span class="alert-severity">low</span>
|
||||
<div><p class="alert-title">Upload completed slowly</p><p class="alert-desc">An upload completed but exceeded the expected duration threshold.</p><p class="alert-trace">code 222, trace uploads.performance.slow_complete</p></div>
|
||||
<div class="alert-actions"><button class="tiny-button" type="button" data-view-meta>Meta</button><button class="tiny-button" type="button" data-dismiss-alert>Close</button></div>
|
||||
</div>
|
||||
<div class="alert-row" data-severity="low" data-alert-title="Session refreshed" data-alert-code="182" data-alert-meta='{"user":"admin","reason":"activity_refresh","recommended_action":"none"}'>
|
||||
<span class="alert-severity">low</span>
|
||||
<div><p class="alert-title">Session refreshed</p><p class="alert-desc">The current local session was refreshed after account activity.</p><p class="alert-trace">code 182, trace auth.session.refreshed</p></div>
|
||||
<div class="alert-actions"><button class="tiny-button" type="button" data-view-meta>Meta</button><button class="tiny-button" type="button" data-dismiss-alert>Close</button></div>
|
||||
</div>
|
||||
<div class="alert-row" data-severity="low" data-alert-title="Box visited from share URL" data-alert-code="401" data-alert-meta='{"box":"BX-39C1","viewer":"guest","recommended_action":"none"}'>
|
||||
<span class="alert-severity">low</span>
|
||||
<div><p class="alert-title">Box visited from share URL</p><p class="alert-desc">A public box was opened through its normal shared page.</p><p class="alert-trace">code 401, trace boxes.share.opened</p></div>
|
||||
<div class="alert-actions"><button class="tiny-button" type="button" data-view-meta>Meta</button><button class="tiny-button" type="button" data-dismiss-alert>Close</button></div>
|
||||
</div>
|
||||
<div class="alert-row" data-severity="low" data-alert-title="Support summary generated" data-alert-code="901" data-alert-meta='{"requested_by":"admin","included_sections":["config","storage","alerts"],"recommended_action":"none"}'>
|
||||
<span class="alert-severity">low</span>
|
||||
<div><p class="alert-title">Support summary generated</p><p class="alert-desc">A local support summary was generated from the toolbar.</p><p class="alert-trace">code 901, trace support.summary.generated</p></div>
|
||||
<div class="alert-actions"><button class="tiny-button" type="button" data-view-meta>Meta</button><button class="tiny-button" type="button" data-dismiss-alert>Close</button></div>
|
||||
</div>
|
||||
{{ if $d.Alerts }}
|
||||
{{ range $d.Alerts }}
|
||||
<div class="alert-row" data-alert-id="{{ .ID }}" data-severity="{{ .Severity }}" data-alert-title="{{ .Title }}" data-alert-code="{{ .Code }}" data-alert-meta='{{ toJSON .Meta }}'>
|
||||
<span class="alert-severity">{{ .Severity }}</span>
|
||||
<div>
|
||||
<p class="alert-title">{{ .Title }}</p>
|
||||
<p class="alert-desc">{{ .Message }}</p>
|
||||
<p class="alert-trace">{{ .Code }} {{ .Trace }} · {{ .CreatedAtLabel }} UTC · {{ .Status }}</p>
|
||||
</div>
|
||||
<div class="alert-actions">
|
||||
<button class="tiny-button" type="button" data-view-meta>Meta</button>
|
||||
<button class="tiny-button" type="button" data-close-alert>Close</button>
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
{{ else }}
|
||||
<div class="dashboard-empty-state">No open alerts. Nice and boring, which is the good kind of admin dashboard.</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<!-- Recent Activity -->
|
||||
<article id="recent-activity" class="win98-window section-window">
|
||||
<div class="win98-titlebar">
|
||||
<div class="win98-titlebar-label">
|
||||
@@ -235,33 +166,31 @@
|
||||
<h2>Recent Activity</h2>
|
||||
</div>
|
||||
<div class="titlebar-actions">
|
||||
<a class="titlebar-link-button" href="/admin/dashboard#recent-activity">Show all</a>
|
||||
<a class="titlebar-link-button" href="/admin/activity">Show all</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section-body sunken-panel">
|
||||
<div class="scroll-panel activity-scroll" aria-label="Scrollable recent activity list">
|
||||
<div class="activity-list">
|
||||
<div class="activity-row"><span class="activity-time">10:12</span><div><p class="activity-title">Box BX-9F31 completed upload</p><p class="activity-meta">4 files, password protected</p></div><span class="tag ok">box</span></div>
|
||||
<div class="activity-row"><span class="activity-time">10:08</span><div><p class="activity-title">Alert 421 created</p><p class="activity-meta">storage.local.capacity.high</p></div><span class="tag danger">alert</span></div>
|
||||
<div class="activity-row"><span class="activity-time">10:04</span><div><p class="activity-title">Guest created box BX-A71D</p><p class="activity-meta">retention 6 hours</p></div><span class="tag ok">upload</span></div>
|
||||
<div class="activity-row"><span class="activity-time">09:58</span><div><p class="activity-title">Thumbnail worker skipped one image</p><p class="activity-meta">decoder unavailable for webp preview</p></div><span class="tag warn">thumbs</span></div>
|
||||
<div class="activity-row"><span class="activity-time">09:51</span><div><p class="activity-title">Cleanup dry run opened</p><p class="activity-meta">17 expired boxes detected</p></div><span class="tag info">tools</span></div>
|
||||
<div class="activity-row"><span class="activity-time">09:44</span><div><p class="activity-title">Large ZIP download completed</p><p class="activity-meta">BX-7D20, 12 files</p></div><span class="tag info">zip</span></div>
|
||||
<div class="activity-row"><span class="activity-time">09:33</span><div><p class="activity-title">Settings snapshot requested</p><p class="activity-meta">admin opened config snapshot from toolbar</p></div><span class="tag info">settings</span></div>
|
||||
<div class="activity-row"><span class="activity-time">09:21</span><div><p class="activity-title">Temporary cleanup skipped</p><p class="activity-meta">BX-1AA2 still had an active file handle</p></div><span class="tag warn">cleanup</span></div>
|
||||
<div class="activity-row"><span class="activity-time">09:09</span><div><p class="activity-title">User maya uploaded 6 files</p><p class="activity-meta">91.9 MiB total</p></div><span class="tag ok">user</span></div>
|
||||
<div class="activity-row"><span class="activity-time">08:55</span><div><p class="activity-title">Box BX-55E0 expired</p><p class="activity-meta">eligible for cleanup</p></div><span class="tag danger">expired</span></div>
|
||||
<div class="activity-row"><span class="activity-time">08:42</span><div><p class="activity-title">One-time box created</p><p class="activity-meta">BX-440C, admin owner</p></div><span class="tag info">one-time</span></div>
|
||||
<div class="activity-row"><span class="activity-time">08:31</span><div><p class="activity-title">User ana uploaded archive set</p><p class="activity-meta">7 files, 520.8 MiB</p></div><span class="tag ok">upload</span></div>
|
||||
<div class="activity-row"><span class="activity-time">08:20</span><div><p class="activity-title">Guest accessed public box</p><p class="activity-meta">BX-39C1 viewed from share link</p></div><span class="tag info">access</span></div>
|
||||
<div class="activity-row"><span class="activity-time">08:07</span><div><p class="activity-title">User mihai created box BX-F02A</p><p class="activity-meta">standard plan quota applied</p></div><span class="tag ok">quota</span></div>
|
||||
<div class="activity-row"><span class="activity-time">07:54</span><div><p class="activity-title">Failed login attempt recorded</p><p class="activity-meta">admin account, single attempt</p></div><span class="tag warn">auth</span></div>
|
||||
{{ if $d.Events }}
|
||||
{{ range $d.Events }}
|
||||
<div class="activity-row">
|
||||
<span class="activity-time">{{ .CreatedAtLabel }}</span>
|
||||
<div>
|
||||
<p class="activity-title">{{ .Message }}</p>
|
||||
<p class="activity-meta">{{ .Kind }} · {{ .Method }} {{ .Path }} {{ if .IP }}· {{ .IP }}{{ end }}</p>
|
||||
</div>
|
||||
<span class="tag {{ .TagClass }}">{{ .TagLabel }}</span>
|
||||
</div>
|
||||
{{ end }}
|
||||
{{ else }}
|
||||
<div class="dashboard-empty-state">No activity has been recorded yet.</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<!-- Recent Boxes (full width) -->
|
||||
<article id="recent-boxes" class="win98-window section-window dashboard-span-2">
|
||||
<div class="win98-titlebar">
|
||||
<div class="win98-titlebar-label">
|
||||
@@ -269,29 +198,33 @@
|
||||
<h2>Recent Boxes</h2>
|
||||
</div>
|
||||
<div class="titlebar-actions">
|
||||
<a class="titlebar-link-button" href="/admin/dashboard#recent-boxes">Show all</a>
|
||||
<a class="titlebar-link-button" href="/admin/boxes">Show all</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section-body sunken-panel">
|
||||
<div class="scroll-panel boxes-scroll" aria-label="Scrollable recent boxes table">
|
||||
<table class="box-table">
|
||||
<thead><tr><th>Box</th><th>Owner</th><th>Files</th><th>Size</th><th>Created</th><th>Expires</th><th>Flags</th><th>Actions</th></tr></thead>
|
||||
<thead><tr><th>Box</th><th>Status</th><th>Files</th><th>Size</th><th>Created</th><th>Expires</th><th>Flags</th><th>Actions</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td>BX-9F31</td><td>maya</td><td>4</td><td>91.9 MiB</td><td>10:12</td><td>5h 41m</td><td><span class="tag ok">complete</span> <span class="tag info">password</span></td><td><div class="box-actions"><a class="win98-button box-action-button" href="/box/BX-9F31">Open</a><a class="win98-button box-action-button" href="/account/boxes/BX-9F31">Manage</a></div></td></tr>
|
||||
<tr><td>BX-A71D</td><td>guest</td><td>12</td><td>1.8 GiB</td><td>10:04</td><td>6h 00m</td><td><span class="tag warn">large</span></td><td><div class="box-actions"><a class="win98-button box-action-button" href="/box/BX-A71D">Open</a><a class="win98-button box-action-button" href="/account/boxes/BX-A71D">Manage</a></div></td></tr>
|
||||
<tr><td>BX-20BD</td><td>operator</td><td>2</td><td>8.4 MiB</td><td>09:58</td><td>1d 12h</td><td><span class="tag ok">complete</span></td><td><div class="box-actions"><a class="win98-button box-action-button" href="/box/BX-20BD">Open</a><a class="win98-button box-action-button" href="/account/boxes/BX-20BD">Manage</a></div></td></tr>
|
||||
<tr><td>BX-7D20</td><td>admin</td><td>12</td><td>856.3 MiB</td><td>09:44</td><td>23h 11m</td><td><span class="tag danger">zip failed</span></td><td><div class="box-actions"><a class="win98-button box-action-button" href="/box/BX-7D20">Open</a><a class="win98-button box-action-button" href="/account/boxes/BX-7D20">Manage</a></div></td></tr>
|
||||
<tr><td>BX-1AA2</td><td>guest</td><td>1</td><td>4.7 GiB</td><td>09:21</td><td>expired</td><td><span class="tag danger">locked</span></td><td><div class="box-actions"><a class="win98-button box-action-button" href="/box/BX-1AA2">Open</a><a class="win98-button box-action-button" href="/account/boxes/BX-1AA2">Manage</a></div></td></tr>
|
||||
<tr><td>BX-C2A8</td><td>maya</td><td>6</td><td>24.8 MiB</td><td>09:09</td><td>2d 03h</td><td><span class="tag ok">complete</span> <span class="tag info">password</span></td><td><div class="box-actions"><a class="win98-button box-action-button" href="/box/BX-C2A8">Open</a><a class="win98-button box-action-button" href="/account/boxes/BX-C2A8">Manage</a></div></td></tr>
|
||||
<tr><td>BX-55E0</td><td>guest</td><td>1</td><td>4.2 MiB</td><td>08:55</td><td>expired</td><td><span class="tag danger">expired</span></td><td><div class="box-actions"><a class="win98-button box-action-button" href="/box/BX-55E0">Open</a><a class="win98-button box-action-button" href="/account/boxes/BX-55E0">Manage</a></div></td></tr>
|
||||
<tr><td>BX-440C</td><td>admin</td><td>3</td><td>63.0 MiB</td><td>08:42</td><td>2d 00h</td><td><span class="tag ok">complete</span> <span class="tag info">one-time</span></td><td><div class="box-actions"><a class="win98-button box-action-button" href="/box/BX-440C">Open</a><a class="win98-button box-action-button" href="/account/boxes/BX-440C">Manage</a></div></td></tr>
|
||||
<tr><td>BX-88B4</td><td>ana</td><td>7</td><td>520.8 MiB</td><td>08:31</td><td>5d 00h</td><td><span class="tag ok">complete</span></td><td><div class="box-actions"><a class="win98-button box-action-button" href="/box/BX-88B4">Open</a><a class="win98-button box-action-button" href="/account/boxes/BX-88B4">Manage</a></div></td></tr>
|
||||
<tr><td>BX-39C1</td><td>guest</td><td>2</td><td>23.1 MiB</td><td>08:20</td><td>16h 00m</td><td><span class="tag info">public</span></td><td><div class="box-actions"><a class="win98-button box-action-button" href="/box/BX-39C1">Open</a><a class="win98-button box-action-button" href="/account/boxes/BX-39C1">Manage</a></div></td></tr>
|
||||
<tr><td>BX-F02A</td><td>mihai</td><td>5</td><td>108.6 MiB</td><td>08:07</td><td>4d 00h</td><td><span class="tag ok">complete</span></td><td><div class="box-actions"><a class="win98-button box-action-button" href="/box/BX-F02A">Open</a><a class="win98-button box-action-button" href="/account/boxes/BX-F02A">Manage</a></div></td></tr>
|
||||
<tr><td>BX-ABC4</td><td>guest</td><td>1</td><td>755 KiB</td><td>07:54</td><td>3h 00m</td><td><span class="tag ok">complete</span></td><td><div class="box-actions"><a class="win98-button box-action-button" href="/box/BX-ABC4">Open</a><a class="win98-button box-action-button" href="/account/boxes/BX-ABC4">Manage</a></div></td></tr>
|
||||
<tr><td>BX-74E9</td><td>operator</td><td>10</td><td>987.3 MiB</td><td>07:41</td><td>7d 00h</td><td><span class="tag info">bulk</span></td><td><div class="box-actions"><a class="win98-button box-action-button" href="/box/BX-74E9">Open</a><a class="win98-button box-action-button" href="/account/boxes/BX-74E9">Manage</a></div></td></tr>
|
||||
<tr><td>BX-218B</td><td>daniel</td><td>3</td><td>44.0 MiB</td><td>07:28</td><td>1d 00h</td><td><span class="tag ok">complete</span></td><td><div class="box-actions"><a class="win98-button box-action-button" href="/box/BX-218B">Open</a><a class="win98-button box-action-button" href="/account/boxes/BX-218B">Manage</a></div></td></tr>
|
||||
<tr><td>BX-00FE</td><td>guest</td><td>2</td><td>13.7 MiB</td><td>07:12</td><td>2h 00m</td><td><span class="tag warn">soon</span></td><td><div class="box-actions"><a class="win98-button box-action-button" href="/box/BX-00FE">Open</a><a class="win98-button box-action-button" href="/account/boxes/BX-00FE">Manage</a></div></td></tr>
|
||||
{{ if $d.Boxes }}
|
||||
{{ range $d.Boxes }}
|
||||
<tr data-box-id="{{ .ID }}">
|
||||
<td>{{ .ID }}</td>
|
||||
<td><span class="tag {{ .StatusClass }}">{{ .StatusLabel }}</span></td>
|
||||
<td>{{ .CompleteFiles }}/{{ .FileCount }}</td>
|
||||
<td>{{ .TotalSizeLabel }}</td>
|
||||
<td>{{ .CreatedAtLabel }}</td>
|
||||
<td>{{ .ExpiresAtLabel }}</td>
|
||||
<td>
|
||||
{{ range .Flags }}<span class="tag info">{{ . }}</span>{{ end }}
|
||||
{{ if not .Flags }}<span class="tag ok">plain</span>{{ end }}
|
||||
</td>
|
||||
<td><div class="box-actions"><a class="win98-button box-action-button" href="{{ .OpenURL }}">Open</a><a class="win98-button box-action-button" href="/admin/boxes?q={{ .ID }}">Manage</a></div></td>
|
||||
</tr>
|
||||
{{ end }}
|
||||
{{ else }}
|
||||
<tr><td colspan="8">No boxes found yet.</td></tr>
|
||||
{{ end }}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@@ -300,20 +233,17 @@
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<!-- Statusbar -->
|
||||
<div class="win98-statusbar admin-dashboard-statusbar">
|
||||
<span id="statusText">Ready</span>
|
||||
<span>WarpBox mock v5</span>
|
||||
<span>Single-window dashboard</span>
|
||||
<span>{{ $d.OpenAlerts }} open alert(s)</span>
|
||||
<span>Live dashboard</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal backdrop -->
|
||||
<div class="modal-backdrop" data-modal-backdrop></div>
|
||||
|
||||
<!-- Alert metadata popup -->
|
||||
<aside class="popup-window win98-window" data-alert-modal aria-label="Alert metadata" aria-hidden="true">
|
||||
<div class="win98-titlebar">
|
||||
<div class="win98-titlebar-label">
|
||||
@@ -327,9 +257,9 @@
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<!-- Toast -->
|
||||
<div class="toast" id="toast" role="status" aria-live="polite"></div>
|
||||
|
||||
<script id="dashboard-data" type="application/json">{{ toJSON $d }}</script>
|
||||
<script src="/static/js/warpbox-ui.js"></script>
|
||||
<script src="/static/js/admin/dashboard.js"></script>
|
||||
</body>
|
||||
|
||||
Reference in New Issue
Block a user