Files
marketplaces/src/app/features/admin/users/pages/admin-users-page.component.html

124 lines
6.3 KiB
HTML
Raw Normal View History

<section class="admin-users-page">
<div class="card">
<h2>{{ 'adminUsers.title' | translate }}</h2>
@if (facade.loading()) {
fix(backoffice): WCAG 2.1 AA accessibility fixes RC A11Y-01 audit pass, Backoffice (admin/*) area. Builds on 712a7b4/63c9cee's composition pass (scope="col", skeletons, empty-states) without redoing that work. - Sidebar nav landmark: admin-layout's <nav aria-label> reused the "Dashboard" nav-item translation key as its landmark label, misleadingly announcing the whole sidebar as "Dashboard" - added a dedicated adminShell.sidebarLabel key ("Admin sidebar navigation") in en/ru/hy. Skip link, #admin-content main landmark, Escape handling, and mobile-drawer focus management were already correct - verified, not touched. - Categories tree drag-and-drop keyboard fallback (WCAG 2.1.1): the category tree's native HTML5 DnD (draggable/dragstart/drop) reorders siblings with no keyboard equivalent - existing arrow-key tree navigation only expands/ collapses/selects, never reorders. Added per-row move-up/move-down icon buttons (disabled at sibling boundaries), reusing the existing `reorder` output so the facade's reorder logic is untouched; new adminCategories.moveUp/moveDown keys in en/ru/hy. - Screen-reader loading announcements: skeleton-row loading states across Products, Categories, Customers, Orders, Transactions, Users, Reviews, Monitoring (webhooks/events), and Analytics (summary cards + top products) were purely visual (app-skeleton is aria-hidden by design) with no accessible "loading" text, unlike the storefront/product-details pattern - added role="status"/aria-live="polite"/aria-busy + sr-only text using the existing common.loading key. - Table row headers: added scope="row" to the primary identifying cell (product/category/customer name, order number, transaction order number, user name, review customer, report target, top-products/low-stock product name, webhook endpoint) on 9 tables that only had scope="col". Added matching `tbody th[scope='row'] { font-weight/color/text-align/ vertical-align }` + last-row border resets in each component's own scss so the semantic change doesn't alter visuals (the shared app-table stylesheet styles all <th> as bold/muted by default). Verified via `git show --stat` of fb1afb7/a03260e and `docs/UI-COMPOSITION- REVIEW.md`'s Backoffice sections first, per instructions - confirmed scope="col" coverage already complete, all admin modals already route through the shared app-dialog (focus-trap/Escape/return-focus already correct, nothing to fix), and the bare-<select> filters still carry aria-label per the accepted Sprint 28 decision (not re-migrated to app-select). Flagged, not fixed: - No toast/notification system exists anywhere in this codebase (product/ category save and delete call the gateway with no success/error UI at all, not even a subscribe error handler) - there is nothing to wire aria-live onto without adding a new UI mechanism, which is out of scope for an a11y-only pass. A prerequisite feature-level fix, not an a11y regression. - Dashboard's per-card metric/status-row/timeline skeletons (dashboard- metric, dashboard-status-row, dashboard-timeline) were left without aria-live wiring - wrapping each of the ~10 simultaneous mini-widgets in its own live region would fire a burst of redundant announcements; needs a single page-level "loading dashboard" region instead, a larger change than this surgical pass. - Monitoring's events table and the notifications dropdown (role="menu" with a static empty-state message, aria-haspopup="true") were left as-is - matches the same partial-widget-pattern precedent already accepted for locale-tabs/product-tabs in the storefront and builder passes. - Analytics `lowStockProducts` table's missing loading-skeleton branch (already flagged, not fixed, in the RC-Visual-02 pass) - untouched again here for the same reason. Verified: npx tsc --noEmit clean; npm run build green (only the pre-existing 768.57 kB vs 700 kB initial-bundle budget warning, unrelated to this pass). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-24 09:18:09 +04:00
<div class="skeleton-rows" role="status" aria-live="polite" aria-busy="true">
@for (i of [1,2,3]; track i) { <app-skeleton shape="rect" height="40px" /> }
fix(backoffice): WCAG 2.1 AA accessibility fixes RC A11Y-01 audit pass, Backoffice (admin/*) area. Builds on 712a7b4/63c9cee's composition pass (scope="col", skeletons, empty-states) without redoing that work. - Sidebar nav landmark: admin-layout's <nav aria-label> reused the "Dashboard" nav-item translation key as its landmark label, misleadingly announcing the whole sidebar as "Dashboard" - added a dedicated adminShell.sidebarLabel key ("Admin sidebar navigation") in en/ru/hy. Skip link, #admin-content main landmark, Escape handling, and mobile-drawer focus management were already correct - verified, not touched. - Categories tree drag-and-drop keyboard fallback (WCAG 2.1.1): the category tree's native HTML5 DnD (draggable/dragstart/drop) reorders siblings with no keyboard equivalent - existing arrow-key tree navigation only expands/ collapses/selects, never reorders. Added per-row move-up/move-down icon buttons (disabled at sibling boundaries), reusing the existing `reorder` output so the facade's reorder logic is untouched; new adminCategories.moveUp/moveDown keys in en/ru/hy. - Screen-reader loading announcements: skeleton-row loading states across Products, Categories, Customers, Orders, Transactions, Users, Reviews, Monitoring (webhooks/events), and Analytics (summary cards + top products) were purely visual (app-skeleton is aria-hidden by design) with no accessible "loading" text, unlike the storefront/product-details pattern - added role="status"/aria-live="polite"/aria-busy + sr-only text using the existing common.loading key. - Table row headers: added scope="row" to the primary identifying cell (product/category/customer name, order number, transaction order number, user name, review customer, report target, top-products/low-stock product name, webhook endpoint) on 9 tables that only had scope="col". Added matching `tbody th[scope='row'] { font-weight/color/text-align/ vertical-align }` + last-row border resets in each component's own scss so the semantic change doesn't alter visuals (the shared app-table stylesheet styles all <th> as bold/muted by default). Verified via `git show --stat` of fb1afb7/a03260e and `docs/UI-COMPOSITION- REVIEW.md`'s Backoffice sections first, per instructions - confirmed scope="col" coverage already complete, all admin modals already route through the shared app-dialog (focus-trap/Escape/return-focus already correct, nothing to fix), and the bare-<select> filters still carry aria-label per the accepted Sprint 28 decision (not re-migrated to app-select). Flagged, not fixed: - No toast/notification system exists anywhere in this codebase (product/ category save and delete call the gateway with no success/error UI at all, not even a subscribe error handler) - there is nothing to wire aria-live onto without adding a new UI mechanism, which is out of scope for an a11y-only pass. A prerequisite feature-level fix, not an a11y regression. - Dashboard's per-card metric/status-row/timeline skeletons (dashboard- metric, dashboard-status-row, dashboard-timeline) were left without aria-live wiring - wrapping each of the ~10 simultaneous mini-widgets in its own live region would fire a burst of redundant announcements; needs a single page-level "loading dashboard" region instead, a larger change than this surgical pass. - Monitoring's events table and the notifications dropdown (role="menu" with a static empty-state message, aria-haspopup="true") were left as-is - matches the same partial-widget-pattern precedent already accepted for locale-tabs/product-tabs in the storefront and builder passes. - Analytics `lowStockProducts` table's missing loading-skeleton branch (already flagged, not fixed, in the RC-Visual-02 pass) - untouched again here for the same reason. Verified: npx tsc --noEmit clean; npm run build green (only the pre-existing 768.57 kB vs 700 kB initial-bundle budget warning, unrelated to this pass). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-24 09:18:09 +04:00
<span class="sr-only">{{ 'common.loading' | translate }}</span>
</div>
fix(backoffice): add error+retry states to Users, Monitoring, Analytics, Reports Phase 8 (RC-01): these 4 list/dashboard pages had no error-state handling on their primary data-load subscriptions — on a gateway error, `loading` was either never reset (Users, Monitoring, Analytics: genuine infinite- spinner risk, nested subscribe chain in Analytics never resolved on failure) or there was no loading/empty/error handling at all (Reports queue: raw table with zero skeleton or fallback). - admin-users.facade.ts, admin-monitoring.facade.ts: add `error` signal, error callback on the primary load subscribe so `loading` always resolves. - admin-analytics.facade.ts: add `error` signal; every level of the 4-deep nested gateway subscribe chain (orders -> products -> categories -> reviews) now has an error handler that resolves loading instead of leaving it stuck true. - admin-moderation.facade.ts: add `reportsLoading`/`reportsError` signals (reports list had none previously). - Templates: reuse existing `app-skeleton`/`app-empty-state`/`app-button` primitives for the new error branch, `common.retry` label, two new generic `common.errorTitle`/`common.errorDescription` i18n keys added to en/ru/hy (reused across all 4 fixes instead of one-off per-page copy). Verified: tsc --noEmit clean, `npm run build` green (pre-existing bundle- budget warning only, unrelated). Live-checked Home (375px) and Backoffice Products (1024px) — no console errors, tables/cards render without overflow. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-25 21:31:30 +04:00
} @else if (facade.error()) {
<app-empty-state [title]="'common.errorTitle' | translate" [description]="'common.errorDescription' | translate">
<span slot="actions">
<app-button variant="primary" (click)="facade.loadAll()">{{ 'common.retry' | translate }}</app-button>
</span>
</app-empty-state>
} @else if (facade.users().length === 0) {
<app-empty-state [title]="'adminUsers.emptyTitle' | translate" [description]="'adminUsers.emptyDescription' | translate" />
} @else {
<app-table>
<thead>
<tr>
fix(backoffice): composition audit fixes for transactions, customers, moderation, users, monitoring, analytics - Transactions list: hardcoded #fff background replaced with var(--bg-primary); missing th scope=col added on all 7 headers - Customers list: hardcoded #fff background replaced with var(--bg-primary); missing th scope=col added on all 7 headers - Customer detail: fake CSS vars --border-subtle/--brand-primary (never defined, silently falling back to hex) replaced with real --border-color/--primary-color - Reviews list: hardcoded #fff card background and sticky-header background replaced with var(--bg-primary)/var(--bg-secondary); missing th scope=col added across dynamic column table header - Reports list: hardcoded #fff background replaced with var(--bg-primary); fake --brand-primary var replaced with --primary-color; missing th scope=col added - Review health widget: fake --surface-muted/--brand-primary vars replaced with real --bg-tertiary/--primary-color (matches product-health-widget precedent) - Users page: hardcoded #fff background replaced with var(--bg-primary); missing th scope=col added on both tables - Monitoring page: hardcoded #fff background replaced with var(--bg-primary); missing th scope=col added on webhooks and events tables - Analytics page: fake --color-primary var (undefined anywhere in codebase) replaced with real --primary-color across tabs/chart/focus rings; fake --surface-muted/--brand-primary on health bar replaced with --bg-tertiary/--primary-color; hardcoded #fff card/summary-card backgrounds replaced with var(--bg-primary); missing th scope=col added across 4 tables Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-23 11:06:17 +04:00
<th scope="col">{{ 'adminUsers.name' | translate }}</th>
<th scope="col">{{ 'adminUsers.scope' | translate }}</th>
<th scope="col">{{ 'adminUsers.role' | translate }}</th>
<th scope="col">{{ 'backoffice.status' | translate }}</th>
<th scope="col">{{ 'adminUsers.lastLogin' | translate }}</th>
<th scope="col">{{ 'adminProducts.actions' | translate }}</th>
</tr>
</thead>
<tbody>
@for (user of facade.users(); track user.id) {
<tr>
fix(backoffice): WCAG 2.1 AA accessibility fixes RC A11Y-01 audit pass, Backoffice (admin/*) area. Builds on 712a7b4/63c9cee's composition pass (scope="col", skeletons, empty-states) without redoing that work. - Sidebar nav landmark: admin-layout's <nav aria-label> reused the "Dashboard" nav-item translation key as its landmark label, misleadingly announcing the whole sidebar as "Dashboard" - added a dedicated adminShell.sidebarLabel key ("Admin sidebar navigation") in en/ru/hy. Skip link, #admin-content main landmark, Escape handling, and mobile-drawer focus management were already correct - verified, not touched. - Categories tree drag-and-drop keyboard fallback (WCAG 2.1.1): the category tree's native HTML5 DnD (draggable/dragstart/drop) reorders siblings with no keyboard equivalent - existing arrow-key tree navigation only expands/ collapses/selects, never reorders. Added per-row move-up/move-down icon buttons (disabled at sibling boundaries), reusing the existing `reorder` output so the facade's reorder logic is untouched; new adminCategories.moveUp/moveDown keys in en/ru/hy. - Screen-reader loading announcements: skeleton-row loading states across Products, Categories, Customers, Orders, Transactions, Users, Reviews, Monitoring (webhooks/events), and Analytics (summary cards + top products) were purely visual (app-skeleton is aria-hidden by design) with no accessible "loading" text, unlike the storefront/product-details pattern - added role="status"/aria-live="polite"/aria-busy + sr-only text using the existing common.loading key. - Table row headers: added scope="row" to the primary identifying cell (product/category/customer name, order number, transaction order number, user name, review customer, report target, top-products/low-stock product name, webhook endpoint) on 9 tables that only had scope="col". Added matching `tbody th[scope='row'] { font-weight/color/text-align/ vertical-align }` + last-row border resets in each component's own scss so the semantic change doesn't alter visuals (the shared app-table stylesheet styles all <th> as bold/muted by default). Verified via `git show --stat` of fb1afb7/a03260e and `docs/UI-COMPOSITION- REVIEW.md`'s Backoffice sections first, per instructions - confirmed scope="col" coverage already complete, all admin modals already route through the shared app-dialog (focus-trap/Escape/return-focus already correct, nothing to fix), and the bare-<select> filters still carry aria-label per the accepted Sprint 28 decision (not re-migrated to app-select). Flagged, not fixed: - No toast/notification system exists anywhere in this codebase (product/ category save and delete call the gateway with no success/error UI at all, not even a subscribe error handler) - there is nothing to wire aria-live onto without adding a new UI mechanism, which is out of scope for an a11y-only pass. A prerequisite feature-level fix, not an a11y regression. - Dashboard's per-card metric/status-row/timeline skeletons (dashboard- metric, dashboard-status-row, dashboard-timeline) were left without aria-live wiring - wrapping each of the ~10 simultaneous mini-widgets in its own live region would fire a burst of redundant announcements; needs a single page-level "loading dashboard" region instead, a larger change than this surgical pass. - Monitoring's events table and the notifications dropdown (role="menu" with a static empty-state message, aria-haspopup="true") were left as-is - matches the same partial-widget-pattern precedent already accepted for locale-tabs/product-tabs in the storefront and builder passes. - Analytics `lowStockProducts` table's missing loading-skeleton branch (already flagged, not fixed, in the RC-Visual-02 pass) - untouched again here for the same reason. Verified: npx tsc --noEmit clean; npm run build green (only the pre-existing 768.57 kB vs 700 kB initial-bundle budget warning, unrelated to this pass). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-24 09:18:09 +04:00
<th scope="row">{{ user.name }}<br /><small>{{ user.telegramUsername }}</small></th>
<td>{{ ('adminUsers.scopeValue.' + user.scope) | translate }}</td>
<td>
<select [attr.aria-label]="'adminUsers.role' | translate" [ngModel]="user.roleId" (ngModelChange)="facade.setRole(user.id, $event)">
@for (role of facade.roles(); track role.id) {
<option [value]="role.id">{{ roleLabel(role.id) }}</option>
}
</select>
</td>
<td><app-badge [variant]="user.status === 'active' ? 'success' : user.status === 'suspended' ? 'danger' : 'neutral'">{{ ('adminUsers.statusValue.' + user.status) | translate }}</app-badge></td>
<td>{{ user.lastLoginAt ? (user.lastLoginAt | date:'short') : '—' }}</td>
<td class="actions">
<app-button variant="secondary" size="sm" (click)="toggleStatus(user.id, user.status)">{{ (user.status === 'suspended' ? 'adminUsers.reactivate' : 'adminUsers.suspend') | translate }}</app-button>
<app-button variant="secondary" size="sm" (click)="facade.openSessions(user)">{{ 'adminUsers.sessions' | translate }}</app-button>
<app-button variant="secondary" size="sm" (click)="facade.openAudit(user)">{{ 'adminTransactions.audit' | translate }}</app-button>
</td>
</tr>
}
</tbody>
</app-table>
}
</div>
<div class="card">
<h2>{{ 'adminUsers.invite' | translate }}</h2>
<p class="hint">{{ 'adminUsers.passwordlessHint' | translate }}</p>
<div class="invite-form">
<app-input type="email" [ngModel]="inviteEmail()" (ngModelChange)="inviteEmail.set($event)" [placeholder]="'adminUsers.email' | translate" />
<select [attr.aria-label]="'adminUsers.role' | translate" [ngModel]="inviteRoleId()" (ngModelChange)="inviteRoleId.set($event)">
@for (role of facade.roles(); track role.id) {
<option [value]="role.id">{{ roleLabel(role.id) }}</option>
}
</select>
<select [attr.aria-label]="'adminUsers.scope' | translate" [ngModel]="inviteScope()" (ngModelChange)="inviteScope.set($event)">
<option value="marketplace">{{ 'adminUsers.scopeValue.marketplace' | translate }}</option>
<option value="office">{{ 'adminUsers.scopeValue.office' | translate }}</option>
</select>
<app-button variant="primary" (click)="sendInvite()">{{ 'adminUsers.sendInvite' | translate }}</app-button>
</div>
@if (facade.invitations().length > 0) {
<app-table>
<thead>
<tr>
fix(backoffice): composition audit fixes for transactions, customers, moderation, users, monitoring, analytics - Transactions list: hardcoded #fff background replaced with var(--bg-primary); missing th scope=col added on all 7 headers - Customers list: hardcoded #fff background replaced with var(--bg-primary); missing th scope=col added on all 7 headers - Customer detail: fake CSS vars --border-subtle/--brand-primary (never defined, silently falling back to hex) replaced with real --border-color/--primary-color - Reviews list: hardcoded #fff card background and sticky-header background replaced with var(--bg-primary)/var(--bg-secondary); missing th scope=col added across dynamic column table header - Reports list: hardcoded #fff background replaced with var(--bg-primary); fake --brand-primary var replaced with --primary-color; missing th scope=col added - Review health widget: fake --surface-muted/--brand-primary vars replaced with real --bg-tertiary/--primary-color (matches product-health-widget precedent) - Users page: hardcoded #fff background replaced with var(--bg-primary); missing th scope=col added on both tables - Monitoring page: hardcoded #fff background replaced with var(--bg-primary); missing th scope=col added on webhooks and events tables - Analytics page: fake --color-primary var (undefined anywhere in codebase) replaced with real --primary-color across tabs/chart/focus rings; fake --surface-muted/--brand-primary on health bar replaced with --bg-tertiary/--primary-color; hardcoded #fff card/summary-card backgrounds replaced with var(--bg-primary); missing th scope=col added across 4 tables Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-23 11:06:17 +04:00
<th scope="col">{{ 'adminUsers.email' | translate }}</th>
<th scope="col">{{ 'adminUsers.role' | translate }}</th>
<th scope="col">{{ 'backoffice.status' | translate }}</th>
<th scope="col">{{ 'adminProducts.actions' | translate }}</th>
</tr>
</thead>
<tbody>
@for (invite of facade.invitations(); track invite.id) {
<tr>
fix(backoffice): WCAG 2.1 AA accessibility fixes RC A11Y-01 audit pass, Backoffice (admin/*) area. Builds on 712a7b4/63c9cee's composition pass (scope="col", skeletons, empty-states) without redoing that work. - Sidebar nav landmark: admin-layout's <nav aria-label> reused the "Dashboard" nav-item translation key as its landmark label, misleadingly announcing the whole sidebar as "Dashboard" - added a dedicated adminShell.sidebarLabel key ("Admin sidebar navigation") in en/ru/hy. Skip link, #admin-content main landmark, Escape handling, and mobile-drawer focus management were already correct - verified, not touched. - Categories tree drag-and-drop keyboard fallback (WCAG 2.1.1): the category tree's native HTML5 DnD (draggable/dragstart/drop) reorders siblings with no keyboard equivalent - existing arrow-key tree navigation only expands/ collapses/selects, never reorders. Added per-row move-up/move-down icon buttons (disabled at sibling boundaries), reusing the existing `reorder` output so the facade's reorder logic is untouched; new adminCategories.moveUp/moveDown keys in en/ru/hy. - Screen-reader loading announcements: skeleton-row loading states across Products, Categories, Customers, Orders, Transactions, Users, Reviews, Monitoring (webhooks/events), and Analytics (summary cards + top products) were purely visual (app-skeleton is aria-hidden by design) with no accessible "loading" text, unlike the storefront/product-details pattern - added role="status"/aria-live="polite"/aria-busy + sr-only text using the existing common.loading key. - Table row headers: added scope="row" to the primary identifying cell (product/category/customer name, order number, transaction order number, user name, review customer, report target, top-products/low-stock product name, webhook endpoint) on 9 tables that only had scope="col". Added matching `tbody th[scope='row'] { font-weight/color/text-align/ vertical-align }` + last-row border resets in each component's own scss so the semantic change doesn't alter visuals (the shared app-table stylesheet styles all <th> as bold/muted by default). Verified via `git show --stat` of fb1afb7/a03260e and `docs/UI-COMPOSITION- REVIEW.md`'s Backoffice sections first, per instructions - confirmed scope="col" coverage already complete, all admin modals already route through the shared app-dialog (focus-trap/Escape/return-focus already correct, nothing to fix), and the bare-<select> filters still carry aria-label per the accepted Sprint 28 decision (not re-migrated to app-select). Flagged, not fixed: - No toast/notification system exists anywhere in this codebase (product/ category save and delete call the gateway with no success/error UI at all, not even a subscribe error handler) - there is nothing to wire aria-live onto without adding a new UI mechanism, which is out of scope for an a11y-only pass. A prerequisite feature-level fix, not an a11y regression. - Dashboard's per-card metric/status-row/timeline skeletons (dashboard- metric, dashboard-status-row, dashboard-timeline) were left without aria-live wiring - wrapping each of the ~10 simultaneous mini-widgets in its own live region would fire a burst of redundant announcements; needs a single page-level "loading dashboard" region instead, a larger change than this surgical pass. - Monitoring's events table and the notifications dropdown (role="menu" with a static empty-state message, aria-haspopup="true") were left as-is - matches the same partial-widget-pattern precedent already accepted for locale-tabs/product-tabs in the storefront and builder passes. - Analytics `lowStockProducts` table's missing loading-skeleton branch (already flagged, not fixed, in the RC-Visual-02 pass) - untouched again here for the same reason. Verified: npx tsc --noEmit clean; npm run build green (only the pre-existing 768.57 kB vs 700 kB initial-bundle budget warning, unrelated to this pass). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-24 09:18:09 +04:00
<th scope="row">{{ invite.email }}</th>
<td>{{ roleLabel(invite.roleId) }}</td>
<td><app-badge variant="neutral">{{ ('adminUsers.invitationStatus.' + invite.status) | translate }}</app-badge></td>
<td>
@if (invite.status === 'pending') {
<app-button variant="danger" size="sm" (click)="facade.revokeInvitation(invite.id)">{{ 'adminUsers.revoke' | translate }}</app-button>
}
</td>
</tr>
}
</tbody>
</app-table>
} @else {
<app-empty-state [title]="'adminUsers.invitationsEmptyTitle' | translate" />
}
</div>
<div class="card">
<h2>{{ 'adminUsers.roles' | translate }}</h2>
@for (role of facade.roles(); track role.id) {
<p><strong>{{ roleLabel(role.id) }}</strong> — {{ role.permissions.map(permissionLabel).join(', ') }}</p>
}
</div>
<app-dialog [open]="!!facade.sessionsTarget()" [titleText]="'adminUsers.sessions' | translate" size="sm" (closed)="facade.closeSessions()">
@for (session of facade.sessions(); track session.id) {
<p>{{ session.device }} — {{ session.ip }} — {{ session.lastActiveAt | date:'short' }}
@if (session.current) { <app-badge variant="success">{{ 'adminUsers.currentSession' | translate }}</app-badge> }
@else { <app-button variant="danger" size="sm" (click)="facade.revokeSession(session.id)">{{ 'adminUsers.revoke' | translate }}</app-button> }
</p>
}
</app-dialog>
<app-dialog [open]="!!facade.auditTarget()" [titleText]="'adminTransactions.audit' | translate" size="sm" (closed)="facade.closeAudit()">
@for (entry of facade.audit(); track $index) {
<p>{{ entry.timestamp | date:'short' }} — {{ ('adminUsers.actor.' + entry.actor) | translate }} — {{ auditText(entry) }}</p>
}
</app-dialog>
</section>