{#- The account itself, plus everything that acts on it.
This is the swap target of every account mutation: a contact edit, a
deactivation and a delete all return this same fragment, so the card an
operator is looking at is always the state the database is in. -#}
<div id="account-card">
{% include "partials/_flash.html" %}
<div class="panel">
<dl class="fields">
<dt>Account</dt><dd><code>{{ account.id }}</code></dd>
<dt>Profile</dt><dd><code>{{ account.profile }}</code></dd>
<dt>Status</dt><dd><span class="badge {{ account.status }}">{{ account.status }}</span></dd>
<dt>Public key</dt><dd><code>{{ account.pubkeyFingerprint }}</code></dd>
<dt>Created</dt><dd>{{ account.createdAt }}</dd>
{#- The credential the account registered under. It may since have been
deleted, in which case the link is a 404 -- which is the truth. -#}
{% if account.eabKid %}
<dt>EAB credential</dt>
<dd><a href="/ui/eab/{{ account.eabKid }}"><code>{{ account.eabKid }}</code></a></dd>
{% endif %}
{#- Traceability, recorded at `newAccount` and advanced on every
authenticated request. Every one is rendered only when present: a blank
`<dd>` under a label would read as "unknown" rather than "never
recorded". The address and its reverse name share one `<dd>` because a
name without an address is not a state that exists -- but an address
without a name is, so the inner test is its own. -#}
{% if account.createdIp %}
<dt>Created from</dt>
<dd><code>{{ account.createdIp }}</code>{% if account.createdPtr %}<br>{{ account.createdPtr }}{% endif %}</dd>
{% endif %}
{% if account.lastSeenAt %}<dt>Last seen</dt><dd>{{ account.lastSeenAt }}</dd>{% endif %}
{% if account.lastSeenIp %}
<dt>Last seen from</dt>
<dd><code>{{ account.lastSeenIp }}</code>{% if account.lastSeenPtr %}<br>{{ account.lastSeenPtr }}{% endif %}</dd>
{% endif %}
<dt>Orders URL</dt><dd class="wrap-anywhere"><code>{{ account.orders }}</code></dd>
<dt>Audit trail</dt>
<dd><a href="/ui/audit?accountId={{ account.id }}">Rows for this account</a></dd>
{% if account.termsOfServiceAgreed is defined %}
<dt>Terms agreed</dt><dd>{{ account.termsOfServiceAgreed }}</dd>
{% endif %}
</dl>
</div>
{#- Hidden below the operator tier; see `pages::fragment_context`. -#}
{% if can_write %}
<div class="panel">
<h2>Contact</h2>
{#- One `mailto:` URI per line. Validated by the same
`acme::rules::contact_shape_error` the ACME `newAccount` path and
the JSON API both call -- the three must not diverge on what a valid
contact is. -#}
<form hx-post="/ui/accounts/{{ account.id }}/contact" hx-target="#account-card">
<div class="field">
<label for="contact">One URI per line, e.g. <code>mailto:ops@example.com</code></label>
<textarea id="contact" name="contact">{{ account.contact | default([]) | join("\n") }}</textarea>
</div>
<div class="actions">
<button type="submit" class="primary">Save contact</button>
</div>
</form>
</div>
{% endif %}
{#- Hidden below the operator tier; see `pages::fragment_context`. -#}
{% if can_write %}
<div class="panel">
<h2>Danger zone</h2>
<div class="actions">
{% if account.status != "deactivated" %}
<button class="danger"
hx-post="/ui/accounts/{{ account.id }}/deactivate"
hx-target="#account-card"
hx-confirm="Deactivate this account? It will no longer be able to request issuance.">
Deactivate
</button>
{% endif %}
{#- The delete cascades; the CLI names the count in its confirmation
prompt, so this one does too. `order_count` and `live_certificates`
are set by both the page and the fragment every account mutation
answers with.
Success answers with a redirect, since the page this button lives on
is the thing being deleted; a refusal answers with this card, hence
the target. Disabled while a certificate is live -- the handler
refuses it anyway, and a button that can only say no is worse. -#}
<button class="danger"
hx-delete="/ui/accounts/{{ account.id }}"
hx-target="#account-card"
{% if live_certificates %}disabled{% endif %}
hx-confirm="Delete this account and its {{ order_count }} order(s), with their authorizations and challenges? This cannot be undone.">
Delete
</button>
</div>
{% if live_certificates %}
<p class="small">
Cannot be deleted while it holds {{ live_certificates }} live
certificate(s) — deleting their orders would leave them impossible to
revoke. Revoke them first, or wait for them to expire.
</p>
{% endif %}
<p class="muted small">
Deactivating is the ACME state change and is reversible only by the
client. Deleting removes the row and cascades to its orders,
authorizations and challenges, and is refused while any of them holds a
certificate that is neither revoked nor expired.
</p>
</div>
{% endif %}
</div>