{#- One operator, plus their live sessions. The swap target of every mutation
on this page: disable/enable, a second-factor reset, and revoking one of
their sessions all re-render this whole fragment, since any of them can
change what the summary panel above the sessions table says too. -#}
<div id="operator-detail">
{% include "partials/_flash.html" %}
<div class="panel">
<dl class="fields">
<dt>Username</dt><dd><code>{{ operator.username }}</code></dd>
<dt>Role</dt><dd><span class="badge role">{{ operator.role }}</span></dd>
<dt>Status</dt><dd><span class="badge {{ operator.status }}">{{ operator.status }}</span></dd>
{#- Where `admin_sign_in`/`admin_credential_changed` are delivered. Absent
is worth saying out loud: it is the `admin_notify_contact_missing`
startup warning, and the panel is where somebody can act on it. -#}
<dt>Contact</dt>
<dd>
{%- if operator.contactEmail -%}
<code>{{ operator.contactEmail }}</code>
{%- else -%}
<span class="muted">none — security notifications cannot reach them</span>
{%- endif -%}
</dd>
{#- Forensics only: compared solely to decide whether a sign-in is from a
new address, never to allow or refuse one. -#}
{% if operator.knownLoginIps %}
<dt>Recent sign-in addresses</dt>
<dd class="wrap-anywhere">
{%- for address in operator.knownLoginIps -%}
{% if not loop.first %}, {% endif %}<code>{{ address }}</code>
{%- endfor -%}
</dd>
{% endif %}
<dt>Second factor</dt>
<dd>
{%- if operator.totpEnabled -%}
<span class="badge">on</span>
{%- elif operator.enrolmentPending -%}
<span class="badge">enrolment unconfirmed</span>
{%- else -%}
<span class="muted">off</span>
{%- endif -%}
</dd>
{% if operator.totpEnabled %}
<dt>Recovery codes</dt>
<dd>{{ operator.recoveryCodesRemaining }} unused</dd>
{% endif %}
<dt>Last login</dt>
<dd>
{%- if operator.lastLoginAt -%}
{{ operator.lastLoginAt }}
{%- else -%}
<span class="muted">never</span>
{%- endif -%}
</dd>
<dt>Created</dt><dd>{{ operator.createdAt }}</dd>
</dl>
{#- Every button below re-proves your own password before it runs, the
same reasoning `account/_card.html` documents for a live factor: this
is a much larger blast radius than a change to your own account, and
a live session alone is not sufficient authority for it. -#}
<div class="field">
<label for="operator-step-up-password">Confirm your password to change any of this</label>
<input id="operator-step-up-password" type="password" name="password"
autocomplete="current-password" required>
</div>
<div class="actions">
{% if operator.status == "disabled" %}
<button hx-post="/ui/operators/{{ operator.username }}/enable"
hx-target="#operator-detail"
hx-include="#operator-step-up-password"
hx-confirm="Enable {{ operator.username }}?">
Enable
</button>
{% else %}
<button class="danger"
hx-post="/ui/operators/{{ operator.username }}/disable"
hx-target="#operator-detail"
hx-include="#operator-step-up-password"
hx-confirm="Disable {{ operator.username }}? Their sessions are revoked immediately.">
Disable
</button>
{% endif %}
{% if operator.totpEnabled or operator.enrolmentPending %}
<button class="danger"
hx-post="/ui/operators/{{ operator.username }}/totp/reset"
hx-target="#operator-detail"
hx-include="#operator-step-up-password"
hx-confirm="Remove {{ operator.username }}'s second factor and every recovery code, and revoke their sessions?">
Reset second factor
</button>
{% endif %}
</div>
{#- Two forms rather than buttons, since each carries a value, and both
pull in the same step-up field through `hx-include`. `roles` comes from
`AdminRole::ALL`, so the select cannot offer what the handler refuses. -#}
<form class="row" hx-post="/ui/operators/{{ operator.username }}/role"
hx-target="#operator-detail"
hx-include="#operator-step-up-password"
hx-confirm="Change {{ operator.username }}'s role? Their sessions are revoked immediately.">
<div class="field">
<label for="operator-role">Role</label>
<select id="operator-role" name="role">
{% for role in roles %}
<option value="{{ role }}" {% if role == operator.role %}selected{% endif %}>{{ role }}</option>
{% endfor %}
</select>
</div>
<div>
<button type="submit">Change role</button>
</div>
</form>
<form class="row" hx-post="/ui/operators/{{ operator.username }}/contact"
hx-target="#operator-detail"
hx-include="#operator-step-up-password">
<div class="field">
<label for="operator-contact">Notification address (empty clears it)</label>
<input type="email" id="operator-contact" name="contact" autocomplete="off"
value="{% if operator.contactEmail %}{{ operator.contactEmail }}{% endif %}">
</div>
<div>
<button type="submit">Save address</button>
</div>
</form>
</div>
<div class="panel">
<h2>Sessions</h2>
{% include "partials/_sessions_table.html" %}
</div>
</div>