panel: put Applications and its create form side by side
On the domain page the "Add an application" card sat below the domain rate limit, so adding a login meant scrolling past the list and back. Move it up next to the Applications table and wrap the pair in a .split grid (1.5fr / 1fr, so the table keeps the wider column). The columns collapse to one below 52rem, list first, and the grid gap keeps the same vertical rhythm as .card + .card. The empty-state text said "Create one below", which is no longer where the form is; it now names the card instead of its position. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -41,6 +41,13 @@ form.inline button { background: none; color: #2563eb; padding: 0; margin: 0; fo
|
|||||||
form.inline button:hover { text-decoration: underline; background: none; }
|
form.inline button:hover { text-decoration: underline; background: none; }
|
||||||
main { max-width: 48rem; }
|
main { max-width: 48rem; }
|
||||||
.card + .card { margin-top: 1.2rem; }
|
.card + .card { margin-top: 1.2rem; }
|
||||||
|
/* Two cards side by side on a wide viewport; below 60rem the columns collapse
|
||||||
|
and the grid gap keeps the same vertical rhythm as .card + .card. The inner
|
||||||
|
margin-top is cleared because the gap already spaces the pair. */
|
||||||
|
.split { display: grid; grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr); gap: 1.2rem; align-items: start; }
|
||||||
|
.split > .card + .card { margin-top: 0; }
|
||||||
|
.card + .split, .split + .card { margin-top: 1.2rem; }
|
||||||
|
@media (max-width: 52rem) { .split { grid-template-columns: minmax(0, 1fr); } }
|
||||||
.flash { background: #ecfdf3; border: 1px solid #abefc6; color: #067647; padding: 0.7rem 1rem; border-radius: 8px; margin-bottom: 1.2rem; }
|
.flash { background: #ecfdf3; border: 1px solid #abefc6; color: #067647; padding: 0.7rem 1rem; border-radius: 8px; margin-bottom: 1.2rem; }
|
||||||
@media (prefers-color-scheme: dark) { .flash { background: #0d2818 !important; border-color: #1a5336 !important; color: #75d99b !important; } }
|
@media (prefers-color-scheme: dark) { .flash { background: #0d2818 !important; border-color: #1a5336 !important; color: #75d99b !important; } }
|
||||||
table { width: 100%; border-collapse: collapse; }
|
table { width: 100%; border-collapse: collapse; }
|
||||||
|
|||||||
@@ -101,6 +101,11 @@
|
|||||||
lost, generate a new one.</p>
|
lost, generate a new one.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{{/* Applications and its create form sit side by side on a wide viewport: the
|
||||||
|
table is read far more often than the form, and pairing them saves scrolling
|
||||||
|
past the list to add one. On a narrow viewport the .split grid stacks them
|
||||||
|
again, list first. */}}
|
||||||
|
<div class="split">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<h2>Applications</h2>
|
<h2>Applications</h2>
|
||||||
<p class="muted">Each application is a SASL login/password an app or script
|
<p class="muted">Each application is a SASL login/password an app or script
|
||||||
@@ -172,10 +177,39 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
{{else}}
|
{{else}}
|
||||||
<p class="muted">No applications yet. Create one below.</p>
|
<p class="muted">No applications yet. Create the first one with the
|
||||||
|
<strong>Add an application</strong> form.</p>
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h2>Add an application</h2>
|
||||||
|
<form method="post" action="/domains/{{.Domain.ID}}/applications">
|
||||||
|
<label for="login">Login</label>
|
||||||
|
<input id="login" name="login" type="text" placeholder="prod-server"
|
||||||
|
autocomplete="off" autocapitalize="none" spellcheck="false"
|
||||||
|
value="{{.FormLogin}}" required>
|
||||||
|
|
||||||
|
<label for="mode">Address mode</label>
|
||||||
|
<select id="mode" name="mode" data-list-mode="{{.List}}">
|
||||||
|
<option value="{{.Wildcard}}" {{if eq .FormMode .Wildcard}}selected{{end}}>Any address of the domain</option>
|
||||||
|
<option value="{{.List}}" {{if eq .FormMode .List}}selected{{end}}>Specific addresses (list)</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<div data-addresses>
|
||||||
|
<label for="addresses">Addresses (one per line or comma-separated)</label>
|
||||||
|
<textarea id="addresses" name="addresses" rows="3"
|
||||||
|
placeholder="alerts@{{.Domain.Name}}">{{.FormAddrs}}</textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{if .Error}}<p class="error">{{.Error}}</p>{{end}}
|
||||||
|
<button type="submit">Create application</button>
|
||||||
|
</form>
|
||||||
|
<p class="muted">A strong password is generated and shown once. The login must
|
||||||
|
be unique across all domains and may contain letters, digits, '.', '-' and '_'.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<h2>Sending rate limit (domain)</h2>
|
<h2>Sending rate limit (domain)</h2>
|
||||||
<p class="muted">Optional level-2 limit (spec 7.4): cap how many messages this
|
<p class="muted">Optional level-2 limit (spec 7.4): cap how many messages this
|
||||||
@@ -211,33 +245,6 @@
|
|||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card">
|
|
||||||
<h2>Add an application</h2>
|
|
||||||
<form method="post" action="/domains/{{.Domain.ID}}/applications">
|
|
||||||
<label for="login">Login</label>
|
|
||||||
<input id="login" name="login" type="text" placeholder="prod-server"
|
|
||||||
autocomplete="off" autocapitalize="none" spellcheck="false"
|
|
||||||
value="{{.FormLogin}}" required>
|
|
||||||
|
|
||||||
<label for="mode">Address mode</label>
|
|
||||||
<select id="mode" name="mode" data-list-mode="{{.List}}">
|
|
||||||
<option value="{{.Wildcard}}" {{if eq .FormMode .Wildcard}}selected{{end}}>Any address of the domain</option>
|
|
||||||
<option value="{{.List}}" {{if eq .FormMode .List}}selected{{end}}>Specific addresses (list)</option>
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<div data-addresses>
|
|
||||||
<label for="addresses">Addresses (one per line or comma-separated)</label>
|
|
||||||
<textarea id="addresses" name="addresses" rows="3"
|
|
||||||
placeholder="alerts@{{.Domain.Name}}">{{.FormAddrs}}</textarea>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{if .Error}}<p class="error">{{.Error}}</p>{{end}}
|
|
||||||
<button type="submit">Create application</button>
|
|
||||||
</form>
|
|
||||||
<p class="muted">A strong password is generated and shown once. The login must
|
|
||||||
be unique across all domains and may contain letters, digits, '.', '-' and '_'.</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<h2>Export domain</h2>
|
<h2>Export domain</h2>
|
||||||
<p class="muted">Download this domain to move it to another SelfPost instance:
|
<p class="muted">Download this domain to move it to another SelfPost instance:
|
||||||
|
|||||||
Reference in New Issue
Block a user