- Add comprehensive API client methods for all advanced analytics endpoints - Create CircadianRhythmCard component for sleep pattern visualization - Create AnomalyAlertsPanel for anomaly detection and alerts - Create GrowthPercentileChart with WHO/CDC percentiles - Create CorrelationInsights for activity correlations - Create TrendAnalysisChart with predictions - Add advanced analytics page with all new components - Add UI component library (shadcn/ui) setup - Add navigation link to advanced analytics from insights page All advanced analytics features are now accessible from the frontend UI.
235 lines
8.2 KiB
TypeScript
235 lines
8.2 KiB
TypeScript
'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 {
|
|
Link,
|
|
Link2Off,
|
|
TrendingUp,
|
|
TrendingDown,
|
|
Moon,
|
|
Utensils,
|
|
Baby,
|
|
Activity,
|
|
Info,
|
|
CheckCircle,
|
|
} from 'lucide-react';
|
|
import { CorrelationAnalysis } from '@/lib/api/analytics';
|
|
|
|
interface CorrelationInsightsProps {
|
|
data: CorrelationAnalysis | null;
|
|
loading?: boolean;
|
|
error?: Error | null;
|
|
}
|
|
|
|
export function CorrelationInsights({ data, loading, error }: CorrelationInsightsProps) {
|
|
if (loading) {
|
|
return (
|
|
<Card>
|
|
<CardContent className="flex items-center justify-center h-64">
|
|
<div className="animate-pulse">Analyzing activity correlations...</div>
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
}
|
|
|
|
if (error) {
|
|
return (
|
|
<Card className="border-red-200">
|
|
<CardContent className="flex items-center justify-center h-64 text-red-500">
|
|
<Link2Off className="h-5 w-5 mr-2" />
|
|
Error loading correlation data
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
}
|
|
|
|
if (!data) {
|
|
return null;
|
|
}
|
|
|
|
const getCorrelationStrength = (value: number) => {
|
|
const absValue = Math.abs(value);
|
|
if (absValue > 0.7) return 'Strong';
|
|
if (absValue > 0.4) return 'Moderate';
|
|
if (absValue > 0.2) return 'Weak';
|
|
return 'Negligible';
|
|
};
|
|
|
|
const getCorrelationColor = (value: number) => {
|
|
const absValue = Math.abs(value);
|
|
if (absValue > 0.7) return 'bg-purple-100 text-purple-800';
|
|
if (absValue > 0.4) return 'bg-blue-100 text-blue-800';
|
|
if (absValue > 0.2) return 'bg-gray-100 text-gray-800';
|
|
return 'bg-gray-50 text-gray-600';
|
|
};
|
|
|
|
const getCorrelationIcon = (value: number) => {
|
|
if (value > 0.3) return <TrendingUp className="h-4 w-4 text-green-500" />;
|
|
if (value < -0.3) return <TrendingDown className="h-4 w-4 text-red-500" />;
|
|
return <Activity className="h-4 w-4 text-gray-400" />;
|
|
};
|
|
|
|
const formatCorrelation = (value: number) => {
|
|
return (value * 100).toFixed(0) + '%';
|
|
};
|
|
|
|
const correlations = [
|
|
{
|
|
name: 'Feeding & Sleep',
|
|
value: data.feedingSleepCorrelation,
|
|
icon1: <Utensils className="h-4 w-4" />,
|
|
icon2: <Moon className="h-4 w-4" />,
|
|
description: data.feedingSleepCorrelation > 0
|
|
? 'Better feeding patterns correlate with better sleep'
|
|
: data.feedingSleepCorrelation < 0
|
|
? 'More feedings may be disrupting sleep patterns'
|
|
: 'No significant relationship detected',
|
|
},
|
|
{
|
|
name: 'Activity & Diapers',
|
|
value: data.activityDiaperCorrelation,
|
|
icon1: <Activity className="h-4 w-4" />,
|
|
icon2: <Baby className="h-4 w-4" />,
|
|
description: data.activityDiaperCorrelation > 0
|
|
? 'More activity correlates with more diaper changes'
|
|
: data.activityDiaperCorrelation < 0
|
|
? 'Less active periods show more diaper changes'
|
|
: 'No clear pattern between activity and diapers',
|
|
},
|
|
...(data.sleepMoodCorrelation !== undefined ? [{
|
|
name: 'Sleep & Mood',
|
|
value: data.sleepMoodCorrelation,
|
|
icon1: <Moon className="h-4 w-4" />,
|
|
icon2: <Activity className="h-4 w-4" />,
|
|
description: data.sleepMoodCorrelation > 0
|
|
? 'Better sleep strongly correlates with better mood'
|
|
: data.sleepMoodCorrelation < 0
|
|
? 'Sleep patterns inversely affect mood'
|
|
: 'Sleep and mood appear independent',
|
|
}] : []),
|
|
];
|
|
|
|
return (
|
|
<Card className="w-full">
|
|
<CardHeader>
|
|
<CardTitle className="flex items-center gap-2">
|
|
<Link className="h-5 w-5" />
|
|
Activity Correlations
|
|
</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="space-y-6">
|
|
{/* Correlation Visualizations */}
|
|
<div className="space-y-4">
|
|
{correlations.map((correlation) => (
|
|
<div key={correlation.name} className="space-y-3">
|
|
<div className="flex items-center justify-between">
|
|
<div className="flex items-center gap-2">
|
|
<div className="flex items-center gap-1 text-gray-600">
|
|
{correlation.icon1}
|
|
<span className="text-sm font-medium">&</span>
|
|
{correlation.icon2}
|
|
</div>
|
|
<span className="text-sm font-medium">{correlation.name}</span>
|
|
</div>
|
|
<div className="flex items-center gap-2">
|
|
{getCorrelationIcon(correlation.value)}
|
|
<Badge className={getCorrelationColor(correlation.value)}>
|
|
{getCorrelationStrength(correlation.value)}
|
|
</Badge>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Correlation Bar */}
|
|
<div className="relative h-8 bg-gray-100 rounded-full overflow-hidden">
|
|
<div
|
|
className="absolute top-0 h-full bg-gradient-to-r from-red-400 via-gray-300 to-green-400"
|
|
style={{
|
|
width: '100%',
|
|
opacity: 0.3,
|
|
}}
|
|
/>
|
|
<div
|
|
className="absolute top-0 h-full flex items-center justify-center"
|
|
style={{
|
|
left: '50%',
|
|
transform: 'translateX(-50%)',
|
|
}}
|
|
>
|
|
<div className="w-0.5 h-full bg-gray-400" />
|
|
</div>
|
|
<div
|
|
className="absolute top-0 h-full flex items-center"
|
|
style={{
|
|
left: correlation.value > 0 ? '50%' : `${50 + correlation.value * 50}%`,
|
|
width: `${Math.abs(correlation.value) * 50}%`,
|
|
}}
|
|
>
|
|
<div
|
|
className={`h-4 rounded-full ${
|
|
correlation.value > 0 ? 'bg-green-500' : 'bg-red-500'
|
|
}`}
|
|
style={{ width: '100%' }}
|
|
/>
|
|
</div>
|
|
<div
|
|
className="absolute top-0 h-full flex items-center justify-end pr-2"
|
|
style={{
|
|
left: correlation.value > 0 ? `${50 + correlation.value * 50}%` : '50%',
|
|
transform: correlation.value > 0 ? 'translateX(-100%)' : 'none',
|
|
}}
|
|
>
|
|
<span className="text-xs font-medium text-white drop-shadow">
|
|
{formatCorrelation(correlation.value)}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
<p className="text-xs text-gray-600">{correlation.description}</p>
|
|
</div>
|
|
))}
|
|
</div>
|
|
|
|
{/* Correlation Scale Legend */}
|
|
<div className="p-3 bg-gray-50 rounded-lg space-y-2">
|
|
<h4 className="text-xs font-medium text-gray-700 uppercase tracking-wider">
|
|
Correlation Scale
|
|
</h4>
|
|
<div className="grid grid-cols-2 gap-2 text-xs">
|
|
<div className="flex items-center gap-2">
|
|
<div className="w-3 h-3 rounded-full bg-green-500" />
|
|
<span>Positive: Activities increase together</span>
|
|
</div>
|
|
<div className="flex items-center gap-2">
|
|
<div className="w-3 h-3 rounded-full bg-red-500" />
|
|
<span>Negative: One increases, other decreases</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Insights */}
|
|
{data.insights.length > 0 && (
|
|
<div className="space-y-2">
|
|
<h4 className="text-sm font-medium text-gray-700 flex items-center gap-2">
|
|
<Info className="h-4 w-4" />
|
|
Key Insights
|
|
</h4>
|
|
<div className="space-y-2">
|
|
{data.insights.map((insight, index) => (
|
|
<div
|
|
key={index}
|
|
className="flex items-start gap-2 p-3 bg-blue-50 rounded-lg"
|
|
>
|
|
<CheckCircle className="h-4 w-4 text-blue-500 mt-0.5 flex-shrink-0" />
|
|
<p className="text-sm text-blue-900">{insight}</p>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
} |