Skip to content

Feature/ticket watchlist system#2862

Open
jainiksha wants to merge 11 commits into
ritesh-1918:mainfrom
jainiksha:feature/ticket-watchlist-system
Open

Feature/ticket watchlist system#2862
jainiksha wants to merge 11 commits into
ritesh-1918:mainfrom
jainiksha:feature/ticket-watchlist-system

Conversation

@jainiksha

@jainiksha jainiksha commented Jun 14, 2026

Copy link
Copy Markdown

Description

Added a Ticket Watchlist feature to the Ticket Details screen. Users can now watch or unwatch tickets directly from the ticket page, making it easier to keep track of important issues without being assigned to them.

Changes Made

  • Added Watch/Unwatch ticket button in the ticket details header.
  • Added local watch state management.
  • Added styling for watchlist controls.
  • Improved ticket tracking experience for users.

Fixes #2857

Summary by CodeRabbit

Release Notes

  • New Features
    • Ticket Activity Heatmap visualization showing hourly ticket distribution
    • SLA Risk indicator and AI Resolution Summary on ticket details
    • Follow-up reminder functionality for ticket tracking
    • Ticket dependency detection with blocking alerts
    • Saved ticket views with custom search filters
    • AI Support Recommendations on ticket creation
    • Accessibility Insights section in admin settings
    • Watch button for mobile ticket details
    • Updated KPI metrics display (Resolved and Pending tickets)

@vercel

vercel Bot commented Jun 14, 2026

Copy link
Copy Markdown

@jainiksha is attempting to deploy a commit to the ritesh Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Jun 14, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Note: The two backend LFS model files had no rangeIds generated for them, so no ranges are assigned to that cohort's layer.

Walkthrough

This PR adds multiple independent UI features across the helpdesk frontend: a ticket activity heatmap in admin analytics, dashboard KPI refactoring with resolved/pending counts, a follow-up reminder card in admin ticket detail, SLA risk and AI resolution summary panels, accessibility insights in settings, AI recommendations during ticket creation, saved filter views in MyTickets, dependency detection in user ticket detail, a watch button in the mobile app, and ticket timeline tracking in the store. Two backend Git LFS model pointer entries are also removed.

Changes

Ticket Store Timeline Tracking

Layer / File(s) Summary
Ticket store timeline events
Frontend/src/store/ticketStore.js
addTicket, updateTicket, and appendMessage now append typed timeline entries (created/updated/comment) to each ticket, including activeTicket when it matches.

Admin Dashboard and Analytics

Layer / File(s) Summary
Dashboard KPI filtering and new metrics
Frontend/src/admin/pages/AdminDashboard.jsx
Adds categoryFilter + filteredTickets memo; KPI calculations now use filtered tickets; replaces the "Escalated Tickets" card with "Resolved" and "Pending" cards; adds recharts imports.
Ticket activity heatmap
Frontend/src/admin/pages/AdminAnalytics.jsx
Extends the stats memo to compute a 7×24 heatmapData array from ticket created_at timestamps and renders it as a colored hourly grid card.
Accessibility Insights card
Frontend/src/admin/pages/AdminSettings.jsx
Replaces the Save import with Accessibility and inserts a static "Accessibility Insights" card with score, status grid, and recommendations.
Follow-up reminder in ticket detail
Frontend/src/admin/pages/AdminTicketDetail.jsx
Adds lastActivity/reminderSent state, initializes activity from fetch data, implements handleSendReminder (local state + toast), and renders a sidebar reminder card.

User-facing Ticket Views

Layer / File(s) Summary
SLA Risk and AI Resolution Summary
Frontend/src/pages/TicketDetailView.jsx
Adds a threshold-colored SLA Risk percentage block and a conditional AI Resolution Summary card for resolved tickets.
AI recommendations during ticket creation
Frontend/src/user/pages/CreateTicket.jsx
Adds debounced mock-recommendation state and a conditional card showing spinner, list, or empty state when issue text exceeds 15 characters.
Saved filter views in MyTickets
Frontend/src/user/pages/MyTickets.jsx
Adds savedViews state backed by localStorage, with save/apply/delete handlers and toolbar UI for managing named filter presets.
Ticket dependency detection
Frontend/src/user/pages/TicketDetail.jsx
Derives isBlocked/dependencyTicket/relatedTicket from ticket.metadata and renders an amber alert banner, dependency fields, and a dependencies summary card.

Mobile App

Layer / File(s) Summary
Watch button in mobile ticket detail
MobileApp/src/screens/user/TicketDetailScreen.js
Adds isWatching state and a header toggle button showing "Watch"/"Watching" with supporting styles.

Backend Model Artifacts

Layer / File(s) Summary
LFS pointer removals
backend/models/classifier/model.safetensors, backend/models/ner/model.safetensors
Old Git LFS oid/size pointer lines removed with no replacement pointer provided.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • ritesh-1918/HELPDESK.AI#2437: Implements the same "Ticket Activity Heatmap" in AdminAnalytics.jsx and the same dashboard KPI/UI wiring changes in AdminDashboard.jsx.
  • ritesh-1918/HELPDESK.AI#2439: Directly overlaps with the user TicketDetail.jsx changes adding isBlocked, "Blocked By", and dependency card UI from ticket.metadata.
  • ritesh-1918/HELPDESK.AI#2436: Shares the AdminTicketDetail follow-up reminder UI (lastActivity/reminderSent/handleSendReminder) and the ticketStore timeline tracking changes.

Suggested labels

gssoc, gssoc:approved, level:intermediate, quality:clean, type:feature

Suggested reviewers

  • ritesh-1918

Poem

🐰 A rabbit hops through tickets bright,
Heatmaps glow by day and night,
Saved views stashed in localStorage neat,
SLA risks colored amber, red — how fleet!
Watch buttons toggle, timelines grow,
Dependencies flagged with amber glow.
This helpdesk blooms — hop, hop, let's go! 🌟

🚥 Pre-merge checks | ✅ 1 | ❌ 4

❌ Failed checks (4 warnings)

Check name Status Explanation Resolution
Title check ⚠️ Warning The title 'Feature/ticket watchlist system' refers to a real part of the changeset but is misleading as primary change is unrelated ticket tracking and analytics features. Rename to better reflect the actual primary changes, such as 'Add ticket analytics, accessibility insights, and partial watchlist support' to accurately capture the scope.
Linked Issues check ⚠️ Warning The changeset includes multiple unrelated features (analytics, accessibility, dependencies, SLA tracking, timeline) beyond the watchlist requirements in #2857. Either split into multiple focused PRs aligned with specific issues, or add linked issues for analytics, accessibility, and other features not covered by #2857.
Out of Scope Changes check ⚠️ Warning Significant out-of-scope changes detected including new analytics/heatmap components, accessibility insights, ticket dependencies, SLA risk tracking, and timeline features not mentioned in #2857. Create separate PRs for each feature scope or establish linked issues for all changes. Ensure PR scope aligns explicitly with linked issue requirements.
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 (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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: 15

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

🟡 Minor comments (6)
Frontend/src/admin/pages/AdminDashboard.jsx-10-10 (1)

10-10: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Remove unused recharts imports.

The recharts components imported on this line are not referenced anywhere in the component. This adds unnecessary bundle size.

🧹 Proposed fix
-import {ResponsiveContainer,LineChart,Line,XAxis,YAxis,Tooltip,CartesianGrid} from "recharts";
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Frontend/src/admin/pages/AdminDashboard.jsx` at line 10, Review the
AdminDashboard component to identify which recharts components from the import
statement (ResponsiveContainer, LineChart, Line, XAxis, YAxis, Tooltip,
CartesianGrid) are actually used in the code. Remove any components that are not
referenced anywhere in the component from the import statement to reduce
unnecessary bundle size.
Frontend/src/user/pages/CreateTicket.jsx-467-467 (1)

467-467: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Align the visibility condition with the fetch logic.

The panel displays when issue.length > 15, but the useEffect at line 62 clears recommendations when issue.trim().length < 15. This mismatch means that typing 20 spaces will show the panel with "No similar issues found," even though no fetch occurred.

🔧 Align the condition
-{issue.length > 15 && (
+{issue.trim().length >= 15 && (
     <div className="mt-4 rounded-2xl border border-emerald-100 bg-emerald-50 p-4">
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Frontend/src/user/pages/CreateTicket.jsx` at line 467, The visibility
condition for the recommendations panel at line 467 uses `issue.length > 15`
while the useEffect at line 62 uses `issue.trim().length < 15` to control when
recommendations are fetched. This mismatch allows the panel to display with
stale content when the input contains only spaces. Align the visibility
condition by changing `issue.length > 15` to `issue.trim().length > 15` to match
the fetch logic and ensure the panel only displays when there is actual
non-whitespace content to search.
Frontend/src/user/pages/MyTickets.jsx-134-154 (1)

134-154: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Prevent duplicate view names.

Users can currently save multiple views with identical names, making it impossible to distinguish them in the UI and potentially causing React key conflicts.

🔧 Add duplicate name check
 const saveCurrentView = () => {
     if (!viewName.trim()) return;
+    
+    if (savedViews.some(v => v.name === viewName.trim())) {
+        setError('A view with this name already exists.');
+        return;
+    }

     const newView = {
-        name: viewName,
+        name: viewName.trim(),
         searchQuery,
         statusFilter,
         priorityFilter
     };
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Frontend/src/user/pages/MyTickets.jsx` around lines 134 - 154, The
saveCurrentView function allows users to save multiple views with identical
names, which causes UI issues and React key conflicts. Add a validation check
before creating the newView object to verify that no existing view in savedViews
already has the same name as the one being saved (compare against view.name for
each item in the savedViews array). If a duplicate name is found, return early
from the function to prevent saving. This ensures each saved view has a unique
name within the savedViews collection.
Frontend/src/user/pages/MyTickets.jsx-258-277 (1)

258-277: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Ensure unique keys for saved view items.

Using key={view.name} will produce duplicate React keys if users save multiple views with the same name (which is currently allowed per line 134). This violates React's key uniqueness requirement and can cause rendering bugs.

🔧 Use index as fallback or generate unique IDs

Short-term fix using index (acceptable after duplicate name prevention is added):

-{savedViews.map((view) => (
+{savedViews.map((view, index) => (
     <div
-        key={view.name}
+        key={`${view.name}-${index}`}
         className="flex items-center gap-2"
     >

Better long-term: add a unique id field when creating views (e.g., Date.now() or crypto.randomUUID()).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Frontend/src/user/pages/MyTickets.jsx` around lines 258 - 277, The
savedViews.map function uses key={view.name} which creates duplicate React keys
if multiple views share the same name, causing potential rendering bugs. Replace
this with a unique identifier: either use the index from the map callback as a
fallback (acceptable once duplicate name prevention is implemented), or
preferably add a unique id field (such as Date.now() or crypto.randomUUID()) to
each view object when it is created, then use key={view.id} instead of
key={view.name}.
Frontend/src/user/pages/TicketDetail.jsx-16-16 (1)

16-16: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Consolidate duplicate import.

AlertTriangle and Link2 are imported separately on line 16, but they should be added to the existing lucide-react import block at lines 3-7.

🔧 Proposed fix
 import {
     ArrowLeft, Clock, Bot, UserCog,
     ShieldCheck, Calendar, Zap, Image as ImageIcon, MessageSquare,
-    RotateCcw, Loader2, CheckCircle2, History
+    RotateCcw, Loader2, CheckCircle2, History,
+    AlertTriangle, Link2
 } from 'lucide-react';
 import { formatFullTimestamp } from '../../utils/dateUtils';
 import { supabase } from "../../lib/supabaseClient";
 import { Card } from "../../components/ui/card";
 import TicketStatusBadge from "../components/TicketStatusBadge";
 import TicketTimeline from "../components/TicketTimeline";
 import TicketChat from "../../components/shared/TicketChat";
 import { formatTicketId } from "../../utils/format";
 import CSATModal from "../components/CSATModal";
-import { AlertTriangle, Link2 } from 'lucide-react';
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Frontend/src/user/pages/TicketDetail.jsx` at line 16, The imports
`AlertTriangle` and `Link2` from 'lucide-react' are being imported separately
instead of being consolidated with the existing lucide-react import block. Add
`AlertTriangle` and `Link2` to the existing lucide-react import statement that
appears earlier in the file, then remove the duplicate import statement that
contains only these two icons.
Frontend/src/user/pages/TicketDetail.jsx-126-128 (1)

126-128: ⚠️ Potential issue | 🟡 Minor

Remove or implement dependency tracking, or clarify if these fields are placeholder for future use.

The code reads ticket.metadata?.blocked_by and ticket.metadata?.related_ticket and displays them in the UI, but these fields are never populated anywhere in the codebase—not in the backend ticket creation/save endpoint, AI analysis pipeline, or any migrations. The metadata field is typed as Json | null with no documented schema for these dependency fields. Either implement the backend logic to populate these fields when tickets have dependencies, or remove the related UI code if this feature is not yet supported. If this is intentional placeholder code, add a comment clarifying the status.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Frontend/src/user/pages/TicketDetail.jsx` around lines 126 - 128, The
variables dependencyTicket and relatedTicket are populated from
ticket.metadata?.blocked_by and ticket.metadata?.related_ticket fields that are
never populated in the backend codebase. Either implement the backend logic to
populate these metadata fields when creating or updating tickets with dependency
information, or remove the references to dependencyTicket and relatedTicket from
the TicketDetail component UI code and related dependent code. If this is
intentional placeholder code for future implementation, add a TODO comment
explaining the planned feature instead.
🧹 Nitpick comments (11)
MobileApp/src/screens/user/TicketDetailScreen.js (1)

142-149: ⚡ Quick win

Missing accessibility properties for the watch button.

The watch button lacks accessibility properties that are important for React Native components to ensure screen reader compatibility and proper accessibility labels.

♿ Proposed fix to add accessibility properties
  <TouchableOpacity
    style={styles.watchBtn}
    onPress={() => setIsWatching(!isWatching)}
+   accessibilityRole="button"
+   accessibilityLabel={isWatching ? "Stop watching this ticket" : "Watch this ticket"}
+   accessibilityHint="Toggle notifications for ticket updates"
  >
    <Text style={styles.watchBtnText}>
      {isWatching ? "Watching" : "Watch"}
    </Text>
  </TouchableOpacity>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@MobileApp/src/screens/user/TicketDetailScreen.js` around lines 142 - 149, The
TouchableOpacity component with the watch button (styled with styles.watchBtn
and onPress handler toggling isWatching) is missing accessibility properties
needed for screen reader compatibility. Add accessibility properties to this
component including accessibilityLabel to provide a descriptive label of the
button's purpose, accessibilityHint to provide additional context about what the
button does, accessibilityRole set to "button" to identify it as an interactive
element, and accessibilityState to indicate the current state (watched or
unwatched) so screen readers can properly convey the button's state to users.
Frontend/src/admin/pages/AdminDashboard.jsx (1)

205-223: ⚡ Quick win

Add navigation handlers or remove button wrappers.

The "Resolved Tickets" and "Pending Tickets" cards are wrapped in <button> elements without onClick handlers, unlike the other KPI cards (Total, Active, AI Auto-Resolved) which navigate to filtered views. This creates an inconsistent user experience where some cards appear clickable but do nothing.

💡 Suggested fix (add navigation)
-<button className="text-left group focus:outline-none">
+<button onClick={() => navigate('/admin/tickets?filter=resolved')} className="text-left group focus:outline-none">
     <StatCard
         label="Resolved Tickets"
         value={metrics.resolvedTickets}
         color="emerald"
         subtitle="Successfully completed"
         customIcon={<TicketIcon />}
     />
 </button>

-<button className="text-left group focus:outline-none">
+<button onClick={() => navigate('/admin/tickets?filter=pending')} className="text-left group focus:outline-none">
     <StatCard
         label="Pending Tickets"
         value={metrics.pendingTickets}
         color="amber"
         subtitle="Waiting for action"
         customIcon={<ActivityIcon />}
     />
 </button>

Or remove the button wrappers entirely if navigation is not needed.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Frontend/src/admin/pages/AdminDashboard.jsx` around lines 205 - 223, The
button elements wrapping the "Resolved Tickets" and "Pending Tickets" StatCard
components lack onClick handlers, creating inconsistency with other KPI cards
(Total, Active, AI Auto-Resolved) which navigate to filtered views. Either add
onClick handlers to these buttons to navigate to the respective filtered ticket
views (matching the pattern of the other cards), or remove the button wrappers
entirely if navigation functionality is not intended for these cards. Ensure the
final state is consistent across all KPI cards in terms of interactivity and
visual feedback.
Frontend/src/admin/pages/AdminAnalytics.jsx (1)

300-352: ⚡ Quick win

Add day labels to improve heatmap readability.

The heatmap uses a 24-column grid that automatically wraps into 7 rows for the days of the week, but there are no visible labels indicating which row represents which day. Users can only see day names in the tooltip on hover, making the heatmap harder to interpret at a glance.

💡 Suggested enhancement

Add day labels to the left side of the heatmap, similar to typical calendar heatmaps (like GitHub's contribution graph):

 <div className="overflow-x-auto">
+    <div className="flex gap-2">
+        <div className="flex flex-col gap-1 justify-around text-xs text-gray-500 pr-2">
+            {DAYS.map(day => (
+                <div key={day} className="h-[22px] flex items-center">{day}</div>
+            ))}
+        </div>
         <div className="grid grid-cols-24 gap-1 min-w-[700px]">
             {stats.heatmapData?.map((cell, index) => (
                 <div
                     key={index}
-                    title={`${days[cell.day]} ${cell.hour}:00 → ${cell.count} tickets`}
+                    title={`${DAYS[cell.day]} ${cell.hour}:00 → ${cell.count} tickets`}
                     style={{
                         width: '22px',
                         height: '22px',
                         borderRadius: '4px',
                         background:
                             cell.count === 0
                                 ? '`#f3f4f6`'
                                 : cell.count < 3
                                 ? '`#bbf7d0`'
                                 : cell.count < 6
                                 ? '`#4ade80`'
                                 : '`#15803d`'
                     }}
                 />
             ))}
         </div>
+    </div>
 </div>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Frontend/src/admin/pages/AdminAnalytics.jsx` around lines 300 - 352, The
heatmap lacks visible day-of-week labels, making it difficult to interpret which
row corresponds to which day without hovering over cells. Wrap the grid
container in a flex layout that includes a left column for day labels. Create
this label column by mapping through the days array (the same array used in the
title tooltip attribute) and displaying each day name aligned with its
corresponding heatmap row. Ensure the day labels are positioned to the left of
the heatmap grid and vertically centered with their respective rows for clarity.
Frontend/src/user/pages/MyTickets.jsx (3)

239-279: ⚡ Quick win

Improve accessibility for saved view controls.

The view name input lacks a label, and the apply/delete buttons have no accessible names for screen readers.

♿ Add accessibility attributes
 <div className="flex items-center gap-2">
+    <label htmlFor="view-name-input" className="sr-only">
+        Saved View Name
+    </label>
     <input
+        id="view-name-input"
         type="text"
         placeholder="View Name"
         value={viewName}
         onChange={(e) => setViewName(e.target.value)}
         className="px-3 py-2 border rounded-lg text-sm"
     />

     <button
         onClick={saveCurrentView}
         className="px-4 py-2 bg-emerald-600 text-white rounded-lg"
+        aria-label="Save current filters as a new view"
     >
         Save View
     </button>
 </div>

 {savedViews.length > 0 && (
     <div className="flex gap-2 flex-wrap">
         {savedViews.map((view, index) => (
             <div
                 key={`${view.name}-${index}`}
                 className="flex items-center gap-2"
             >
                 <button
                     onClick={() => applySavedView(view.name)}
                     className="px-3 py-1 bg-gray-100 rounded-lg text-sm"
+                    aria-label={`Apply saved view: ${view.name}`}
                 >
                     {view.name}
                 </button>

                 <button
                     onClick={() => deleteView(view.name)}
                     className="text-red-500 text-xs"
+                    aria-label={`Delete saved view: ${view.name}`}
                 >
                     ✕
                 </button>
             </div>
         ))}
     </div>
 )}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Frontend/src/user/pages/MyTickets.jsx` around lines 239 - 279, The view name
input field and the action buttons for saved views lack proper accessibility
features. Add an associated label for the text input field that accepts the view
name (currently with placeholder "View Name"), and add aria-label attributes to
the button that applies a saved view (onClick={() => applySavedView(view.name)})
and the button that deletes a view (onClick={() => deleteView(view.name)}) so
that screen readers can properly announce their purposes to users.

156-166: 💤 Low value

Remove or utilize the unused selectedView state.

setSelectedView(name) is called when applying a view, but selectedView is never read or used to provide visual feedback in the UI. This is dead code.

Consider either:

  1. Removing the selectedView state entirely if it's not needed, or
  2. Using it to highlight the currently active view in the UI:
💡 Example: Highlight active view
 <button
     onClick={() => applySavedView(view.name)}
-    className="px-3 py-1 bg-gray-100 rounded-lg text-sm"
+    className={`px-3 py-1 rounded-lg text-sm ${
+        selectedView === view.name 
+            ? 'bg-emerald-100 text-emerald-700 font-bold' 
+            : 'bg-gray-100'
+    }`}
 >
     {view.name}
 </button>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Frontend/src/user/pages/MyTickets.jsx` around lines 156 - 166, The
`setSelectedView(name)` call in the `applySavedView` function sets state that is
never read or used to provide visual feedback in the UI, making it dead code.
Either remove the `setSelectedView(name)` call and the corresponding
`selectedView` state variable entirely if visual indication of the active view
is not needed, or utilize the `selectedView` state by comparing it against view
names in the UI to highlight the currently active saved view with a visual
indicator such as a CSS class or styling.

270-275: ⚡ Quick win

Add confirmation before deleting saved views.

Users can accidentally delete a view with a single click and no recovery option. A confirmation dialog reduces the risk of data loss.

🛡️ Add delete confirmation
 <button
-    onClick={() => deleteView(view.name)}
+    onClick={() => {
+        if (window.confirm(`Delete saved view "${view.name}"?`)) {
+            deleteView(view.name);
+        }
+    }}
     className="text-red-500 text-xs"
     aria-label={`Delete saved view: ${view.name}`}
 >
     ✕
 </button>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Frontend/src/user/pages/MyTickets.jsx` around lines 270 - 275, The delete
button for saved views in the MyTickets component currently triggers deleteView
immediately without any user confirmation. Modify the onClick handler in the
button element to prompt the user for confirmation before executing the
deleteView call. Wrap the deleteView(view.name) invocation with a confirmation
dialog that asks the user to verify they want to delete the view, proceeding
only if they confirm the action.
Frontend/src/user/pages/CreateTicket.jsx (3)

483-495: ⚡ Quick win

Replace index-based keys with stable identifiers.

Using key={index} can cause React to misidentify items when the recommendations array changes, leading to incorrect rendering or lost component state.

♻️ Use a stable key

If the API returns unique IDs:

-{recommendations.map((item, index) => (
+{recommendations.map((item) => (
     <div
-        key={index}
+        key={item.id}
         className="bg-white rounded-xl p-3 border border-emerald-100"
     >

Otherwise, construct a stable key from the content:

 {recommendations.map((item, index) => (
     <div
-        key={index}
+        key={`${item.title}-${index}`}
         className="bg-white rounded-xl p-3 border border-emerald-100"
     >
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Frontend/src/user/pages/CreateTicket.jsx` around lines 483 - 495, The
recommendations.map function is using key={index} which is an unstable
identifier that causes React to misidentify items when the array changes.
Replace the index-based key with a stable identifier from the item object. If
the API response includes a unique ID property on each item, use that (like
key={item.id}). If no unique ID exists, construct a stable key from the item
content itself, such as combining item.title with item.solution or using another
immutable property that uniquely identifies each recommendation.

467-503: ⚡ Quick win

Announce dynamic recommendations to screen readers.

When recommendations load or change, screen readers are not notified. Adding an aria-live region ensures assistive technologies announce the updates.

♿ Add aria-live region
 {issue.trim().length >= 15 && (
-    <div className="mt-4 rounded-2xl border border-emerald-100 bg-emerald-50 p-4">
+    <div 
+        className="mt-4 rounded-2xl border border-emerald-100 bg-emerald-50 p-4"
+        aria-live="polite"
+        aria-atomic="true"
+    >
         <div className="flex items-center gap-2 mb-3">
             <BrainCircuit size={18} className="text-emerald-600" />
             <h4 className="font-bold text-emerald-700">
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Frontend/src/user/pages/CreateTicket.jsx` around lines 467 - 503, The
recommendations section in CreateTicket.jsx does not announce content changes to
screen readers. Add an aria-live attribute with a value of "polite" to the main
container div that wraps the conditional rendering of the loading state,
recommendations list, and empty state message (the div with className containing
"mt-4 rounded-2xl border border-emerald-100 bg-emerald-50 p-4"). This will
ensure that when isFetchingRecommendations changes or recommendations load,
assistive technologies will announce the updates to users.

61-92: Plan for async cleanup when integrating the real API.

The cleanup function currently only clears the debounce timer. When replacing the mock data with an actual API call, add an AbortController to handle in-flight request cancellation and prevent state updates after unmount. This is especially important since the effect re-runs on every issue change—rapid user input could create race conditions without proper cleanup.

The codebase already uses this pattern (see AIProcessing.jsx lines 126-137), so follow that established convention.

💡 Recommended async cleanup pattern
 useEffect(() => {
     if (issue.trim().length < 15) {
         setRecommendations([]);
         return;
     }
+    
+    const abortController = new AbortController();

     const timer = setTimeout(async () => {
         try {
             setIsFetchingRecommendations(true);

-            // Replace with actual API later
-            const mockRecommendations = [...]
+            const response = await fetch('/api/recommendations', {
+                method: 'POST',
+                signal: abortController.signal,
+                body: JSON.stringify({ issue })
+            });
+            const data = await response.json();

-            setRecommendations(mockRecommendations);
+            setRecommendations(data.recommendations);
         } catch (error) {
+            if (error.name === 'AbortError') return;
             console.error(error);
+            // Show user-facing error message
         } finally {
             setIsFetchingRecommendations(false);
         }
     }, 800);

-    return () => clearTimeout(timer);
+    return () => {
+        clearTimeout(timer);
+        abortController.abort();
+    };
 }, [issue]);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Frontend/src/user/pages/CreateTicket.jsx` around lines 61 - 92, The useEffect
hook in CreateTicket.jsx currently only clears the debounce timer in its cleanup
function. When replacing the mock recommendations with an actual API call, add
an AbortController instance at the start of the effect, pass its signal to the
API request, and call abort() in the cleanup function to cancel any in-flight
requests. This prevents race conditions and state updates after unmount,
especially since the effect re-runs frequently on issue changes. Follow the
established pattern already implemented in AIProcessing.jsx (lines 126-137) to
maintain consistency across the codebase.
Frontend/src/pages/TicketDetailView.jsx (1)

224-242: 💤 Low value

Consider more specific fallback messaging.

The AI Resolution Summary card uses a generic fallback message when ticket.resolution_summary is not available. While functional, this default text ("This issue has been successfully resolved...") doesn't add value beyond what the user already knows from the resolved status.

Consider either:

  1. Omitting the card entirely when resolution_summary is missing
  2. Providing a more actionable message like "No detailed resolution summary is available for this ticket."
💡 Alternative approach
-{isResolved && (
+{isResolved && ticket.resolution_summary && (
     <Card className="p-0 overflow-hidden border-none shadow-xl shadow-gray-200/50">
         <CardHeader className="bg-emerald-50 px-8 py-5 border-b border-emerald-100">
             <CardTitle className="font-bold text-emerald-800 flex items-center gap-2">
                 <BrainCircuit size={18} />
                 AI Resolution Summary
             </CardTitle>
         </CardHeader>
         <CardContent className="p-8">
             <div className="bg-emerald-50 border border-emerald-100 rounded-xl p-4">
                 <p className="text-gray-700 leading-relaxed">
-                    {ticket.resolution_summary ||
-                        "This issue has been successfully resolved by the support team. All reported concerns were reviewed and appropriate actions were taken to address the problem."}
+                    {ticket.resolution_summary}
                 </p>
             </div>
         </CardContent>
     </Card>
 )}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Frontend/src/pages/TicketDetailView.jsx` around lines 224 - 242, The AI
Resolution Summary card in TicketDetailView.jsx currently displays a generic
fallback message when ticket.resolution_summary is not available. This message
lacks value since it merely restates what the user already knows from the
resolved status. Modify the logic around the Card that checks isResolved to also
verify that ticket.resolution_summary exists before rendering the entire card,
OR if you prefer to always show the card, replace the current fallback string
with a more specific and actionable message such as "No detailed resolution
summary is available for this ticket." to better communicate the actual
situation to the user.
Frontend/src/user/pages/TicketDetail.jsx (1)

188-202: ⚡ Quick win

Make dependency ticket IDs clickable for better UX.

Dependency ticket IDs are displayed as plain text in multiple locations (blocked alert card, field values, dependencies card), but users cannot navigate to them. This creates a frustrating experience when users want to view the blocking or related tickets.

🔗 Suggested enhancement: Add navigation links

Create clickable links that navigate to the dependency tickets:

 {isBlocked && (
     <Card className="p-4 border-amber-200 bg-amber-50">
         <div className="flex items-center gap-3">
             <AlertTriangle className="w-5 h-5 text-amber-600" />
             <div>
                 <p className="font-bold text-amber-800">
                     Ticket Dependency Detected
                 </p>
                 <p className="text-sm text-amber-700">
-                    This ticket is blocked by Ticket #{dependencyTicket}
+                    This ticket is blocked by{' '}
+                    <button
+                        onClick={() => navigate(`/ticket/${dependencyTicket}`)}
+                        className="font-bold underline hover:text-amber-900"
+                    >
+                        Ticket #{dependencyTicket}
+                    </button>
                 </p>
             </div>
         </div>
     </Card>
 )}

Apply similar changes to lines 223, 232, and 354-356.

Also applies to: 223-223, 232-232, 354-356

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Frontend/src/user/pages/TicketDetail.jsx` around lines 188 - 202, Replace
plain text displays of dependency ticket IDs with clickable navigation links to
improve UX. In the blocked alert card (where dependencyTicket is shown), convert
the Ticket #{dependencyTicket} reference into a clickable link that navigates to
that ticket's detail page. Apply the same treatment to all other locations where
dependency ticket IDs are displayed as plain text (field values and dependencies
card). Use a consistent link styling and ensure navigation occurs when users
click these ticket ID references.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Frontend/src/admin/pages/AdminAnalytics.jsx`:
- Around line 124-165: The days array is currently defined inside the useMemo
hook but needs to be accessible in the JSX render section outside of that hook.
Move the days array definition (containing the day name strings 'Sun', 'Mon',
'Tue', etc.) outside and above the useMemo hook where the heatmapData is being
calculated. This will make it available in the component's scope for use both
within and outside the memoized calculation, preventing the ReferenceError when
the heatmap render tries to access the day names.
- Around line 139-141: Replace the direct new Date(ticket.created_at)
instantiation at lines 139-141 with a call to formatTimelineDate(), which is
already used at line 120 of the same component for timeline data extraction.
This ensures consistent timezone handling throughout the component so that both
the heatmap and timeline display activity using the same timezone adjustment
logic (UTC forcing for timestamps without timezone markers). Extract the day and
hour values from the result of formatTimelineDate() instead of from the raw Date
object.

In `@Frontend/src/admin/pages/AdminDashboard.jsx`:
- Line 276: The issue is that formatTimelineDate(new Date()) in the
AdminDashboard component displays the current render time rather than when the
dashboard data was actually fetched, causing the "Last Synced" timestamp to
constantly update on every render. To fix this, store the actual fetch timestamp
in component state when the data is retrieved (likely in a useEffect hook where
your data fetching occurs), and then pass that stored fetch timestamp to
formatTimelineDate() instead of creating a new Date object on each render.

In `@Frontend/src/admin/pages/AdminSettings.jsx`:
- Around line 172-256: The Accessibility Insights Card component in
AdminSettings.jsx displays hardcoded static values like "92%" and fixed status
messages without performing actual accessibility measurements, which could
mislead administrators about real WCAG compliance status. Address this by
either: (1) integrating an actual accessibility testing library such as axe-core
or pa11y to dynamically calculate and display real accessibility metrics and
recommendations instead of the hardcoded "Accessibility Score" percentage and
status values, (2) adding a clear visual indicator or banner labeling the entire
section as "Example" or "Placeholder" to make it clear these are not real
measurements, or (3) removing the entire Card section until real accessibility
testing capabilities are available. Choose the approach that best fits your
project roadmap and implement accordingly to ensure administrators are not
misled about actual accessibility compliance.

In `@Frontend/src/admin/pages/AdminTicketDetail.jsx`:
- Around line 181-203: The handleSendReminder function updates only local React
state without making any API call to actually send the reminder or persist the
last_reminder_sent metadata to the database. This means the reminder data will
be lost on page refresh. Add an API call (e.g., using fetch or your HTTP client)
before the success toast in handleSendReminder to persist the updated metadata
with the last_reminder_sent timestamp to the database. Wrap the state update and
toast in a .then() block after the API call succeeds, and ensure errors from the
API call are caught in the catch block.

In `@Frontend/src/pages/TicketDetailView.jsx`:
- Around line 171-187: The SLA Risk panel is incorrectly nested as a child
element inside the Current Status div instead of being a separate grid item. To
fix this, close the Current Status div (around line 170) before the SLA Risk div
begins, making SLA Risk a sibling grid item. Additionally, update the parent
grid container at line 157 from md:grid-cols-2 to md:grid-cols-3 to properly
accommodate three columns for the Assigned Team, Current Status, and SLA Risk
panels.

In `@Frontend/src/store/ticketStore.js`:
- Around line 128-130: The activeTicket object update is missing the timeline
entry that gets added to the tickets array. When shouldUpdateActive is true and
appending a message, the activeTicket update needs to include both the new
message in the messages array AND the timeline entry (similar to what happens in
lines 109-121 for the tickets array). Modify the activeTicket update to append
both the message and the corresponding timeline entry to maintain state
consistency between the tickets array and the activeTicket object.
- Around line 28-44: The timeline data structure is implemented inconsistently:
ticketStore.js creates timeline as an array of event objects, but
TicketTimeline.jsx expects timeline to be an object indexed by pipeline stage
keys (created, ai_analyzed, triaged, metadata_harvested, routed,
resolution_started). Fix the following four locations in
Frontend/src/store/ticketStore.js: (1) In addTicket method (lines 28-44),
replace the timeline array initialization with an object containing a created
key mapped to the ISO timestamp. (2) In the first updateTicket method (lines
48-69), replace the timeline array spread pattern with an object merge pattern
that uses the appropriate stage key (e.g., routed or triaged) based on the
operation. (3) In the second updateTicket method handling activeTicket (lines
77-96), apply the same object merge pattern instead of array spreading. (4) In
the appendMessage method (lines 109-121), remove the timeline mutation entirely
since message comments should not update pipeline stage timestamps.

In `@Frontend/src/user/pages/MyTickets.jsx`:
- Around line 148-151: Add error handling to both localStorage write operations
in MyTickets.jsx. In the saveCurrentView function at lines 148-151, wrap the
localStorage.setItem call in a try-catch block that catches QuotaExceededError
and other storage exceptions; on failure, revert the updatedViews state change
and display an error message to the user such as "Unable to save view. Storage
quota may be exceeded." In the deleteView function at lines 174-177, similarly
wrap the localStorage.setItem call in a try-catch block; on failure, revert the
savedViews state change and notify the user of the failure. Both sites must
restore their respective state objects to their previous values before the
failed write attempt and provide user-facing feedback.
- Around line 168-178: The deleteView function's localStorage.setItem call lacks
error handling and can throw exceptions. Wrap the localStorage.setItem operation
inside a try-catch block to handle any potential errors (similar to how it
should be handled in saveCurrentView). In the catch block, log the error
appropriately so failures to persist view deletions to localStorage are properly
captured and don't cause the application to crash.
- Around line 61-67: The useEffect hook that calls JSON.parse on localStorage
data lacks error handling and validation, which can crash the component if the
stored JSON is corrupted or invalid. Wrap the JSON.parse call and
localStorage.getItem access in a try-catch block to handle any errors that occur
during parsing. Additionally, add validation after parsing to ensure the
retrieved data is actually an array with the expected structure, and provide a
fallback to an empty array if parsing fails, validation fails, or if there is
any error. Pass this validated result to setSavedViews.
- Around line 134-154: The saveCurrentView function does not handle potential
exceptions from localStorage.setItem, which can throw a QuotaExceededError when
storage quota is full, causing the save operation to crash without user
feedback. Wrap the localStorage.setItem call in a try-catch block to handle
storage exceptions gracefully. In the catch block, handle the error
appropriately (such as displaying a user-friendly error message to inform the
user that the view could not be saved due to storage limitations) and consider
whether to revert the state update if localStorage persistence fails.

In `@Frontend/src/user/pages/TicketDetail.jsx`:
- Around line 218-234: The "Blocked By" and "Related Ticket" field divs are
currently positioned outside of the Ticket Details Card container, making them
float unstyled in the layout. Move these two div blocks that contain the
dependency fields (the ones displaying dependencyTicket and relatedTicket
values) from their current location to inside the Ticket Details Card container,
positioning them after the existing fields within the card's grid layout to
maintain visual consistency and proper containment.

In `@MobileApp/src/screens/user/TicketDetailScreen.js`:
- Line 25: The isWatching state is initialized to false but never updates from
the backend data. Add a useEffect hook in the TicketDetailScreen component that
runs on mount and fetches the watch status from the backend for the current
ticket and user. Parse the response and call setIsWatching with the actual watch
status retrieved from the server, ensuring the button displays the correct state
based on whether the user is already watching this ticket.
- Around line 142-149: The watch button toggle in the TouchableOpacity component
with the onPress handler only updates local state and does not persist the watch
action to the backend. To fix this, you need to: (1) create a `ticket_watchers`
table in the database to store which users are watching which tickets, including
a unique constraint on ticket_id and user_id; (2) create backend API endpoints
for adding and removing watchers that handle the user-ticket watch relationship;
(3) replace the onPress handler that currently calls setIsWatching(!isWatching)
with an actual API call to add or remove the current user as a watcher based on
the current isWatching state; (4) integrate the watch functionality with your
notification system so that users watching a ticket receive notifications when
the ticket status changes, comments are added, or assignments are updated.

---

Minor comments:
In `@Frontend/src/admin/pages/AdminDashboard.jsx`:
- Line 10: Review the AdminDashboard component to identify which recharts
components from the import statement (ResponsiveContainer, LineChart, Line,
XAxis, YAxis, Tooltip, CartesianGrid) are actually used in the code. Remove any
components that are not referenced anywhere in the component from the import
statement to reduce unnecessary bundle size.

In `@Frontend/src/user/pages/CreateTicket.jsx`:
- Line 467: The visibility condition for the recommendations panel at line 467
uses `issue.length > 15` while the useEffect at line 62 uses
`issue.trim().length < 15` to control when recommendations are fetched. This
mismatch allows the panel to display with stale content when the input contains
only spaces. Align the visibility condition by changing `issue.length > 15` to
`issue.trim().length > 15` to match the fetch logic and ensure the panel only
displays when there is actual non-whitespace content to search.

In `@Frontend/src/user/pages/MyTickets.jsx`:
- Around line 134-154: The saveCurrentView function allows users to save
multiple views with identical names, which causes UI issues and React key
conflicts. Add a validation check before creating the newView object to verify
that no existing view in savedViews already has the same name as the one being
saved (compare against view.name for each item in the savedViews array). If a
duplicate name is found, return early from the function to prevent saving. This
ensures each saved view has a unique name within the savedViews collection.
- Around line 258-277: The savedViews.map function uses key={view.name} which
creates duplicate React keys if multiple views share the same name, causing
potential rendering bugs. Replace this with a unique identifier: either use the
index from the map callback as a fallback (acceptable once duplicate name
prevention is implemented), or preferably add a unique id field (such as
Date.now() or crypto.randomUUID()) to each view object when it is created, then
use key={view.id} instead of key={view.name}.

In `@Frontend/src/user/pages/TicketDetail.jsx`:
- Line 16: The imports `AlertTriangle` and `Link2` from 'lucide-react' are being
imported separately instead of being consolidated with the existing lucide-react
import block. Add `AlertTriangle` and `Link2` to the existing lucide-react
import statement that appears earlier in the file, then remove the duplicate
import statement that contains only these two icons.
- Around line 126-128: The variables dependencyTicket and relatedTicket are
populated from ticket.metadata?.blocked_by and ticket.metadata?.related_ticket
fields that are never populated in the backend codebase. Either implement the
backend logic to populate these metadata fields when creating or updating
tickets with dependency information, or remove the references to
dependencyTicket and relatedTicket from the TicketDetail component UI code and
related dependent code. If this is intentional placeholder code for future
implementation, add a TODO comment explaining the planned feature instead.

---

Nitpick comments:
In `@Frontend/src/admin/pages/AdminAnalytics.jsx`:
- Around line 300-352: The heatmap lacks visible day-of-week labels, making it
difficult to interpret which row corresponds to which day without hovering over
cells. Wrap the grid container in a flex layout that includes a left column for
day labels. Create this label column by mapping through the days array (the same
array used in the title tooltip attribute) and displaying each day name aligned
with its corresponding heatmap row. Ensure the day labels are positioned to the
left of the heatmap grid and vertically centered with their respective rows for
clarity.

In `@Frontend/src/admin/pages/AdminDashboard.jsx`:
- Around line 205-223: The button elements wrapping the "Resolved Tickets" and
"Pending Tickets" StatCard components lack onClick handlers, creating
inconsistency with other KPI cards (Total, Active, AI Auto-Resolved) which
navigate to filtered views. Either add onClick handlers to these buttons to
navigate to the respective filtered ticket views (matching the pattern of the
other cards), or remove the button wrappers entirely if navigation functionality
is not intended for these cards. Ensure the final state is consistent across all
KPI cards in terms of interactivity and visual feedback.

In `@Frontend/src/pages/TicketDetailView.jsx`:
- Around line 224-242: The AI Resolution Summary card in TicketDetailView.jsx
currently displays a generic fallback message when ticket.resolution_summary is
not available. This message lacks value since it merely restates what the user
already knows from the resolved status. Modify the logic around the Card that
checks isResolved to also verify that ticket.resolution_summary exists before
rendering the entire card, OR if you prefer to always show the card, replace the
current fallback string with a more specific and actionable message such as "No
detailed resolution summary is available for this ticket." to better communicate
the actual situation to the user.

In `@Frontend/src/user/pages/CreateTicket.jsx`:
- Around line 483-495: The recommendations.map function is using key={index}
which is an unstable identifier that causes React to misidentify items when the
array changes. Replace the index-based key with a stable identifier from the
item object. If the API response includes a unique ID property on each item, use
that (like key={item.id}). If no unique ID exists, construct a stable key from
the item content itself, such as combining item.title with item.solution or
using another immutable property that uniquely identifies each recommendation.
- Around line 467-503: The recommendations section in CreateTicket.jsx does not
announce content changes to screen readers. Add an aria-live attribute with a
value of "polite" to the main container div that wraps the conditional rendering
of the loading state, recommendations list, and empty state message (the div
with className containing "mt-4 rounded-2xl border border-emerald-100
bg-emerald-50 p-4"). This will ensure that when isFetchingRecommendations
changes or recommendations load, assistive technologies will announce the
updates to users.
- Around line 61-92: The useEffect hook in CreateTicket.jsx currently only
clears the debounce timer in its cleanup function. When replacing the mock
recommendations with an actual API call, add an AbortController instance at the
start of the effect, pass its signal to the API request, and call abort() in the
cleanup function to cancel any in-flight requests. This prevents race conditions
and state updates after unmount, especially since the effect re-runs frequently
on issue changes. Follow the established pattern already implemented in
AIProcessing.jsx (lines 126-137) to maintain consistency across the codebase.

In `@Frontend/src/user/pages/MyTickets.jsx`:
- Around line 239-279: The view name input field and the action buttons for
saved views lack proper accessibility features. Add an associated label for the
text input field that accepts the view name (currently with placeholder "View
Name"), and add aria-label attributes to the button that applies a saved view
(onClick={() => applySavedView(view.name)}) and the button that deletes a view
(onClick={() => deleteView(view.name)}) so that screen readers can properly
announce their purposes to users.
- Around line 156-166: The `setSelectedView(name)` call in the `applySavedView`
function sets state that is never read or used to provide visual feedback in the
UI, making it dead code. Either remove the `setSelectedView(name)` call and the
corresponding `selectedView` state variable entirely if visual indication of the
active view is not needed, or utilize the `selectedView` state by comparing it
against view names in the UI to highlight the currently active saved view with a
visual indicator such as a CSS class or styling.
- Around line 270-275: The delete button for saved views in the MyTickets
component currently triggers deleteView immediately without any user
confirmation. Modify the onClick handler in the button element to prompt the
user for confirmation before executing the deleteView call. Wrap the
deleteView(view.name) invocation with a confirmation dialog that asks the user
to verify they want to delete the view, proceeding only if they confirm the
action.

In `@Frontend/src/user/pages/TicketDetail.jsx`:
- Around line 188-202: Replace plain text displays of dependency ticket IDs with
clickable navigation links to improve UX. In the blocked alert card (where
dependencyTicket is shown), convert the Ticket #{dependencyTicket} reference
into a clickable link that navigates to that ticket's detail page. Apply the
same treatment to all other locations where dependency ticket IDs are displayed
as plain text (field values and dependencies card). Use a consistent link
styling and ensure navigation occurs when users click these ticket ID
references.

In `@MobileApp/src/screens/user/TicketDetailScreen.js`:
- Around line 142-149: The TouchableOpacity component with the watch button
(styled with styles.watchBtn and onPress handler toggling isWatching) is missing
accessibility properties needed for screen reader compatibility. Add
accessibility properties to this component including accessibilityLabel to
provide a descriptive label of the button's purpose, accessibilityHint to
provide additional context about what the button does, accessibilityRole set to
"button" to identify it as an interactive element, and accessibilityState to
indicate the current state (watched or unwatched) so screen readers can properly
convey the button's state to users.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 59aad131-7835-4fa7-b51e-a44a9df4af4d

📥 Commits

Reviewing files that changed from the base of the PR and between da8faf2 and 82ba359.

📒 Files selected for processing (12)
  • Frontend/src/admin/pages/AdminAnalytics.jsx
  • Frontend/src/admin/pages/AdminDashboard.jsx
  • Frontend/src/admin/pages/AdminSettings.jsx
  • Frontend/src/admin/pages/AdminTicketDetail.jsx
  • Frontend/src/pages/TicketDetailView.jsx
  • Frontend/src/store/ticketStore.js
  • Frontend/src/user/pages/CreateTicket.jsx
  • Frontend/src/user/pages/MyTickets.jsx
  • Frontend/src/user/pages/TicketDetail.jsx
  • MobileApp/src/screens/user/TicketDetailScreen.js
  • backend/models/classifier/model.safetensors
  • backend/models/ner/model.safetensors
💤 Files with no reviewable changes (2)
  • backend/models/ner/model.safetensors
  • backend/models/classifier/model.safetensors

Comment on lines +124 to +165
const heatmapData = [];

for (let day = 0; day < 7; day++) {
for (let hour = 0; hour < 24; hour++) {
heatmapData.push({
day,
hour,
count: 0
});
}
}

tickets.forEach(ticket => {
if (!ticket.created_at) return;

const date = new Date(ticket.created_at);
const day = date.getDay();
const hour = date.getHours();

const cell = heatmapData.find(
item => item.day === day && item.hour === hour
);

if (cell) {
cell.count += 1;
}
});

const days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];

return {
total, open, resolved, highPriority,
volumeTimeline, categoryData, teamData, resolutionData, liveFeed
};
total,
open,
resolved,
highPriority,
volumeTimeline,
categoryData,
teamData,
resolutionData,
liveFeed,
heatmapData
};

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 | 🔴 Critical | ⚡ Quick win

Move days array outside useMemo to fix ReferenceError.

The days array is defined on line 152 inside the stats useMemo, but it's referenced on line 325 in the JSX render where it's not in scope. This will cause a runtime ReferenceError when the heatmap tries to display day names.

🐛 Proposed fix
+const DAYS = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
+
 const AdminAnalytics = () => {
     const { profile } = useAuthStore();
     const [tickets, setTickets] = useState([]);
     const [loading, setLoading] = useState(true);
     
     // ... fetchAnalytics ...
     
     const stats = useMemo(() => {
         // ... other stats ...
         
         const heatmapData = [];
 
         for (let day = 0; day < 7; day++) {
             for (let hour = 0; hour < 24; hour++) {
                 heatmapData.push({
                     day,
                     hour,
                     count: 0
                 });
             }
         }
 
         tickets.forEach(ticket => {
             if (!ticket.created_at) return;
 
             const date = new Date(ticket.created_at);
             const day = date.getDay();
             const hour = date.getHours();
 
             const cell = heatmapData.find(
                 item => item.day === day && item.hour === hour
             );
 
             if (cell) {
                 cell.count += 1;
             }
         });
 
-        const days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
 
         return {
             total,
             open,
             resolved,
             highPriority,
             volumeTimeline,
             categoryData,
             teamData,
             resolutionData,
             liveFeed,
             heatmapData
         };
     }, [tickets]);
     
     // ... render ...
     
-            title={`${days[cell.day]} ${cell.hour}:00 → ${cell.count} tickets`}
+            title={`${DAYS[cell.day]} ${cell.hour}:00 → ${cell.count} tickets`}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Frontend/src/admin/pages/AdminAnalytics.jsx` around lines 124 - 165, The days
array is currently defined inside the useMemo hook but needs to be accessible in
the JSX render section outside of that hook. Move the days array definition
(containing the day name strings 'Sun', 'Mon', 'Tue', etc.) outside and above
the useMemo hook where the heatmapData is being calculated. This will make it
available in the component's scope for use both within and outside the memoized
calculation, preventing the ReferenceError when the heatmap render tries to
access the day names.

Comment on lines +139 to +141
const date = new Date(ticket.created_at);
const day = date.getDay();
const hour = date.getHours();

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 | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify how created_at timestamps are stored and whether consistent timezone display is expected

# Check if there are any timezone utilities or UTC conversion functions
rg -n -C3 'UTC|timezone|getUTC' --type=js --type=ts -g '!node_modules'

# Look for any timezone-related configuration or documentation
fd -e md -e txt README TIMEZONE TIME_ZONE -i --exec cat {}

Repository: ritesh-1918/HELPDESK.AI

Length of output: 2826


🏁 Script executed:

# View the AdminAnalytics.jsx file around the problematic lines to understand context
sed -n '130,150p' Frontend/src/admin/pages/AdminAnalytics.jsx

# Check if formatTimelineDate is imported or used anywhere in AdminAnalytics.jsx
rg -n 'formatTimelineDate|dateUtils' Frontend/src/admin/pages/AdminAnalytics.jsx

# Look at the full heatmap processing logic to understand intent
sed -n '135,160p' Frontend/src/admin/pages/AdminAnalytics.jsx

Repository: ritesh-1918/HELPDESK.AI

Length of output: 1205


🏁 Script executed:

# Check for any comments or logic around the heatmap that might explain why formatTimelineDate isn't used
sed -n '125,155p' Frontend/src/admin/pages/AdminAnalytics.jsx

# Verify the structure and purpose of heatmap vs timeline data
rg -n -B5 -A5 'heatmapData' Frontend/src/admin/pages/AdminAnalytics.jsx | head -40

Repository: ritesh-1918/HELPDESK.AI

Length of output: 1708


Use formatTimelineDate() for heatmap date extraction to match timezone handling elsewhere in the component.

Lines 139-141 use direct new Date(ticket.created_at) calls, but the same file uses formatTimelineDate() for timeline data (line 120), which explicitly applies UTC forcing for timestamps without timezone markers. The inconsistency means the heatmap displays activity in the viewer's local timezone while the timeline uses UTC-adjusted times, resulting in misaligned data visualization within the same component. Replace with formatTimelineDate() or document the intentional difference.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Frontend/src/admin/pages/AdminAnalytics.jsx` around lines 139 - 141, Replace
the direct new Date(ticket.created_at) instantiation at lines 139-141 with a
call to formatTimelineDate(), which is already used at line 120 of the same
component for timeline data extraction. This ensures consistent timezone
handling throughout the component so that both the heatmap and timeline display
activity using the same timezone adjustment logic (UTC forcing for timestamps
without timezone markers). Extract the day and hour values from the result of
formatTimelineDate() instead of from the raw Date object.

<div style={{ display: 'flex', alignItems: 'center', gap: '6px', padding: '4px 10px', background: '#f8faf9', borderRadius: '100px', border: '1px solid #e5e7eb' }}>
<Activity size={10} color="#9ca3af" />
<span style={{ fontSize: '9px', fontWeight: 600, color: '#9ca3af', textTransform: 'uppercase', letterSpacing: '0.1em' }}>
Last Synced: {formatTimelineDate(new Date())}

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 | 🟠 Major | ⚡ Quick win

"Last Synced" timestamp is misleading.

formatTimelineDate(new Date()) formats the current render time, not the actual data fetch time. This means "Last Synced" will constantly update with the current time instead of showing when the dashboard data was last refreshed. Track the fetch timestamp in state and display that instead.

🔧 Proposed fix
 const AdminDashboard = () => {
     const navigate = useNavigate();
     const { profile } = useAuthStore();
     const [tickets, setTickets] = React.useState([]);
     const [isLoading, setIsLoading] = React.useState(true);
     const [categoryFilter, setCategoryFilter] = React.useState("all");
+    const [lastSyncTime, setLastSyncTime] = React.useState(null);

     React.useEffect(() => {
         if (profile) {
             const fetchStats = async () => {
                 setIsLoading(true);
                 try {
                     // ... existing fetch logic ...
                     } else {
                         setTickets(data || []);
                     }
+                    setLastSyncTime(new Date());
                 } catch (err) { console.error("Dashboard fetch error:", err); }
                 finally { setIsLoading(false); }
             };

             fetchStats();
             const interval = setInterval(fetchStats, 30000);
             return () => clearInterval(interval);
         }
     }, [profile]);
     
     // ... rest of component ...
     
-                            Last Synced: {formatTimelineDate(new Date())}
+                            Last Synced: {lastSyncTime ? formatTimelineDate(lastSyncTime) : 'Processing...'}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Last Synced: {formatTimelineDate(new Date())}
const AdminDashboard = () => {
const navigate = useNavigate();
const { profile } = useAuthStore();
const [tickets, setTickets] = React.useState([]);
const [isLoading, setIsLoading] = React.useState(true);
const [categoryFilter, setCategoryFilter] = React.useState("all");
const [lastSyncTime, setLastSyncTime] = React.useState(null);
React.useEffect(() => {
if (profile) {
const fetchStats = async () => {
setIsLoading(true);
try {
// ... existing fetch logic ...
} else {
setTickets(data || []);
}
setLastSyncTime(new Date());
} catch (err) { console.error("Dashboard fetch error:", err); }
finally { setIsLoading(false); }
};
fetchStats();
const interval = setInterval(fetchStats, 30000);
return () => clearInterval(interval);
}
}, [profile]);
// ... rest of component ...
Last Synced: {lastSyncTime ? formatTimelineDate(lastSyncTime) : 'Processing...'}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Frontend/src/admin/pages/AdminDashboard.jsx` at line 276, The issue is that
formatTimelineDate(new Date()) in the AdminDashboard component displays the
current render time rather than when the dashboard data was actually fetched,
causing the "Last Synced" timestamp to constantly update on every render. To fix
this, store the actual fetch timestamp in component state when the data is
retrieved (likely in a useEffect hook where your data fetching occurs), and then
pass that stored fetch timestamp to formatTimelineDate() instead of creating a
new Date object on each render.

Comment on lines +172 to +256
{/* 5. Accessibility Insights */}
<Card className="border-none shadow-2xl shadow-slate-200/40 rounded-[2rem] overflow-hidden bg-white">
<div className="px-8 py-6 bg-slate-50 border-b border-slate-100 flex items-center justify-between">
<h3 className="text-sm font-black text-slate-900 uppercase italic tracking-tight flex items-center gap-3">
<Accessibility size={18} className="text-violet-500" />
Accessibility Insights
</h3>
</div>

<CardContent className="p-8 space-y-6">

<div className="bg-violet-50 border border-violet-100 rounded-2xl p-5">
<h4 className="text-sm font-black text-violet-700 uppercase">
Accessibility Score
</h4>

<div className="mt-3 flex items-center gap-4">
<div className="text-4xl font-black text-violet-600">
92%
</div>

<div className="text-xs text-slate-500 font-bold uppercase tracking-widest">
Excellent Accessibility
</div>
</div>
</div>

<div className="grid md:grid-cols-2 gap-4">

<div className="border rounded-xl p-4">
<h4 className="font-black text-xs uppercase">
Keyboard Navigation
</h4>

<p className="text-xs text-emerald-600 mt-2">
Enabled
</p>
</div>

<div className="border rounded-xl p-4">
<h4 className="font-black text-xs uppercase">
Responsive Design
</h4>

<p className="text-xs text-emerald-600 mt-2">
Mobile Ready
</p>
</div>

<div className="border rounded-xl p-4">
<h4 className="font-black text-xs uppercase">
Readability
</h4>

<p className="text-xs text-amber-600 mt-2">
Consider larger text options
</p>
</div>

<div className="border rounded-xl p-4">
<h4 className="font-black text-xs uppercase">
Screen Readers
</h4>

<p className="text-xs text-emerald-600 mt-2">
Supported
</p>
</div>

</div>

<div className="bg-slate-50 rounded-xl p-4">
<h4 className="text-xs font-black uppercase mb-2">
Recommendations
</h4>

<ul className="text-xs text-slate-600 space-y-2">
<li>• Enable dark mode for reduced eye strain.</li>
<li>• Use larger fonts for improved readability.</li>
<li>• Ensure keyboard-only navigation remains accessible.</li>
</ul>
</div>

</CardContent>
</Card>

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 | 🟠 Major | 🏗️ Heavy lift

Static accessibility data may mislead administrators.

The Accessibility Insights section displays a hardcoded "92%" score and fixed status values without performing actual accessibility measurements. This could give administrators false confidence about WCAG compliance and lead to missed accessibility issues that require attention.

Consider one of these approaches:

  • Integrate an actual accessibility testing library (e.g., axe-core, pa11y) to provide real metrics
  • Clearly label this section as "Example" or "Placeholder" until measurements are implemented
  • Remove the section until real accessibility testing is available

The current implementation risks creating compliance gaps if administrators rely on these static indicators.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Frontend/src/admin/pages/AdminSettings.jsx` around lines 172 - 256, The
Accessibility Insights Card component in AdminSettings.jsx displays hardcoded
static values like "92%" and fixed status messages without performing actual
accessibility measurements, which could mislead administrators about real WCAG
compliance status. Address this by either: (1) integrating an actual
accessibility testing library such as axe-core or pa11y to dynamically calculate
and display real accessibility metrics and recommendations instead of the
hardcoded "Accessibility Score" percentage and status values, (2) adding a clear
visual indicator or banner labeling the entire section as "Example" or
"Placeholder" to make it clear these are not real measurements, or (3) removing
the entire Card section until real accessibility testing capabilities are
available. Choose the approach that best fits your project roadmap and implement
accordingly to ensure administrators are not misled about actual accessibility
compliance.

Comment on lines +181 to +203
const handleSendReminder = async () => {
try {
setReminderSent(true);

showToast(
"Follow-up reminder sent successfully.",
"success"
);

setTicket(prev => ({
...prev,
metadata: {
...prev.metadata,
last_reminder_sent: new Date().toISOString()
}
}));
} catch (err) {
showToast(
"Failed to send reminder.",
"error"
);
}
};

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 | 🔴 Critical | ⚡ Quick win

Reminder is not actually sent or persisted.

The handleSendReminder function updates only local React state and displays a success toast, but it never makes an API call to send the reminder or persist the last_reminder_sent metadata to the database. The reminderSent flag and metadata update will be lost on page refresh. Either add a database update to persist the reminder timestamp, or implement actual reminder sending logic (e.g., email notification, in-app notification).

🔧 Proposed fix (persist to database)
 const handleSendReminder = async () => {
     try {
+        // Persist reminder metadata to database
+        const { error: updateError } = await supabase
+            .from('tickets')
+            .update({
+                metadata: {
+                    ...ticket.metadata,
+                    last_reminder_sent: new Date().toISOString()
+                }
+            })
+            .eq('id', ticket_id);
+        
+        if (updateError) throw updateError;
+        
         setReminderSent(true);
 
         showToast(
             "Follow-up reminder sent successfully.",
             "success"
         );
 
         setTicket(prev => ({
             ...prev,
             metadata: {
                 ...prev.metadata,
                 last_reminder_sent: new Date().toISOString()
             }
         }));
     } catch (err) {
         showToast(
-            "Failed to send reminder.",
+            "Failed to send reminder: " + err.message,
             "error"
         );
     }
 };

Note: This persists the timestamp but doesn't actually send a notification. If the feature should send emails or other notifications, implement that logic as well.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Frontend/src/admin/pages/AdminTicketDetail.jsx` around lines 181 - 203, The
handleSendReminder function updates only local React state without making any
API call to actually send the reminder or persist the last_reminder_sent
metadata to the database. This means the reminder data will be lost on page
refresh. Add an API call (e.g., using fetch or your HTTP client) before the
success toast in handleSendReminder to persist the updated metadata with the
last_reminder_sent timestamp to the database. Wrap the state update and toast in
a .then() block after the API call succeeds, and ensure errors from the API call
are caught in the catch block.

Comment on lines +148 to +151
localStorage.setItem(
'savedTicketViews',
JSON.stringify(updatedViews)
);

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 | 🟠 Major | ⚡ Quick win

Wrap all localStorage write operations in error handling. Both saveCurrentView and deleteView call localStorage.setItem without catching QuotaExceededError or other storage exceptions; the shared root cause is missing resilience around browser storage APIs.

  • Frontend/src/user/pages/MyTickets.jsx#L148-L151: wrap the setItem call in saveCurrentView in try-catch, revert state on failure, and show user feedback (e.g., "Unable to save view. Storage quota may be exceeded.").
  • Frontend/src/user/pages/MyTickets.jsx#L174-L177: wrap the setItem call in deleteView in try-catch, revert savedViews state on failure, and notify the user.
📍 Affects 1 file
  • Frontend/src/user/pages/MyTickets.jsx#L148-L151 (this comment)
  • Frontend/src/user/pages/MyTickets.jsx#L174-L177
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Frontend/src/user/pages/MyTickets.jsx` around lines 148 - 151, Add error
handling to both localStorage write operations in MyTickets.jsx. In the
saveCurrentView function at lines 148-151, wrap the localStorage.setItem call in
a try-catch block that catches QuotaExceededError and other storage exceptions;
on failure, revert the updatedViews state change and display an error message to
the user such as "Unable to save view. Storage quota may be exceeded." In the
deleteView function at lines 174-177, similarly wrap the localStorage.setItem
call in a try-catch block; on failure, revert the savedViews state change and
notify the user of the failure. Both sites must restore their respective state
objects to their previous values before the failed write attempt and provide
user-facing feedback.

Comment on lines +168 to +178
const deleteView = (name) => {
const updatedViews =
savedViews.filter(v => v.name !== name);

setSavedViews(updatedViews);

localStorage.setItem(
'savedTicketViews',
JSON.stringify(updatedViews)
);
};

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 | 🟠 Major | ⚡ Quick win

Handle localStorage errors in deleteView.

Similar to saveCurrentView, localStorage.setItem can throw but is not wrapped in error handling.

🛡️ Add error handling
 const deleteView = (name) => {
     const updatedViews =
         savedViews.filter(v => v.name !== name);

     setSavedViews(updatedViews);

-    localStorage.setItem(
-        'savedTicketViews',
-        JSON.stringify(updatedViews)
-    );
+    try {
+        localStorage.setItem(
+            'savedTicketViews',
+            JSON.stringify(updatedViews)
+        );
+    } catch (error) {
+        console.error('Failed to delete view:', error);
+        // Revert state
+        setSavedViews(savedViews);
+        setError('Unable to delete view.');
+    }
 };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const deleteView = (name) => {
const updatedViews =
savedViews.filter(v => v.name !== name);
setSavedViews(updatedViews);
localStorage.setItem(
'savedTicketViews',
JSON.stringify(updatedViews)
);
};
const deleteView = (name) => {
const updatedViews =
savedViews.filter(v => v.name !== name);
setSavedViews(updatedViews);
try {
localStorage.setItem(
'savedTicketViews',
JSON.stringify(updatedViews)
);
} catch (error) {
console.error('Failed to delete view:', error);
// Revert state
setSavedViews(savedViews);
setError('Unable to delete view.');
}
};
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Frontend/src/user/pages/MyTickets.jsx` around lines 168 - 178, The deleteView
function's localStorage.setItem call lacks error handling and can throw
exceptions. Wrap the localStorage.setItem operation inside a try-catch block to
handle any potential errors (similar to how it should be handled in
saveCurrentView). In the catch block, log the error appropriately so failures to
persist view deletions to localStorage are properly captured and don't cause the
application to crash.

Comment on lines +218 to +234
<div>
<p className="text-xs uppercase font-bold text-gray-400 tracking-wider mb-1.5">
Blocked By
</p>
<p className="text-sm font-semibold text-gray-900 bg-gray-50 border border-gray-100 rounded-lg px-3 py-2">
{dependencyTicket ? `#${dependencyTicket}` : 'None'}
</p>
</div>

<div>
<p className="text-xs uppercase font-bold text-gray-400 tracking-wider mb-1.5">
Related Ticket
</p>
<p className="text-sm font-semibold text-gray-900 bg-gray-50 border border-gray-100 rounded-lg px-3 py-2">
{relatedTicket ? `#${relatedTicket}` : 'None'}
</p>
</div>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Verify placement of dependency fields.

The "Blocked By" and "Related Ticket" field divs (lines 218-234) appear to be placed outside any Card container. The "Ticket Details" Card opens at line 235, which means these fields are floating in the left column without proper containment.

If these fields are intended to be part of the Ticket Details card, they should be moved inside it. If they're meant to be standalone, consider wrapping them in a Card for visual consistency.

💡 Suggested fix: Move fields inside Ticket Details card

Move lines 218-234 to appear after the existing fields inside the Ticket Details card grid (after line 263):

                             <div>
                                 <p className="text-xs uppercase font-bold text-gray-400 tracking-wider mb-1.5">Assigned Team</p>
                                 <p className="text-sm font-semibold text-gray-900 bg-gray-50 border border-gray-100 rounded-lg px-3 py-2">
                                     {ticket.assigned_team || 'General Support'}
                                 </p>
                             </div>
+                            <div>
+                                <p className="text-xs uppercase font-bold text-gray-400 tracking-wider mb-1.5">
+                                    Blocked By
+                                </p>
+                                <p className="text-sm font-semibold text-gray-900 bg-gray-50 border border-gray-100 rounded-lg px-3 py-2">
+                                    {dependencyTicket ? `#${dependencyTicket}` : 'None'}
+                                </p>
+                            </div>
+                            <div>
+                                <p className="text-xs uppercase font-bold text-gray-400 tracking-wider mb-1.5">
+                                    Related Ticket
+                                </p>
+                                <p className="text-sm font-semibold text-gray-900 bg-gray-50 border border-gray-100 rounded-lg px-3 py-2">
+                                    {relatedTicket ? `#${relatedTicket}` : 'None'}
+                                </p>
+                            </div>
                         </div>
                     </Card>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Frontend/src/user/pages/TicketDetail.jsx` around lines 218 - 234, The
"Blocked By" and "Related Ticket" field divs are currently positioned outside of
the Ticket Details Card container, making them float unstyled in the layout.
Move these two div blocks that contain the dependency fields (the ones
displaying dependencyTicket and relatedTicket values) from their current
location to inside the Ticket Details Card container, positioning them after the
existing fields within the card's grid layout to maintain visual consistency and
proper containment.

const [newMessage, setNewMessage] = useState('');
const [loading, setLoading] = useState(true);
const [ticket, setTicket] = useState(null);
const [isWatching, setIsWatching] = useState(false);

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 | 🔴 Critical | ⚡ Quick win

Critical: Watch state is never initialized from the backend.

The isWatching state is hardcoded to false and never fetched from the server. When the component mounts, it should query the backend to check if the current user is already watching this ticket. Without this, the button will always show "Watch" even if the user has previously watched the ticket.

🔍 Proposed fix to initialize watch state from backend

Add a fetch call in the useEffect to load the watch status:

  const [ticket, setTicket] = useState(null);
- const [isWatching, setIsWatching] = useState(false);
+ const [isWatching, setIsWatching] = useState(false);
  const navigation = useNavigation();
  const flatListRef = useRef(null);

  useEffect(() => {
    fetchTicketDetails();
    fetchMessages();
+   fetchWatchStatus();

    // Set up real-time subscription for messages

And add the fetch function:

+ const fetchWatchStatus = async () => {
+   try {
+     const { data: { user } } = await supabase.auth.getUser();
+     if (!user) return;
+     
+     const { data, error } = await supabase
+       .from('ticket_watchers')
+       .select('id')
+       .eq('ticket_id', ticketId)
+       .eq('user_id', user.id)
+       .single();
+     
+     if (!error && data) {
+       setIsWatching(true);
+     }
+   } catch (error) {
+     console.error('Error fetching watch status:', error);
+   }
+ };
+
  const fetchTicketDetails = async () => {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@MobileApp/src/screens/user/TicketDetailScreen.js` at line 25, The isWatching
state is initialized to false but never updates from the backend data. Add a
useEffect hook in the TicketDetailScreen component that runs on mount and
fetches the watch status from the backend for the current ticket and user. Parse
the response and call setIsWatching with the actual watch status retrieved from
the server, ensuring the button displays the correct state based on whether the
user is already watching this ticket.

Comment on lines +142 to +149
<TouchableOpacity
style={styles.watchBtn}
onPress={() => setIsWatching(!isWatching)}
>
<Text style={styles.watchBtnText}>
{isWatching ? "Watching" : "Watch"}
</Text>
</TouchableOpacity>

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 | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Check for ticket_watchers table or watch-related schema

# Search for watch-related database schema or migrations
rg -n --type-add 'sql:*.sql' --type sql -i 'ticket_watch|watch.*ticket' 

# Search for watch-related Supabase queries in the codebase
rg -n -C3 'ticket_watchers|ticket_watch'

# Check for any existing watch API endpoints
rg -n -C3 "'/api.*watch|watch.*endpoint'"

Repository: ritesh-1918/HELPDESK.AI

Length of output: 49


🏁 Script executed:

# Check repository structure and locate backend
git ls-files | head -30

Repository: ritesh-1918/HELPDESK.AI

Length of output: 825


🏁 Script executed:

# Check if Supabase is set up and how it's used in the app
rg -n "supabase" MobileApp/src/screens/user/TicketDetailScreen.js

# Look for any backend directory
fd -t d "backend|server|api" --max-depth 2

Repository: ritesh-1918/HELPDESK.AI

Length of output: 518


🏁 Script executed:

# Examine the actual TicketDetailScreen.js file to understand the implementation
wc -l MobileApp/src/screens/user/TicketDetailScreen.js

Repository: ritesh-1918/HELPDESK.AI

Length of output: 120


🏁 Script executed:

# Check the backend directory structure
fd -t f "." backend/ --max-depth 2 | head -30

Repository: ritesh-1918/HELPDESK.AI

Length of output: 1014


🏁 Script executed:

# Look at the specific lines in TicketDetailScreen.js around the watch button
sed -n '130,160p' MobileApp/src/screens/user/TicketDetailScreen.js

Repository: ritesh-1918/HELPDESK.AI

Length of output: 1178


🏁 Script executed:

# Search for setIsWatching to see if there's any backend call
rg -n "setIsWatching|isWatching" MobileApp/src/screens/user/TicketDetailScreen.js

Repository: ritesh-1918/HELPDESK.AI

Length of output: 218


🏁 Script executed:

# Check for any watch-related code in backend (with different possible naming)
rg -i "watch" backend/ --max-count 20

Repository: ritesh-1918/HELPDESK.AI

Length of output: 718


🏁 Script executed:

# Check the complete useEffect hooks and functions in TicketDetailScreen.js to see if watch state is persisted
sed -n '1,100p' MobileApp/src/screens/user/TicketDetailScreen.js

Repository: ritesh-1918/HELPDESK.AI

Length of output: 2886


🏁 Script executed:

# Check Supabase schema/tables setup - look for profiles, tickets tables to understand schema
rg -n "\.from\(" MobileApp/src/screens/user/TicketDetailScreen.js | head -20

Repository: ritesh-1918/HELPDESK.AI

Length of output: 280


🏁 Script executed:

# Search for any database schema or migrations that define tables
find . -name "*.sql" -type f | xargs cat 2>/dev/null | head -100

Repository: ritesh-1918/HELPDESK.AI

Length of output: 3855


🏁 Script executed:

# Check if there's any watch-related functionality in the entire MobileApp codebase
rg -r "ticket_watchers|watchers" MobileApp/ 2>/dev/null

Repository: ritesh-1918/HELPDESK.AI

Length of output: 1233


Critical: Watch toggle lacks backend persistence and infrastructure.

The watch button only toggles local state without persisting changes to the backend:

  • Watch state is lost on app restart or navigation away
  • No ticket_watchers table exists in the database schema
  • No watch API endpoints or backend integration
  • Users will never receive notifications from watching a ticket

This contradicts the PR objective of users receiving "notifications on status changes, comments, and assignment updates" when watching. Implementing this feature requires:

  1. Adding a ticket_watchers table to track which users watch which tickets
  2. Creating backend API endpoints for adding/removing watchers
  3. Integrating with the notification system to send alerts to watchers
🔧 Proposed implementation

Create the database table:

CREATE TABLE ticket_watchers (
  id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
  ticket_id UUID NOT NULL REFERENCES tickets(id) ON DELETE CASCADE,
  user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
  created_at TIMESTAMP WITH TIME ZONE DEFAULT now(),
  UNIQUE(ticket_id, user_id)
);

Update the component:

- <TouchableOpacity
-   style={styles.watchBtn}
-   onPress={() => setIsWatching(!isWatching)}
- >
+ <TouchableOpacity
+   style={styles.watchBtn}
+   onPress={handleWatchToggle}
+ >
   <Text style={styles.watchBtnText}>
     {isWatching ? "Watching" : "Watch"}
   </Text>
 </TouchableOpacity>

Add the handler:

+  const handleWatchToggle = async () => {
+    try {
+      const { data: { user } } = await supabase.auth.getUser();
+      if (!user) {
+        console.error('User not authenticated');
+        return;
+      }
+
+      if (isWatching) {
+        const { error } = await supabase
+          .from('ticket_watchers')
+          .delete()
+          .eq('ticket_id', ticketId)
+          .eq('user_id', user.id);
+        if (error) throw error;
+        setIsWatching(false);
+      } else {
+        const { error } = await supabase
+          .from('ticket_watchers')
+          .insert({ ticket_id: ticketId, user_id: user.id });
+        if (error) throw error;
+        setIsWatching(true);
+      }
+    } catch (error) {
+      console.error('Error toggling watch status:', error);
+    }
+  };
+
   return (
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@MobileApp/src/screens/user/TicketDetailScreen.js` around lines 142 - 149, The
watch button toggle in the TouchableOpacity component with the onPress handler
only updates local state and does not persist the watch action to the backend.
To fix this, you need to: (1) create a `ticket_watchers` table in the database
to store which users are watching which tickets, including a unique constraint
on ticket_id and user_id; (2) create backend API endpoints for adding and
removing watchers that handle the user-ticket watch relationship; (3) replace
the onPress handler that currently calls setIsWatching(!isWatching) with an
actual API call to add or remove the current user as a watcher based on the
current isWatching state; (4) integrate the watch functionality with your
notification system so that users watching a ticket receive notifications when
the ticket status changes, comments are added, or assignments are updated.

@ritesh-1918 ritesh-1918 added gssoc GirlScript Summer of Code gssoc:approved GSSoC Approved PR level:intermediate Intermediate level difficulty quality:clean Clean code quality type:feature New feature labels Jun 25, 2026
@ritesh-1918

Copy link
Copy Markdown
Owner

Hi @jainiksha! Please redirect your PR target branch to gssoc instead of main so that we can review and merge your changes. Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gssoc:approved GSSoC Approved PR gssoc GirlScript Summer of Code level:intermediate Intermediate level difficulty quality:clean Clean code quality type:feature New feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] Ticket Watchlist and Subscription System

2 participants