feat: enhance metrics dashboard with new components and data handling
- Introduced `MetricsOverview`, `MetricsMainCharts`, `MetricsDistributionCharts`, `MetricsVelocitySection`, and `MetricsProductivitySection` for improved metrics visualization. - Updated `MetricsTab` to integrate new components and streamline data presentation. - Added compatibility fields in `JiraTask` and `AssigneeDistribution` for better data handling. - Refactored `calculateAssigneeDistribution` to include a count for total issues. - Enhanced `JiraAnalyticsService` and `JiraAdvancedFiltersService` to support new metrics calculations. - Cleaned up unused imports and components for a more maintainable codebase.
This commit is contained in:
128
src/components/jira/sprint/SprintOverview.tsx
Normal file
128
src/components/jira/sprint/SprintOverview.tsx
Normal file
@@ -0,0 +1,128 @@
|
||||
'use client';
|
||||
|
||||
import { Card, CardHeader, CardContent } from '@/components/ui/Card';
|
||||
import { Badge } from '@/components/ui/Badge';
|
||||
import { SprintDetails } from '../SprintDetailModal';
|
||||
import { formatDateForDisplay } from '@/lib/date-utils';
|
||||
|
||||
interface SprintOverviewProps {
|
||||
sprintDetails: SprintDetails;
|
||||
}
|
||||
|
||||
export function SprintOverview({ sprintDetails }: SprintOverviewProps) {
|
||||
const { sprint, metrics, assigneeDistribution, statusDistribution } = sprintDetails;
|
||||
|
||||
const getVelocityTrendIcon = (trend: string) => {
|
||||
switch (trend) {
|
||||
case 'up': return '📈';
|
||||
case 'down': return '📉';
|
||||
case 'stable': return '➡️';
|
||||
default: return '📊';
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
{/* Informations générales */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<h3 className="font-semibold">📋 Informations générales</h3>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<p className="text-sm text-gray-600">Nom du sprint</p>
|
||||
<p className="font-medium">{sprint.sprintName}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm text-gray-600">Vélocité</p>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="font-medium">{sprint.velocity || sprint.completedPoints} points</span>
|
||||
<span>{getVelocityTrendIcon(metrics.velocityTrend)}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm text-gray-600">Période</p>
|
||||
<p className="text-sm">
|
||||
{formatDateForDisplay(new Date(sprint.startDate))} - {formatDateForDisplay(new Date(sprint.endDate))}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm text-gray-600">Cycle time moyen</p>
|
||||
<p className="font-medium">{metrics.averageCycleTime.toFixed(1)} jours</p>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Métriques clés */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<h3 className="font-semibold">📊 Métriques clés</h3>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4">
|
||||
<div className="text-center p-3 bg-blue-50 rounded-lg">
|
||||
<div className="text-2xl font-bold text-blue-600">{metrics.totalIssues}</div>
|
||||
<div className="text-sm text-blue-600">Total issues</div>
|
||||
</div>
|
||||
<div className="text-center p-3 bg-green-50 rounded-lg">
|
||||
<div className="text-2xl font-bold text-green-600">{metrics.completedIssues}</div>
|
||||
<div className="text-sm text-green-600">Terminées</div>
|
||||
</div>
|
||||
<div className="text-center p-3 bg-orange-50 rounded-lg">
|
||||
<div className="text-2xl font-bold text-orange-600">{metrics.inProgressIssues}</div>
|
||||
<div className="text-sm text-orange-600">En cours</div>
|
||||
</div>
|
||||
<div className="text-center p-3 bg-red-50 rounded-lg">
|
||||
<div className="text-2xl font-bold text-red-600">{metrics.blockedIssues}</div>
|
||||
<div className="text-sm text-red-600">Bloquées</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Répartition par assigné */}
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<h3 className="font-semibold">👥 Répartition par assigné</h3>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="space-y-2">
|
||||
{assigneeDistribution.map((assignee, index) => (
|
||||
<div key={index} className="flex items-center justify-between p-2 bg-gray-50 rounded">
|
||||
<span className="text-sm font-medium">
|
||||
{assignee.assignee || 'Non assigné'}
|
||||
</span>
|
||||
<Badge variant="outline" size="sm">
|
||||
{assignee.count || assignee.totalIssues} issues
|
||||
</Badge>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Répartition par statut */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<h3 className="font-semibold">📈 Répartition par statut</h3>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="space-y-2">
|
||||
{statusDistribution.map((status, index) => (
|
||||
<div key={index} className="flex items-center justify-between p-2 bg-gray-50 rounded">
|
||||
<span className="text-sm font-medium">{status.status}</span>
|
||||
<Badge variant="outline" size="sm">
|
||||
{status.count} issues
|
||||
</Badge>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user