Files
maternal-app/maternal-web/components/analytics/CorrelationInsights.tsx
Andrei b0ac2f71df
Some checks failed
CI/CD Pipeline / Lint and Test (push) Has been cancelled
CI/CD Pipeline / E2E Tests (push) Has been cancelled
CI/CD Pipeline / Build Application (push) Has been cancelled
feat: Add advanced analytics UI components in frontend
- 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.
2025-10-06 11:46:05 +00:00

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>
);
}