{% comment %}
A failed or denied action is the reason most people open an audit trail, so
the counts lead with those rather than with total volume. Do not make the
failure count a red tile and stop there: the label says "Failed", which is
what carries the meaning.
{% endcomment %}
{% include "brickwork/components/_stat.html" with label="Entries in range" value="4,812" icon="file" %}
{% include "brickwork/components/_stat.html" with label="Failed" value="37" icon="alert-circle" trend="down" trend_label="14 fewer than last month" %}
{% include "brickwork/components/_stat.html" with label="Permission denied" value="9" icon="lock" trend="up" trend_label="6 more than last month" %}
{% include "brickwork/components/_stat.html" with label="Distinct actors" value="64" icon="users" trend="flat" trend_label="unchanged on last month" %}
{% comment %}
Filtering IS the audit trail's primary control, so the bar sits above the
trail rather than beside it, and the fields your form should carry are the
three questions a reader arrives with: which actor, which action, which
dates. Copy examples/app/date-range-picker.html for the date pair.
{% endcomment %}
{% include "brickwork/components/_filter_bar.html" with fields=filter_form submit_label="Search trail" clear_href="/audit/" hx_get="/audit/" hx_target="#audit-table" %}
{% comment %}
The trail itself. No selectable, no row urls: nothing here is actionable, and
a row link would invite a reader to navigate away mid-sequence. sticky_header
keeps the column labels in view while a long trail scrolls.
Build each row's cells in your view as pre-rendered strings, one per column:
a timestamp, an actor, a rendered outcome badge, an action, and the target it
acted on. Format timestamps to the reader's timezone in the view; a trail
read in the wrong timezone is worse than no trail.
{% endcomment %}
{% include "brickwork/components/_data_table.html" with table_id="audit-table" columns=audit_columns rows=audit_rows current_sort="-occurred_at" sticky_header=True empty_heading="No entries match" empty_body="No recorded activity matches these filters. Widen the date range or clear the actor filter." %}
{% include "brickwork/components/_pagination.html" with page_obj=audit_page %}
{% comment %}
One entry expanded in full. A trail table can only show a summary per row, but
an investigation always ends up needing one entry's whole payload: the old and
new values, the request id, the source address. Expanding it in place keeps
the reader's position in the sequence, which navigating to a detail page
destroys.
This is a native
, so it opens and closes with no JavaScript. Pass
content as pre-rendered markup you mark safe at the call site. Sharing one
name across several disclosures makes them a single-open accordion.
{% endcomment %}
{% include "brickwork/components/_disclosure.html" with label="28 Aug 2026, 14:07 - Priya Raman changed billing plan on Acme Corp" content=entry_detail variant="bordered" %}
{% comment %}
Who is doing the most, which is the second question an investigation asks
after "what failed". Rows take label and amount; the component computes the
bar geometry and marks it aria-hidden, so the label and value carry the
reading in words:
top_actors [{"label": "Priya Raman", "amount": 412, "value": "412 actions"}, ...]
{% endcomment %}
{% bw_ranked_list top_actors label="Most active accounts in range" basis="max" empty_heading="No activity in range" empty_body="Nothing was recorded between these dates." %}
{% endblock %}