S34: admin page templates (dashboard/users/repos/jobs/audit/system/email)
- SHA
31f7200f032ffecd92f63169fbbfdb1e9c0e967a- Parents
-
4fa8d24 - Tree
35f1479
31f7200
31f7200f032ffecd92f63169fbbfdb1e9c0e967a4fa8d24
35f1479internal/web/templates/admin/audit_list.htmladded@@ -0,0 +1,42 @@ | ||
| 1 | +{{ define "page" -}} | |
| 2 | +<div class="shithub-settings-page"> | |
| 3 | + {{ template "admin-nav" . }} | |
| 4 | + <div class="shithub-settings-content"> | |
| 5 | + <h1>Audit log</h1> | |
| 6 | + | |
| 7 | + <form method="GET" action="/admin/audit" class="shithub-finder-form"> | |
| 8 | + <input type="text" name="actor" value="{{ .Q.Get "actor" }}" placeholder="actor id"> | |
| 9 | + <input type="text" name="action" value="{{ .Q.Get "action" }}" placeholder="action prefix"> | |
| 10 | + <input type="text" name="target_type" value="{{ .Q.Get "target_type" }}" placeholder="target type"> | |
| 11 | + <input type="text" name="target_id" value="{{ .Q.Get "target_id" }}" placeholder="target id"> | |
| 12 | + <input type="date" name="since" value="{{ .Q.Get "since" }}"> | |
| 13 | + <input type="date" name="until" value="{{ .Q.Get "until" }}"> | |
| 14 | + <button type="submit" class="shithub-button">Filter</button> | |
| 15 | + </form> | |
| 16 | + | |
| 17 | + {{ if .Rows }} | |
| 18 | + <table class="shithub-branches-table"> | |
| 19 | + <thead><tr><th>When</th><th>Actor</th><th>Action</th><th>Target</th><th>Meta</th></tr></thead> | |
| 20 | + <tbody> | |
| 21 | + {{ range .Rows }} | |
| 22 | + <tr> | |
| 23 | + <td>{{ relativeTime .CreatedAt.Time }}</td> | |
| 24 | + <td>{{ if .ActorID.Valid }}#{{ .ActorID.Int64 }}{{ else }}<span class="shithub-fg-muted">system</span>{{ end }}</td> | |
| 25 | + <td><code>{{ .Action }}</code></td> | |
| 26 | + <td>{{ .TargetType }}{{ if .TargetID.Valid }} #{{ .TargetID.Int64 }}{{ end }}</td> | |
| 27 | + <td><code>{{ printf "%s" .Meta }}</code></td> | |
| 28 | + </tr> | |
| 29 | + {{ end }} | |
| 30 | + </tbody> | |
| 31 | + </table> | |
| 32 | + {{ else }} | |
| 33 | + <p class="shithub-empty-note">No audit rows match.</p> | |
| 34 | + {{ end }} | |
| 35 | + | |
| 36 | + <nav class="shithub-pagination"> | |
| 37 | + {{ if gt .Page 1 }}<a href="?page={{ .PrevPage }}&{{ .Filters }}">← Prev</a>{{ end }} | |
| 38 | + {{ if .HasMore }}<a href="?page={{ .NextPage }}&{{ .Filters }}">Next →</a>{{ end }} | |
| 39 | + </nav> | |
| 40 | + </div> | |
| 41 | +</div> | |
| 42 | +{{- end }} | |
internal/web/templates/admin/dashboard.htmladded@@ -0,0 +1,36 @@ | ||
| 1 | +{{ define "page" -}} | |
| 2 | +<div class="shithub-settings-page"> | |
| 3 | + {{ template "admin-nav" . }} | |
| 4 | + <div class="shithub-settings-content"> | |
| 5 | + <h1>Dashboard</h1> | |
| 6 | + | |
| 7 | + <section class="shithub-settings-section"> | |
| 8 | + <h2>Counts</h2> | |
| 9 | + <ul> | |
| 10 | + <li>Active users: <strong>{{ index .Counts "users" }}</strong></li> | |
| 11 | + <li>Suspended users: <strong>{{ index .Counts "suspended" }}</strong></li> | |
| 12 | + <li>Site admins: <strong>{{ index .Counts "admins" }}</strong></li> | |
| 13 | + <li>Active repositories: <strong>{{ index .Counts "repos" }}</strong></li> | |
| 14 | + <li>Active organizations: <strong>{{ index .Counts "orgs" }}</strong></li> | |
| 15 | + </ul> | |
| 16 | + </section> | |
| 17 | + | |
| 18 | + <section class="shithub-settings-section"> | |
| 19 | + <h2>Job queue</h2> | |
| 20 | + {{ if .JobsByStatus }} | |
| 21 | + <table class="shithub-branches-table"> | |
| 22 | + <thead><tr><th>Status</th><th>Count</th></tr></thead> | |
| 23 | + <tbody> | |
| 24 | + {{ range .JobsByStatus }} | |
| 25 | + <tr><td><code>{{ .Status }}</code></td><td>{{ .N }}</td></tr> | |
| 26 | + {{ end }} | |
| 27 | + </tbody> | |
| 28 | + </table> | |
| 29 | + {{ else }} | |
| 30 | + <p class="shithub-empty-note">No jobs in the queue.</p> | |
| 31 | + {{ end }} | |
| 32 | + <p><a href="/admin/jobs">Open job queue →</a></p> | |
| 33 | + </section> | |
| 34 | + </div> | |
| 35 | +</div> | |
| 36 | +{{- end }} | |
internal/web/templates/admin/email.htmladded@@ -0,0 +1,29 @@ | ||
| 1 | +{{ define "page" -}} | |
| 2 | +<div class="shithub-settings-page"> | |
| 3 | + {{ template "admin-nav" . }} | |
| 4 | + <div class="shithub-settings-content"> | |
| 5 | + <h1>Email queue</h1> | |
| 6 | + <p class="shithub-hint">Recent transactional email (auth, settings, admin actions). Notification-fanout email lives separately in <code>notification_email_log</code> and is reachable via the audit view.</p> | |
| 7 | + | |
| 8 | + {{ if .Rows }} | |
| 9 | + <table class="shithub-branches-table"> | |
| 10 | + <thead><tr><th>When</th><th>Kind</th><th>To</th><th>Subject</th><th>Status</th><th>Error</th></tr></thead> | |
| 11 | + <tbody> | |
| 12 | + {{ range .Rows }} | |
| 13 | + <tr> | |
| 14 | + <td>{{ relativeTime .SentAt.Time }}</td> | |
| 15 | + <td><code>{{ .Kind }}</code></td> | |
| 16 | + <td>{{ .RecipientEmail }}</td> | |
| 17 | + <td>{{ .Subject }}</td> | |
| 18 | + <td><code>{{ .Status }}</code></td> | |
| 19 | + <td>{{ if .ErrorSummary.Valid }}{{ .ErrorSummary.String }}{{ end }}</td> | |
| 20 | + </tr> | |
| 21 | + {{ end }} | |
| 22 | + </tbody> | |
| 23 | + </table> | |
| 24 | + {{ else }} | |
| 25 | + <p class="shithub-empty-note">No transactional email logged yet.</p> | |
| 26 | + {{ end }} | |
| 27 | + </div> | |
| 28 | +</div> | |
| 29 | +{{- end }} | |
internal/web/templates/admin/job_view.htmladded@@ -0,0 +1,34 @@ | ||
| 1 | +{{ define "page" -}} | |
| 2 | +<div class="shithub-settings-page"> | |
| 3 | + {{ template "admin-nav" . }} | |
| 4 | + <div class="shithub-settings-content"> | |
| 5 | + <h1>Job #{{ .Job.ID }} <span class="shithub-fg-muted">{{ .Job.Kind }}</span></h1> | |
| 6 | + <p><a href="/admin/jobs">← All jobs</a></p> | |
| 7 | + | |
| 8 | + <section class="shithub-settings-section"> | |
| 9 | + <h2>Summary</h2> | |
| 10 | + <ul> | |
| 11 | + <li>Attempts: {{ .Job.Attempts }} of {{ .Job.MaxAttempts }}</li> | |
| 12 | + <li>Run at: {{ .Job.RunAt.Time }}</li> | |
| 13 | + {{ if .Job.LockedBy.Valid }}<li>Locked by: <code>{{ .Job.LockedBy.String }}</code> at {{ relativeTime .Job.LockedAt.Time }}</li>{{ end }} | |
| 14 | + {{ if .Job.CompletedAt.Valid }}<li>Completed: {{ relativeTime .Job.CompletedAt.Time }}</li>{{ end }} | |
| 15 | + {{ if .Job.FailedAt.Valid }}<li>Failed: {{ relativeTime .Job.FailedAt.Time }}</li>{{ end }} | |
| 16 | + {{ if .Job.LastError.Valid }}<li>Last error: <code>{{ .Job.LastError.String }}</code></li>{{ end }} | |
| 17 | + </ul> | |
| 18 | + <form method="POST" action="/admin/jobs/{{ .Job.ID }}/retry" style="display:inline"> | |
| 19 | + <input type="hidden" name="csrf_token" value="{{ .CSRFToken }}"> | |
| 20 | + <button type="submit" class="shithub-button shithub-button-primary">Retry now</button> | |
| 21 | + </form> | |
| 22 | + <form method="POST" action="/admin/jobs/{{ .Job.ID }}/discard" style="display:inline"> | |
| 23 | + <input type="hidden" name="csrf_token" value="{{ .CSRFToken }}"> | |
| 24 | + <button type="submit" class="shithub-button shithub-button-danger">Discard</button> | |
| 25 | + </form> | |
| 26 | + </section> | |
| 27 | + | |
| 28 | + <section class="shithub-settings-section"> | |
| 29 | + <h2>Payload</h2> | |
| 30 | + <pre class="shithub-code-pre">{{ .PayloadStr }}</pre> | |
| 31 | + </section> | |
| 32 | + </div> | |
| 33 | +</div> | |
| 34 | +{{- end }} | |
internal/web/templates/admin/jobs_list.htmladded@@ -0,0 +1,50 @@ | ||
| 1 | +{{ define "page" -}} | |
| 2 | +<div class="shithub-settings-page"> | |
| 3 | + {{ template "admin-nav" . }} | |
| 4 | + <div class="shithub-settings-content"> | |
| 5 | + <h1>Jobs</h1> | |
| 6 | + {{ with .Notice }}<p class="shithub-flash shithub-flash-notice">{{ . }}</p>{{ end }} | |
| 7 | + | |
| 8 | + <p> | |
| 9 | + {{ range .Counts }}<code>{{ .Status }}</code>: {{ .N }} {{ end }} | |
| 10 | + </p> | |
| 11 | + | |
| 12 | + <form method="GET" action="/admin/jobs" class="shithub-finder-form"> | |
| 13 | + <input type="text" name="kind" value="{{ .Kind }}" placeholder="kind (e.g. push:process)"> | |
| 14 | + <select name="status"> | |
| 15 | + <option value="">any</option> | |
| 16 | + <option value="queued" {{ if eq .Status "queued" }}selected{{ end }}>queued</option> | |
| 17 | + <option value="running" {{ if eq .Status "running" }}selected{{ end }}>running</option> | |
| 18 | + <option value="failed" {{ if eq .Status "failed" }}selected{{ end }}>failed</option> | |
| 19 | + <option value="completed" {{ if eq .Status "completed" }}selected{{ end }}>completed</option> | |
| 20 | + </select> | |
| 21 | + <button type="submit" class="shithub-button">Filter</button> | |
| 22 | + </form> | |
| 23 | + | |
| 24 | + {{ if .Jobs }} | |
| 25 | + <table class="shithub-branches-table"> | |
| 26 | + <thead><tr><th>ID</th><th>Kind</th><th>Status</th><th>Attempts</th><th>Created</th><th></th></tr></thead> | |
| 27 | + <tbody> | |
| 28 | + {{ range .Jobs }} | |
| 29 | + <tr> | |
| 30 | + <td>#{{ .ID }}</td> | |
| 31 | + <td><code>{{ .Kind }}</code></td> | |
| 32 | + <td><code>{{ .Status }}</code></td> | |
| 33 | + <td>{{ .Attempts }}/{{ .MaxAttempts }}</td> | |
| 34 | + <td>{{ relativeTime .CreatedAt.Time }}</td> | |
| 35 | + <td><a href="/admin/jobs/{{ .ID }}">View →</a></td> | |
| 36 | + </tr> | |
| 37 | + {{ end }} | |
| 38 | + </tbody> | |
| 39 | + </table> | |
| 40 | + {{ else }} | |
| 41 | + <p class="shithub-empty-note">No jobs match.</p> | |
| 42 | + {{ end }} | |
| 43 | + | |
| 44 | + <nav class="shithub-pagination"> | |
| 45 | + {{ if gt .Page 1 }}<a href="?page={{ .PrevPage }}">← Prev</a>{{ end }} | |
| 46 | + {{ if .HasMore }}<a href="?page={{ .NextPage }}">Next →</a>{{ end }} | |
| 47 | + </nav> | |
| 48 | + </div> | |
| 49 | +</div> | |
| 50 | +{{- end }} | |
internal/web/templates/admin/repo_view.htmladded@@ -0,0 +1,38 @@ | ||
| 1 | +{{ define "page" -}} | |
| 2 | +<div class="shithub-settings-page"> | |
| 3 | + {{ template "admin-nav" . }} | |
| 4 | + <div class="shithub-settings-content"> | |
| 5 | + <h1>{{ .Repo.Name }} <span class="shithub-fg-muted">#{{ .Repo.ID }}</span></h1> | |
| 6 | + {{ with .Notice }}<p class="shithub-flash shithub-flash-notice">{{ . }}</p>{{ end }} | |
| 7 | + | |
| 8 | + <section class="shithub-settings-section"> | |
| 9 | + <h2>Details</h2> | |
| 10 | + <ul> | |
| 11 | + <li>Visibility: <code>{{ .Repo.Visibility }}</code></li> | |
| 12 | + <li>Default branch: <code>{{ .Repo.DefaultBranch }}</code></li> | |
| 13 | + <li>Disk used: {{ .Repo.DiskUsedBytes }} bytes</li> | |
| 14 | + <li>Stars: {{ .Repo.StarCount }} · Watchers: {{ .Repo.WatcherCount }} · Forks: {{ .Repo.ForkCount }}</li> | |
| 15 | + <li>Created: {{ relativeTime .Repo.CreatedAt.Time }}</li> | |
| 16 | + {{ if .Repo.IsArchived }}<li>Archived {{ relativeTime .Repo.ArchivedAt.Time }}</li>{{ end }} | |
| 17 | + {{ if .Repo.DeletedAt.Valid }}<li>Soft-deleted {{ relativeTime .Repo.DeletedAt.Time }}</li>{{ end }} | |
| 18 | + </ul> | |
| 19 | + </section> | |
| 20 | + | |
| 21 | + <section class="shithub-settings-section"> | |
| 22 | + <h2>Force actions</h2> | |
| 23 | + <p class="shithub-hint">These bypass owner consent. Use only for emergency takedown / abuse response.</p> | |
| 24 | + <form method="POST" action="/admin/repos/{{ .Repo.ID }}/archive" style="display:inline"> | |
| 25 | + <input type="hidden" name="csrf_token" value="{{ .CSRFToken }}"> | |
| 26 | + <button type="submit" class="shithub-button">{{ if .Repo.IsArchived }}Force-unarchive{{ else }}Force-archive{{ end }}</button> | |
| 27 | + </form> | |
| 28 | + | |
| 29 | + <form method="POST" action="/admin/repos/{{ .Repo.ID }}/delete"> | |
| 30 | + <input type="hidden" name="csrf_token" value="{{ .CSRFToken }}"> | |
| 31 | + <label>Type <code>{{ .Repo.Name }}</code> to confirm | |
| 32 | + <input type="text" name="confirm" required></label> | |
| 33 | + <button type="submit" class="shithub-button shithub-button-danger">Force-delete (skip grace)</button> | |
| 34 | + </form> | |
| 35 | + </section> | |
| 36 | + </div> | |
| 37 | +</div> | |
| 38 | +{{- end }} | |
internal/web/templates/admin/repos_list.htmladded@@ -0,0 +1,47 @@ | ||
| 1 | +{{ define "page" -}} | |
| 2 | +<div class="shithub-settings-page"> | |
| 3 | + {{ template "admin-nav" . }} | |
| 4 | + <div class="shithub-settings-content"> | |
| 5 | + <h1>Repositories</h1> | |
| 6 | + {{ with .Notice }}<p class="shithub-flash shithub-flash-notice">{{ . }}</p>{{ end }} | |
| 7 | + | |
| 8 | + <form method="GET" action="/admin/repos" class="shithub-finder-form"> | |
| 9 | + <input type="text" name="q" value="{{ .Q }}" placeholder="name prefix"> | |
| 10 | + <select name="visibility"> | |
| 11 | + <option value="">any visibility</option> | |
| 12 | + <option value="public" {{ if eq .Visibility "public" }}selected{{ end }}>public</option> | |
| 13 | + <option value="private" {{ if eq .Visibility "private" }}selected{{ end }}>private</option> | |
| 14 | + </select> | |
| 15 | + <label><input type="checkbox" name="archived" value="1" {{ if .ArchivedOnly }}checked{{ end }}> Archived</label> | |
| 16 | + <label><input type="checkbox" name="deleted" value="1" {{ if .DeletedOnly }}checked{{ end }}> Deleted</label> | |
| 17 | + <button type="submit" class="shithub-button">Filter</button> | |
| 18 | + </form> | |
| 19 | + | |
| 20 | + {{ if .Repos }} | |
| 21 | + <table class="shithub-branches-table"> | |
| 22 | + <thead><tr><th>ID</th><th>Owner</th><th>Name</th><th>Visibility</th><th>State</th><th>Disk</th><th></th></tr></thead> | |
| 23 | + <tbody> | |
| 24 | + {{ range .Repos }} | |
| 25 | + <tr> | |
| 26 | + <td>#{{ .ID }}</td> | |
| 27 | + <td>{{ if .OwnerUserUsername }}{{ .OwnerUserUsername }}{{ else if .OwnerOrgSlug }}{{ .OwnerOrgSlug }} (org){{ else }}<span class="shithub-fg-muted">—</span>{{ end }}</td> | |
| 28 | + <td><a href="/admin/repos/{{ .ID }}">{{ .Name }}</a></td> | |
| 29 | + <td><code>{{ .Visibility }}</code></td> | |
| 30 | + <td>{{ if .DeletedAt.Valid }}deleted{{ else if .IsArchived }}archived{{ else }}active{{ end }}</td> | |
| 31 | + <td>{{ .DiskUsedBytes }}</td> | |
| 32 | + <td><a href="/admin/repos/{{ .ID }}">View →</a></td> | |
| 33 | + </tr> | |
| 34 | + {{ end }} | |
| 35 | + </tbody> | |
| 36 | + </table> | |
| 37 | + {{ else }} | |
| 38 | + <p class="shithub-empty-note">No repositories match.</p> | |
| 39 | + {{ end }} | |
| 40 | + | |
| 41 | + <nav class="shithub-pagination"> | |
| 42 | + {{ if gt .Page 1 }}<a href="?page={{ .PrevPage }}">← Prev</a>{{ end }} | |
| 43 | + {{ if .HasMore }}<a href="?page={{ .NextPage }}">Next →</a>{{ end }} | |
| 44 | + </nav> | |
| 45 | + </div> | |
| 46 | +</div> | |
| 47 | +{{- end }} | |
internal/web/templates/admin/system.htmladded@@ -0,0 +1,37 @@ | ||
| 1 | +{{ define "page" -}} | |
| 2 | +<div class="shithub-settings-page"> | |
| 3 | + {{ template "admin-nav" . }} | |
| 4 | + <div class="shithub-settings-content"> | |
| 5 | + <h1>System</h1> | |
| 6 | + | |
| 7 | + <section class="shithub-settings-section"> | |
| 8 | + <h2>Process</h2> | |
| 9 | + <ul> | |
| 10 | + <li>shithubd version: <code>{{ .Version }}</code></li> | |
| 11 | + <li>Go runtime: <code>{{ .GoVersion }}</code> ({{ .GoOS }}/{{ .GoArch }})</li> | |
| 12 | + </ul> | |
| 13 | + </section> | |
| 14 | + | |
| 15 | + <section class="shithub-settings-section"> | |
| 16 | + <h2>Database</h2> | |
| 17 | + <ul> | |
| 18 | + <li>Database: <code>{{ index .DB "name" }}</code> as <code>{{ index .DB "user" }}</code></li> | |
| 19 | + <li>Active connections: {{ index .DB "conns" }}</li> | |
| 20 | + </ul> | |
| 21 | + <h3>pgx pool</h3> | |
| 22 | + <ul> | |
| 23 | + <li>Acquired / idle / total / max: {{ index .Pool "acquired" }} / {{ index .Pool "idle" }} / {{ index .Pool "total" }} / {{ index .Pool "max" }}</li> | |
| 24 | + <li>Lifetime acquires: {{ index .Pool "acquireCount" }}</li> | |
| 25 | + </ul> | |
| 26 | + </section> | |
| 27 | + | |
| 28 | + <section class="shithub-settings-section"> | |
| 29 | + <h2>Repositories</h2> | |
| 30 | + <ul> | |
| 31 | + <li>Repos: {{ index .Repos "count" }}</li> | |
| 32 | + <li>Disk used (sum): {{ index .Repos "diskBytes" }} bytes</li> | |
| 33 | + </ul> | |
| 34 | + </section> | |
| 35 | + </div> | |
| 36 | +</div> | |
| 37 | +{{- end }} | |
internal/web/templates/admin/user_view.htmladded@@ -0,0 +1,55 @@ | ||
| 1 | +{{ define "page" -}} | |
| 2 | +<div class="shithub-settings-page"> | |
| 3 | + {{ template "admin-nav" . }} | |
| 4 | + <div class="shithub-settings-content"> | |
| 5 | + <h1>{{ .User.Username }} <span class="shithub-fg-muted">#{{ .User.ID }}</span></h1> | |
| 6 | + {{ with .Notice }}<p class="shithub-flash shithub-flash-notice">{{ . }}</p>{{ end }} | |
| 7 | + | |
| 8 | + <section class="shithub-settings-section"> | |
| 9 | + <h2>Profile</h2> | |
| 10 | + <ul> | |
| 11 | + <li>Display name: {{ if .User.DisplayName }}{{ .User.DisplayName }}{{ else }}<span class="shithub-fg-muted">unset</span>{{ end }}</li> | |
| 12 | + <li>Email verified: {{ if .User.EmailVerified }}yes{{ else }}<span class="shithub-fg-muted">no</span>{{ end }}</li> | |
| 13 | + <li>Last login: {{ if .User.LastLoginAt.Valid }}{{ relativeTime .User.LastLoginAt.Time }}{{ else }}<span class="shithub-fg-muted">never</span>{{ end }}</li> | |
| 14 | + <li>Created: {{ relativeTime .User.CreatedAt.Time }}</li> | |
| 15 | + {{ if .User.SuspendedAt.Valid }}<li>Suspended {{ relativeTime .User.SuspendedAt.Time }} — <em>{{ .User.SuspendedReason.String }}</em></li>{{ end }} | |
| 16 | + {{ if .User.DeletedAt.Valid }}<li>Soft-deleted {{ relativeTime .User.DeletedAt.Time }}</li>{{ end }} | |
| 17 | + <li>Site admin: {{ if .User.IsSiteAdmin }}<strong>yes</strong>{{ else }}no{{ end }}</li> | |
| 18 | + </ul> | |
| 19 | + </section> | |
| 20 | + | |
| 21 | + <section class="shithub-settings-section"> | |
| 22 | + <h2>Actions</h2> | |
| 23 | + {{ if .User.SuspendedAt.Valid }} | |
| 24 | + <form method="POST" action="/admin/users/{{ .User.ID }}/unsuspend" style="display:inline"> | |
| 25 | + <input type="hidden" name="csrf_token" value="{{ .CSRFToken }}"> | |
| 26 | + <button type="submit" class="shithub-button">Unsuspend</button> | |
| 27 | + </form> | |
| 28 | + {{ else }} | |
| 29 | + <form method="POST" action="/admin/users/{{ .User.ID }}/suspend"> | |
| 30 | + <input type="hidden" name="csrf_token" value="{{ .CSRFToken }}"> | |
| 31 | + <label>Reason (visible to user) <input type="text" name="reason" maxlength="200"></label> | |
| 32 | + <button type="submit" class="shithub-button shithub-button-danger">Suspend</button> | |
| 33 | + </form> | |
| 34 | + {{ end }} | |
| 35 | + | |
| 36 | + <form method="POST" action="/admin/users/{{ .User.ID }}/site-admin" style="display:inline"> | |
| 37 | + <input type="hidden" name="csrf_token" value="{{ .CSRFToken }}"> | |
| 38 | + <button type="submit" class="shithub-button">{{ if .User.IsSiteAdmin }}Revoke admin{{ else }}Grant admin{{ end }}</button> | |
| 39 | + </form> | |
| 40 | + | |
| 41 | + <form method="POST" action="/admin/users/{{ .User.ID }}/reset-password" style="display:inline"> | |
| 42 | + <input type="hidden" name="csrf_token" value="{{ .CSRFToken }}"> | |
| 43 | + <button type="submit" class="shithub-button">Send password reset</button> | |
| 44 | + </form> | |
| 45 | + | |
| 46 | + {{ if not .User.DeletedAt.Valid }} | |
| 47 | + <form method="POST" action="/admin/impersonate/{{ .User.ID }}" style="display:inline"> | |
| 48 | + <input type="hidden" name="csrf_token" value="{{ .CSRFToken }}"> | |
| 49 | + <button type="submit" class="shithub-button">Impersonate (read-only)</button> | |
| 50 | + </form> | |
| 51 | + {{ end }} | |
| 52 | + </section> | |
| 53 | + </div> | |
| 54 | +</div> | |
| 55 | +{{- end }} | |
internal/web/templates/admin/users_list.htmladded@@ -0,0 +1,46 @@ | ||
| 1 | +{{ define "page" -}} | |
| 2 | +<div class="shithub-settings-page"> | |
| 3 | + {{ template "admin-nav" . }} | |
| 4 | + <div class="shithub-settings-content"> | |
| 5 | + <h1>Users</h1> | |
| 6 | + {{ with .Notice }}<p class="shithub-flash shithub-flash-notice">{{ . }}</p>{{ end }} | |
| 7 | + | |
| 8 | + <form method="GET" action="/admin/users" class="shithub-finder-form"> | |
| 9 | + <input type="text" name="q" value="{{ .Q }}" placeholder="username prefix"> | |
| 10 | + <label><input type="checkbox" name="suspended" value="1" {{ if .SuspendedOnly }}checked{{ end }}> Suspended</label> | |
| 11 | + <label><input type="checkbox" name="deleted" value="1" {{ if .DeletedOnly }}checked{{ end }}> Deleted</label> | |
| 12 | + <button type="submit" class="shithub-button">Filter</button> | |
| 13 | + </form> | |
| 14 | + | |
| 15 | + {{ if .Users }} | |
| 16 | + <table class="shithub-branches-table"> | |
| 17 | + <thead><tr><th>ID</th><th>Username</th><th>Display name</th><th>State</th><th>Last login</th><th></th></tr></thead> | |
| 18 | + <tbody> | |
| 19 | + {{ range .Users }} | |
| 20 | + <tr> | |
| 21 | + <td>#{{ .ID }}</td> | |
| 22 | + <td><a href="/admin/users/{{ .ID }}">{{ .Username }}</a></td> | |
| 23 | + <td>{{ .DisplayName }}</td> | |
| 24 | + <td> | |
| 25 | + {{ if .DeletedAt.Valid }}<span class="shithub-fg-muted">deleted</span> | |
| 26 | + {{ else if .SuspendedAt.Valid }}<span class="shithub-fg-muted">suspended</span> | |
| 27 | + {{ else }}active{{ end }} | |
| 28 | + {{ if .IsSiteAdmin }}<span class="shithub-pill">admin</span>{{ end }} | |
| 29 | + </td> | |
| 30 | + <td>{{ if .LastLoginAt.Valid }}{{ relativeTime .LastLoginAt.Time }}{{ else }}<span class="shithub-fg-muted">never</span>{{ end }}</td> | |
| 31 | + <td><a href="/admin/users/{{ .ID }}">View →</a></td> | |
| 32 | + </tr> | |
| 33 | + {{ end }} | |
| 34 | + </tbody> | |
| 35 | + </table> | |
| 36 | + {{ else }} | |
| 37 | + <p class="shithub-empty-note">No users match.</p> | |
| 38 | + {{ end }} | |
| 39 | + | |
| 40 | + <nav class="shithub-pagination"> | |
| 41 | + {{ if gt .Page 1 }}<a href="?page={{ .PrevPage }}">← Prev</a>{{ end }} | |
| 42 | + {{ if .HasMore }}<a href="?page={{ .NextPage }}">Next →</a>{{ end }} | |
| 43 | + </nav> | |
| 44 | + </div> | |
| 45 | +</div> | |
| 46 | +{{- end }} | |