Skip to content

feat(dashboard): add interactive gradcam heatmap overlay#105

Closed
Deepak06-v wants to merge 1 commit into
jpdevhub:mainfrom
Deepak06-v:feat/gradcam-overlay
Closed

feat(dashboard): add interactive gradcam heatmap overlay#105
Deepak06-v wants to merge 1 commit into
jpdevhub:mainfrom
Deepak06-v:feat/gradcam-overlay

Conversation

@Deepak06-v

@Deepak06-v Deepak06-v commented Jun 14, 2026

Copy link
Copy Markdown

Description

Checklist

  • npm run lint passes with no errors
  • npm run build compiles without TypeScript errors
  • python -m pytest passes (including new tests I added)
  • No .env files, API keys, secrets, model weights, or __pycache__ in this diff
  • Branch is rebased on main, not merged

Summary by CodeRabbit

  • New Features

    • Added Grad-CAM heatmap visualization overlay for fish specimen images with two viewing modes: slider (draggable horizontal reveal) and toggle (fade transition).
    • Redesigned dashboard layout with image visualizer on the left and analysis details organized on the right.
  • Improvements

    • Enhanced support for touch and mouse interactions on heatmap visualization.
    • Optimized image caching for improved analysis performance.

@vercel

vercel Bot commented Jun 14, 2026

Copy link
Copy Markdown

@Deepak06-v is attempting to deploy a commit to the karan3431's projects 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

Warning

.coderabbit.yaml has a parsing error

The CodeRabbit configuration file in this repository has a parsing error and default settings were used instead. Please fix the error(s) in the configuration file. You can initialize chat with CodeRabbit to get help with the configuration file.

💥 Parsing errors (1)
Validation error: Invalid input: expected object, received boolean at "reviews.auto_review"
⚙️ Configuration instructions
  • Please see the configuration documentation for more information.
  • You can also validate your configuration using the online YAML validator.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json
📝 Walkthrough

Walkthrough

ScannerPage now saves the scanned image blob as a base64 data URL in sessionStorage under lastScanImage. AnalysisDashboard adds a new FishImageOverlay component that retrieves this cached image (or fetches from the photo URL), calls api.getGradcam to obtain a heatmap, and renders it in slider or toggle mode. The dashboard layout changes to a two-column grid.

Changes

Grad-CAM Heatmap Overlay Feature

Layer / File(s) Summary
Scan image persistence to sessionStorage
src/pages/ScannerPage.tsx, src/pages/AnalysisDashboard.tsx
After storing lastScanId, ScannerPage reads the scanned blob with FileReader and writes the base64 data URL to sessionStorage as lastScanImage. AnalysisDashboard imports are extended with useRef, useCallback, and additional Lucide icons to support the new overlay component.
FishImageOverlay component
src/pages/AnalysisDashboard.tsx
New non-exported component that fetches the Grad-CAM heatmap via api.getGradcam, using the sessionStorage cached image or falling back to a direct fetch of the photo URL. Manages loading/error states, renders a slider mode with clipPath-based drag (mouse and touch) and a toggle mode with opacity transitions, and shows a heat-intensity legend.
Two-column dashboard layout
src/pages/AnalysisDashboard.tsx
Replaces the single-row score/species layout with a responsive two-column grid: FishImageOverlay on the left, grade/freshness card and species panel stacked on the right. Card padding and sizing classes are updated to match.

Sequence Diagram

sequenceDiagram
  participant ScannerPage
  participant sessionStorage
  participant FishImageOverlay
  participant api

  ScannerPage->>sessionStorage: store lastScanImage (base64)
  Note over ScannerPage,sessionStorage: On successful scan submit

  FishImageOverlay->>sessionStorage: read lastScanImage
  alt base64 cached
    sessionStorage-->>FishImageOverlay: base64 data URL → Blob
  else not cached
    FishImageOverlay->>FishImageOverlay: fetch(photoUrl) → Blob
  end
  FishImageOverlay->>api: getGradcam(blob)
  api-->>FishImageOverlay: heatmap blob URL
  FishImageOverlay->>FishImageOverlay: render slider / toggle overlay
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐟 Hop hop, the rabbit peers through the lens,
A heatmap glows where freshness bends.
Slider left, slider right — the colors reveal,
Grad-CAM whispers what only fish feel.
Base64 cached in sessionStorage's burrow,
The dashboard shines, clear and thorough! 🌈

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat(dashboard): add interactive gradcam heatmap overlay' directly matches the main change in the changeset: introducing a Grad-CAM heatmap overlay component with interactive visualization modes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
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: 2

🤖 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 `@src/pages/AnalysisDashboard.tsx`:
- Around line 41-72: The fetchHeatmap effect does not cancel previous inflight
requests when the scanId changes, allowing stale responses to overwrite newer
data. Add an AbortController to the fetchHeatmap function to track the request,
pass the abort signal to the fetch call, and implement a cleanup function in
useEffect that aborts the previous request when dependencies change. Before
setting state with the response (setHeatmapSrc, setOriginalSrc), check if the
request was aborted to ensure stale responses don't overwrite data from the
current scan.

In `@src/pages/ScannerPage.tsx`:
- Around line 76-85: The issue is that sessionStorage.setItem for lastScanId is
called synchronously before the FileReader asynchronously updates lastScanImage,
creating a race condition where AnalysisDashboard could pair a new scan ID with
a stale image. To fix this, first clear any stale lastScanImage from
sessionStorage before creating the FileReader, then move the
sessionStorage.setItem call for lastScanId inside the reader.onload callback to
ensure both lastScanImage and lastScanId are updated atomically only after the
blob has been fully serialized to base64.
🪄 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 Plus

Run ID: 76db203e-1d7f-40e4-8c81-a6b992906e12

📥 Commits

Reviewing files that changed from the base of the PR and between b748bcf and 40cf0e4.

📒 Files selected for processing (2)
  • src/pages/AnalysisDashboard.tsx
  • src/pages/ScannerPage.tsx

Comment on lines +41 to +72
useEffect(() => {
async function fetchHeatmap() {
let imgSrc = photoUrl;
const lastScanId = sessionStorage.getItem('lastScanId');
const lastScanImage = sessionStorage.getItem('lastScanImage');
if (lastScanId === scanId && lastScanImage) {
imgSrc = lastScanImage;
}

if (!imgSrc) {
return;
}

setOriginalSrc(imgSrc);
setLoading(true);
setError(null);

try {
const response = await fetch(imgSrc);
const blob = await response.blob();
const res = await api.getGradcam(blob);
setHeatmapSrc(res.gradcam_image);
} catch (err: unknown) {
console.error('Failed to load GradCAM overlay:', err);
setError(err instanceof Error ? err.message : 'Failed to generate heatmap.');
} finally {
setLoading(false);
}
}

fetchHeatmap();
}, [photoUrl, scanId]);

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

Reset and cancel overlay state when the scan changes.

This effect keeps the previous originalSrc/heatmapSrc until the next request finishes, and an older async response can still win the race and overwrite the newer scan. When the dashboard switches to a different scanId, that can leave scan B’s metrics beside scan A’s image or heatmap.

Suggested fix
   useEffect(() => {
+    let cancelled = false;
+
     async function fetchHeatmap() {
       let imgSrc = photoUrl;
       const lastScanId = sessionStorage.getItem('lastScanId');
       const lastScanImage = sessionStorage.getItem('lastScanImage');
       if (lastScanId === scanId && lastScanImage) {
         imgSrc = lastScanImage;
       }
 
+      setOriginalSrc(imgSrc ?? null);
+      setHeatmapSrc(null);
+      setError(null);
+
       if (!imgSrc) {
+        setLoading(false);
         return;
       }
 
-      setOriginalSrc(imgSrc);
       setLoading(true);
-      setError(null);
 
       try {
         const response = await fetch(imgSrc);
+        if (!response.ok) {
+          throw new Error('Failed to load source image.');
+        }
         const blob = await response.blob();
         const res = await api.getGradcam(blob);
-        setHeatmapSrc(res.gradcam_image);
+        if (!cancelled) {
+          setHeatmapSrc(res.gradcam_image);
+        }
       } catch (err: unknown) {
-        console.error('Failed to load GradCAM overlay:', err);
-        setError(err instanceof Error ? err.message : 'Failed to generate heatmap.');
+        if (!cancelled) {
+          console.error('Failed to load GradCAM overlay:', err);
+          setError(err instanceof Error ? err.message : 'Failed to generate heatmap.');
+        }
       } finally {
-        setLoading(false);
+        if (!cancelled) {
+          setLoading(false);
+        }
       }
     }
 
     fetchHeatmap();
+    return () => {
+      cancelled = true;
+    };
   }, [photoUrl, scanId]);
🤖 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 `@src/pages/AnalysisDashboard.tsx` around lines 41 - 72, The fetchHeatmap
effect does not cancel previous inflight requests when the scanId changes,
allowing stale responses to overwrite newer data. Add an AbortController to the
fetchHeatmap function to track the request, pass the abort signal to the fetch
call, and implement a cleanup function in useEffect that aborts the previous
request when dependencies change. Before setting state with the response
(setHeatmapSrc, setOriginalSrc), check if the request was aborted to ensure
stale responses don't overwrite data from the current scan.

Comment thread src/pages/ScannerPage.tsx
@Deepak06-v Deepak06-v closed this Jun 14, 2026
@Deepak06-v Deepak06-v deleted the feat/gradcam-overlay branch June 14, 2026 09:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant