feat(frontend): improve error handling and UX - Sprint 3 Story 4

Add comprehensive error handling with Error Boundary and improve user feedback.

Changes:
- Created global ErrorBoundary component with fallback UI using react-error-boundary
- Integrated ErrorBoundary in root layout to catch all errors
- Created Loading component with variants (sm, md, lg) for consistent loading states
- Created EmptyState component for better empty data display with CTAs
- Improved form error messages in login and register pages (consistent destructive styling)
- Updated projects page to use EmptyState component
- Added better error handling with retry actions

UX improvements:
- Better error messages and recovery options with clear action buttons
- Consistent loading indicators across all pages
- Helpful empty states with clear descriptions and CTAs
- Graceful error handling without crashes
- Consistent destructive color theme for all error messages

Technical:
- Installed react-error-boundary package (v5)
- All TypeScript types are properly defined
- Build and type checking pass successfully

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Yaojia Wang
2025-11-05 20:04:00 +01:00
parent 358ee9b7f4
commit 99ba4c4b1a
9 changed files with 204 additions and 43 deletions

View File

@@ -2,9 +2,9 @@
import { useState } from 'react';
import Link from 'next/link';
import { Plus, FolderKanban, Calendar } from 'lucide-react';
import { Plus, FolderKanban, Calendar, AlertCircle } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Badge } from '@/components/ui/badge';
import { Skeleton } from '@/components/ui/skeleton';
import {
@@ -17,6 +17,7 @@ import {
import { useProjects } from '@/lib/hooks/use-projects';
import { ProjectForm } from '@/components/projects/project-form';
import { formatDistanceToNow } from 'date-fns';
import { EmptyState } from '@/components/ui/empty-state';
export default function ProjectsPage() {
const [isCreateDialogOpen, setIsCreateDialogOpen] = useState(false);
@@ -52,19 +53,15 @@ export default function ProjectsPage() {
if (error) {
return (
<div className="flex items-center justify-center min-h-[400px]">
<Card className="w-full max-w-md">
<CardHeader>
<CardTitle className="text-destructive">Error Loading Projects</CardTitle>
<CardDescription>
{error instanceof Error ? error.message : 'Failed to load projects'}
</CardDescription>
</CardHeader>
<CardContent>
<Button onClick={() => window.location.reload()}>Retry</Button>
</CardContent>
</Card>
</div>
<EmptyState
icon={AlertCircle}
title="Failed to load projects"
description={error instanceof Error ? error.message : 'An error occurred while loading projects. Please try again.'}
action={{
label: 'Retry',
onClick: () => window.location.reload(),
}}
/>
);
}
@@ -121,17 +118,15 @@ export default function ProjectsPage() {
))}
</div>
) : (
<Card className="flex flex-col items-center justify-center py-16">
<FolderKanban className="h-12 w-12 text-muted-foreground mb-4" />
<CardTitle className="mb-2">No projects yet</CardTitle>
<CardDescription className="mb-4">
Get started by creating your first project
</CardDescription>
<Button onClick={() => setIsCreateDialogOpen(true)}>
<Plus className="mr-2 h-4 w-4" />
Create Project
</Button>
</Card>
<EmptyState
icon={FolderKanban}
title="No projects yet"
description="Get started by creating your first project to organize your work and track progress."
action={{
label: 'Create Project',
onClick: () => setIsCreateDialogOpen(true),
}}
/>
)}
{/* Create Project Dialog */}