Extend the keep-until-next-scan behaviour to the bulk delete modal: instead
of removing matched cards on success, mark them _deleted and keep them greyed
with a "🗑 Deleted" badge and hidden buttons. /api/delete_bulk now returns
deleted_ids so the grid marks exactly the items the server actually deleted —
partial failures stay active and re-deletable. Already-handled (_deleted /
_redacted) items are excluded from the bulk-delete match set so they aren't
re-counted or re-processed.
201 tests pass.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Immutable audit_log table in the scanner DB records every significant
admin action (profile save/delete, token create/revoke, PIN changes,
source add/update/delete, scheduler job changes, scan start/stop, SMTP
save, dispositions, item delete/redact). GET /api/audit_log exposes
entries newest-first. New Audit Log tab in the Settings modal renders
the table on demand. Settings modal widened 540→640 px and tab labels
set to white-space:nowrap so the six-tab row fits on one line.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- New Role dropdown in filter bar (All / Ansatte / Elever) — filters the
results grid client-side via applyFilters() and clearFilters().
- Exports respect the active role: exportExcel() and exportArticle30()
append ?role=student|staff to the fetch URL when a role is selected.
- _build_excel_bytes(role='') and _build_article30_docx(role='') filter
to a local _items list at the top; all internal sheets (Summary, GPS,
External transfers, Art.30 staff/student tables) see only the filtered
subset. Filenames get _elever or _ansatte suffix.
- i18n: m365_filter_all_roles / m365_filter_staff / m365_filter_student
added to en/da/de.json.
- CLAUDE.md, README.md, CHANGELOG.md, MANUAL-EN.md, MANUAL-DA.md updated.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>