mirror of
https://github.com/MeshTender/MeshTender.git
synced 2026-09-09 13:15:33 +00:00
251 lines
11 KiB
HTML
251 lines
11 KiB
HTML
{{define "title"}}{{.Org.Name}} · MeshTender{{end}}
|
|
{{define "header"}}
|
|
<div class="row g-2 align-items-center">
|
|
<div class="col">
|
|
<div class="page-pretitle">Organization · you're {{if .IsAdmin}}an admin{{else}}a member{{end}}</div>
|
|
<h2 class="page-title">{{.Org.Name}}</h2>
|
|
</div>
|
|
<div class="col-auto ms-auto d-print-none">
|
|
<div class="btn-list">
|
|
{{if .IsAdmin}}<a class="btn" href="/orgs/{{.Org.Slug}}/permissions">{{template "icon-settings" "me-1"}}Edit permissions</a>{{end}}
|
|
<a class="btn" href="/orgs/{{.Org.Slug}}?view=public">{{template "icon-world" "me-1"}}Public page</a>
|
|
<form method="post" action="/orgs/{{.Org.Slug}}/leave" class="m-0" onsubmit="return confirm('Leave this organization?')">
|
|
<button type="submit" class="btn btn-ghost-danger">Leave</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{{end}}
|
|
{{define "content"}}
|
|
{{if .Error}}<div class="alert alert-danger">{{.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-users" ""}}</span></div>
|
|
<div class="col"><div class="h1 mb-0">{{.MemberCount}}</div><div class="text-secondary">Member{{if ne .MemberCount 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-green text-white avatar">{{template "icon-user" ""}}</span></div>
|
|
<div class="col"><div class="h1 mb-0">{{.AdminCount}}</div><div class="text-secondary">Admin{{if ne .AdminCount 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-azure text-white avatar">{{template "icon-antenna" ""}}</span></div>
|
|
<div class="col"><div class="h1 mb-0">{{.RepeaterCount}}</div><div class="text-secondary">Repeater{{if ne .RepeaterCount 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-map-pin" ""}}</span></div>
|
|
<div class="col text-truncate">
|
|
<div class="h1 mb-0 text-truncate">{{if .Org.Region}}{{.Org.Region}}{{else}}<span class="text-secondary fw-normal">Not set</span>{{end}}</div>
|
|
<div class="text-secondary">Region</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row row-cards mt-1">
|
|
<!-- Left column -->
|
|
<div class="col-lg-8">
|
|
{{if .HasMap}}
|
|
<div class="card">
|
|
<div class="card-header"><h3 class="card-title">Repeater map</h3></div>
|
|
<div class="card-body p-2">
|
|
<link rel="stylesheet" href="/static/leaflet.css">
|
|
<div id="map" style="height:300px" role="region" aria-label="Map of organization repeater locations"></div>
|
|
<script src="/static/leaflet.js"></script>
|
|
<script src="/static/meshmap.js"></script>
|
|
<script>
|
|
meshMap('map', [
|
|
{{range .Repeaters}}{{if .HasLocation}}{name: {{.Name}}, lat: {{.Lat}}, lon: {{.Lon}}},{{end}}{{end}}
|
|
]);
|
|
</script>
|
|
</div>
|
|
</div>
|
|
{{end}}
|
|
|
|
<div class="card {{if .HasMap}}mt-3{{end}}">
|
|
<div class="card-header"><h3 class="card-title">Repeaters</h3></div>
|
|
{{if .Repeaters}}
|
|
<div class="list-group list-group-flush">
|
|
{{range .Repeaters}}
|
|
<div class="list-group-item d-flex align-items-center gap-2">
|
|
<span class="text-secondary">{{template "icon-antenna" ""}}</span>
|
|
<div class="flex-fill text-truncate">
|
|
<a class="fw-bold text-reset text-decoration-none" href="/repeaters/{{.RepeaterPublicID}}">{{.Name}}</a>
|
|
<div class="text-secondary small">owner {{.OwnerName}}</div>
|
|
</div>
|
|
<a class="btn btn-sm" href="/repeaters/{{.RepeaterPublicID}}/console">{{template "icon-terminal" "me-1"}}Console</a>
|
|
</div>
|
|
{{end}}
|
|
</div>
|
|
{{else}}
|
|
<div class="card-body"><p class="text-secondary mb-0">No repeaters have been contributed yet. Members can contribute their own from a repeater's “Organizations” page.</p></div>
|
|
{{end}}
|
|
</div>
|
|
|
|
<div class="card mt-3 member-card">
|
|
<div class="card-header">
|
|
<h3 class="card-title">Members</h3>
|
|
<div class="card-actions">
|
|
<div class="btn-group" role="group" aria-label="Filter members">
|
|
<input type="radio" class="btn-check" name="member-filter" id="mf-all" value="all" autocomplete="off" checked>
|
|
<label for="mf-all" class="btn btn-sm">All</label>
|
|
<input type="radio" class="btn-check" name="member-filter" id="mf-member" value="member" autocomplete="off">
|
|
<label for="mf-member" class="btn btn-sm">Members</label>
|
|
<input type="radio" class="btn-check" name="member-filter" id="mf-admin" value="admin" autocomplete="off">
|
|
<label for="mf-admin" class="btn btn-sm">Admins</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="list-group list-group-flush" id="member-list">
|
|
{{range .Members}}
|
|
<div class="list-group-item d-flex align-items-center flex-wrap gap-2" data-role="{{.Role}}">
|
|
<div class="flex-fill text-truncate">
|
|
<span class="fw-bold">{{.Name}}</span>
|
|
<span class="text-secondary">@{{.Username}}</span>
|
|
</div>
|
|
{{if eq .Role "admin"}}<span class="badge bg-success-lt">admin</span>{{else}}<span class="badge bg-azure-lt">member</span>{{end}}
|
|
{{if and $.IsAdmin (ne .UserID $.Self)}}
|
|
<form method="post" action="/orgs/{{$.Org.Slug}}/members/{{.UserID}}" class="m-0 d-flex gap-2">
|
|
{{if eq .Role "admin"}}
|
|
<button type="submit" name="action" value="demote" class="btn btn-sm">Demote</button>
|
|
{{else}}
|
|
<button type="submit" name="action" value="promote" class="btn btn-sm">Make admin</button>
|
|
{{end}}
|
|
<button type="submit" name="action" value="remove" class="btn btn-sm btn-ghost-danger">Remove</button>
|
|
</form>
|
|
{{end}}
|
|
</div>
|
|
{{end}}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Right column -->
|
|
<div class="col-lg-4">
|
|
{{if not .IsAdmin}}
|
|
<div class="card">
|
|
<div class="card-header"><h3 class="card-title">About</h3></div>
|
|
<div class="card-body">
|
|
{{if .Org.Description}}
|
|
<p class="mb-0">{{.Org.Description}}</p>
|
|
{{else}}
|
|
<p class="text-secondary mb-0">No description yet.</p>
|
|
{{end}}
|
|
</div>
|
|
</div>
|
|
{{end}}
|
|
|
|
{{if .IsAdmin}}
|
|
<div class="card">
|
|
<div class="card-header"><h3 class="card-title">Organization profile <span class="text-secondary">(public)</span></h3></div>
|
|
<div class="card-body">
|
|
<form method="post" action="/orgs/{{.Org.Slug}}/edit">
|
|
<div class="mb-3">
|
|
<label class="form-label" for="org_name">Name</label>
|
|
<input type="text" class="form-control" id="org_name" name="name" value="{{.Org.Name}}" maxlength="80" required>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label" for="org_slug">URL slug</label>
|
|
<div class="input-group">
|
|
<span class="input-group-text">/orgs/</span>
|
|
<input type="text" class="form-control font-monospace" id="org_slug" name="slug" value="{{.Org.Slug}}"
|
|
pattern="[a-z0-9]+(-[a-z0-9]+)*" minlength="3" maxlength="40" required>
|
|
</div>
|
|
<small class="form-hint">Lowercase letters, numbers, and hyphens. Changing this changes the org's URL.</small>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label" for="org_region">Region <span class="text-secondary">(optional)</span></label>
|
|
<input type="text" class="form-control" id="org_region" name="region" value="{{.Org.Region}}" maxlength="120" placeholder="e.g. Buffalo, NY">
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label" for="org_description">Description</label>
|
|
<textarea class="form-control" id="org_description" name="description" rows="3" maxlength="2000" placeholder="What this organization is about, where it operates, etc.">{{.Org.Description}}</textarea>
|
|
</div>
|
|
<button type="submit" class="btn btn-primary">Save profile</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
{{/* Custom domains — hidden for now; the hosting/TLS infrastructure isn't in
|
|
place yet. Re-enable this card (plus the domains data in pageOrg and the
|
|
/domains routes in web.go) once it is.
|
|
<div class="card mt-3">
|
|
<div class="card-header"><h3 class="card-title">Custom domains</h3></div>
|
|
<div class="card-body">
|
|
<p class="text-secondary">
|
|
Point your own domain at MeshTender (CNAME it to this app). Once verified, visiting that
|
|
domain shows this organization's public page. Sign-in and management always happen on the
|
|
main site. TLS certificates for your domain are handled by your hosting/ops setup.
|
|
</p>
|
|
{{if .Domains}}
|
|
<div class="list-group list-group-flush mb-3">
|
|
{{range .Domains}}
|
|
<div class="list-group-item px-0">
|
|
<div class="d-flex align-items-center gap-2 mb-1">
|
|
<span class="fw-bold font-monospace">{{.Hostname}}</span>
|
|
{{if .Verified}}
|
|
<span class="badge bg-success-lt">verified</span>
|
|
{{else}}
|
|
<span class="badge bg-warning-lt">pending verification</span>
|
|
{{end}}
|
|
<form method="post" action="/orgs/{{$.Org.Slug}}/domains/delete" class="m-0 ms-auto">
|
|
<input type="hidden" name="domain_id" value="{{.ID}}">
|
|
<button type="submit" class="btn btn-sm btn-ghost-danger">Remove</button>
|
|
</form>
|
|
</div>
|
|
{{if not .Verified}}
|
|
<p class="text-secondary mb-2">Add this DNS TXT record, then click Verify:</p>
|
|
<div class="mb-2">
|
|
<code class="d-block">{{$.TXTName}}{{.Hostname}}</code>
|
|
<code class="d-block">TXT {{.VerificationToken}}</code>
|
|
</div>
|
|
<form method="post" action="/orgs/{{$.Org.Slug}}/domains/verify" class="m-0">
|
|
<input type="hidden" name="domain_id" value="{{.ID}}">
|
|
<button type="submit" class="btn btn-sm">Verify</button>
|
|
</form>
|
|
{{end}}
|
|
</div>
|
|
{{end}}
|
|
</div>
|
|
{{end}}
|
|
<form method="post" action="/orgs/{{.Org.Slug}}/domains">
|
|
<div class="mb-3">
|
|
<label class="form-label" for="add_domain">Add a domain</label>
|
|
<input type="text" class="form-control font-monospace" id="add_domain" name="hostname" placeholder="mesh.example.org" maxlength="253">
|
|
</div>
|
|
<button type="submit" class="btn btn-primary">Add domain</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
*/}}
|
|
{{end}}
|
|
</div>
|
|
</div>
|
|
|
|
<a class="back-link mt-3" href="/orgs">{{template "icon-arrow-left" "me-1"}}All organizations</a>
|
|
{{end}}
|