Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .Jules/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@

## [Unreleased]

### Added
- Dashboard skeleton loading state (`DashboardSkeleton`) to improve perceived performance during data fetch.

### Planned
- See `todo.md` for queued tasks

Expand Down
10 changes: 4 additions & 6 deletions .Jules/todo.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,10 @@

### Web

- [ ] **[ux]** Complete skeleton loading system for Dashboard
- Files: `web/pages/Dashboard.tsx`, `web/components/ui/Skeleton.tsx`
- Context: Replace "Loading stats..." text with full skeleton for cards + chart
- Impact: Professional loading experience, reduces perceived wait time
- Size: ~40 lines
- Added: 2026-01-01
- [x] **[ux]** Complete skeleton loading system for Dashboard
- Completed: 2026-01-01
- Files modified: `web/pages/Dashboard.tsx`, `web/components/skeletons/DashboardSkeleton.tsx`
- Impact: Professional loading experience that mimics actual content layout

- [ ] **[ux]** Toast notification system for user actions
- Files: Create `web/components/ui/Toast.tsx`, integrate into actions
Expand Down
53 changes: 53 additions & 0 deletions web/components/skeletons/DashboardSkeleton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { Card } from '../ui/Card';
import { Skeleton } from '../ui/Skeleton';

export const DashboardSkeleton = () => {
return (
<div className="p-6 space-y-6 max-w-7xl mx-auto">
{/* Summary Cards Grid */}
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
{[1, 2, 3].map((i) => (
<Card key={i} className="flex flex-col items-center justify-center text-center h-[200px]">
{/* Icon placeholder */}
<Skeleton className="w-16 h-16 rounded-full mb-4" />
{/* Label placeholder */}
<Skeleton className="w-24 h-4 mb-2" />
{/* Value placeholder */}
<Skeleton className="w-32 h-8" />
</Card>
))}
</div>

{/* Main Content Grid */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
{/* Chart Section */}
<Card title="Balance Overview">
<div className="mt-4 space-y-4">
{/* Chart bars placeholder */}
<div className="flex items-end justify-between h-[250px] px-4 space-x-4">
<Skeleton className="w-full h-[40%]" />
<Skeleton className="w-full h-[70%]" />
<Skeleton className="w-full h-[50%]" />
<Skeleton className="w-full h-[80%]" />
</div>
</div>
</Card>

{/* Recent Activity Section */}
<Card title="Recent Activity">
<div className="mt-4 space-y-4">
{[1, 2, 3].map((i) => (
<div key={i} className="flex items-center space-x-4">
<Skeleton className="w-10 h-10 rounded-full" />
<div className="flex-1 space-y-2">
<Skeleton className="w-3/4 h-4" />
<Skeleton className="w-1/2 h-3" />
</div>
</div>
))}
</div>
</Card>
</div>
</div>
);
};
Comment on lines +4 to +53
Copy link
Contributor

@coderabbitai coderabbitai bot Jan 1, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Add ARIA attributes to communicate loading state to screen readers.

The skeleton component currently lacks accessibility attributes, preventing screen reader users from knowing that content is loading. This impacts users with disabilities and creates an accessibility compliance gap.

🔎 Proposed fix

Add aria-busy="true" and aria-label to the container, and optionally aria-hidden="true" to decorative skeleton elements:

 export const DashboardSkeleton = () => {
   return (
-    <div className="p-6 space-y-6 max-w-7xl mx-auto">
+    <div className="p-6 space-y-6 max-w-7xl mx-auto" role="status" aria-busy="true" aria-label="Loading dashboard">
       {/* Summary Cards Grid */}
       <div className="grid grid-cols-1 md:grid-cols-3 gap-6">

Alternatively, you can add aria-live="polite" instead of role="status" for dynamic updates.

📝 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 const DashboardSkeleton = () => {
return (
<div className="p-6 space-y-6 max-w-7xl mx-auto">
{/* Summary Cards Grid */}
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
{[1, 2, 3].map((i) => (
<Card key={i} className="flex flex-col items-center justify-center text-center h-[200px]">
{/* Icon placeholder */}
<Skeleton className="w-16 h-16 rounded-full mb-4" />
{/* Label placeholder */}
<Skeleton className="w-24 h-4 mb-2" />
{/* Value placeholder */}
<Skeleton className="w-32 h-8" />
</Card>
))}
</div>
{/* Main Content Grid */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
{/* Chart Section */}
<Card title="Balance Overview">
<div className="mt-4 space-y-4">
{/* Chart bars placeholder */}
<div className="flex items-end justify-between h-[250px] px-4 space-x-4">
<Skeleton className="w-full h-[40%]" />
<Skeleton className="w-full h-[70%]" />
<Skeleton className="w-full h-[50%]" />
<Skeleton className="w-full h-[80%]" />
</div>
</div>
</Card>
{/* Recent Activity Section */}
<Card title="Recent Activity">
<div className="mt-4 space-y-4">
{[1, 2, 3].map((i) => (
<div key={i} className="flex items-center space-x-4">
<Skeleton className="w-10 h-10 rounded-full" />
<div className="flex-1 space-y-2">
<Skeleton className="w-3/4 h-4" />
<Skeleton className="w-1/2 h-3" />
</div>
</div>
))}
</div>
</Card>
</div>
</div>
);
};
export const DashboardSkeleton = () => {
return (
<div className="p-6 space-y-6 max-w-7xl mx-auto" role="status" aria-busy="true" aria-label="Loading dashboard">
{/* Summary Cards Grid */}
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
{[1, 2, 3].map((i) => (
<Card key={i} className="flex flex-col items-center justify-center text-center h-[200px]">
{/* Icon placeholder */}
<Skeleton className="w-16 h-16 rounded-full mb-4" />
{/* Label placeholder */}
<Skeleton className="w-24 h-4 mb-2" />
{/* Value placeholder */}
<Skeleton className="w-32 h-8" />
</Card>
))}
</div>
{/* Main Content Grid */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
{/* Chart Section */}
<Card title="Balance Overview">
<div className="mt-4 space-y-4">
{/* Chart bars placeholder */}
<div className="flex items-end justify-between h-[250px] px-4 space-x-4">
<Skeleton className="w-full h-[40%]" />
<Skeleton className="w-full h-[70%]" />
<Skeleton className="w-full h-[50%]" />
<Skeleton className="w-full h-[80%]" />
</div>
</div>
</Card>
{/* Recent Activity Section */}
<Card title="Recent Activity">
<div className="mt-4 space-y-4">
{[1, 2, 3].map((i) => (
<div key={i} className="flex items-center space-x-4">
<Skeleton className="w-10 h-10 rounded-full" />
<div className="flex-1 space-y-2">
<Skeleton className="w-3/4 h-4" />
<Skeleton className="w-1/2 h-3" />
</div>
</div>
))}
</div>
</Card>
</div>
</div>
);
};
🤖 Prompt for AI Agents
In web/components/skeletons/DashboardSkeleton.tsx around lines 4–53 the skeleton
markup lacks accessibility attributes to indicate loading state; add
aria-busy="true" and a descriptive aria-label (e.g., "Dashboard loading") or
role="status" with a visually-hidden "Loading…" text on the main container div,
and mark purely decorative Skeleton elements as aria-hidden="true" so screen
readers ignore them; ensure any dynamic updates use aria-live="polite" if you
prefer live region semantics instead of role="status".

Copy link
Owner

Choose a reason for hiding this comment

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

@jules In web/components/skeletons/DashboardSkeleton.tsx, around lines 4–53, the skeleton
markup lacks accessibility attributes to indicate loading state; add
aria-busy="true" and a descriptive aria-label (e.g., "Dashboard loading") or
role="status" with a visually-hidden "Loading…" text on the main container div,
and mark purely decorative Skeleton elements as aria-hidden="true" so screen
readers ignore them; ensure any dynamic updates use aria-live="polite" if you
prefer live region semantics instead of role="status".

Copy link
Contributor

Choose a reason for hiding this comment

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

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Loading
Loading