'use client'; import React from 'react'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; import { Progress } from '@/components/ui/progress'; import { Activity, FileText, Clock, TrendingUp, TrendingDown, Minus, Info, CheckCircle, AlertTriangle, XCircle } from 'lucide-react'; import { cn } from '@/lib/utils'; import { ActivityScore, ContentScore, TimelinessScore, TimeEntryAnalysis, AggregateAnalysis } from '@/lib/types/analytics'; interface ScoreCardProps { title: string; score: number; description?: string; trend?: 'up' | 'down' | 'neutral'; trendValue?: number; icon?: React.ReactNode; size?: 'sm' | 'md' | 'lg'; className?: string; } export function ScoreCard({ title, score, description, trend, trendValue, icon, size = 'md', className }: ScoreCardProps) { const percentage = Math.round(score * 100); const scoreColor = getScoreColor(score); const scoreLabel = getScoreLabel(score); const sizeClasses = { sm: 'p-4', md: 'p-6', lg: 'p-8', }; const titleSizeClasses = { sm: 'text-sm', md: 'text-base', lg: 'text-lg', }; const scoreSizeClasses = { sm: 'text-2xl', md: 'text-3xl', lg: 'text-4xl', }; return ( {icon} {title} {trend && ( {trend === 'up' && } {trend === 'down' && } {trend === 'neutral' && } {(trendValue !== undefined) && ( 0 ? "text-green-600" : trendValue < 0 ? "text-red-600" : "text-gray-600" )}> {trendValue > 0 ? '+' : ''}{trendValue}% )} )} {description && ( {description} )} {/* Score Display */} {percentage}% {scoreLabel} {getScoreIcon(score)} {scoreLabel} {/* Progress Bar */} Poor Average Excellent ); } interface ActivityScoreCardProps { title: string; score: ActivityScore; icon?: React.ReactNode; className?: string; } export function ActivityScoreCard({ title, score, icon, className }: ActivityScoreCardProps) { return ( {icon || } {title} {/* Overall Score */} {Math.round(score.score * 100)}% Activity Score {/* Breakdown */} Score Breakdown Completeness {Math.round(score.breakdown.completeness * 100)}% Consistency {Math.round(score.breakdown.consistency * 100)}% Duration {Math.round(score.breakdown.duration * 100)}% Categorization {Math.round(score.breakdown.categorization * 100)}% {/* Positive Factors */} {score.factors.length > 0 && ( Positive Factors {score.factors.map((factor: string, index: number) => ( {factor} ))} )} ); } interface ContentScoreCardProps { title: string; score: ContentScore; icon?: React.ReactNode; className?: string; } export function ContentScoreCard({ title, score, icon, className }: ContentScoreCardProps) { return ( {icon || } {title} {/* Overall Score */} {Math.round(score.score * 100)}% Content Score {/* Breakdown */} Score Breakdown Notes Quality {Math.round(score.breakdown.notesQuality * 100)}% Title Clarity {Math.round(score.breakdown.titleClarity * 100)}% Internal Notes {Math.round(score.breakdown.internalNotes * 100)}% Technical Detail {Math.round(score.breakdown.technicalDetail * 100)}% {/* Positive Factors */} {score.factors.length > 0 && ( Positive Factors {score.factors.map((factor: string, index: number) => ( {factor} ))} )} ); } interface TimelinessScoreCardProps { title: string; score: TimelinessScore; icon?: React.ReactNode; className?: string; } export function TimelinessScoreCard({ title, score, icon, className }: TimelinessScoreCardProps) { return ( {icon || } {title} {/* Overall Score */} {Math.round(score.score * 100)}% Timeliness Score {/* Breakdown */} Score Breakdown Entry Delay {Math.round(score.breakdown.entryDelay * 100)}% Business Hours {Math.round(score.breakdown.businessHours * 100)}% Regularity {Math.round(score.breakdown.regularity * 100)}% Approval Time {Math.round(score.breakdown.approvalTimeliness * 100)}% {/* Positive Factors */} {score.factors.length > 0 && ( Positive Factors {score.factors.map((factor: string, index: number) => ( {factor} ))} )} ); } interface AggregateScoreCardProps { analysis: AggregateAnalysis; className?: string; } export function AggregateScoreCard({ analysis, className }: AggregateScoreCardProps) { return ( Overall Performance {/* Overall Score */} {Math.round(analysis.scores.overall * 100)}% Overall Score {/* Individual Scores */} {Math.round(analysis.scores.activity * 100)}% Activity {Math.round(analysis.scores.content * 100)}% Content {Math.round(analysis.scores.timeliness * 100)}% Timeliness {/* Summary Stats */} Total Entries: {analysis.totalEntries} Total Hours: {Number(analysis.totalHours).toFixed(1)} Avg Hours/Entry: {Number(analysis.averageHoursPerEntry).toFixed(1)} Date Range: {analysis.dateRange.latest.toLocaleDateString()} ); } // Helper functions function getScoreColor(score: number) { if (score >= 0.8) { return { text: 'text-green-600', badge: 'bg-green-100 text-green-800 border-green-200', progress: 'bg-green-500', }; } if (score >= 0.6) { return { text: 'text-yellow-600', badge: 'bg-yellow-100 text-yellow-800 border-yellow-200', progress: 'bg-yellow-500', }; } return { text: 'text-red-600', badge: 'bg-red-100 text-red-800 border-red-200', progress: 'bg-red-500', }; } function getScoreLabel(score: number) { if (score >= 0.8) return 'Excellent'; if (score >= 0.6) return 'Good'; if (score >= 0.4) return 'Average'; return 'Poor'; } function getScoreIcon(score: number) { if (score >= 0.8) { return ; } if (score >= 0.6) { return ; } return ; }
{description}