import React from 'react';
import { useApp } from '../contexts/AppContext';
import { Card } from './ui/card';
import { Badge } from './ui/badge';
import { Progress } from './ui/progress';
import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, LineChart, Line } from 'recharts';
import { CheckCircle2, Clock, AlertCircle, TrendingUp } from 'lucide-react';
import { differenceInDays, format } from 'date-fns';
export const Dashboard: React.FC = () => {
const { tasks, userStories, activeSprint } = useApp();
if (!activeSprint) {
return (
No Active Sprint
Please activate a sprint from the Sprint Management page to view the dashboard.
);
}
const sprintTasks = tasks.filter(task => {
const story = userStories.find(s => s.id === task.userStoryId);
return story?.sprintId === activeSprint.id;
});
const todoTasks = sprintTasks.filter(t => t.status === 'todo').length;
const inProgressTasks = sprintTasks.filter(t => t.status === 'in-progress').length;
const blockedTasks = sprintTasks.filter(t => t.status === 'blocked').length;
const doneTasks = sprintTasks.filter(t => t.status === 'done').length;
const sprintStories = userStories.filter(s => s.sprintId === activeSprint.id);
const totalPoints = sprintStories.reduce((sum, s) => sum + s.storyPoints, 0);
const completedPoints = sprintStories
.filter(story => {
const storyTasks = tasks.filter(t => t.userStoryId === story.id);
return storyTasks.length > 0 && storyTasks.every(t => t.status === 'done');
})
.reduce((sum, s) => sum + s.storyPoints, 0);
const progressPercentage = totalPoints > 0 ? (completedPoints / totalPoints) * 100 : 0;
const daysTotal = differenceInDays(new Date(activeSprint.endDate), new Date(activeSprint.startDate));
const daysElapsed = differenceInDays(new Date(), new Date(activeSprint.startDate));
const daysRemaining = Math.max(0, differenceInDays(new Date(activeSprint.endDate), new Date()));
// Task distribution data
const taskDistribution = [
{ name: 'To Do', value: todoTasks, fill: '#94a3b8' },
{ name: 'In Progress', value: inProgressTasks, fill: '#3b82f6' },
{ name: 'Blocked', value: blockedTasks, fill: '#ef4444' },
{ name: 'Done', value: doneTasks, fill: '#10b981' }
];
// Mock burndown data (in a real app, this would be calculated from historical data)
const generateBurndownData = () => {
const data = [];
const idealBurnRate = totalPoints / daysTotal;
for (let day = 0; day <= daysTotal; day++) {
const ideal = Math.max(0, totalPoints - (idealBurnRate * day));
const actual = day <= daysElapsed
? Math.max(0, totalPoints - (completedPoints * (day / Math.max(daysElapsed, 1))))
: null;
data.push({
day: day,
ideal: Math.round(ideal),
actual: actual !== null ? Math.round(actual) : null
});
}
return data;
};
const burndownData = generateBurndownData();
// Find blocked tasks
const blockedTasksList = sprintTasks.filter(t => t.status === 'blocked');
return (
Sprint Dashboard
{activeSprint.name} - {activeSprint.goal}
{/* Key Metrics */}
Total Tasks
{sprintTasks.length}
In Progress
{inProgressTasks}
Days Remaining
{daysRemaining}
{/* Sprint Progress */}
Sprint Progress
{completedPoints} / {totalPoints} points
{Math.round(progressPercentage)}% complete
{format(new Date(activeSprint.startDate), 'MMM dd')} - {format(new Date(activeSprint.endDate), 'MMM dd')}
{/* Charts */}
{/* Task Distribution */}
Task Distribution
{/* Sprint Burndown */}
Sprint Burndown
{/* Blocked Items Alert */}
{blockedTasks > 0 && (
Blocked Tasks Require Attention
{blockedTasks} task{blockedTasks !== 1 ? 's are' : ' is'} currently blocked and may impact sprint progress.
{blockedTasksList.map(task => (
• {task.title}
))}
)}
{/* Team Velocity Insights */}
Team Insights
Sprint Capacity
{activeSprint.capacity} points
Committed Points
{totalPoints} points
Completed Points
{completedPoints} points
Capacity Utilization
{activeSprint.capacity > 0 ? Math.round((totalPoints / activeSprint.capacity) * 100) : 0}%
);
};