Skip to content

Feature/audit frontend#264

Merged
mateodurante merged 19 commits into
developfrom
feature/audit_frontend
Jun 18, 2026
Merged

Feature/audit frontend#264
mateodurante merged 19 commits into
developfrom
feature/audit_frontend

Conversation

@mateodurante

Copy link
Copy Markdown
Contributor

This pull request introduces a comprehensive frontend audit log feature, including new API services, UI components, menu integration, and localization enhancements. The audit log allows users to view, filter, and inspect object-level change history, with support for detailed change rendering and multi-language support.

Audit Log Feature Implementation:

  • Added a new API service (getAudits, getObjectAudits) for fetching audit logs and object-specific audit history. (frontend/src/api/services/audit.jsx)
  • Created a full-featured audit log list page with filtering, searching, ordering, and pagination. (frontend/src/views/audits/ListAudit.jsx)
  • Added a reusable table component for displaying audit entries, including expandable change details and action badges. (frontend/src/views/audits/components/TableAudit.jsx)
  • Implemented an audit modal component for displaying object-specific audit history with "load more" functionality and rich change rendering. (frontend/src/views/audits/components/AuditModal.jsx)

Navigation and Routing:

  • Integrated the audit log into the main menu and application routes, with permission checks for visibility. (frontend/src/menu-items.jsx, frontend/src/routes.jsx) [1] [2]
  • Added the audit modal import to relevant case view for object-level history access. (frontend/src/views/case/ReadCase.jsx)

Localization and UI Enhancements:

  • Added new translation keys for audit log UI elements and actions in both English and Spanish locale files. (frontend/public/locales/en/translation.json, frontend/public/locales/es/translation.json) [1] [2] [3] [4]
  • Updated dark mode styles for better dropdown menu appearance. (frontend/src/assets/scss/partials/_dark-mode.scss)

Configuration:

  • Registered the audit API endpoint in the component URL constants. (frontend/src/config/constant.jsx)

Backend:
- AuditFilter with content_type__model (method-based) and object_id
- AuditViewSet: select_related, search_fields, ordering_fields, ordering
- Exclude last_login from User auditlog tracking

Frontend — new files:
- api/services/audit.jsx: getAudits, getObjectAudits with pagination
- views/audits/ListAudit.jsx: global audit log page with search/pagination
- views/audits/components/TableAudit.jsx: table with action badges
- views/audits/components/AuditModal.jsx: reusable modal with paginated
  'Load more' + auto-scroll, per-object audit history

Frontend — AuditModal button added to 12 views:
- ReadEvent, ReadCase, ViewNetwork, ViewContact (dedicated pages)
- TableFeed, TableContact, TableTaxonomy, TableStates, TablePriorities,
  TableReport, TableUsers, ModalDetailNetwork (table modals)

Frontend — routing/menu:
- Route /audits with view_logentry permission
- Menu item 'Audit Log' under Configuration
- i18n: w.action, w.changes, w.load_more, ngen.audit.title, menu.audit
- AuditViewSet was missing filter_backends, so filterset_class
  (AuditFilter) was silently ignored — content_type__model and
  object_id filtering had no effect.
- Added filter_backends with DjangoFilterBackend, SearchFilter,
  OrderingFilter so filtering, search, and ordering all work.
- Added 'filters' to rest_framework imports.
- TestAuditFilter (4 cases): verifies method-based filter correctly
  excludes entries from other content_types sharing the same
  object_id, handles nonexistent models, and returns all models
  when content_type filter is omitted.
- Register ngen.TaggedObject (custom taggit through model) with auditlog
- Add post_save signal on TaggedObject that creates LogEntry for
  the parent (Event/Case) when tags are added/updated
- Generic THROUGH_MODELS dict in AuditFilter maps parent models to
  their through models (fk or gfk), enabling filter_by_model to
  include through-model entries via Subquery
- Covers: Event (TodoTask, Evidence, ArtifactRelation, TaggedObject)
  and Case (Evidence, ArtifactRelation, TaggedObject)
- Tests: signal creates audit entry for parent; filter includes
  through-model entries for event
- post_delete handler creates LogEntry for parent (Event/Case) with
  changes={"tags": ["", "removed: <tag>"]} when a tag is removed
- Test verifies tag removal generates audit entry for parent
Signals (signals.py):
- m2m_changed handler that creates LogEntry for parent model when
  M2M relations change (post_add/post_remove/post_clear)
- Covers Network.contacts, Contact.users, Playbook.taxonomy — the
  three M2M fields without custom through models that were not
  previously tracked

Tests (test_audit.py):
- TestM2MAuditSignals: 4 tests for network contacts add/remove,
  contact users add, playbook taxonomy add
- TestCaseBasicAudit: 2 tests verifying Case creation and update
  generate audit entries
- Total: 13 audit tests, all passing
…fined objectId

ReadCase uses id as a plain string (from URL split), ViewNetwork
and ViewContact destructure {id} from useParams(). All three were
passing id.id (undefined) instead of id. Only ReadEvent was correct
because it stores the full useParams() object in useState.
- AuditModal.jsx: rows expand/collapse on click showing full
  changes text without truncation
- TableAudit.jsx: same expandable behavior for global audit list
- Clicking a row toggles between truncated (maxWidth + ellipsis)
  and expanded (full content, no width limit)
Backend (signals.py):
- m2m_changed signal now stores related model name alongside PKs
  e.g. 'post_add [contact]: [1, 2, 3]' instead of 'post_add: [1, 2, 3]'

Frontend (AuditModal.jsx, TableAudit.jsx):
- renderValue function parses M2M change patterns and renders each
  PK as a React Router Link to e.g. /contacts/view/1, /users/view/2
- Covers: Network.contacts → contact, Contact.users → user,
  Playbook.taxonomy → taxonomy
- pre_save signal stores old case_id before Event save
- post_save signal compares old vs new case_id and creates
  LogEntry on the Case side (added/removed event)
- Tests: link event to case creates audit on Case; unlink also
  creates audit on Case (in addition to existing Event audit)
Backend (signals.py):
- _log_event_case_audit now includes event UUID/domain and uses
  format 'added [event]: #21 (domain.com)' for FK link detection

Frontend (AuditModal.jsx, TableAudit.jsx):
- renderValue now detects FK patterns like 'added [model]: #pk (...)'
  and renders the PK as a clickable Link to /models/view/pk
- Works alongside existing M2M pattern detection
AuditSerializer now adds actor_username and content_type_model
as computed fields (via SerializerMethodField) instead of relying
on HyperlinkedRelatedField URLs. Frontend tables updated to use
these new fields instead of a.actor?.username and a.content_type?.model
which were always undefined.
auditlog.register(TaggedObject) creates LogEntry records but
TaggedObject has no DRF ViewSet, so generic_detail_link raises
NoReverseMatch. Catch both ObjectDoesNotExist and NoReverseMatch
in get_related() to prevent 500 errors on /api/audit/.
Backend (AuditFilter):
- action: ChoiceFilter (0=Create, 1=Update, 2=Delete)
- actor__username: CharFilter (icontains)
- timestamp_after / timestamp_before: DateTimeFilter (gte/lte)

Frontend (ListAudit.jsx):
- Action dropdown (Create/Update/Delete/All)
- Reporter text input
- Type text input
- Date range (datetime-local from/to)

Tests: action filter + actor filter (17 total)
Match the design pattern used by /events: filters are hidden behind
a toggle button (FilterToolbar) with Bootstrap Collapse. FilterToolbar
also provides clear-filters and reload buttons.
…izing

Filters now use the same visual style as ListEvent.jsx:
- Date inputs use type='date' (not datetime-local)
- Form controls at default size (not size='sm')
- Same Col layout (sm={4} lg={4}) for filter row
- FilterToolbar positioned identically
Replace transparent var(--bs-dropdown-bg) with solid #1e1f22
for both react-select__menu and react-select__menu-list to fix
transparent background issue in dark mode.
Replace native Form.Select with react-select component to match
the filter style used across the frontend (/events, etc.)
Copilot AI review requested due to automatic review settings June 16, 2026 14:25

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds an end-to-end Audit Log feature spanning backend filtering/serialization and a new frontend audit UI (list page + per-object modal), plus navigation and translations to expose it across the app.

Changes:

  • Backend: adds audit filtering/search/ordering support, enriches audit serialization, and adds signals to log tag/m2m/event↔case relationship changes.
  • Frontend: introduces an audit list page and reusable audit components (table + modal) and wires the modal into multiple entity views.
  • UI/UX: adds menu + route entry for audit log, adds EN/ES translations, and tweaks dark mode styling for react-select.

Reviewed changes

Copilot reviewed 27 out of 27 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
ngen/views/tools.py Adds AuditFilter and enables DRF filtering/search/ordering for the audit endpoint.
ngen/tests/api/test_audit.py Adds backend tests for audit filtering and signal-driven audit entries.
ngen/signals.py Adds audit logging for tags, selected m2m relations, and Event↔Case link/unlink actions.
ngen/serializers/tools.py Extends audit serializer with actor_username and content_type_model for frontend display.
ngen/models/init.py Registers additional models with auditlog (User/TaggedObject).
frontend/src/views/user/components/TableUsers.jsx Adds per-user audit modal access from the user detail modal.
frontend/src/views/taxonomy/components/TableTaxonomy.jsx Adds per-taxonomy audit modal access from taxonomy modal.
frontend/src/views/state/components/TableStates.jsx Adds per-state audit modal access from state modal.
frontend/src/views/report/components/TableReport.jsx Adds per-report audit modal access from report modal.
frontend/src/views/priority/components/TablePriorities.jsx Adds per-priority audit modal access from priority modal.
frontend/src/views/network/ViewNetwork.jsx Adds audit modal access from network view page.
frontend/src/views/network/components/ModalDetailNetwork.jsx Adds audit modal access from network detail modal.
frontend/src/views/feeds/components/TableFeed.jsx Adds per-feed audit modal access from feed modal.
frontend/src/views/event/ReadEvent.jsx Adds audit modal access from event read page.
frontend/src/views/contact/ViewContact.jsx Adds audit modal access from contact view page.
frontend/src/views/contact/components/TableContact.jsx Adds per-contact audit modal access from contact modal.
frontend/src/views/case/ReadCase.jsx Adds audit modal access from case read page.
frontend/src/views/audits/ListAudit.jsx New audit log list page with filters, search, ordering, pagination.
frontend/src/views/audits/components/TableAudit.jsx New table component for rendering audit entries and change details.
frontend/src/views/audits/components/AuditModal.jsx New per-object audit history modal with “load more” pagination.
frontend/src/routes.jsx Registers /audits route guarded by view_logentry.
frontend/src/menu-items.jsx Adds Audit Log entry to the main menu.
frontend/src/config/constant.jsx Registers audit API endpoint base path.
frontend/src/assets/scss/partials/_dark-mode.scss Adjusts react-select menu background in dark mode.
frontend/src/api/services/audit.jsx Adds API service functions to fetch audits and per-object audits.
frontend/public/locales/es/translation.json Adds ES strings for audit UI.
frontend/public/locales/en/translation.json Adds EN strings for audit UI.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread ngen/models/__init__.py
Comment thread ngen/signals.py
Comment thread ngen/signals.py Outdated
Comment thread ngen/views/tools.py
Comment thread ngen/views/tools.py Outdated
Comment thread frontend/src/views/priority/components/TablePriorities.jsx
Comment thread frontend/src/views/audits/components/TableAudit.jsx
Comment thread frontend/src/views/audits/components/TableAudit.jsx
Comment thread frontend/src/views/audits/components/AuditModal.jsx
Comment thread frontend/src/views/audits/components/AuditModal.jsx
Audit tests (TestAuditFilterThroughModels) create Events which
advance the auto-increment sequence even when rolled back. The
case post test hardcoded pk=1 as the event URL, causing 400 when
the test Event got a higher PK. Fixed by using the actual event.pk.
…crash

Backend fixes:
- CaseSerializer.update(): always sync events on PUT — elif is_put
  unlinks all when events key is missing from FormData
- EventSerializer: declare case with allow_null=True, required=False
- audit_m2m_changes: guard sorted(pk_set) for post_clear (pk_set=None)
- _store_event_old_case/_audit_event_case_link: use instance attribute
  instead of module-level dict (thread-safe)
- AuditViewSet: ModelViewSet → ReadOnlyModelViewSet (defense in depth)
- FormEvent.deleteCaseFromForm(): clear body.case for event unlink
- AuditFilter: THROUGH_MODELS for M2M/dependent audit coverage
- TaggedObject signals: post_save/post_delete for tag audit tracking
- m2m_changed handler: audit Contact.users, Network.contacts,
  Playbook.taxonomy
- User auditlog register: exclude_fields=['last_login']
- auto_link_contact_by_email: post_save signal on User creation

Frontend fixes:
- setupInterceptors.jsx: setAlert → console.error (dedup)
- users.jsx: remove duplicate setAlert calls
- TablePriorities.jsx: fix priority.data.url crash
- FormCase.jsx: always send events in editCase (no conditional guard)
- Audit list: FilterToolbar + Collapse, date range filters
- Audit modal: per-object paginated audit view with Load more
- Audit renderValue: parse M2M and FK patterns into links/display

Tests:
- test_case_put_unlinks_removed_events, test_case_put_empty_events_unlinks_all
- test_event_put_without_case, test_event_patch_clear_case
- 17 audit tests covering filters, through models, M2M signals, case audit
- 504 tests passing

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 34 out of 34 changed files in this pull request and generated 6 comments.

Comment thread ngen/models/__init__.py
Comment thread ngen/views/tools.py
Comment thread frontend/src/views/audits/components/TableAudit.jsx
Comment thread frontend/src/views/audits/components/TableAudit.jsx
Comment thread frontend/src/views/audits/components/AuditModal.jsx
Comment thread frontend/src/views/audits/components/AuditModal.jsx
@mateodurante mateodurante merged commit bbd59ae into develop Jun 18, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants