Files
member-console/internal/embeds/templates/partials/operator_persons.html
T
cgalo5758 257955c9d3 Add operator list-scale contract and People directory
Governed operator lists (organizations, grants, people, billing×4) gain
server-side search, status filters, and 50-row pages with true totals
from count(*) OVER(); state is URL-addressable, out-of-range pages
clamp,
and no-match is distinct from true-empty.

People is the eighth flat sidebar entry: /operator/persons lists persons
newest-joined first (excluding the reserved system person), rows linking
to the existing detail.

Billing gains an operator invoice detail at
/operator/billing/invoices/{invoiceID} reusing the member projection;
open invoices past due present as Overdue (derived, filterable, stored
status untouched); all four views lead with the linked organization and
mute object IDs.

Grants filter over the derived Live/Superseded/Inactive state, the SQL
HAVING predicate pinned to the Go derivation by test. Embedded lists
(org composite ledger, Tier changes) adopt the shared controls under
namespaced params with sibling-state-preserving URLs and scoped htmx
swaps that hold the viewport.

Review corrections: blocked ladder Delete renders disabled with tooltip
and mutations fire toasts; collapse triggers paint their open state;
sections use outside headings; plan topology drops the orphan-product
check; domains policy collapses behind a disclosure.
2026-08-24 03:58:18 -05:00

65 lines
2.4 KiB
HTML

{{/* operator_persons.html
People directory body partial for GET /operator/persons
(operator-people-directory D6), dispatched by GetPersonsPage.
Governed by operator-list-scale (operator_list_controls.html):
search over display name and email, true-total pagination. No status
facet -- People has no status vocabulary controls filter on.
Newest-joined first, so "who joined this week?" is page one.
Three states:
- Error: the load failed.
- Zero rows AND Nav.Filtered: the search/clear-search matched
nothing -- listNoMatch, distinct from true-empty
(empty-state-guidance).
- Zero rows AND not Nav.Filtered: the deployment has no persons at
all yet -- honest empty copy naming how a person row comes to
exist, no controls (nothing to search or clear).
*/}}
<div class="d-flex align-items-center justify-content-between mb-3">
<h1 class="h2 mb-0">People</h1>
</div>
{{ if .Error }}
<div class="alert alert-danger" role="alert">
<strong>Error:</strong> {{ .Error }}
</div>
{{ else if eq (len .Persons) 0 }}
{{ if .Nav.Filtered }}
{{ template "listControls" .Nav }}
{{ template "listNoMatch" .Nav }}
{{ else }}
<div class="text-center py-4">
<p class="text-muted mb-0">No one has signed in yet. Person records are created automatically the first time someone logs in.</p>
</div>
{{ end }}
{{ else }}
{{ template "listControls" .Nav }}
<div class="table-responsive">
<table class="table table-hover table-sm">
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th>Status</th>
<th>Joined</th>
</tr>
</thead>
<tbody>
{{ range .Persons }}
<tr>
<td>
<a href="/operator/persons/{{ .PersonID }}">{{ .DisplayName }}</a>
</td>
<td>
{{ .Email }}
{{ if .EmailVerified }}<span class="badge text-bg-secondary ms-1">Verified</span>{{ else }}<span class="badge text-bg-warning ms-1">Unverified</span>{{ end }}
</td>
<td class="text-muted">{{ .Status }}</td>
<td>{{ .JoinedAt }}</td>
</tr>
{{ end }}
</tbody>
</table>
</div>
{{ template "listPager" .Nav }}
{{ end }}