Files
warpbox/templates/admin/dashboard.html

268 lines
19 KiB
HTML
Raw Normal View History

{{ define "admin/dashboard.html" }}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>WarpBox Admin Dashboard</title>
<link rel="icon" type="image/png" href="/static/WarpBoxLogo.png">
<link rel="stylesheet" href="/static/css/app.css">
<link rel="stylesheet" href="/static/css/window.css">
<link rel="stylesheet" href="/static/css/dashboard.css">
<link rel="stylesheet" href="/static/css/components/buttons.css">
<link rel="stylesheet" href="/static/css/components/toast.css">
<link rel="stylesheet" href="/static/css/admin.css">
</head>
<body>
{{ $d := .Dashboard }}
<div class="admin-shell">
<div class="admin-frame">
{{ template "admin/header.html" . }}
<div class="win98-window admin-dashboard-window" role="main">
<div class="win98-titlebar">
<div class="win98-titlebar-label">
<img class="win98-titlebar-icon" src="/static/WarpBoxLogo.png" alt="" aria-hidden="true">
<h1>WarpBox Account Control Panel</h1>
</div>
<div class="win98-window-controls" aria-hidden="true">
<button class="win98-control" type="button">_</button>
<button class="win98-control" type="button"></button>
<button class="win98-control" type="button">x</button>
</div>
</div>
<nav class="menu-bar" aria-label="Dashboard toolbar">
<div class="menu-item">
<button class="menu-button" type="button" aria-expanded="false">File</button>
<div class="menu-popup">
<button class="menu-action" type="button" data-command="refresh"><span>R</span><span>Refresh dashboard</span><span class="shortcut">F5</span></button>
<button class="menu-action" type="button" data-command="dashboard-snapshot"><span>S</span><span>Export dashboard snapshot</span><span></span></button>
<div class="menu-separator"></div>
<button class="menu-action" type="button" data-command="logout"><span>Q</span><span>Log out</span><span></span></button>
</div>
</div>
<div class="menu-item">
<button class="menu-button" type="button" aria-expanded="false">View</button>
<div class="menu-popup">
<button class="menu-action" type="button" data-scroll-to="alerts"><span>!</span><span>Go to alerts</span><span class="shortcut">Alt+A</span></button>
<button class="menu-action" type="button" data-scroll-to="recent-boxes"><span>B</span><span>Go to recent boxes</span><span class="shortcut">Alt+B</span></button>
<button class="menu-action" type="button" data-scroll-to="recent-activity"><span>T</span><span>Go to recent activity</span><span class="shortcut">Alt+R</span></button>
<div class="menu-separator"></div>
<button class="menu-action" type="button" data-command="compact-mode"><span>C</span><span>Toggle compact density</span><span></span></button>
</div>
</div>
<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>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>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="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="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 dashboard</span><span></span></button>
</div>
</div>
</nav>
<div class="dashboard-body">
<section class="dashboard-hero raised-panel" aria-labelledby="dashboardTitle">
<div class="hero-copy">
<h2 id="dashboardTitle">Dashboard</h2>
<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="{{ 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>
<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">{{ $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">{{ $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">{{ $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">{{ $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>
<section class="dashboard-main-grid" aria-label="Dashboard panels">
<article id="alerts" class="win98-window section-window">
<div class="win98-titlebar">
<div class="win98-titlebar-label">
<span class="win98-titlebar-icon">!</span>
<h2>Alerts Inbox</h2>
</div>
<div class="titlebar-actions">
<a class="titlebar-link-button" href="/admin/alerts">Show all</a>
</div>
</div>
<div class="section-body sunken-panel">
<div class="scroll-panel alerts-scroll" aria-label="Scrollable alerts inbox">
<div class="alert-list">
{{ 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>
<article id="recent-activity" class="win98-window section-window">
<div class="win98-titlebar">
<div class="win98-titlebar-label">
<span class="win98-titlebar-icon">T</span>
<h2>Recent Activity</h2>
</div>
<div class="titlebar-actions">
<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">
{{ 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>
<article id="recent-boxes" class="win98-window section-window dashboard-span-2">
<div class="win98-titlebar">
<div class="win98-titlebar-label">
<span class="win98-titlebar-icon">B</span>
<h2>Recent Boxes</h2>
</div>
<div class="titlebar-actions">
<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>Status</th><th>Files</th><th>Size</th><th>Created</th><th>Expires</th><th>Flags</th><th>Actions</th></tr></thead>
<tbody>
{{ 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>
</div>
</article>
</section>
</div>
<div class="win98-statusbar admin-dashboard-statusbar">
<span id="statusText">Ready</span>
<span>{{ $d.OpenAlerts }} open alert(s)</span>
<span>Live dashboard</span>
</div>
</div>
</div>
</div>
<div class="modal-backdrop" data-modal-backdrop></div>
<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">
<span class="win98-titlebar-icon">!</span>
<h2 id="modalTitle">Alert Metadata</h2>
</div>
<button class="win98-control" type="button" data-close-modal>x</button>
</div>
<div class="popup-body sunken-panel">
<pre class="metadata-pre" id="modalMeta">{}</pre>
</div>
</aside>
<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>
</html>
{{ end }}