Widen the administration interface
This commit is contained in:
+56
-30
@@ -202,50 +202,76 @@ header nav form { display: inline; }
|
||||
header nav .who { color: var(--muted); }
|
||||
header nav .who::before { content: "\1F511\00a0"; }
|
||||
|
||||
/* A listing needs far more room than the reading measure a form wants. */
|
||||
body.wide header,
|
||||
body.wide main,
|
||||
body.wide footer { max-width: 72rem; }
|
||||
|
||||
dl.stats { grid-template-columns: auto 1fr; }
|
||||
dl.stats em { font-style: normal; color: var(--muted); }
|
||||
|
||||
/* Tables scroll on their own rather than making the page scroll sideways. */
|
||||
.tablewrap { overflow-x: auto; margin: 0 -.25rem; }
|
||||
|
||||
table.admin { border-collapse: collapse; width: 100%; font-size: .8125rem; }
|
||||
table.admin th, table.admin td { padding: .4rem .5rem; text-align: left; vertical-align: top; border-bottom: 1px solid var(--line); }
|
||||
table.admin th,
|
||||
table.admin td {
|
||||
padding: .45rem .5rem;
|
||||
text-align: left;
|
||||
vertical-align: top;
|
||||
border-bottom: 1px solid var(--line);
|
||||
}
|
||||
table.admin th { color: var(--muted); font-weight: 500; white-space: nowrap; }
|
||||
table.admin th a { color: inherit; text-decoration: none; }
|
||||
table.admin th a:hover { color: var(--accent); text-decoration: underline; }
|
||||
table.admin tr:last-child td { border-bottom: 0; }
|
||||
table.admin .num { text-align: right; white-space: nowrap; }
|
||||
table.admin .small { font-size: .75rem; color: var(--muted); white-space: nowrap; }
|
||||
table.admin .wrap { overflow-wrap: anywhere; min-width: 8rem; }
|
||||
table.admin .num { text-align: right; }
|
||||
table.admin .nowrap { white-space: nowrap; }
|
||||
table.admin form { margin: 0; }
|
||||
table.admin .actions-cell { text-align: right; width: 1%; }
|
||||
|
||||
/* The filename carries the link and the identifier sits under it, which saves
|
||||
a whole column of UUID. */
|
||||
table.admin .filecell a { overflow-wrap: anywhere; font-weight: 500; }
|
||||
table.admin .filecell .id {
|
||||
display: block;
|
||||
font-size: .6875rem;
|
||||
color: var(--muted);
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
button.small { padding: .2rem .5rem; font-size: .75rem; }
|
||||
|
||||
.visually-hidden {
|
||||
position: absolute;
|
||||
width: 1px; height: 1px;
|
||||
margin: -1px; padding: 0; border: 0;
|
||||
clip-path: inset(50%);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Narrow screens get one card per row rather than a horizontal scrollbar,
|
||||
which is unusable for picking a row and pressing a button in it. */
|
||||
@media (max-width: 46rem) {
|
||||
table.admin,
|
||||
table.admin tbody,
|
||||
table.admin tr,
|
||||
table.admin td { display: block; width: 100%; }
|
||||
table.admin thead { display: none; }
|
||||
table.admin tr {
|
||||
padding: .625rem 0;
|
||||
border-bottom: 1px solid var(--line);
|
||||
}
|
||||
table.admin td { border: 0; padding: .1rem 0; }
|
||||
table.admin td[data-label]::before {
|
||||
content: attr(data-label) ": ";
|
||||
color: var(--muted);
|
||||
}
|
||||
table.admin .num,
|
||||
table.admin .actions-cell { text-align: left; width: auto; }
|
||||
table.admin .actions-cell { padding-top: .5rem; }
|
||||
table.admin .filecell::before { content: none; }
|
||||
}
|
||||
|
||||
.cli code, .hint code { background: var(--bg); padding: .1rem .3rem; border-radius: 4px; }
|
||||
|
||||
/* Disclosure sections and inline errors ----------------------------------- */
|
||||
|
||||
details > summary {
|
||||
cursor: pointer;
|
||||
font-weight: 500;
|
||||
padding: .125rem 0;
|
||||
}
|
||||
details[open] > summary { margin-bottom: .75rem; }
|
||||
details .field { margin-top: .75rem; max-width: 28rem; }
|
||||
|
||||
p.error {
|
||||
margin: 0 0 .75rem;
|
||||
padding: .5rem .75rem;
|
||||
color: var(--danger);
|
||||
background: var(--warn-bg);
|
||||
border: 1px solid var(--danger);
|
||||
border-radius: 6px;
|
||||
font-size: .875rem;
|
||||
}
|
||||
|
||||
.card .actions { margin: 1.25rem 0; }
|
||||
.card > .field:last-child { margin-bottom: 0; }
|
||||
|
||||
/* One-off token, tucked away so the common path stays a single button. */
|
||||
.onceoff { margin-bottom: 1rem; }
|
||||
.onceoff summary { font-weight: 400; font-size: .875rem; color: var(--muted); }
|
||||
|
||||
+52
-55
@@ -13,69 +13,66 @@
|
||||
{{if not .Objects}}
|
||||
<p class="hint">Nothing stored right now.</p>
|
||||
{{else}}
|
||||
<div class="tablewrap">
|
||||
<table class="admin">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><a href="?sort=name">Name</a></th>
|
||||
<th><a href="?sort=name">Filename</a></th>
|
||||
<th class="num"><a href="?sort=size">Size</a></th>
|
||||
<th><a href="?sort=owner">Owner</a></th>
|
||||
<th><a href="?sort=created">Uploaded</a></th>
|
||||
<th><a href="?sort=expires">Expires</a></th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{range .Objects}}
|
||||
<tr>
|
||||
<td class="mono"><a href="{{$.Base}}i/{{.ID}}">{{.ID}}</a>{{if .Vanity}} <em title="custom name">★</em>{{end}}</td>
|
||||
<td class="wrap">{{.Filename}}</td>
|
||||
<td class="num">{{.Size}}</td>
|
||||
<td>{{if .Owner}}{{.Owner}}{{else}}<em>anonymous</em>{{end}}</td>
|
||||
<td class="mono small">{{.Created}}</td>
|
||||
<td class="mono small">{{.Expires}}</td>
|
||||
<td>
|
||||
<form method="post" action="{{$.Base}}api/d/{{.ID}}/delete">
|
||||
<input type="hidden" name="from" value="admin">
|
||||
<button type="submit" class="danger small"
|
||||
data-confirm="Delete {{.Filename}}?">Delete</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<table class="admin files">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><a href="?sort=name">File</a></th>
|
||||
<th class="num"><a href="?sort=size">Size</a></th>
|
||||
<th><a href="?sort=owner">Owner</a></th>
|
||||
<th><a href="?sort=created">Uploaded</a></th>
|
||||
<th><a href="?sort=expires">Expires</a></th>
|
||||
<th><span class="visually-hidden">Actions</span></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{range .Objects}}
|
||||
<tr>
|
||||
<td data-label="File" class="filecell">
|
||||
<a href="{{$.Base}}i/{{.ID}}">{{.Filename}}</a>
|
||||
<span class="id mono">{{.ID}}{{if .Vanity}} <em title="custom name">★</em>{{end}}</span>
|
||||
</td>
|
||||
<td data-label="Size" class="num nowrap">{{.Size}}</td>
|
||||
<td data-label="Owner">{{if .Owner}}{{.Owner}}{{else}}<em>anonymous</em>{{end}}</td>
|
||||
<td data-label="Uploaded" class="nowrap" title="{{.Created}}">{{.CreatedAgo}}</td>
|
||||
<td data-label="Expires" class="nowrap" title="{{.Expires}}">{{.ExpiresIn}}</td>
|
||||
<td class="actions-cell">
|
||||
<form method="post" action="{{$.Base}}api/d/{{.ID}}/delete">
|
||||
<input type="hidden" name="from" value="admin">
|
||||
<button type="submit" class="danger small"
|
||||
data-confirm="Delete {{.Filename}}? This cannot be undone.">Delete</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
{{end}}
|
||||
</section>
|
||||
|
||||
<section class="card">
|
||||
<h2>Tokens</h2>
|
||||
<p class="hint">
|
||||
Tokens are minted and revoked with <code>send token</code> on the server.
|
||||
They are deliberately not manageable from here: nothing that hands out
|
||||
credentials should be reachable over the network.
|
||||
Tokens are minted, rotated and revoked with <code>./send token</code> on the
|
||||
server. They are deliberately not manageable from here: nothing that hands
|
||||
out credentials should be reachable over the network.
|
||||
</p>
|
||||
{{if .Tokens}}
|
||||
<div class="tablewrap">
|
||||
<table class="admin">
|
||||
<thead>
|
||||
<tr><th>Name</th><th>Max size</th><th>Max lifetime</th><th>Vanity</th><th>Admin</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{range .Tokens}}
|
||||
<tr>
|
||||
<td>{{.Name}}</td>
|
||||
<td>{{.MaxSize}}</td>
|
||||
<td>{{.MaxExpiry}}</td>
|
||||
<td>{{if .Vanity}}yes{{else}}no{{end}}</td>
|
||||
<td>{{if .Admin}}yes{{else}}no{{end}}</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<table class="admin">
|
||||
<thead>
|
||||
<tr><th>Name</th><th>Max size</th><th>Max lifetime</th><th>Vanity</th><th>Admin</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{range .Tokens}}
|
||||
<tr>
|
||||
<td data-label="Name">{{.Name}}</td>
|
||||
<td data-label="Max size" class="nowrap">{{.MaxSize}}</td>
|
||||
<td data-label="Max lifetime" class="nowrap">{{.MaxExpiry}}</td>
|
||||
<td data-label="Vanity">{{if .Vanity}}yes{{else}}no{{end}}</td>
|
||||
<td data-label="Admin">{{if .Admin}}yes{{else}}no{{end}}</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
{{end}}
|
||||
</section>
|
||||
{{end}}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<title>{{.Title}} · Uncensored Send</title>
|
||||
<link rel="stylesheet" href="{{.Base}}static/style.css">
|
||||
</head>
|
||||
<body data-base="{{.Base}}">
|
||||
<body data-base="{{.Base}}"{{if .Wide}} class="wide"{{end}}>
|
||||
<header>
|
||||
<a class="brand" href="{{.Base}}">Uncensored Send</a>
|
||||
<nav>
|
||||
|
||||
Reference in New Issue
Block a user