1253 lines
29 KiB
Markdown
1253 lines
29 KiB
Markdown
# Sprint 3: Frontend Code Quality Optimization - Complete Implementation Guide
|
|
|
|
**Sprint ID**: Sprint 3
|
|
**Duration**: 2 weeks (2025-11-05 to 2025-11-19)
|
|
**Goal**: Complete frontend code quality optimization based on code review findings
|
|
**Priority**: High (M1 Quality Improvement)
|
|
|
|
---
|
|
|
|
## Overview
|
|
|
|
This Sprint focuses on addressing code quality issues identified in the Frontend Code Review Report (FRONTEND_CODE_REVIEW_REPORT.md). The work is organized into 6 Stories with 35 total tasks.
|
|
|
|
**Success Metrics**:
|
|
- Code Quality: 7.1/10 → 9.0/10
|
|
- Type Safety: 6/10 → 9/10
|
|
- Performance: 6/10 → 8/10
|
|
- Accessibility: 7/10 → 9/10
|
|
|
|
---
|
|
|
|
## Story Summary
|
|
|
|
| Story | Title | Priority | Estimated | Tasks | Status |
|
|
|-------|-------|----------|-----------|-------|--------|
|
|
| Story 1 | Complete Logging Utility Migration | P1 High | 1-2 days | 6 | not_started |
|
|
| Story 2 | Component Performance Optimization | P1 High | 2-3 days | 6 | not_started |
|
|
| Story 3 | Next.js 15 Async Params Migration | P0 Critical | 1 day | 5 | not_started |
|
|
| Story 4 | Error Handling Improvements | P1 High | 2 days | 6 | not_started |
|
|
| Story 5 | Accessibility Enhancements | P2 Medium | 2-3 days | 6 | not_started |
|
|
| Story 6 | Code Quality Tooling | P2 Medium | 1 day | 6 | not_started |
|
|
|
|
**Total**: 6 Stories, 35 Tasks, 9-12 days estimated
|
|
|
|
---
|
|
|
|
# Story 1: Complete Logging Utility Migration
|
|
|
|
**Priority**: P1 (High)
|
|
**Estimated**: 1-2 days
|
|
**Story Points**: 3
|
|
|
|
## Description
|
|
|
|
Replace all remaining console.log statements in the frontend codebase with the unified logger utility. This eliminates production console spam, enables environment-aware logging, and provides foundation for error tracking integration.
|
|
|
|
## Acceptance Criteria
|
|
|
|
- [ ] No console.log in `lib/hooks/` directory
|
|
- [ ] No console.log in `lib/signalr/` directory
|
|
- [ ] All logging uses `logger.debug()`, `logger.info()`, or `logger.error()`
|
|
- [ ] Development logs are verbose
|
|
- [ ] Production logs are minimal (errors only)
|
|
- [ ] Verification: `grep -r "console.log" lib/` returns zero results
|
|
|
|
## Tasks
|
|
|
|
### Task 1.1: Replace console.log in use-projects.ts
|
|
|
|
**File**: `lib/hooks/use-projects.ts`
|
|
**Estimated**: 30 minutes
|
|
|
|
**Steps**:
|
|
1. Import logger: `import { logger } from '@/lib/utils/logger';`
|
|
2. Replace all `console.log` with `logger.debug()`
|
|
3. Replace all `console.error` with `logger.error()`
|
|
4. Test in development mode
|
|
|
|
**Before**:
|
|
```typescript
|
|
console.log('[useProjects] Fetching projects...', { page, pageSize });
|
|
console.error('[useProjects] Fetch failed:', error);
|
|
```
|
|
|
|
**After**:
|
|
```typescript
|
|
logger.debug('Fetching projects', { page, pageSize });
|
|
logger.error('Failed to fetch projects', error);
|
|
```
|
|
|
|
### Task 1.2: Replace console.log in use-stories.ts
|
|
|
|
**File**: `lib/hooks/use-stories.ts`
|
|
**Estimated**: 30 minutes
|
|
|
|
Same pattern as Task 1.1.
|
|
|
|
### Task 1.3: Replace console.log in use-tasks.ts
|
|
|
|
**File**: `lib/hooks/use-tasks.ts`
|
|
**Estimated**: 30 minutes
|
|
|
|
Same pattern as Task 1.1.
|
|
|
|
### Task 1.4: Replace console.log in other React Query hooks
|
|
|
|
**Files**: `lib/hooks/use-epics.ts`, `lib/hooks/use-sprints.ts`, etc.
|
|
**Estimated**: 1 hour
|
|
|
|
**Steps**:
|
|
1. List all hook files: `ls lib/hooks/use-*.ts`
|
|
2. For each file with console.log:
|
|
- Import logger
|
|
- Replace console statements
|
|
- Test functionality
|
|
|
|
### Task 1.5: Replace console.log in SignalR files
|
|
|
|
**Files**: `lib/signalr/ConnectionManager.ts`, `lib/signalr/ProjectHub.ts`
|
|
**Estimated**: 1 hour
|
|
|
|
**Steps**:
|
|
1. Import logger in SignalR files
|
|
2. Replace connection logging
|
|
3. Replace error logging
|
|
4. Test SignalR connection and events
|
|
|
|
**Before**:
|
|
```typescript
|
|
console.log('[SignalR] Connection state changed:', state);
|
|
console.error('[SignalR] Connection error:', error);
|
|
```
|
|
|
|
**After**:
|
|
```typescript
|
|
logger.debug('SignalR connection state changed', { state });
|
|
logger.error('SignalR connection error', error);
|
|
```
|
|
|
|
### Task 1.6: Verify no console.log remains
|
|
|
|
**Estimated**: 30 minutes
|
|
|
|
**Verification**:
|
|
```bash
|
|
# Should return zero results
|
|
grep -r "console.log" lib/hooks/
|
|
grep -r "console.log" lib/signalr/
|
|
grep -r "console.error" lib/hooks/ | grep -v logger
|
|
```
|
|
|
|
**Test**:
|
|
1. Run dev server: `npm run dev`
|
|
2. Use application and check console
|
|
3. Build for production: `npm run build`
|
|
4. Verify minimal production logs
|
|
|
|
---
|
|
|
|
# Story 2: Component Performance Optimization
|
|
|
|
**Priority**: P1 (High)
|
|
**Estimated**: 2-3 days
|
|
**Story Points**: 5
|
|
|
|
## Description
|
|
|
|
Add React.memo, useCallback, and useMemo optimizations to presentational components to reduce unnecessary re-renders and improve application performance, especially for kanban boards with many cards.
|
|
|
|
## Acceptance Criteria
|
|
|
|
- [ ] All list/card components wrapped with React.memo
|
|
- [ ] All event handlers use useCallback
|
|
- [ ] Expensive computations use useMemo
|
|
- [ ] Performance improvement verified (React DevTools Profiler)
|
|
- [ ] No performance regressions
|
|
- [ ] Lighthouse performance score >= 90
|
|
|
|
## Tasks
|
|
|
|
### Task 2.1: Add React.memo to IssueCard
|
|
|
|
**File**: `components/features/kanban/IssueCard.tsx`
|
|
**Estimated**: 1 hour
|
|
|
|
**Before**:
|
|
```typescript
|
|
export function IssueCard({ issue }: IssueCardProps) {
|
|
// Component code
|
|
}
|
|
```
|
|
|
|
**After**:
|
|
```typescript
|
|
import { memo } from 'react';
|
|
|
|
export const IssueCard = memo(function IssueCard({ issue }: IssueCardProps) {
|
|
// Component code
|
|
});
|
|
```
|
|
|
|
**Verification**:
|
|
- Use React DevTools Profiler
|
|
- Move a card in kanban
|
|
- Verify other cards don't re-render
|
|
|
|
### Task 2.2: Add React.memo to ProjectCard
|
|
|
|
**File**: `components/features/projects/ProjectCard.tsx`
|
|
**Estimated**: 45 minutes
|
|
|
|
Same pattern as Task 2.1.
|
|
|
|
### Task 2.3: Add React.memo to SprintCard
|
|
|
|
**File**: `components/features/sprints/SprintCard.tsx`
|
|
**Estimated**: 45 minutes
|
|
|
|
Same pattern as Task 2.1.
|
|
|
|
### Task 2.4: Add React.memo to TaskCard
|
|
|
|
**File**: `components/features/kanban/TaskCard.tsx` (if exists separately)
|
|
**Estimated**: 45 minutes
|
|
|
|
Same pattern as Task 2.1.
|
|
|
|
### Task 2.5: Add useCallback to list component event handlers
|
|
|
|
**Files**: Various list components
|
|
**Estimated**: 2 hours
|
|
|
|
**Pattern**:
|
|
```typescript
|
|
import { useCallback } from 'react';
|
|
|
|
// Before
|
|
const handleClick = (id: string) => {
|
|
// handler code
|
|
};
|
|
|
|
// After
|
|
const handleClick = useCallback((id: string) => {
|
|
// handler code
|
|
}, [/* dependencies */]);
|
|
```
|
|
|
|
**Files to update**:
|
|
- `components/features/projects/ProjectList.tsx`
|
|
- `components/features/sprints/SprintList.tsx`
|
|
- `components/features/kanban/KanbanBoard.tsx`
|
|
- Dialog components with onSubmit handlers
|
|
|
|
### Task 2.6: Performance testing and benchmarking
|
|
|
|
**Estimated**: 2 hours
|
|
|
|
**Steps**:
|
|
1. **Before optimization**:
|
|
- Open React DevTools Profiler
|
|
- Record interaction (e.g., drag card)
|
|
- Note number of component re-renders
|
|
- Run Lighthouse audit
|
|
- Save baseline scores
|
|
|
|
2. **After optimization**:
|
|
- Repeat same interactions
|
|
- Compare render counts
|
|
- Run Lighthouse audit
|
|
- Document improvements
|
|
|
|
**Expected Results**:
|
|
- 30-50% reduction in re-renders
|
|
- Lighthouse performance >= 90
|
|
- Faster interaction times
|
|
|
|
**Documentation**:
|
|
Create `docs/PERFORMANCE_METRICS.md`:
|
|
```markdown
|
|
# Performance Optimization Results
|
|
|
|
## Before
|
|
- Kanban card drag: 25 component re-renders
|
|
- Lighthouse Performance: 78
|
|
- Time to Interactive: 3.8s
|
|
|
|
## After
|
|
- Kanban card drag: 8 component re-renders (-68%)
|
|
- Lighthouse Performance: 92 (+14)
|
|
- Time to Interactive: 2.1s (-45%)
|
|
```
|
|
|
|
---
|
|
|
|
# Story 3: Next.js 15 Async Params Migration
|
|
|
|
**Priority**: P0 (Critical)
|
|
**Estimated**: 1 day
|
|
**Story Points**: 3
|
|
|
|
## Description
|
|
|
|
Update all dynamic route pages to use Next.js 15's async params pattern. This ensures compatibility with Next.js 15+ and prevents future deprecation warnings.
|
|
|
|
## Acceptance Criteria
|
|
|
|
- [ ] All pages with `[id]` params use async pattern
|
|
- [ ] No TypeScript errors
|
|
- [ ] All routes work correctly
|
|
- [ ] No hydration warnings
|
|
- [ ] Documentation updated
|
|
|
|
## Tasks
|
|
|
|
### Task 3.1: Fix app/projects/[id]/page.tsx
|
|
|
|
**File**: `app/projects/[id]/page.tsx`
|
|
**Estimated**: 1 hour
|
|
|
|
**Before**:
|
|
```typescript
|
|
export default function ProjectPage({ params }: { params: { id: string } }) {
|
|
const projectId = params.id;
|
|
// ...
|
|
}
|
|
```
|
|
|
|
**After**:
|
|
```typescript
|
|
interface PageProps {
|
|
params: Promise<{ id: string }>;
|
|
}
|
|
|
|
export default async function ProjectPage({ params }: PageProps) {
|
|
const { id } = await params;
|
|
// ...
|
|
}
|
|
```
|
|
|
|
**Testing**:
|
|
- Navigate to `/projects/123`
|
|
- Verify page loads
|
|
- Check no console warnings
|
|
|
|
### Task 3.2: Fix app/projects/[id]/sprints/[sprintId]/page.tsx
|
|
|
|
**File**: `app/projects/[id]/sprints/[sprintId]/page.tsx`
|
|
**Estimated**: 1 hour
|
|
|
|
**Pattern for nested params**:
|
|
```typescript
|
|
interface PageProps {
|
|
params: Promise<{ id: string; sprintId: string }>;
|
|
}
|
|
|
|
export default async function SprintPage({ params }: PageProps) {
|
|
const { id, sprintId } = await params;
|
|
// ...
|
|
}
|
|
```
|
|
|
|
### Task 3.3: Find and fix other dynamic route pages
|
|
|
|
**Estimated**: 2 hours
|
|
|
|
**Discovery**:
|
|
```bash
|
|
# Find all dynamic route pages
|
|
find app -name "\[*\]" -type d
|
|
find app -path "*/\[*\]/page.tsx"
|
|
```
|
|
|
|
**Update each file**:
|
|
1. Change params type to Promise
|
|
2. Make component async
|
|
3. Await params before use
|
|
4. Update TypeScript interfaces
|
|
|
|
**Typical files**:
|
|
- `app/projects/[id]/epics/[epicId]/page.tsx`
|
|
- `app/projects/[id]/stories/[storyId]/page.tsx`
|
|
- Any other `[param]/page.tsx` files
|
|
|
|
### Task 3.4: Update TypeScript types for async params
|
|
|
|
**File**: `types/page.ts` (create if doesn't exist)
|
|
**Estimated**: 30 minutes
|
|
|
|
```typescript
|
|
// types/page.ts
|
|
export interface PageProps<T extends Record<string, string> = Record<string, string>> {
|
|
params: Promise<T>;
|
|
searchParams?: Promise<Record<string, string | string[]>>;
|
|
}
|
|
|
|
// Usage in pages
|
|
import { PageProps } from '@/types/page';
|
|
|
|
export default async function ProjectPage({ params }: PageProps<{ id: string }>) {
|
|
const { id } = await params;
|
|
// ...
|
|
}
|
|
```
|
|
|
|
### Task 3.5: Test all dynamic routes
|
|
|
|
**Estimated**: 1 hour
|
|
|
|
**Test matrix**:
|
|
| Route | Test Case | Expected |
|
|
|-------|-----------|----------|
|
|
| `/projects/[id]` | Navigate to project detail | Page loads, no warnings |
|
|
| `/projects/[id]/sprints/[sprintId]` | Navigate to sprint detail | Page loads, params correct |
|
|
| `/projects/[id]/epics/[epicId]` | Navigate to epic detail | Page loads |
|
|
| Direct URL access | Type URL in browser | No hydration errors |
|
|
| Back/forward navigation | Use browser navigation | Params maintained |
|
|
|
|
**Verification checklist**:
|
|
- [ ] No console warnings
|
|
- [ ] No TypeScript errors
|
|
- [ ] Pages load correctly
|
|
- [ ] Params are accessible
|
|
- [ ] No hydration mismatches
|
|
|
|
---
|
|
|
|
# Story 4: Error Handling Improvements
|
|
|
|
**Priority**: P1 (High)
|
|
**Estimated**: 2 days
|
|
**Story Points**: 5
|
|
|
|
## Description
|
|
|
|
Improve error handling across the application by adding Error Boundaries, better error messages in forms, improved loading states, and helpful empty states.
|
|
|
|
## Acceptance Criteria
|
|
|
|
- [ ] Error boundaries catch and display errors gracefully
|
|
- [ ] All forms show clear error messages
|
|
- [ ] All data fetching shows loading states
|
|
- [ ] Empty states provide helpful guidance
|
|
- [ ] Error tracking integration ready
|
|
|
|
## Tasks
|
|
|
|
### Task 4.1: Create global Error Boundary component
|
|
|
|
**File**: `components/ErrorBoundary.tsx`
|
|
**Estimated**: 2 hours
|
|
|
|
```typescript
|
|
'use client';
|
|
|
|
import React from 'react';
|
|
import { Card, CardHeader, CardTitle, CardDescription, CardContent } from '@/components/ui/card';
|
|
import { Button } from '@/components/ui/button';
|
|
import { AlertTriangle } from 'lucide-react';
|
|
|
|
interface Props {
|
|
children: React.ReactNode;
|
|
}
|
|
|
|
interface State {
|
|
hasError: boolean;
|
|
error: Error | null;
|
|
}
|
|
|
|
export class ErrorBoundary extends React.Component<Props, State> {
|
|
constructor(props: Props) {
|
|
super(props);
|
|
this.state = { hasError: false, error: null };
|
|
}
|
|
|
|
static getDerivedStateFromError(error: Error): State {
|
|
return { hasError: true, error };
|
|
}
|
|
|
|
componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
|
|
// Log to error tracking service (e.g., Sentry)
|
|
console.error('Error boundary caught:', error, errorInfo);
|
|
}
|
|
|
|
render() {
|
|
if (this.state.hasError) {
|
|
return (
|
|
<div className="flex items-center justify-center min-h-screen p-4">
|
|
<Card className="w-full max-w-md">
|
|
<CardHeader>
|
|
<div className="flex items-center gap-2">
|
|
<AlertTriangle className="h-5 w-5 text-destructive" />
|
|
<CardTitle>Something went wrong</CardTitle>
|
|
</div>
|
|
<CardDescription>
|
|
We're sorry, but something unexpected happened.
|
|
</CardDescription>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<p className="text-sm text-muted-foreground mb-4">
|
|
{this.state.error?.message}
|
|
</p>
|
|
<div className="flex gap-2">
|
|
<Button onClick={() => window.location.reload()}>
|
|
Reload Page
|
|
</Button>
|
|
<Button variant="outline" onClick={() => window.history.back()}>
|
|
Go Back
|
|
</Button>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return this.props.children;
|
|
}
|
|
}
|
|
```
|
|
|
|
### Task 4.2: Add Error Boundary to root layout
|
|
|
|
**File**: `app/layout.tsx`
|
|
**Estimated**: 30 minutes
|
|
|
|
```typescript
|
|
import { ErrorBoundary } from '@/components/ErrorBoundary';
|
|
|
|
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
return (
|
|
<html lang="en" suppressHydrationWarning>
|
|
<body>
|
|
<ErrorBoundary>
|
|
<QueryProvider>
|
|
<SignalRProvider>
|
|
{children}
|
|
</SignalRProvider>
|
|
</QueryProvider>
|
|
</ErrorBoundary>
|
|
</body>
|
|
</html>
|
|
);
|
|
}
|
|
```
|
|
|
|
### Task 4.3: Improve form error messages
|
|
|
|
**Files**: All form components
|
|
**Estimated**: 3 hours
|
|
|
|
**Pattern for better error messages**:
|
|
|
|
```typescript
|
|
// Before: Generic error
|
|
toast.error('Failed to create project');
|
|
|
|
// After: Specific, actionable error
|
|
const handleError = (error: ApiError) => {
|
|
const message = error.response?.data?.message || 'Failed to create project';
|
|
const details = error.response?.data?.errors;
|
|
|
|
if (details) {
|
|
Object.entries(details).forEach(([field, messages]) => {
|
|
form.setError(field as any, {
|
|
message: (messages as string[]).join(', ')
|
|
});
|
|
});
|
|
}
|
|
|
|
toast.error(message, {
|
|
description: 'Please check the form and try again.'
|
|
});
|
|
};
|
|
```
|
|
|
|
**Files to update**:
|
|
- `components/features/projects/CreateProjectDialog.tsx`
|
|
- `components/features/epics/EpicForm.tsx`
|
|
- `components/features/stories/StoryForm.tsx`
|
|
- `components/features/tasks/TaskForm.tsx`
|
|
|
|
### Task 4.4: Add loading state improvements
|
|
|
|
**Estimated**: 2 hours
|
|
|
|
**Create Skeleton components**:
|
|
|
|
```typescript
|
|
// components/ui/skeleton.tsx (if not exists)
|
|
export function Skeleton({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
|
|
return (
|
|
<div
|
|
className={cn("animate-pulse rounded-md bg-muted", className)}
|
|
{...props}
|
|
/>
|
|
);
|
|
}
|
|
|
|
// Usage in components
|
|
function ProjectList() {
|
|
const { data, isLoading } = useProjects();
|
|
|
|
if (isLoading) {
|
|
return (
|
|
<div className="grid gap-4">
|
|
{[...Array(3)].map((_, i) => (
|
|
<Card key={i}>
|
|
<CardHeader>
|
|
<Skeleton className="h-4 w-[250px]" />
|
|
<Skeleton className="h-4 w-[200px]" />
|
|
</CardHeader>
|
|
</Card>
|
|
))}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
// ... rest of component
|
|
}
|
|
```
|
|
|
|
**Files to update**:
|
|
- `components/features/projects/ProjectList.tsx`
|
|
- `components/features/kanban/KanbanBoard.tsx`
|
|
- Any component that fetches data
|
|
|
|
### Task 4.5: Add empty state components
|
|
|
|
**File**: `components/EmptyState.tsx`
|
|
**Estimated**: 1.5 hours
|
|
|
|
```typescript
|
|
interface EmptyStateProps {
|
|
icon?: React.ReactNode;
|
|
title: string;
|
|
description: string;
|
|
action?: {
|
|
label: string;
|
|
onClick: () => void;
|
|
};
|
|
}
|
|
|
|
export function EmptyState({ icon, title, description, action }: EmptyStateProps) {
|
|
return (
|
|
<div className="flex flex-col items-center justify-center p-8 text-center">
|
|
{icon && <div className="mb-4">{icon}</div>}
|
|
<h3 className="text-lg font-semibold">{title}</h3>
|
|
<p className="text-sm text-muted-foreground mb-4">{description}</p>
|
|
{action && (
|
|
<Button onClick={action.onClick}>{action.label}</Button>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
// Usage
|
|
function ProjectList() {
|
|
const { data } = useProjects();
|
|
|
|
if (!data || data.length === 0) {
|
|
return (
|
|
<EmptyState
|
|
icon={<FolderPlus className="h-12 w-12 text-muted-foreground" />}
|
|
title="No projects yet"
|
|
description="Create your first project to get started"
|
|
action={{
|
|
label: "Create Project",
|
|
onClick: () => setCreateDialogOpen(true)
|
|
}}
|
|
/>
|
|
);
|
|
}
|
|
|
|
// ... rest of component
|
|
}
|
|
```
|
|
|
|
### Task 4.6: Test error scenarios
|
|
|
|
**Estimated**: 1 hour
|
|
|
|
**Test matrix**:
|
|
| Scenario | Expected Behavior |
|
|
|----------|-------------------|
|
|
| Network error | Toast with "Network error" + retry button |
|
|
| 400 Bad Request | Form field errors displayed |
|
|
| 401 Unauthorized | Redirect to login |
|
|
| 403 Forbidden | "Access denied" message |
|
|
| 404 Not Found | "Resource not found" with back button |
|
|
| 500 Server Error | "Server error" with reload button |
|
|
| Component error | Error boundary catches, shows fallback UI |
|
|
| Empty data | Empty state with helpful CTA |
|
|
|
|
**Testing steps**:
|
|
1. Use Network tab to throttle/block requests
|
|
2. Trigger validation errors
|
|
3. Test with expired token
|
|
4. Throw error in component render
|
|
5. Load page with no data
|
|
|
|
---
|
|
|
|
# Story 5: Accessibility Enhancements
|
|
|
|
**Priority**: P2 (Medium)
|
|
**Estimated**: 2-3 days
|
|
**Story Points**: 5
|
|
|
|
## Description
|
|
|
|
Improve application accessibility to meet WCAG 2.1 Level AA standards by adding proper ARIA labels, improving keyboard navigation, and ensuring screen reader compatibility.
|
|
|
|
## Acceptance Criteria
|
|
|
|
- [ ] All interactive elements have proper ARIA labels
|
|
- [ ] Keyboard navigation works for all features
|
|
- [ ] Tab order is logical
|
|
- [ ] Screen reader announces changes properly
|
|
- [ ] WCAG 2.1 Level AA compliant
|
|
- [ ] Lighthouse accessibility score >= 95
|
|
|
|
## Tasks
|
|
|
|
### Task 5.1: Add ARIA labels to interactive cards
|
|
|
|
**Files**: Card components
|
|
**Estimated**: 2 hours
|
|
|
|
**IssueCard example**:
|
|
```typescript
|
|
<Card
|
|
ref={setNodeRef}
|
|
style={style}
|
|
{...attributes}
|
|
{...listeners}
|
|
className="cursor-grab active:cursor-grabbing"
|
|
role="button"
|
|
aria-label={`${issue.type}: ${issue.title}, priority ${issue.priority}, status ${issue.status}`}
|
|
tabIndex={0}
|
|
>
|
|
{/* Card content */}
|
|
</Card>
|
|
```
|
|
|
|
**Files to update**:
|
|
- `components/features/kanban/IssueCard.tsx`
|
|
- `components/features/projects/ProjectCard.tsx`
|
|
- `components/features/sprints/SprintCard.tsx`
|
|
|
|
### Task 5.2: Improve keyboard navigation in kanban board
|
|
|
|
**File**: `components/features/kanban/KanbanBoard.tsx`
|
|
**Estimated**: 3 hours
|
|
|
|
**Add keyboard handlers**:
|
|
```typescript
|
|
const handleKeyDown = (e: React.KeyboardEvent, issue: Issue) => {
|
|
switch (e.key) {
|
|
case 'Enter':
|
|
case ' ':
|
|
e.preventDefault();
|
|
openIssueDialog(issue);
|
|
break;
|
|
case 'ArrowRight':
|
|
e.preventDefault();
|
|
focusNextCard();
|
|
break;
|
|
case 'ArrowLeft':
|
|
e.preventDefault();
|
|
focusPreviousCard();
|
|
break;
|
|
case 'ArrowDown':
|
|
e.preventDefault();
|
|
focusCardBelow();
|
|
break;
|
|
case 'ArrowUp':
|
|
e.preventDefault();
|
|
focusCardAbove();
|
|
break;
|
|
}
|
|
};
|
|
```
|
|
|
|
**Features to implement**:
|
|
- Arrow key navigation between cards
|
|
- Enter/Space to open card details
|
|
- Tab to move between columns
|
|
- Escape to close dialogs
|
|
- Focus indicators
|
|
|
|
### Task 5.3: Add focus management for dialogs
|
|
|
|
**Files**: Dialog components
|
|
**Estimated**: 2 hours
|
|
|
|
**Focus trap implementation**:
|
|
```typescript
|
|
import { useEffect, useRef } from 'react';
|
|
|
|
function CreateProjectDialog({ open, onOpenChange }: Props) {
|
|
const firstFocusableRef = useRef<HTMLInputElement>(null);
|
|
const previousFocusRef = useRef<HTMLElement | null>(null);
|
|
|
|
useEffect(() => {
|
|
if (open) {
|
|
// Save current focus
|
|
previousFocusRef.current = document.activeElement as HTMLElement;
|
|
// Focus first input
|
|
firstFocusableRef.current?.focus();
|
|
} else {
|
|
// Restore focus when closed
|
|
previousFocusRef.current?.focus();
|
|
}
|
|
}, [open]);
|
|
|
|
return (
|
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
|
<DialogContent>
|
|
<DialogHeader>
|
|
<DialogTitle>Create Project</DialogTitle>
|
|
</DialogHeader>
|
|
<Form>
|
|
<Input ref={firstFocusableRef} {...} />
|
|
{/* Other fields */}
|
|
</Form>
|
|
</DialogContent>
|
|
</Dialog>
|
|
);
|
|
}
|
|
```
|
|
|
|
### Task 5.4: Add skip navigation links
|
|
|
|
**File**: `app/layout.tsx`
|
|
**Estimated**: 1 hour
|
|
|
|
```typescript
|
|
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
return (
|
|
<html lang="en">
|
|
<body>
|
|
<a
|
|
href="#main-content"
|
|
className="sr-only focus:not-sr-only focus:absolute focus:top-4 focus:left-4 focus:z-50 focus:px-4 focus:py-2 focus:bg-primary focus:text-primary-foreground"
|
|
>
|
|
Skip to main content
|
|
</a>
|
|
<Header />
|
|
<main id="main-content" className="flex-1">
|
|
{children}
|
|
</main>
|
|
</body>
|
|
</html>
|
|
);
|
|
}
|
|
```
|
|
|
|
### Task 5.5: Test with screen reader
|
|
|
|
**Estimated**: 2 hours
|
|
|
|
**Testing procedure**:
|
|
|
|
**Windows (NVDA/JAWS)**:
|
|
1. Install NVDA (free) or JAWS
|
|
2. Navigate through application
|
|
3. Verify announcements for:
|
|
- Page titles
|
|
- Form labels
|
|
- Button purposes
|
|
- Error messages
|
|
- Dynamic updates
|
|
|
|
**macOS (VoiceOver)**:
|
|
1. Enable VoiceOver (Cmd+F5)
|
|
2. Navigate using VO+Arrow keys
|
|
3. Test form filling
|
|
4. Test kanban board navigation
|
|
|
|
**Checklist**:
|
|
- [ ] All form fields have labels
|
|
- [ ] Buttons have descriptive text
|
|
- [ ] Images have alt text
|
|
- [ ] Status changes are announced
|
|
- [ ] Error messages are announced
|
|
- [ ] Dialogs trap focus properly
|
|
|
|
### Task 5.6: Run accessibility audit
|
|
|
|
**Estimated**: 1 hour
|
|
|
|
**Tools**:
|
|
1. **Lighthouse** (Chrome DevTools)
|
|
- Run audit
|
|
- Target score >= 95
|
|
- Fix all issues
|
|
|
|
2. **axe DevTools** (Browser extension)
|
|
- Install extension
|
|
- Run scan on each page
|
|
- Fix Critical and Serious issues
|
|
|
|
3. **WAVE** (Web accessibility evaluation tool)
|
|
- Scan pages
|
|
- Review errors and alerts
|
|
|
|
**Create audit report**:
|
|
```markdown
|
|
# Accessibility Audit Results
|
|
|
|
## Lighthouse Score: 96/100
|
|
|
|
## Issues Fixed
|
|
- Added 15 missing ARIA labels
|
|
- Fixed 8 color contrast issues
|
|
- Added keyboard navigation to kanban
|
|
- Implemented focus management for dialogs
|
|
|
|
## Remaining Issues
|
|
- None (all Critical and Serious issues resolved)
|
|
|
|
## WCAG 2.1 Level AA Compliance: ✅
|
|
```
|
|
|
|
---
|
|
|
|
# Story 6: Code Quality Tooling
|
|
|
|
**Priority**: P2 (Medium)
|
|
**Estimated**: 1 day
|
|
**Story Points**: 3
|
|
|
|
## Description
|
|
|
|
Configure code quality tools (ESLint, Prettier, Husky) to prevent code quality issues from being committed and ensure consistent code style across the team.
|
|
|
|
## Acceptance Criteria
|
|
|
|
- [ ] ESLint fails on `any` type usage
|
|
- [ ] Pre-commit hooks prevent bad code
|
|
- [ ] Code is auto-formatted on commit
|
|
- [ ] TypeScript strict mode enforced
|
|
- [ ] Team has consistent VS Code settings
|
|
|
|
## Tasks
|
|
|
|
### Task 6.1: Configure ESLint to prohibit 'any' type
|
|
|
|
**File**: `.eslintrc.json` or `eslint.config.js`
|
|
**Estimated**: 30 minutes
|
|
|
|
```json
|
|
{
|
|
"extends": ["next/core-web-vitals"],
|
|
"rules": {
|
|
"@typescript-eslint/no-explicit-any": "error",
|
|
"@typescript-eslint/no-unsafe-assignment": "warn",
|
|
"@typescript-eslint/no-unsafe-member-access": "warn",
|
|
"@typescript-eslint/no-unsafe-call": "warn",
|
|
"no-console": ["warn", { "allow": ["warn", "error"] }]
|
|
}
|
|
}
|
|
```
|
|
|
|
**Test**:
|
|
```bash
|
|
npm run lint
|
|
# Should show errors for any 'any' types
|
|
```
|
|
|
|
### Task 6.2: Configure pre-commit hooks with Husky
|
|
|
|
**Estimated**: 1 hour
|
|
|
|
**Setup**:
|
|
```bash
|
|
# Install Husky
|
|
npm install -D husky
|
|
|
|
# Initialize Husky
|
|
npx husky init
|
|
|
|
# Create pre-commit hook
|
|
npx husky add .husky/pre-commit "npm run lint-staged"
|
|
```
|
|
|
|
**File**: `.husky/pre-commit`
|
|
```bash
|
|
#!/usr/bin/env sh
|
|
. "$(dirname -- "$0")/_/husky.sh"
|
|
|
|
npm run lint-staged
|
|
```
|
|
|
|
### Task 6.3: Add TypeScript strict checks to pre-commit
|
|
|
|
**File**: `package.json`
|
|
**Estimated**: 30 minutes
|
|
|
|
```json
|
|
{
|
|
"scripts": {
|
|
"type-check": "tsc --noEmit",
|
|
"lint-staged": "lint-staged"
|
|
}
|
|
}
|
|
```
|
|
|
|
**File**: `.lintstagedrc.js`
|
|
```javascript
|
|
module.exports = {
|
|
'*.{ts,tsx}': [
|
|
'eslint --fix',
|
|
'tsc-files --noEmit'
|
|
],
|
|
};
|
|
```
|
|
|
|
### Task 6.4: Add Prettier formatting check
|
|
|
|
**Estimated**: 45 minutes
|
|
|
|
**Install**:
|
|
```bash
|
|
npm install -D prettier
|
|
```
|
|
|
|
**File**: `.prettierrc`
|
|
```json
|
|
{
|
|
"semi": true,
|
|
"trailingComma": "es5",
|
|
"singleQuote": true,
|
|
"printWidth": 100,
|
|
"tabWidth": 2
|
|
}
|
|
```
|
|
|
|
**File**: `.prettierignore`
|
|
```
|
|
node_modules
|
|
.next
|
|
dist
|
|
build
|
|
*.min.js
|
|
```
|
|
|
|
**Update lint-staged**:
|
|
```javascript
|
|
module.exports = {
|
|
'*.{ts,tsx}': [
|
|
'prettier --write',
|
|
'eslint --fix',
|
|
'tsc-files --noEmit'
|
|
],
|
|
'*.{json,md}': 'prettier --write'
|
|
};
|
|
```
|
|
|
|
### Task 6.5: Add lint-staged for faster checks
|
|
|
|
**Estimated**: 30 minutes
|
|
|
|
**Install**:
|
|
```bash
|
|
npm install -D lint-staged
|
|
```
|
|
|
|
**Configuration already added in Task 6.4**
|
|
|
|
**Test**:
|
|
```bash
|
|
# Make a change
|
|
git add .
|
|
git commit -m "test"
|
|
# Should run lint-staged before commit
|
|
```
|
|
|
|
### Task 6.6: Document code quality standards
|
|
|
|
**File**: `CONTRIBUTING.md`
|
|
**Estimated**: 1 hour
|
|
|
|
```markdown
|
|
# Contributing to ColaFlow
|
|
|
|
## Code Quality Standards
|
|
|
|
### TypeScript
|
|
- No `any` types (ESLint will error)
|
|
- Use strict type checking
|
|
- Define interfaces for all data structures
|
|
|
|
### Code Style
|
|
- Prettier for formatting (auto-formats on commit)
|
|
- ESLint for code quality
|
|
- Follow existing patterns
|
|
|
|
### Before Committing
|
|
Pre-commit hooks will automatically:
|
|
1. Format code with Prettier
|
|
2. Run ESLint and fix auto-fixable issues
|
|
3. Run TypeScript type checking
|
|
|
|
If checks fail, fix errors before committing.
|
|
|
|
### Running Checks Manually
|
|
```bash
|
|
npm run lint # Run ESLint
|
|
npm run type-check # Run TypeScript
|
|
npm run format # Format with Prettier
|
|
```
|
|
|
|
### Component Guidelines
|
|
- Use React.memo for presentational components
|
|
- Use useCallback for event handlers
|
|
- Use useMemo for expensive computations
|
|
- Add proper TypeScript types
|
|
- Include ARIA labels for accessibility
|
|
|
|
### Testing
|
|
- Write tests for complex logic
|
|
- Test accessibility with screen readers
|
|
- Test keyboard navigation
|
|
```
|
|
|
|
---
|
|
|
|
## Sprint Timeline
|
|
|
|
### Week 1 (Nov 5-8)
|
|
|
|
**Day 1-2: Story 1 + Story 3**
|
|
- Complete logging utility migration
|
|
- Fix Next.js 15 async params
|
|
- High priority, quick wins
|
|
|
|
**Day 3: Story 6**
|
|
- Set up code quality tools
|
|
- Enables prevention of future issues
|
|
|
|
### Week 2 (Nov 11-15)
|
|
|
|
**Day 4-6: Story 2**
|
|
- Performance optimization
|
|
- Critical for user experience
|
|
|
|
**Day 7-8: Story 4**
|
|
- Error handling improvements
|
|
- Important for production readiness
|
|
|
|
### Week 3 (Nov 18-19)
|
|
|
|
**Day 9-10: Story 5**
|
|
- Accessibility enhancements
|
|
- Important but not blocking
|
|
|
|
**Day 11: Buffer + Sprint Review**
|
|
- Handle any overrun
|
|
- Final testing
|
|
- Sprint retrospective
|
|
|
|
---
|
|
|
|
## Testing & Verification
|
|
|
|
### Story 1: Logging
|
|
```bash
|
|
grep -r "console.log" lib/ | wc -l # Should be 0
|
|
npm run build # Verify no console spam in production
|
|
```
|
|
|
|
### Story 2: Performance
|
|
- React DevTools Profiler (re-render count)
|
|
- Lighthouse audit (score >= 90)
|
|
- Manual testing (app feels faster)
|
|
|
|
### Story 3: Next.js 15
|
|
- All routes load without errors
|
|
- No TypeScript errors
|
|
- No console warnings
|
|
|
|
### Story 4: Error Handling
|
|
- Trigger network error → shows proper message
|
|
- Fill invalid form → shows field errors
|
|
- Throw component error → error boundary catches
|
|
|
|
### Story 5: Accessibility
|
|
- Lighthouse accessibility score >= 95
|
|
- axe DevTools scan (0 Critical issues)
|
|
- Screen reader testing (all content announced)
|
|
|
|
### Story 6: Code Quality
|
|
- Try to commit `any` type → blocked by ESLint
|
|
- Commit with formatting issues → auto-formatted
|
|
- TypeScript errors → commit blocked
|
|
|
|
---
|
|
|
|
## Success Criteria
|
|
|
|
### Code Quality Metrics
|
|
|
|
**Before Sprint 3**:
|
|
- Type Safety: 6/10
|
|
- Performance: 6/10
|
|
- Accessibility: 7/10
|
|
- Overall: 7.1/10
|
|
|
|
**After Sprint 3 (Target)**:
|
|
- Type Safety: 9/10 ✅
|
|
- Performance: 8/10 ✅
|
|
- Accessibility: 9/10 ✅
|
|
- Overall: 9.0/10 ✅
|
|
|
|
### Specific Metrics
|
|
- ✅ Zero `any` types in production code
|
|
- ✅ Zero console.log in production code
|
|
- ✅ All components use React.memo where appropriate
|
|
- ✅ All dynamic routes use async params
|
|
- ✅ Error boundaries implemented
|
|
- ✅ WCAG 2.1 Level AA compliant
|
|
- ✅ Lighthouse scores: Performance >= 90, Accessibility >= 95
|
|
- ✅ Pre-commit hooks active
|
|
|
|
---
|
|
|
|
## Risks & Mitigation
|
|
|
|
| Risk | Impact | Probability | Mitigation |
|
|
|------|--------|-------------|------------|
|
|
| React.memo breaks functionality | High | Low | Thorough testing, use React DevTools |
|
|
| Performance optimization takes longer | Medium | Medium | Prioritize critical components first |
|
|
| Async params migration breaks routes | High | Low | Test all routes systematically |
|
|
| Pre-commit hooks slow workflow | Low | Medium | Optimize hook performance, use lint-staged |
|
|
|
|
---
|
|
|
|
## Notes
|
|
|
|
**Why This Sprint Matters**:
|
|
- Foundation for M2 (MCP Server)
|
|
- Improves developer experience
|
|
- Reduces technical debt
|
|
- Better user experience
|
|
- Production-ready quality
|
|
|
|
**Quick Wins**:
|
|
- Story 1 (Logging): Simple find-replace
|
|
- Story 3 (Async Params): Pattern-based fixes
|
|
- Story 6 (Tooling): One-time setup
|
|
|
|
**Challenging Parts**:
|
|
- Story 2 (Performance): Requires careful testing
|
|
- Story 5 (Accessibility): Requires screen reader testing
|
|
|
|
---
|
|
|
|
**Created**: 2025-11-05
|
|
**Sprint Owner**: Frontend Team
|
|
**Review Date**: 2025-11-12 (Mid-sprint)
|
|
**Target Completion**: 2025-11-19
|