Skip to content

[PULSE-13] Org Audit Log UI with advanced filtering#10

Merged
uz1mani merged 9 commits intomainfrom
staging
Feb 5, 2026
Merged

[PULSE-13] Org Audit Log UI with advanced filtering#10
uz1mani merged 9 commits intomainfrom
staging

Conversation

@uz1mani
Copy link
Copy Markdown
Member

@uz1mani uz1mani commented Feb 5, 2026

Work Item

PULSE-13

Summary

  • Added a new "Audit log" tab to the Organization Settings page.
  • Implemented a responsive data table to display immutable audit log entries.
  • Built an advanced filtering interface allowing users to search by Log ID, Action, and Date Range.

Changes

  • Components:
    • OrganizationSettings.tsx: Added the Audit Log tab, data table, and filter grid logic.
    • lib/api/audit.ts: Created API client for fetching and filtering audit logs.
  • Features:
    • Displays Actor, Action, Resource, and Time for every event.
    • Supports filtering by specific 6-character Log IDs.
    • Removed unused "Resource ID" column from the view to reduce clutter.

Test Plan

[ ] Navigate to Organization Settings -> Audit log.
[ ] Verify the table loads with recent events.
[ ] Test the "Log ID" filter with a known ID (e.g., 8a2b3c).
[ ] Test the "Action" filter (e.g., site_created) and Date Range inputs.
[ ] Verify that pagination works correctly.

…filters and loading states for improved organization activity tracking
…ay and total is a number, improving data handling in OrganizationSettings
… ID and improving actor identification with email fallback
…ng filtering capabilities for improved data retrieval
@uz1mani uz1mani self-assigned this Feb 5, 2026
@greptile-apps
Copy link
Copy Markdown

greptile-apps bot commented Feb 5, 2026

Greptile Overview

Greptile Summary

Added a comprehensive audit log feature to Organization Settings with filtering and pagination capabilities. The implementation uses a trigger-based approach to coordinate filter changes and pagination, ensuring proper data fetching without loops. All previously identified issues have been addressed:

  • Filter changes now properly trigger data reload using auditFetchTrigger state
  • Tab navigation syncs with URL query parameters via handleTabChange
  • Infinite loop resolved by removing loadAudit from debounced effect dependencies
  • Double API calls eliminated by merging pagination logic into single effect
  • Standard API client with token refresh now used via legacy endpoint support
  • Unnecessary useEffect for ref updates removed

The audit log displays actor, action, resource type, and timestamp for each entry with support for filtering by Log ID, action type, and date range.

Confidence Score: 4/5

  • This PR is safe to merge with minimal risk after addressing previous review feedback
  • The implementation demonstrates thorough attention to previous feedback with all major issues resolved. State management uses refs and triggers appropriately to avoid re-render issues. API integration follows existing patterns with proper error handling.
  • No files require special attention - all previous issues have been addressed

Important Files Changed

Filename Overview
components/settings/OrganizationSettings.tsx Added audit log tab with filter UI and pagination; refactored state management using refs and trigger mechanism
lib/api/audit.ts New API client for audit log with proper defensive handling of response data
lib/api/client.ts Modified to support legacy /api/ endpoints alongside /api/v1 endpoints

Sequence Diagram

sequenceDiagram
    participant User
    participant UI as OrganizationSettings
    participant Filter as Filter State
    participant Trigger as auditFetchTrigger
    participant API as lib/api/audit
    participant Client as lib/api/client
    participant Backend as /api/audit

    User->>UI: Navigate to Audit log tab
    UI->>API: getAuditLog(params)
    API->>Client: apiRequest('/api/audit?...')
    Client->>Backend: GET /api/audit (with cookies)
    Backend-->>Client: { entries: [...], total: N }
    Client-->>API: Response data
    API-->>UI: { entries, total }
    UI->>UI: Display audit table

    User->>Filter: Change filter (action/logId/date)
    Filter->>Filter: Update filter state
    Note over Filter: 500ms debounce timer
    Filter->>Trigger: setAuditPage(0)
    Filter->>Trigger: setAuditFetchTrigger(prev + 1)
    Trigger->>UI: Trigger useEffect
    UI->>API: getAuditLog(params with filters)
    API->>Client: apiRequest('/api/audit?...')
    Client->>Backend: GET /api/audit?action=X&log_id=Y (with cookies)
    Backend-->>Client: Filtered results
    Client-->>API: Response data
    API-->>UI: { entries, total }
    UI->>UI: Display filtered results

    User->>UI: Click pagination (Next/Previous)
    UI->>UI: setAuditPage(newPage)
    UI->>API: getAuditLog(params with new offset)
    API->>Client: apiRequest('/api/audit?offset=...')
    Client->>Backend: GET /api/audit (with cookies)
    alt Token expired (401)
        Backend-->>Client: 401 Unauthorized
        Client->>Client: Detect 401, start refresh
        Client->>Backend: POST /api/auth/refresh (via /api/auth/refresh route)
        Backend-->>Client: New tokens (HttpOnly cookies)
        Client->>Backend: Retry GET /api/audit (with new cookies)
        Backend-->>Client: { entries: [...], total: N }
    else Token valid
        Backend-->>Client: { entries: [...], total: N }
    end
    Client-->>API: Response data
    API-->>UI: { entries, total }
    UI->>UI: Display paginated results

    User->>UI: Click Clear Filters
    UI->>Filter: Reset all filters to ''
    UI->>Trigger: setAuditPage(0)
    UI->>Trigger: setAuditFetchTrigger(prev + 1)
    Trigger->>UI: Trigger useEffect
    UI->>API: getAuditLog(params without filters)
    API->>Client: apiRequest('/api/audit')
    Client->>Backend: GET /api/audit (with cookies)
    Backend-->>Client: All entries (unfiltered)
    Client-->>API: Response data
    API-->>UI: { entries, total }
    UI->>UI: Display all results
Loading

Copy link
Copy Markdown

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

2 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

…ng refs to stabilize state updates and improve performance
Copy link
Copy Markdown

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

2 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

@greptile-apps
Copy link
Copy Markdown

greptile-apps bot commented Feb 5, 2026

Additional Comments (2)

components/settings/OrganizationSettings.tsx
URL tab state desync

Sidebar tab buttons call setActiveTab(...) directly (e.g. onClick={() => setActiveTab('general')}), but nothing updates the ?tab= query param. Since activeTab is initialized/synced from useSearchParams() (components/settings/OrganizationSettings.tsx:44-47 and :155-160), the URL and UI can drift (refresh/back/forward can land on a different tab than the one last clicked). You likely want a single tab-change handler that updates both state and the URL, and use it for all tab buttons.

Also appears at: components/settings/OrganizationSettings.tsx:333-356 (members/billing/audit buttons).

Prompt To Fix With AI
This is a comment left during a code review.
Path: components/settings/OrganizationSettings.tsx
Line: 321:323

Comment:
**URL tab state desync**

Sidebar tab buttons call `setActiveTab(...)` directly (e.g. `onClick={() => setActiveTab('general')}`), but nothing updates the `?tab=` query param. Since `activeTab` is initialized/synced from `useSearchParams()` (`components/settings/OrganizationSettings.tsx:44-47` and `:155-160`), the URL and UI can drift (refresh/back/forward can land on a different tab than the one last clicked). You likely want a single tab-change handler that updates both state and the URL, and use it for all tab buttons.

Also appears at: components/settings/OrganizationSettings.tsx:333-356 (members/billing/audit buttons).

How can I resolve this? If you propose a fix, please make it concise.

components/settings/OrganizationSettings.tsx
Filter + pagination inconsistency

loadAudit() computes offset from auditPage (offset: auditPage * auditPageSize). When a user changes filters while on a later page, auditPage is not reset to 0, so the request can legitimately return an empty page even though matching results exist on earlier pages. This will happen in normal use (e.g., go to page 3 then narrow Action). Consider resetting auditPage to 0 on any filter change (not just when clicking “Clear Filters”).

Prompt To Fix With AI
This is a comment left during a code review.
Path: components/settings/OrganizationSettings.tsx
Line: 169:176

Comment:
**Filter + pagination inconsistency**

`loadAudit()` computes `offset` from `auditPage` (`offset: auditPage * auditPageSize`). When a user changes filters while on a later page, `auditPage` is not reset to 0, so the request can legitimately return an empty page even though matching results exist on earlier pages. This will happen in normal use (e.g., go to page 3 then narrow `Action`). Consider resetting `auditPage` to 0 on any filter change (not just when clicking “Clear Filters”).


How can I resolve this? If you propose a fix, please make it concise.

Copy link
Copy Markdown

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

2 files reviewed, 3 comments

Edit Code Review Agent Settings | Greptile

…hance data loading efficiency and improve filter responsiveness
Copy link
Copy Markdown

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

2 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

@uz1mani uz1mani merged commit 73f2d37 into main Feb 5, 2026
1 check 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.

1 participant