Skip to content

Add shimmer loading skeleton for analysis dashboard#98

Merged
jpdevhub merged 3 commits into
jpdevhub:mainfrom
kushwahnihal25-rgb:clean-pr
Jun 18, 2026
Merged

Add shimmer loading skeleton for analysis dashboard#98
jpdevhub merged 3 commits into
jpdevhub:mainfrom
kushwahnihal25-rgb:clean-pr

Conversation

@kushwahnihal25-rgb

@kushwahnihal25-rgb kushwahnihal25-rgb commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Closes #9

Description

Implemented a shimmer loading skeleton for the Analysis Dashboard.

Changes

  • Added reusable ScanSkeleton component in src/components/shared/
  • Updated AnalysisDashboard.tsx to use the shared skeleton component
  • Added shimmer/skeleton styles in src/index.css
  • Removed debug log from Analysis Dashboard

Notes

  • All out-of-scope changes to package.json and package-lock.json were removed.
  • PR now only includes the requested files.

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

Summary of changes

  • New Features

    • Added an animated scan-loading skeleton to the Analysis Dashboard for a clearer loading experience.
    • Offline scan results are now cached and reused to seamlessly continue the analysis view.
  • Improvements

    • Enriched the offline scan output with derived grades, confidence, classification, species, biomarkers, and recommendations.
  • Style

    • Implemented a left-to-right shimmering effect for skeleton placeholders to improve perceived responsiveness.

@vercel

vercel Bot commented Jun 12, 2026

Copy link
Copy Markdown

@kushwahnihal25-rgb 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 12, 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

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 48785248-4971-4333-8a7d-9028a2e0df29

📥 Commits

Reviewing files that changed from the base of the PR and between 9e0a2d0 and 4c5836b.

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

📝 Walkthrough

Walkthrough

This PR introduces a reusable ScanSkeleton loading component with shimmer animation CSS, offline scan result caching via sessionStorage, and integrates both into AnalysisDashboard. The skeleton component renders an animated pulse layout that replaces the previous hardcoded loading UI, while offline results are persisted from ScannerPage and consumed in the dashboard to bypass API calls when available.

Changes

Skeleton Loading UI and Offline Scan Caching

Layer / File(s) Summary
Shimmer animation and skeleton styling
src/index.css
CSS foundation closes the @media print block and adds @keyframes shimmer animation with .skeleton-shimmer class featuring a left-to-right gradient background and infinite animation.
ScanSkeleton loading component
src/components/shared/ScanSkeleton.tsx
New default-exported React component renders an animated pulse layout with multiple GlassCard wrappers containing sized skeleton placeholder divs styled with the shimmer class.
Offline ONNX scan result persistence
src/pages/ScannerPage.tsx
During offline inference, constructs a rich offlineScanResult object from ONNX output with derived grade, parsed confidence, classification, and nested fields, then persists to sessionStorage before proceeding with local display and navigation.
Dashboard loading state with offline fallback
src/pages/AnalysisDashboard.tsx
AnalysisDashboard imports ScanSkeleton, checks sessionStorage for cached offlineScanResult with freshness_index to bypass API calls, and renders the skeleton component during loading instead of the previous StatusTerminal.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

  • jpdevhub/FreshScanAi#107: Both PRs modify the offline workflow in src/pages/ScannerPage.tsx (building/persisting offlineScanResult to sessionStorage) and src/pages/AnalysisDashboard.tsx (reading offlineScanResult when unavailable), so they are directly related.

Suggested labels

Medium

Suggested reviewers

  • jpdevhub

Poem

🐰 A shimmer so bright, left to right does it flow,
Skeleton frames dance with a graceful glow,
Offline results cached for when networks delay,
No more hard-coded waits—loading's got style today! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add shimmer loading skeleton for analysis dashboard' clearly and concisely describes the main change: implementing a shimmer skeleton loading component for the dashboard.
Linked Issues check ✅ Passed The PR implements all key requirements from issue #9: a reusable ScanSkeleton component with shimmer animation (keyframes and skeleton-shimmer class) and CSS gradient styling, integrated into AnalysisDashboard to replace loading states.
Out of Scope Changes check ✅ Passed Changes to ScannerPage.tsx for offline sessionStorage persistence and AnalysisDashboard.tsx fallback logic align with the reviewer's feedback to preserve recent offline-mode fixes and are necessary context for the skeleton implementation.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
⚔️ Resolve merge conflicts
  • Resolve merge conflict in branch clean-pr

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

🤖 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/components/shared/ScanSkeleton.tsx`:
- Around line 2-31: The ScanSkeleton component currently provides no accessible
loading semantics; update the top-level container returned by ScanSkeleton to
include role="status" and aria-live="polite" (or "assertive" if immediate
interruption is desired) and add a visually-hidden text node such as a <span
className="sr-only">Loading…</span> (or localized message) inside it so screen
readers announce the loading state; keep the existing visual structure
(GlassCard usage) but ensure the hidden text is inside the same root element so
assistive tech receives the status.

In `@src/index.css`:
- Line 418: The .skeleton-shimmer rule contains an extra empty line before the
background-size declaration which triggers the Stylelint
declaration-empty-line-before error; open the .skeleton-shimmer block, remove
the blank line immediately preceding the background-size: 200% 100%; line (or
adjust to match your project's declaration-empty-line-before style) so the
declaration follows the previous rule without an unexpected empty line.
- Around line 410-420: The shimmer animation on the .skeleton-shimmer selector
lacks a prefers-reduced-motion override; add a media query for
prefers-reduced-motion: reduce that disables or greatly reduces the animation
for .skeleton-shimmer (e.g., set animation to none or animation-duration to 0
and fix background-position), and ensure the `@keyframes` shimmer remains but is
not applied in that media query so motion-sensitive users do not get the
infinite shimmer.
🪄 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: 2537c6bf-f10a-4b87-9306-3e436a5247f6

📥 Commits

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

📒 Files selected for processing (3)
  • src/components/shared/ScanSkeleton.tsx
  • src/index.css
  • src/pages/AnalysisDashboard.tsx

Comment on lines +2 to +31
export default function ScanSkeleton() {
return (
<div className="p-6 space-y-6 animate-pulse">
<div className="flex flex-col md:flex-row gap-6">
<GlassCard className="flex-1 p-8">
<div className="h-4 w-32 skeleton-shimmer rounded mb-4"></div>
<div className="h-16 w-40 skeleton-shimmer rounded mb-4"></div>
<div className="h-2 w-full skeleton-shimmer rounded"></div>
</GlassCard>

<GlassCard className="md:w-72 p-6">
<div className="h-4 w-24 skeleton-shimmer rounded mb-4"></div>
<div className="h-8 w-full skeleton-shimmer rounded mb-3"></div>
<div className="h-8 w-full skeleton-shimmer rounded"></div>
</GlassCard>
</div>

<GlassCard className="p-6">
<div className="h-5 w-40 skeleton-shimmer rounded mb-4"></div>
<div className="space-y-3">
<div className="h-16 skeleton-shimmer rounded"></div>
<div className="h-16 skeleton-shimmer rounded"></div>
<div className="h-16 skeleton-shimmer rounded"></div>
</div>
</GlassCard>

<GlassCard className="p-4">
<div className="h-12 skeleton-shimmer rounded"></div>
</GlassCard>
</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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Expose loading semantics for assistive tech.

This skeleton is now the loading UI, but it has no role="status"/aria-live/loading text, so screen reader users don’t get a meaningful loading announcement.

Suggested fix
 export default function ScanSkeleton()  {
   return (
-    <div className="p-6 space-y-6 animate-pulse">
+    <div
+      className="p-6 space-y-6 animate-pulse"
+      role="status"
+      aria-live="polite"
+      aria-busy="true"
+    >
+      <span className="sr-only">Loading analysis dashboard</span>
       <div className="flex flex-col md:flex-row gap-6">
📝 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
export default function ScanSkeleton() {
return (
<div className="p-6 space-y-6 animate-pulse">
<div className="flex flex-col md:flex-row gap-6">
<GlassCard className="flex-1 p-8">
<div className="h-4 w-32 skeleton-shimmer rounded mb-4"></div>
<div className="h-16 w-40 skeleton-shimmer rounded mb-4"></div>
<div className="h-2 w-full skeleton-shimmer rounded"></div>
</GlassCard>
<GlassCard className="md:w-72 p-6">
<div className="h-4 w-24 skeleton-shimmer rounded mb-4"></div>
<div className="h-8 w-full skeleton-shimmer rounded mb-3"></div>
<div className="h-8 w-full skeleton-shimmer rounded"></div>
</GlassCard>
</div>
<GlassCard className="p-6">
<div className="h-5 w-40 skeleton-shimmer rounded mb-4"></div>
<div className="space-y-3">
<div className="h-16 skeleton-shimmer rounded"></div>
<div className="h-16 skeleton-shimmer rounded"></div>
<div className="h-16 skeleton-shimmer rounded"></div>
</div>
</GlassCard>
<GlassCard className="p-4">
<div className="h-12 skeleton-shimmer rounded"></div>
</GlassCard>
</div>
export default function ScanSkeleton() {
return (
<div
className="p-6 space-y-6 animate-pulse"
role="status"
aria-live="polite"
aria-busy="true"
>
<span className="sr-only">Loading analysis dashboard</span>
<div className="flex flex-col md:flex-row gap-6">
<GlassCard className="flex-1 p-8">
<div className="h-4 w-32 skeleton-shimmer rounded mb-4"></div>
<div className="h-16 w-40 skeleton-shimmer rounded mb-4"></div>
<div className="h-2 w-full skeleton-shimmer rounded"></div>
</GlassCard>
<GlassCard className="md:w-72 p-6">
<div className="h-4 w-24 skeleton-shimmer rounded mb-4"></div>
<div className="h-8 w-full skeleton-shimmer rounded mb-3"></div>
<div className="h-8 w-full skeleton-shimmer rounded"></div>
</GlassCard>
</div>
<GlassCard className="p-6">
<div className="h-5 w-40 skeleton-shimmer rounded mb-4"></div>
<div className="space-y-3">
<div className="h-16 skeleton-shimmer rounded"></div>
<div className="h-16 skeleton-shimmer rounded"></div>
<div className="h-16 skeleton-shimmer rounded"></div>
</div>
</GlassCard>
<GlassCard className="p-4">
<div className="h-12 skeleton-shimmer rounded"></div>
</GlassCard>
</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 `@src/components/shared/ScanSkeleton.tsx` around lines 2 - 31, The ScanSkeleton
component currently provides no accessible loading semantics; update the
top-level container returned by ScanSkeleton to include role="status" and
aria-live="polite" (or "assertive" if immediate interruption is desired) and add
a visually-hidden text node such as a <span className="sr-only">Loading…</span>
(or localized message) inside it so screen readers announce the loading state;
keep the existing visual structure (GlassCard usage) but ensure the hidden text
is inside the same root element so assistive tech receives the status.

Comment thread src/index.css
Comment on lines +410 to 420
.skeleton-shimmer {
background: linear-gradient(
90deg,
var(--color-surface-mid) 25%,
var(--color-surface-highest) 50%,
var(--color-surface-mid) 75%
);

background-size: 200% 100%;
animation: shimmer 1.5s linear infinite;
} No newline at end of file

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

Add a reduced-motion fallback for shimmer animation.

Line 419 runs an infinite animation, but there’s no prefers-reduced-motion override. This can cause accessibility issues for motion-sensitive users and should be disabled/reduced in that mode.

Suggested fix
 .skeleton-shimmer {
   background: linear-gradient(
     90deg,
     var(--color-surface-mid) 25%,
     var(--color-surface-highest) 50%,
     var(--color-surface-mid) 75%
   );
   background-size: 200% 100%;
   animation: shimmer 1.5s linear infinite;
 }
+
+@media (prefers-reduced-motion: reduce) {
+  .skeleton-shimmer {
+    animation: none;
+    background-position: 0 0;
+  }
+}
🧰 Tools
🪛 Stylelint (17.12.0)

[error] 418-418: Expected no empty line before declaration (declaration-empty-line-before)

(declaration-empty-line-before)

🤖 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/index.css` around lines 410 - 420, The shimmer animation on the
.skeleton-shimmer selector lacks a prefers-reduced-motion override; add a media
query for prefers-reduced-motion: reduce that disables or greatly reduces the
animation for .skeleton-shimmer (e.g., set animation to none or
animation-duration to 0 and fix background-position), and ensure the `@keyframes`
shimmer remains but is not applied in that media query so motion-sensitive users
do not get the infinite shimmer.

Comment thread src/index.css
var(--color-surface-mid) 75%
);

background-size: 200% 100%;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Resolve the Stylelint rule violation in .skeleton-shimmer.

Line 418 is currently flagged by Stylelint (declaration-empty-line-before). Please remove the extra empty line before background-size (or align with your stylelint config) to keep lint green.

🧰 Tools
🪛 Stylelint (17.12.0)

[error] 418-418: Expected no empty line before declaration (declaration-empty-line-before)

(declaration-empty-line-before)

🤖 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/index.css` at line 418, The .skeleton-shimmer rule contains an extra
empty line before the background-size declaration which triggers the Stylelint
declaration-empty-line-before error; open the .skeleton-shimmer block, remove
the blank line immediately preceding the background-size: 200% 100%; line (or
adjust to match your project's declaration-empty-line-before style) so the
declaration follows the previous rule without an unexpected empty line.

Source: Linters/SAST tools

@kushwahnihal25-rgb

Copy link
Copy Markdown
Contributor Author

Hi @jpdevhub ,

This is the clean PR requested in the review.

Changes included:

  • src/components/shared/ScanSkeleton.tsx
  • src/index.css
  • src/pages/AnalysisDashboard.tsx

All out-of-scope package.json and package-lock.json changes have been removed.

Thank you for reviewing.

@jpdevhub

Copy link
Copy Markdown
Owner

Hi! The ScanSkeleton looks good, but your branch is severely out-of-date with main. Your PR currently deletes the recent offline-mode fixes we merged in api.ts, ScannerPage.tsx, and AnalysisDashboard.tsx (like the silent: true flag and the sessionStorage fallback). Please run git pull origin main, resolve the merge conflicts carefully to preserve our recent fixes, and push again. Let me know when it's updated!

@kushwahnihal25-rgb

Copy link
Copy Markdown
Contributor Author

Hi @jpdevhub ,

I've rebased the branch onto the latest main and force-pushed the updated PR. The recent offline-mode/sessionStorage related fixes should now be preserved.

Could you please take another look when you have time?

Thank you!

@jpdevhub jpdevhub left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Thanks for contributing

@vercel

vercel Bot commented Jun 18, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
fresh-scan-ai Ready Ready Preview, Comment Jun 18, 2026 4:08pm

@jpdevhub jpdevhub merged commit af2f4f7 into jpdevhub:main Jun 18, 2026
10 checks passed
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.

FE-04: Replace Synchronous Loading Flash with Skeleton UI for Scan Results

2 participants