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

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}}