|
@@ -42,5 +42,97 @@
|
|
|
</div>
|
|
</div>
|
|
|
{% endif %}
|
|
{% endif %}
|
|
|
</form>
|
|
</form>
|
|
|
|
|
+
|
|
|
|
|
+ <section class="mt-8 overflow-hidden rounded-2xl border border-slate-200 bg-white shadow-sm dark:border-slate-800 dark:bg-slate-900">
|
|
|
|
|
+ <header class="flex items-center justify-between gap-3 border-b border-slate-200 px-5 py-3 dark:border-slate-800">
|
|
|
|
|
+ <div>
|
|
|
|
|
+ <h2 class="text-sm font-semibold tracking-tight">Last activity</h2>
|
|
|
|
|
+ <p class="text-xs text-slate-500 dark:text-slate-400">10 most recent audit entries for this consumer.</p>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <a href="/app/audit?entity_type=consumer&entity_id={{ consumer.id }}" class="whitespace-nowrap text-xs font-medium text-indigo-600 hover:underline dark:text-indigo-400">View all in audit log →</a>
|
|
|
|
|
+ </header>
|
|
|
|
|
+ {% if audit_items|default([])|length > 0 %}
|
|
|
|
|
+ <div x-data="{ open: null }">
|
|
|
|
|
+ <table class="w-full text-sm">
|
|
|
|
|
+ <thead class="border-b border-slate-200 bg-slate-50 text-left text-xs uppercase tracking-wider text-slate-500 dark:border-slate-800 dark:bg-slate-950 dark:text-slate-400">
|
|
|
|
|
+ <tr>
|
|
|
|
|
+ <th class="px-5 py-2 font-medium">When</th>
|
|
|
|
|
+ <th class="px-5 py-2 font-medium">Actor</th>
|
|
|
|
|
+ <th class="px-5 py-2 font-medium">Action</th>
|
|
|
|
|
+ <th class="px-5 py-2 text-right font-medium">Payload</th>
|
|
|
|
|
+ </tr>
|
|
|
|
|
+ </thead>
|
|
|
|
|
+ <tbody class="divide-y divide-slate-100 dark:divide-slate-800">
|
|
|
|
|
+ {% for ev in audit_items %}
|
|
|
|
|
+ <tr>
|
|
|
|
|
+ <td class="px-5 py-2 align-top"><time class="irdb-dt font-mono text-xs text-slate-600 dark:text-slate-300" datetime="{{ ev.occurred_at }}" title="{{ ev.occurred_at }}">{{ ev.occurred_at }}</time></td>
|
|
|
|
|
+ <td class="px-5 py-2 align-top text-xs">
|
|
|
|
|
+ <span class="rounded bg-slate-100 px-1.5 py-0.5 font-mono uppercase tracking-tight text-slate-700 dark:bg-slate-800 dark:text-slate-300">{{ ev.actor_kind }}</span>
|
|
|
|
|
+ {% if ev.actor_id %}<span class="ml-1 font-mono text-slate-500">#{{ ev.actor_id }}</span>{% endif %}
|
|
|
|
|
+ </td>
|
|
|
|
|
+ <td class="px-5 py-2 align-top">
|
|
|
|
|
+ <span class="inline-block rounded bg-cyan-100 px-2 py-0.5 font-mono text-[0.7rem] uppercase tracking-tight text-cyan-900 dark:bg-cyan-900 dark:text-cyan-100">{{ ev.action }}</span>
|
|
|
|
|
+ </td>
|
|
|
|
|
+ <td class="px-5 py-2 align-top text-right">
|
|
|
|
|
+ {% if ev.details %}
|
|
|
|
|
+ <button type="button" x-on:click="open = (open === {{ ev.id }} ? null : {{ ev.id }})" class="rounded border border-slate-300 px-2 py-0.5 text-xs hover:bg-slate-50 dark:border-slate-700 dark:hover:bg-slate-800">View</button>
|
|
|
|
|
+ {% else %}
|
|
|
|
|
+ <span class="text-xs text-slate-400">—</span>
|
|
|
|
|
+ {% endif %}
|
|
|
|
|
+ </td>
|
|
|
|
|
+ </tr>
|
|
|
|
|
+ {% if ev.details %}
|
|
|
|
|
+ <tr x-show="open === {{ ev.id }}" x-cloak>
|
|
|
|
|
+ <td colspan="4" class="bg-slate-50 px-5 py-3 dark:bg-slate-950">
|
|
|
|
|
+ {% if ev.details.changes is defined and ev.details.changes is iterable and ev.details.changes|length > 0 %}
|
|
|
|
|
+ <div class="mb-2 text-[0.7rem] font-semibold uppercase tracking-wider text-slate-500 dark:text-slate-400">Changes</div>
|
|
|
|
|
+ <table class="w-full text-xs">
|
|
|
|
|
+ <thead class="text-left text-[0.7rem] uppercase text-slate-500 dark:text-slate-400">
|
|
|
|
|
+ <tr>
|
|
|
|
|
+ <th class="px-2 py-1 font-medium">Field</th>
|
|
|
|
|
+ <th class="px-2 py-1 font-medium">Before</th>
|
|
|
|
|
+ <th class="px-2 py-1 font-medium">After</th>
|
|
|
|
|
+ </tr>
|
|
|
|
|
+ </thead>
|
|
|
|
|
+ <tbody class="divide-y divide-slate-200 bg-white dark:divide-slate-800 dark:bg-slate-900">
|
|
|
|
|
+ {% for field, change in ev.details.changes %}
|
|
|
|
|
+ <tr>
|
|
|
|
|
+ <td class="px-2 py-1 font-mono text-slate-700 dark:text-slate-200">{{ field }}</td>
|
|
|
|
|
+ <td class="px-2 py-1 align-top">
|
|
|
|
|
+ {% if change.from is null %}
|
|
|
|
|
+ <span class="text-slate-400">∅</span>
|
|
|
|
|
+ {% elseif change.from is iterable %}
|
|
|
|
|
+ <pre class="overflow-x-auto rounded bg-rose-50 px-2 py-1 font-mono text-[0.7rem] text-rose-900 dark:bg-rose-950 dark:text-rose-200">{{ change.from|json_encode(constant('JSON_PRETTY_PRINT')) }}</pre>
|
|
|
|
|
+ {% else %}
|
|
|
|
|
+ <span class="rounded bg-rose-50 px-1.5 py-0.5 font-mono text-rose-900 dark:bg-rose-950 dark:text-rose-200">{{ change.from }}</span>
|
|
|
|
|
+ {% endif %}
|
|
|
|
|
+ </td>
|
|
|
|
|
+ <td class="px-2 py-1 align-top">
|
|
|
|
|
+ {% if change.to is null %}
|
|
|
|
|
+ <span class="text-slate-400">∅</span>
|
|
|
|
|
+ {% elseif change.to is iterable %}
|
|
|
|
|
+ <pre class="overflow-x-auto rounded bg-emerald-50 px-2 py-1 font-mono text-[0.7rem] text-emerald-900 dark:bg-emerald-950 dark:text-emerald-200">{{ change.to|json_encode(constant('JSON_PRETTY_PRINT')) }}</pre>
|
|
|
|
|
+ {% else %}
|
|
|
|
|
+ <span class="rounded bg-emerald-50 px-1.5 py-0.5 font-mono text-emerald-900 dark:bg-emerald-950 dark:text-emerald-200">{{ change.to }}</span>
|
|
|
|
|
+ {% endif %}
|
|
|
|
|
+ </td>
|
|
|
|
|
+ </tr>
|
|
|
|
|
+ {% endfor %}
|
|
|
|
|
+ </tbody>
|
|
|
|
|
+ </table>
|
|
|
|
|
+ {% else %}
|
|
|
|
|
+ <pre class="overflow-x-auto rounded bg-white p-3 text-xs dark:bg-slate-900">{{ ev.details|json_encode(constant('JSON_PRETTY_PRINT')) }}</pre>
|
|
|
|
|
+ {% endif %}
|
|
|
|
|
+ </td>
|
|
|
|
|
+ </tr>
|
|
|
|
|
+ {% endif %}
|
|
|
|
|
+ {% endfor %}
|
|
|
|
|
+ </tbody>
|
|
|
|
|
+ </table>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ {% else %}
|
|
|
|
|
+ <p class="px-5 py-6 text-center text-sm text-slate-400">No activity yet.</p>
|
|
|
|
|
+ {% endif %}
|
|
|
|
|
+ </section>
|
|
|
</div>
|
|
</div>
|
|
|
{% endblock %}
|
|
{% endblock %}
|