mirror of
https://github.com/MeshTender/MeshTender.git
synced 2026-09-01 17:38:15 +00:00
147 lines
5.8 KiB
HTML
147 lines
5.8 KiB
HTML
{{define "title"}}Dashboard · MeshTender{{end}}
|
|
{{define "header"}}
|
|
<div class="row g-2 align-items-center">
|
|
<div class="col">
|
|
<div class="page-pretitle">Dashboard</div>
|
|
<h1 class="page-title fs-1">Welcome back{{if .UserName}}, {{.UserName}}{{end}}</h1>
|
|
</div>
|
|
</div>
|
|
{{end}}
|
|
{{define "content"}}
|
|
{{if .Error}}<div class="alert alert-danger" role="alert">{{.Error}}</div>{{end}}
|
|
|
|
<!-- Summary stats -->
|
|
<div class="row row-cards">
|
|
<div class="col-sm-6 col-lg-3">
|
|
<div class="card card-sm">
|
|
<div class="card-body">
|
|
<div class="row align-items-center">
|
|
<div class="col-auto"><span class="bg-primary text-white avatar">{{template "icon-antenna" ""}}</span></div>
|
|
<div class="col">
|
|
<div class="h1 mb-0">{{.OwnedCount}}</div>
|
|
<div class="text-secondary">Repeater{{if ne .OwnedCount 1}}s{{end}} owned</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-sm-6 col-lg-3">
|
|
<div class="card card-sm">
|
|
<div class="card-body">
|
|
<div class="row align-items-center">
|
|
<div class="col-auto"><span class="bg-green text-white avatar">{{template "icon-check" ""}}</span></div>
|
|
<div class="col">
|
|
<div class="h1 mb-0">{{.Confirmed}}</div>
|
|
<div class="text-secondary">Confirmed{{if .Unconfirmed}} · {{.Unconfirmed}} pending{{end}}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-sm-6 col-lg-3">
|
|
<div class="card card-sm">
|
|
<div class="card-body">
|
|
<div class="row align-items-center">
|
|
<div class="col-auto"><span class="bg-azure text-white avatar">{{template "icon-world" ""}}</span></div>
|
|
<div class="col">
|
|
<div class="h1 mb-0">{{.OrgCount}}</div>
|
|
<div class="text-secondary">Organization{{if ne .OrgCount 1}}s{{end}}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-sm-6 col-lg-3">
|
|
<div class="card card-sm">
|
|
<div class="card-body">
|
|
<div class="row align-items-center">
|
|
<div class="col-auto"><span class="bg-purple text-white avatar">{{template "icon-share" ""}}</span></div>
|
|
<div class="col">
|
|
<div class="h1 mb-0">{{.SharedCount}}</div>
|
|
<div class="text-secondary">Shared with you</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Getting-started checklist: shown only while a step is still pending -->
|
|
{{if .Onboarding}}
|
|
<div class="card mt-3">
|
|
<div class="card-header">
|
|
<h2 class="card-title">Get started</h2>
|
|
<div class="card-actions text-secondary small">{{.OnboardingDone}} of {{.OnboardingTotal}} done</div>
|
|
</div>
|
|
<div class="list-group list-group-flush">
|
|
{{range .Onboarding}}
|
|
<div class="list-group-item d-flex align-items-center gap-3">
|
|
<input class="form-check-input m-0" type="checkbox" {{if .Done}}checked{{end}} disabled aria-label="{{.Title}}">
|
|
<div class="flex-fill" style="min-width:0">
|
|
<div class="fw-bold{{if .Done}} text-secondary text-decoration-line-through{{end}}">{{.Title}}</div>
|
|
{{if not .Done}}<div class="text-secondary small">{{.Desc}}</div>{{end}}
|
|
</div>
|
|
{{if not .Done}}<a href="{{.Href}}" class="btn btn-sm btn-primary">{{.Action}}</a>{{end}}
|
|
</div>
|
|
{{end}}
|
|
</div>
|
|
</div>
|
|
{{end}}
|
|
|
|
<!-- Activity + map: only meaningful once the user owns a repeater -->
|
|
{{if .OwnedCount}}
|
|
<div class="row row-cards mt-1">
|
|
<!-- Left column -->
|
|
<div class="col-lg-8">
|
|
<!-- Recent activity -->
|
|
<div class="card">
|
|
<div class="card-header"><h2 class="card-title">Recent activity</h2></div>
|
|
{{if .Recent}}
|
|
<div class="table-responsive">
|
|
<table class="table table-vcenter card-table">
|
|
<thead><tr><th>Repeater</th><th>Command</th><th>By</th><th>When</th><th></th></tr></thead>
|
|
<tbody>
|
|
{{range .Recent}}
|
|
<tr>
|
|
<td class="text-truncate" style="max-width:10rem"><a class="text-reset" href="/repeaters/{{.RepeaterPublicID}}/log">{{.RepeaterName}}</a></td>
|
|
<td><code>{{.CommandText}}</code></td>
|
|
<td class="text-secondary">{{if .SenderUsername}}<a class="text-reset" href="{{$.RootURL}}/u/{{.SenderUsername}}">{{.SenderName}}</a>{{else}}{{.SenderName}}{{end}}</td>
|
|
<td class="text-secondary text-nowrap">{{ts .SentAt "datetime"}}</td>
|
|
<td>{{if .AckReceived}}<span class="badge bg-success-lt">reply</span>{{else}}<span class="badge bg-yellow-lt">no reply</span>{{end}}</td>
|
|
</tr>
|
|
{{end}}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
{{else}}
|
|
<div class="card-body"><p class="text-secondary mb-0">No commands have been run on your repeaters yet. Open a repeater's console to send one.</p></div>
|
|
{{end}}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Right column -->
|
|
<div class="col-lg-4">
|
|
<!-- Map -->
|
|
<div class="card">
|
|
<div class="card-header"><h2 class="card-title">Repeater map</h2></div>
|
|
{{if .Mapped}}
|
|
<div class="card-body p-2">
|
|
<link rel="stylesheet" href="{{ asset "/static/leaflet.css" }}">
|
|
<div id="map" style="height:260px" role="region" aria-label="Map of your repeater locations"></div>
|
|
<script src="{{ asset "/static/leaflet.js" }}"></script>
|
|
<script src="{{ asset "/static/meshmap.js" }}"></script>
|
|
<script nonce="{{.Nonce}}">
|
|
meshMap('map', [
|
|
{{range .Mapped}}{name: {{.Name}}, lat: {{.Latitude}}, lon: {{.Longitude}}},{{end}}
|
|
]);
|
|
</script>
|
|
</div>
|
|
{{else}}
|
|
<div class="card-body"><p class="text-secondary mb-0">No repeater locations yet. Open a repeater's console, connect your modem, and use “Fetch location” to read its coordinates onto the map — or set them when you add it.</p></div>
|
|
{{end}}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{{end}}
|
|
{{end}}
|