Files
maternal-app/maternal-web/components/analytics/CircadianRhythmCard.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

197 lines
7.6 KiB
TypeScript

'use client';
import React from 'react';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Progress } from '@/components/ui/progress';
import { Badge } from '@/components/ui/badge';
import { Moon, Sun, Clock, Brain, AlertCircle, CheckCircle2 } from 'lucide-react';
import { CircadianRhythm } from '@/lib/api/analytics';
interface CircadianRhythmCardProps {
data: CircadianRhythm | null;
loading?: boolean;
error?: Error | null;
}
export function CircadianRhythmCard({ data, loading, error }: CircadianRhythmCardProps) {
if (loading) {
return (
<Card>
<CardContent className="flex items-center justify-center h-64">
<div className="animate-pulse">Loading circadian rhythm analysis...</div>
</CardContent>
</Card>
);
}
if (error) {
return (
<Card className="border-red-200">
<CardContent className="flex items-center justify-center h-64 text-red-500">
<AlertCircle className="h-5 w-5 mr-2" />
Error loading circadian rhythm data
</CardContent>
</Card>
);
}
if (!data) {
return null;
}
const getChronotypeIcon = () => {
switch (data.chronotype) {
case 'early_bird':
return <Sun className="h-5 w-5 text-yellow-500" />;
case 'night_owl':
return <Moon className="h-5 w-5 text-indigo-500" />;
default:
return <Clock className="h-5 w-5 text-gray-500" />;
}
};
const getChronotypeColor = () => {
switch (data.chronotype) {
case 'early_bird':
return 'bg-yellow-100 text-yellow-800';
case 'night_owl':
return 'bg-indigo-100 text-indigo-800';
default:
return 'bg-gray-100 text-gray-800';
}
};
const formatTime = (time: string) => {
// Convert HH:MM to 12-hour format
const [hours, minutes] = time.split(':');
const hour = parseInt(hours);
const ampm = hour >= 12 ? 'PM' : 'AM';
const displayHour = hour === 0 ? 12 : hour > 12 ? hour - 12 : hour;
return `${displayHour}:${minutes} ${ampm}`;
};
return (
<Card className="w-full">
<CardHeader>
<CardTitle className="flex items-center justify-between">
<span className="flex items-center gap-2">
<Brain className="h-5 w-5" />
Circadian Rhythm Analysis
</span>
<Badge className={getChronotypeColor()}>
<span className="flex items-center gap-1">
{getChronotypeIcon()}
{data.chronotype.replace('_', ' ')}
</span>
</Badge>
</CardTitle>
</CardHeader>
<CardContent className="space-y-6">
{/* Sleep Consistency Score */}
<div className="space-y-2">
<div className="flex justify-between text-sm">
<span className="text-gray-600">Sleep Consistency</span>
<span className="font-medium">{Math.round(data.consistency * 100)}%</span>
</div>
<Progress value={data.consistency * 100} className="h-2" />
<p className="text-xs text-gray-500">
{data.consistency > 0.8
? 'Excellent - Very consistent sleep schedule'
: data.consistency > 0.6
? 'Good - Fairly consistent schedule'
: 'Needs improvement - Irregular sleep pattern'}
</p>
</div>
{/* Optimal Times */}
<div className="grid grid-cols-2 gap-4">
<div className="space-y-1">
<div className="flex items-center gap-2">
<Moon className="h-4 w-4 text-blue-500" />
<span className="text-sm text-gray-600">Optimal Bedtime</span>
</div>
<p className="text-lg font-semibold">{formatTime(data.optimalBedtime)}</p>
</div>
<div className="space-y-1">
<div className="flex items-center gap-2">
<Sun className="h-4 w-4 text-yellow-500" />
<span className="text-sm text-gray-600">Optimal Wake Time</span>
</div>
<p className="text-lg font-semibold">{formatTime(data.optimalWakeTime)}</p>
</div>
</div>
{/* Sleep Phase Shift */}
<div className="space-y-2">
<div className="flex items-center justify-between">
<span className="text-sm text-gray-600">Sleep Phase Shift</span>
<span className="text-sm font-medium">
{data.sleepPhaseShift > 0 ? '+' : ''}{data.sleepPhaseShift.toFixed(1)} hours
</span>
</div>
<div className="flex items-center gap-2">
{Math.abs(data.sleepPhaseShift) < 1 ? (
<CheckCircle2 className="h-4 w-4 text-green-500" />
) : (
<AlertCircle className="h-4 w-4 text-yellow-500" />
)}
<p className="text-xs text-gray-500">
{Math.abs(data.sleepPhaseShift) < 1
? 'Sleep schedule aligned with typical patterns'
: data.sleepPhaseShift > 0
? `Bedtime is ${Math.abs(data.sleepPhaseShift).toFixed(1)} hours later than typical`
: `Bedtime is ${Math.abs(data.sleepPhaseShift).toFixed(1)} hours earlier than typical`}
</p>
</div>
</div>
{/* Melatonin Onset */}
<div className="p-3 bg-purple-50 rounded-lg">
<div className="flex items-center gap-2 mb-1">
<Brain className="h-4 w-4 text-purple-600" />
<span className="text-sm font-medium text-purple-900">Estimated Melatonin Onset</span>
</div>
<p className="text-lg font-semibold text-purple-700">{formatTime(data.melatoninOnset)}</p>
<p className="text-xs text-purple-600 mt-1">
Natural sleepiness begins around this time
</p>
</div>
{/* Recommended Schedule */}
<div className="space-y-3">
<h4 className="text-sm font-medium text-gray-700">Recommended Daily Schedule</h4>
<div className="space-y-2 text-sm">
<div className="flex justify-between py-2 border-b">
<span className="text-gray-600">Wake Time</span>
<span className="font-medium">{formatTime(data.recommendedSchedule.wakeTime)}</span>
</div>
{data.recommendedSchedule.morningNap && (
<div className="flex justify-between py-2 border-b">
<span className="text-gray-600">Morning Nap</span>
<span className="font-medium">
{formatTime(data.recommendedSchedule.morningNap.start)} ({data.recommendedSchedule.morningNap.duration} min)
</span>
</div>
)}
{data.recommendedSchedule.afternoonNap && (
<div className="flex justify-between py-2 border-b">
<span className="text-gray-600">Afternoon Nap</span>
<span className="font-medium">
{formatTime(data.recommendedSchedule.afternoonNap.start)} ({data.recommendedSchedule.afternoonNap.duration} min)
</span>
</div>
)}
<div className="flex justify-between py-2 border-b">
<span className="text-gray-600">Bedtime</span>
<span className="font-medium">{formatTime(data.recommendedSchedule.bedtime)}</span>
</div>
<div className="flex justify-between py-2">
<span className="text-gray-600">Daily Sleep Target</span>
<span className="font-medium">{Math.round(data.recommendedSchedule.totalSleepTarget / 60)} hours</span>
</div>
</div>
</div>
</CardContent>
</Card>
);
}