Files
MeshTender/internal/core/templates/repeaters.html
T

113 lines
5.6 KiB
HTML

{{define "title"}}Repeaters · MeshTender{{end}}
{{define "header"}}
<div class="row g-2 align-items-center">
<div class="col">
<div class="page-pretitle">Your equipment</div>
<h1 class="page-title fs-1">Repeaters</h1>
</div>
<div class="col-auto ms-auto d-print-none">
<a class="btn btn-primary" href="/repeaters/add">{{template "icon-plus" "me-1"}}Add repeater</a>
</div>
</div>
{{end}}
{{define "content"}}
{{if .Error}}<div class="alert alert-danger" role="alert">{{.Error}}</div>{{end}}
{{if .Repeaters}}
<div class="row row-cards" data-filter-root>
{{/* Search/filters come FIRST in the DOM and move to the right of the list at lg
(order-lg-last). Source order governs both the mobile stack and the tab order,
so ordering with CSS alone would leave keyboard and screen-reader users
traversing every row to reach the control that exists to avoid exactly that. */}}
<div class="col-lg-3 order-lg-last">
<div class="card">
<div class="card-body">
<div class="mb-3">
<label class="form-label" for="rep-search">Search</label>
<input type="search" id="rep-search" class="form-control" data-filter-search placeholder="Repeater name" aria-label="Search your repeaters">
</div>
<div class="mb-3">
<label class="form-label" for="rep-access">Access</label>
<select id="rep-access" class="form-select" data-filter-key="access" aria-label="Filter by access">
<option value="">All</option>
<option value="owned">Owned</option>
<option value="shared">Shared with me</option>
</select>
</div>
<div class="mb-3">
<label class="form-label" for="rep-status">Status</label>
<select id="rep-status" class="form-select" data-filter-key="status" aria-label="Filter by confirmation status">
<option value="">Any status</option>
<option value="confirmed">Confirmed</option>
<option value="unconfirmed">Unconfirmed</option>
</select>
</div>
<label class="form-check">
<input class="form-check-input" type="checkbox" data-filter-flag="sharedout">
<span class="form-check-label">Shared with others</span>
<span class="form-hint mt-0">Repeaters you own and have shared to at least one person or organization.</span>
</label>
</div>
</div>
</div>
<!-- The repeater cards -->
<div class="col-lg-9">
<div class="row row-cards">
{{range .Repeaters}}
{{$shareCounts := index $.ShareCounts .ID}}
<div class="col-12" data-filter-item
data-name="{{.Name}}"
data-access="{{if .Shared}}shared{{else}}owned{{end}}"
data-status="{{if .Confirmed}}confirmed{{else}}unconfirmed{{end}}"
data-sharedout="{{if and (not .Shared) (or (gt $shareCounts.Users 0) (gt $shareCounts.Orgs 0))}}1{{else}}0{{end}}">
<a class="card card-link card-link-pop text-reset" href="/repeaters/{{.PublicID}}">
<div class="card-body py-3">
<div class="d-flex align-items-center gap-3">
<span class="avatar bg-blue-lt d-none d-sm-flex">{{template "icon-antenna" ""}}</span>
<div class="flex-fill" style="min-width:0">
<div class="d-flex align-items-center flex-wrap gap-2">
<h2 class="m-0 fs-3">{{.Name}}</h2>
{{if .Shared}}<span class="badge bg-purple-lt">Shared with me</span>{{else}}<span class="badge bg-blue-lt">Owned</span>{{end}}
{{template "repstatus" .}}
</div>
<div class="d-flex align-items-center flex-wrap gap-2 mt-1 text-secondary">
<span class="font-monospace">{{slice .PublicKeyHex 0 8}}…</span>
<span class="ms-1">·</span>
{{if .Shared}}<span>Shared by {{.OwnerName}}</span>{{else}}<span>{{template "sharetext" $shareCounts}}</span>{{end}}
</div>
</div>
</div>
{{if and (not .Shared) .Confirmed .AccessKnown (not .IsAdmin)}}
<div class="alert alert-warning mt-3 mb-0">
MeshTender only has <strong>guest</strong> access here. Guest is available to anyone with a blank password, so there's no point operating at this level — re-run <code>setperm &lt;your key&gt; 3</code> on the repeater to grant admin, then re-test.
</div>
{{end}}
</div>
</a>
</div>
{{end}}
</div>
<div class="empty py-4" data-filter-empty hidden>
<p class="empty-title">No matches</p>
<p class="empty-subtitle text-secondary">No repeaters match your search and filters.</p>
</div>
</div>
</div>
{{else}}
<div class="empty">
<div class="empty-icon">{{template "icon-antenna" ""}}</div>
<p class="empty-title">No repeaters yet</p>
<p class="empty-subtitle text-secondary">Add one to grant MeshTender access and test it with a modem.</p>
<div class="empty-action">
<a href="/repeaters/add" class="btn btn-primary">{{template "icon-plus" "me-1"}}Add repeater</a>
</div>
</div>
{{end}}
{{end}}
{{/* sharetext renders a repeater's sharing summary as plain text: e.g.
"Shared with 3 users, 2 organizations", omitting a zero count, or
"Not shared" when both are zero. Dot is a *store.ShareCount {Users, Orgs}. */}}
{{define "sharetext"}}{{if and (eq .Users 0) (eq .Orgs 0)}}Not shared{{else}}Shared with{{if gt .Users 0}} {{.Users}} user{{if ne .Users 1}}s{{end}}{{end}}{{if and (gt .Users 0) (gt .Orgs 0)}},{{end}}{{if gt .Orgs 0}} {{.Orgs}} organization{{if ne .Orgs 1}}s{{end}}{{end}}{{end}}{{end}}