Skip to content

feat: display audit log on ticket details page#136

Merged
apaegs merged 2 commits into
mainfrom
feature/audit-log-ticket-details
Apr 27, 2026
Merged

feat: display audit log on ticket details page#136
apaegs merged 2 commits into
mainfrom
feature/audit-log-ticket-details

Conversation

@viktorlindell12

@viktorlindell12 viktorlindell12 commented Apr 27, 2026

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • New Features

    • Added Activity Log section to ticket details, showing actions with ISO-formatted timestamps.
  • Improvements

    • Dynamic, styled ticket status badge for clearer status display.
    • Comments now appear only when present; per-comment layout simplified.
    • Assigned-to shows username with "Unassigned" fallback.
    • Footer actions and navigation adapt to ticket status (e.g., Close hidden for closed/solved).

@coderabbitai

coderabbitai Bot commented Apr 27, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d94d54ae-f50c-44b3-a81a-d27f20fe7ab5

📥 Commits

Reviewing files that changed from the base of the PR and between de7c207 and 7ef8024.

📒 Files selected for processing (1)
  • src/main/resources/templates/ticket.html
✅ Files skipped from review due to trivial changes (1)
  • src/main/resources/templates/ticket.html

📝 Walkthrough

Walkthrough

Controller now accepts an AuditLogService, loads audit logs in the ticket details handler, and the ticket template was updated to render an Activity Log, adjust comments rendering, and change footer/navigation visibility based on status.

Changes

Cohort / File(s) Summary
Controller Enhancement
src/main/java/org/example/untitled/usercase/controller/CaseController.java
Added AuditLogService constructor injection and load auditLogs into the model in showTicketDetails.
Template Restructuring
src/main/resources/templates/ticket.html
Updated Thymeleaf markup: added conditional "Activity Log" rendering of auditLogs (action + ISO timestamp), made comments section conditional, adjusted header/status badge and footer navigation (status-dependent "Close", role-aware "Back").

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Controller as CaseController
    participant CaseSvc as CaseService
    participant CommentSvc as CommentService
    participant AuditSvc as AuditLogService
    participant View as Thymeleaf Template

    Client->>Controller: GET /cases/{id}
    Controller->>CaseSvc: fetch case by id
    CaseSvc-->>Controller: case data
    Controller->>CommentSvc: fetch comments for case
    CommentSvc-->>Controller: comments
    Controller->>AuditSvc: fetch audit logs for case
    AuditSvc-->>Controller: auditLogs
    Controller->>View: render "ticket" with model (case, comments, auditLogs)
    View-->>Client: HTML page (ticket + comments + Activity Log)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • apaegs
  • FeFFe1996

Poem

🐰✨ I hop through logs both new and old,
Timestamps shining, stories told,
Comments nestle when they choose to show,
Activity trails help the whole case grow,
A little rabbit cheers the flow!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding audit log display functionality to the ticket details page, which is confirmed by the controller modification and template updates.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/audit-log-ticket-details

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/main/resources/templates/ticket.html (1)

39-40: Add getDisplayName() method to AuditAction and format timestamps in user locale.

  1. ${entry.action} outputs the raw enum name (e.g. CASE_CREATED, CASE_STATUS_CHANGED). Add a getDisplayName() method to AuditAction similar to CaseStatus.getDisplayName() so the activity log reads naturally to users (e.g., "Case created", "Case status changed").

  2. AuditLog.timestamp is a java.time.Instant. While #temporals.formatISO from thymeleaf-extras-java8time does support Instant, it formats using the system's default time zone, which may not match the user's locale. Consider formatting in the user's locale/zone (e.g. #temporals.format(entry.timestamp, 'yyyy-MM-dd HH:mm', #locale) after converting to a ZonedDateTime).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/resources/templates/ticket.html` around lines 39 - 40, Add a
human-friendly display method to the AuditAction enum (like
CaseStatus.getDisplayName()) by implementing getDisplayName() that returns
readable phrases (e.g., "Case created", "Case status changed"), then update the
template to use that method/property instead of the raw enum (replace
${entry.action} with ${entry.action.displayName} or
${entry.action.getDisplayName()}); for timestamps, format the Instant in the
user locale/zone by converting AuditLog.timestamp to a ZonedDateTime and using
`#temporals.format` with a pattern and `#locale` (e.g., call
`#temporals.format`(zonedTimestamp, 'yyyy-MM-dd HH:mm', `#locale`) — do the
conversion either in the template or prepare a ZonedDateTime in the model before
rendering).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/main/resources/templates/ticket.html`:
- Around line 47-48: The Close button is shown to non-owners and will 403 when
they click it; update the ownership check in the view: set an isOwner boolean in
CaseController.showTicketDetails (compute by comparing ticket.getOwner().getId()
to current user id) and include it in the model, then change the th:if on the
Close link in ticket.html to require both the status check and isOwner (or
alternatively use Spring Security sec:authorize with a model attribute). Ensure
CaseController.closeTicket behavior remains unchanged but the UI only renders
the Close anchor when model attribute isOwner is true and ticket.status is not
CLOSED or SOLVED.

---

Nitpick comments:
In `@src/main/resources/templates/ticket.html`:
- Around line 39-40: Add a human-friendly display method to the AuditAction enum
(like CaseStatus.getDisplayName()) by implementing getDisplayName() that returns
readable phrases (e.g., "Case created", "Case status changed"), then update the
template to use that method/property instead of the raw enum (replace
${entry.action} with ${entry.action.displayName} or
${entry.action.getDisplayName()}); for timestamps, format the Instant in the
user locale/zone by converting AuditLog.timestamp to a ZonedDateTime and using
`#temporals.format` with a pattern and `#locale` (e.g., call
`#temporals.format`(zonedTimestamp, 'yyyy-MM-dd HH:mm', `#locale`) — do the
conversion either in the template or prepare a ZonedDateTime in the model before
rendering).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 022c29fc-3900-4f90-aee7-18ce1e9271d7

📥 Commits

Reviewing files that changed from the base of the PR and between 7f58682 and de7c207.

📒 Files selected for processing (2)
  • src/main/java/org/example/untitled/usercase/controller/CaseController.java
  • src/main/resources/templates/ticket.html

Comment on lines +47 to +48
<a th:if="${ticket.status.name() != 'CLOSED' and ticket.status.name() != 'SOLVED'}"
th:href="@{/tickets/{id}/close(id=${ticket.id})}" class="btn btn-danger">Close</a>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Close button shown to non-owners will 403.

The visibility predicate only checks status, but CaseController.closeTicket (lines 118–131) throws 403 FORBIDDEN for anyone who is not the ticket owner. A HANDLER/SUPERVISOR/ADMIN viewing a ticket they don't own will see this "Close" button and get an error page when clicking it.

Consider also gating it on ownership, e.g. by exposing an isOwner flag from the controller or using Spring Security's sec:authorize together with a model attribute:

Proposed fix

In CaseController.showTicketDetails:

+        boolean isOwner = !caseService.isNotOwner(ticket, userDetails.getUsername());
         model.addAttribute("ticket", ticket);
         model.addAttribute("comments", comments);
         model.addAttribute("auditLogs", auditLogs);
+        model.addAttribute("isOwner", isOwner);

In ticket.html:

-            <a th:if="${ticket.status.name() != 'CLOSED' and ticket.status.name() != 'SOLVED'}"
-               th:href="@{/tickets/{id}/close(id=${ticket.id})}" class="btn btn-danger">Close</a>
+            <a th:if="${isOwner and ticket.status.name() != 'CLOSED' and ticket.status.name() != 'SOLVED'}"
+               th:href="@{/tickets/{id}/close(id=${ticket.id})}" class="btn btn-danger">Close</a>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/resources/templates/ticket.html` around lines 47 - 48, The Close
button is shown to non-owners and will 403 when they click it; update the
ownership check in the view: set an isOwner boolean in
CaseController.showTicketDetails (compute by comparing ticket.getOwner().getId()
to current user id) and include it in the model, then change the th:if on the
Close link in ticket.html to require both the status check and isOwner (or
alternatively use Spring Security sec:authorize with a model attribute). Ensure
CaseController.closeTicket behavior remains unchanged but the UI only renders
the Close anchor when model attribute isOwner is true and ticket.status is not
CLOSED or SOLVED.

@apaegs
apaegs merged commit 3943463 into main Apr 27, 2026
2 checks passed
@apaegs
apaegs deleted the feature/audit-log-ticket-details branch April 27, 2026 19:42
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