Files
member-console/internal/integrations/discourse/templates/discourse_operator.html
T
cgalo5758 9b96e9c9e9 Rework operator IA and unify UI vocabulary
- Restructure operator sidebar into a flat task list with indented
  children; fold plan topology into plan ladders
- Expand member catalog non-plan section to all published non-tier
  products; require recurring Stripe-mapped prices for purchase
- Add operator domains placements and terminal-claims ledger; redirect
  /domains to the FedWiki Sites Domains anchor
- Apply canonical vocabulary and chrome/form conventions; migrate seeded
  FedWiki Sites display name
2026-08-23 17:12:42 -05:00

143 lines
7.0 KiB
HTML

<!-- Discourse integration operator page body: group-mapping management and
link/sync visibility. Swap target for the mutation forms is
#operator-body (this whole partial re-renders on mutations). -->
<div class="d-flex justify-content-between align-items-start flex-wrap gap-2 mb-1">
<h1 class="mb-0">Discourse forum</h1>
<a href="/operator/integrations/discourse/settings" class="btn btn-sm btn-outline-secondary">Settings</a>
</div>
<p class="text-muted">Delivery of forum access to entitled members: group mappings, member&harr;forum-account links, and sync health. The <em>sweep</em> is the periodic background job that reconciles forum group membership with entitlements. Part of <a href="/operator/integrations">Integrations</a>.</p>
{{ if not .Configured }}
<div class="alert alert-info" role="status">
<strong>Discourse is not configured.</strong>
Set <code>discourse-base-url</code> and <code>discourse-api-key</code>
(and optionally <code>discourse-webhook-secret</code>) to enable the
integration. Until then it is registered but dormant.
</div>
{{ else }}
{{ if .Error }}
<div class="alert alert-danger" role="alert">
<strong>Error:</strong> {{ .Error }}
</div>
{{ end }}
<div class="card mb-4">
<div class="card-header"><h2 class="h6 mb-0">Sync health</h2></div>
<div class="card-body">
<dl class="row mb-0">
<dt class="col-sm-3">Last sweep</dt>
<dd class="col-sm-9">
{{ .SweepStatus }}{{ if .SweepTime }} <span class="text-muted">— {{ .SweepTime }}</span>{{ end }}
</dd>
</dl>
</div>
</div>
<div class="card mb-4">
<div class="card-header"><h2 class="h6 mb-0">Managed group mappings</h2></div>
<div class="card-body">
{{ if eq (len .Mappings) 0 }}
<p class="text-muted">No groups are mapped yet. Map an entitlement to a Discourse group below to start delivering forum access.</p>
{{ else }}
<div class="table-responsive">
<table class="table table-hover table-sm">
<thead>
<tr>
<th>Entitlement</th>
<th>Discourse group</th>
<th>Group ID</th>
<th>Mapped</th>
<th></th>
</tr>
</thead>
<tbody>
{{ range .Mappings }}
<tr>
<td>{{ if .DisplayName }}{{ .DisplayName }} <span class="text-muted small">({{ .ResourceKey }})</span>{{ else }}<code>{{ .ResourceKey }}</code>{{ end }}</td>
<td>{{ .GroupName }}</td>
<td>{{ .GroupID }}</td>
<td>{{ .CreatedAt }}</td>
<td>
<button type="button" class="btn btn-outline-danger btn-sm"
data-bs-toggle="modal"
data-bs-target="#confirmActionModal"
data-action-url="{{ .DeleteURL }}"
data-action-method="delete"
data-action-target="#operator-body"
data-action-title="Remove mapping"
data-action-body="Stop managing membership of &quot;{{ .GroupName }}&quot;? Current forum-side membership is left as-is; the console just stops converging it."
data-action-confirm-label="Remove mapping">
Remove
</button>
</td>
</tr>
{{ end }}
</tbody>
</table>
</div>
{{ end }}
<form hx-post="/partials/operator/discourse/mappings"
hx-target="#operator-body"
hx-swap="innerHTML"
class="row g-2 align-items-end mt-2">
<div class="col-auto">
<label for="discourse-mapping-resource-key" class="form-label">Entitlement</label>
<select class="form-select form-select-sm{{ if .FieldErrors.resource_key }} is-invalid{{ end }}"
id="discourse-mapping-resource-key" name="resource_key" autocomplete="off">
{{ $selected := .FormValues.resource_key }}
{{ range .ResourceKeys }}
<option value="{{ .Key }}" {{ if eq .Key $selected }}selected{{ end }}>{{ .DisplayName }} ({{ .Key }})</option>
{{ end }}
</select>
{{ if .FieldErrors.resource_key }}<div class="invalid-feedback d-block">{{ .FieldErrors.resource_key }}</div>{{ end }}
</div>
<div class="col-auto">
<label for="discourse-mapping-group-name" class="form-label">Discourse group name</label>
<input type="text" class="form-control form-control-sm{{ if .FieldErrors.group_name }} is-invalid{{ end }}"
id="discourse-mapping-group-name" name="group_name"
value="{{ .FormValues.group_name }}" autocomplete="off"
placeholder="e.g. members">
{{ if .FieldErrors.group_name }}<div class="invalid-feedback d-block">{{ .FieldErrors.group_name }}</div>{{ end }}
</div>
<div class="col-auto">
<button type="submit" class="btn btn-primary btn-sm">Map group</button>
</div>
</form>
</div>
</div>
<div class="card mb-4">
<div class="card-header">
<h2 class="h6 mb-0">Entitled but unlinked <span class="badge text-bg-secondary">{{ len .Unlinked }}</span></h2>
</div>
<div class="card-body">
{{ if eq (len .Unlinked) 0 }}
<p class="text-muted mb-0">Every entitled member has a linked forum account.</p>
{{ else }}
<p class="text-muted">These members hold forum access but have no linked Discourse account yet. In <code>oidc</code> mode they link automatically on their first forum login; this is a normal state, not a sync failure.</p>
<ul class="mb-0">
{{ range .Unlinked }}
<li>{{ .DisplayName }} <span class="text-muted">({{ .Email }})</span></li>
{{ end }}
</ul>
{{ end }}
</div>
</div>
{{ if gt (len .Conflicts) 0 }}
<div class="card mb-4 border-warning">
<div class="card-header"><h2 class="h6 mb-0">Link conflicts <span class="badge text-bg-warning">{{ len .Conflicts }}</span></h2></div>
<div class="card-body">
<p class="text-muted">These links resolved to a forum account that another person's identity also claims. They are excluded from group convergence until resolved; the console never reassigns a forum identity on its own.</p>
<ul class="mb-0">
{{ range .Conflicts }}
<li>Forum user <strong>{{ .DiscourseUsername }}</strong> — person <code>{{ .PersonID }}</code></li>
{{ end }}
</ul>
</div>
</div>
{{ end }}
{{ end }}