- Created EnhancedInsightsDashboard with multiple chart types: - Area charts with gradients for activity trends - Radar chart for weekly activity patterns - 24-hour heatmap visualization - Bubble/scatter chart for correlations - Time of day distribution bar chart - Added toggle between basic and enhanced chart views - Implemented chart export functionality (PNG/PDF) - Fixed API endpoint URLs (circadian-rhythm, query params) - Fixed component library conflicts (shadcn/ui → MUI) - Added comprehensive null safety for timestamp handling - Added alert type translations in all 5 languages - Installed html2canvas and jspdf for export features - Applied consistent minimum width styling to all charts 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
537 lines
13 KiB
TypeScript
537 lines
13 KiB
TypeScript
import { apiClient } from './client';
|
|
|
|
export interface SleepPattern {
|
|
averageDuration: number;
|
|
averageBedtime: string;
|
|
averageWakeTime: string;
|
|
nightWakings: number;
|
|
napCount: number;
|
|
consistency: number;
|
|
trend: 'improving' | 'stable' | 'declining';
|
|
}
|
|
|
|
export interface FeedingPattern {
|
|
averageInterval: number;
|
|
averageDuration: number;
|
|
totalFeedings: number;
|
|
feedingMethod: Record<string, number>;
|
|
consistency: number;
|
|
trend: 'increasing' | 'stable' | 'decreasing';
|
|
}
|
|
|
|
export interface DiaperPattern {
|
|
wetDiapersPerDay: number;
|
|
dirtyDiapersPerDay: number;
|
|
averageInterval: number;
|
|
isHealthy: boolean;
|
|
notes: string;
|
|
}
|
|
|
|
export interface GrowthSpurtDetection {
|
|
isLikelyGrowthSpurt: boolean;
|
|
confidence: number;
|
|
indicators: string[];
|
|
expectedDuration: string;
|
|
recommendations: string[];
|
|
}
|
|
|
|
export interface PatternInsights {
|
|
sleep: SleepPattern | null;
|
|
feeding: FeedingPattern | null;
|
|
diaper: DiaperPattern | null;
|
|
growthSpurt: GrowthSpurtDetection | null;
|
|
recommendations: string[];
|
|
concernsDetected: string[];
|
|
}
|
|
|
|
export interface SleepPrediction {
|
|
nextNapTime: Date | null;
|
|
nextNapConfidence: number;
|
|
nextBedtime: Date | null;
|
|
bedtimeConfidence: number;
|
|
optimalWakeWindows: number[];
|
|
reasoning: string;
|
|
}
|
|
|
|
export interface FeedingPrediction {
|
|
nextFeedingTime: Date | null;
|
|
confidence: number;
|
|
expectedInterval: number;
|
|
reasoning: string;
|
|
}
|
|
|
|
export interface PredictionInsights {
|
|
sleep: SleepPrediction;
|
|
feeding: FeedingPrediction;
|
|
generatedAt: Date;
|
|
}
|
|
|
|
export interface WeeklyReport {
|
|
childId: string;
|
|
weekStart: Date;
|
|
weekEnd: Date;
|
|
summary: {
|
|
totalFeedings: number;
|
|
averageFeedingsPerDay: number;
|
|
totalSleepHours: number;
|
|
averageSleepHoursPerDay: number;
|
|
totalDiapers: number;
|
|
averageDiapersPerDay: number;
|
|
};
|
|
dailyData: Array<{
|
|
date: Date;
|
|
feedings: number;
|
|
sleepHours: number;
|
|
diapers: number;
|
|
}>;
|
|
trends: {
|
|
feedingTrend: 'increasing' | 'stable' | 'decreasing';
|
|
sleepTrend: 'improving' | 'stable' | 'declining';
|
|
};
|
|
highlights: string[];
|
|
}
|
|
|
|
export interface MonthlyReport {
|
|
childId: string;
|
|
month: Date;
|
|
summary: {
|
|
totalFeedings: number;
|
|
totalSleepHours: number;
|
|
totalDiapers: number;
|
|
averageFeedingsPerDay: number;
|
|
averageSleepHoursPerDay: number;
|
|
averageDiapersPerDay: number;
|
|
};
|
|
weeklyData: Array<{
|
|
weekStart: Date;
|
|
feedings: number;
|
|
sleepHours: number;
|
|
diapers: number;
|
|
}>;
|
|
milestones: string[];
|
|
trends: string[];
|
|
}
|
|
|
|
// Advanced Analytics Interfaces
|
|
export interface CircadianRhythm {
|
|
sleepPhaseShift: number;
|
|
consistency: number;
|
|
optimalBedtime: string;
|
|
optimalWakeTime: string;
|
|
chronotype: 'early_bird' | 'night_owl' | 'typical';
|
|
melatoninOnset: string;
|
|
recommendedSchedule: {
|
|
wakeTime: string;
|
|
morningNap?: { start: string; duration: number };
|
|
afternoonNap?: { start: string; duration: number };
|
|
bedtime: string;
|
|
totalSleepTarget: number;
|
|
};
|
|
}
|
|
|
|
export interface AnomalyDetection {
|
|
anomalies: Array<{
|
|
activityId: string;
|
|
type: string;
|
|
timestamp: Date;
|
|
severity: 'low' | 'medium' | 'high';
|
|
description: string;
|
|
deviation: number;
|
|
}>;
|
|
alerts: Array<{
|
|
type: string;
|
|
severity: 'info' | 'warning' | 'critical';
|
|
message: string;
|
|
recommendations: string[];
|
|
}>;
|
|
confidenceScore: number;
|
|
}
|
|
|
|
export interface PatternCluster {
|
|
clusterId: string;
|
|
label: string;
|
|
activities: any[];
|
|
centroid: {
|
|
averageTime: string;
|
|
averageDuration: number;
|
|
characteristics: Record<string, any>;
|
|
};
|
|
confidence: number;
|
|
}
|
|
|
|
export interface CorrelationAnalysis {
|
|
feedingSleepCorrelation: number;
|
|
activityDiaperCorrelation: number;
|
|
sleepMoodCorrelation?: number;
|
|
insights: string[];
|
|
}
|
|
|
|
export interface TrendAnalysis {
|
|
shortTermTrend: {
|
|
direction: 'improving' | 'stable' | 'declining';
|
|
slope: number;
|
|
confidence: number;
|
|
r2Score: number;
|
|
changePercent: number;
|
|
};
|
|
mediumTermTrend: {
|
|
direction: 'improving' | 'stable' | 'declining';
|
|
slope: number;
|
|
confidence: number;
|
|
r2Score: number;
|
|
changePercent: number;
|
|
};
|
|
longTermTrend: {
|
|
direction: 'improving' | 'stable' | 'declining';
|
|
slope: number;
|
|
confidence: number;
|
|
r2Score: number;
|
|
changePercent: number;
|
|
};
|
|
seasonalPatterns?: Array<{
|
|
type: 'weekly' | 'monthly';
|
|
pattern: string;
|
|
strength: number;
|
|
}>;
|
|
prediction: {
|
|
next7Days: Array<{
|
|
date: Date;
|
|
predictedValue: number;
|
|
confidenceInterval: { lower: number; upper: number };
|
|
}>;
|
|
confidence: number;
|
|
factors: string[];
|
|
};
|
|
}
|
|
|
|
export interface GrowthPercentile {
|
|
weight?: {
|
|
value: number;
|
|
percentile: number;
|
|
zScore: number;
|
|
interpretation: string;
|
|
};
|
|
height?: {
|
|
value: number;
|
|
percentile: number;
|
|
zScore: number;
|
|
interpretation: string;
|
|
};
|
|
headCircumference?: {
|
|
value: number;
|
|
percentile: number;
|
|
zScore: number;
|
|
interpretation: string;
|
|
};
|
|
bmi?: {
|
|
value: number;
|
|
percentile: number;
|
|
zScore: number;
|
|
interpretation: string;
|
|
};
|
|
}
|
|
|
|
export interface GrowthAnalysis {
|
|
currentPercentiles: GrowthPercentile;
|
|
growthVelocity: {
|
|
weightVelocity?: {
|
|
value: number;
|
|
unit: string;
|
|
percentile: number;
|
|
};
|
|
heightVelocity?: {
|
|
value: number;
|
|
unit: string;
|
|
percentile: number;
|
|
};
|
|
interpretation: string;
|
|
concerns: string[];
|
|
};
|
|
growthCurve: {
|
|
measurements: Array<{
|
|
date: Date;
|
|
weight?: number;
|
|
height?: number;
|
|
headCircumference?: number;
|
|
}>;
|
|
percentileCurves: {
|
|
weight: Record<string, number[]>;
|
|
height: Record<string, number[]>;
|
|
headCircumference: Record<string, number[]>;
|
|
};
|
|
};
|
|
projections: {
|
|
threeMonths: GrowthPercentile;
|
|
sixMonths: GrowthPercentile;
|
|
confidence: number;
|
|
};
|
|
recommendations: string[];
|
|
alerts: Array<{
|
|
type: string;
|
|
severity: 'low' | 'medium' | 'high';
|
|
message: string;
|
|
action: string;
|
|
}>;
|
|
}
|
|
|
|
export interface AdvancedAnalyticsDashboard {
|
|
circadianRhythm: CircadianRhythm;
|
|
anomalies: AnomalyDetection;
|
|
correlations: CorrelationAnalysis;
|
|
growthAnalysis: GrowthAnalysis;
|
|
trends: Record<string, TrendAnalysis>;
|
|
clusters: Record<string, PatternCluster[]>;
|
|
}
|
|
|
|
export const analyticsApi = {
|
|
// Get pattern insights
|
|
getInsights: async (childId: string, days: number = 7): Promise<PatternInsights> => {
|
|
const response = await apiClient.get(`/api/v1/analytics/insights/${childId}`, {
|
|
params: { days },
|
|
});
|
|
return response.data.data;
|
|
},
|
|
|
|
// Get predictions
|
|
getPredictions: async (childId: string): Promise<PredictionInsights> => {
|
|
const response = await apiClient.get(`/api/v1/analytics/predictions/${childId}`);
|
|
const data = response.data.data;
|
|
|
|
// Convert date strings to Date objects
|
|
return {
|
|
...data,
|
|
generatedAt: new Date(data.generatedAt),
|
|
sleep: {
|
|
...data.sleep,
|
|
nextNapTime: data.sleep.nextNapTime ? new Date(data.sleep.nextNapTime) : null,
|
|
nextBedtime: data.sleep.nextBedtime ? new Date(data.sleep.nextBedtime) : null,
|
|
},
|
|
feeding: {
|
|
...data.feeding,
|
|
nextFeedingTime: data.feeding.nextFeedingTime ? new Date(data.feeding.nextFeedingTime) : null,
|
|
},
|
|
};
|
|
},
|
|
|
|
// Get weekly report
|
|
getWeeklyReport: async (childId: string, startDate?: Date): Promise<WeeklyReport> => {
|
|
const response = await apiClient.get(`/api/v1/analytics/reports/${childId}/weekly`, {
|
|
params: startDate ? { startDate: startDate.toISOString() } : {},
|
|
});
|
|
const data = response.data.data;
|
|
|
|
// Convert date strings
|
|
return {
|
|
...data,
|
|
weekStart: new Date(data.weekStart),
|
|
weekEnd: new Date(data.weekEnd),
|
|
dailyData: data.dailyData.map((d: any) => ({
|
|
...d,
|
|
date: new Date(d.date),
|
|
})),
|
|
};
|
|
},
|
|
|
|
// Get monthly report
|
|
getMonthlyReport: async (childId: string, month?: Date): Promise<MonthlyReport> => {
|
|
const response = await apiClient.get(`/api/v1/analytics/reports/${childId}/monthly`, {
|
|
params: month ? { month: month.toISOString() } : {},
|
|
});
|
|
const data = response.data.data;
|
|
|
|
// Convert date strings
|
|
return {
|
|
...data,
|
|
month: new Date(data.month),
|
|
weeklyData: data.weeklyData.map((w: any) => ({
|
|
...w,
|
|
weekStart: new Date(w.weekStart),
|
|
})),
|
|
};
|
|
},
|
|
|
|
// Export data
|
|
exportData: async (
|
|
childId: string,
|
|
format: 'json' | 'csv' | 'pdf',
|
|
startDate?: Date,
|
|
endDate?: Date,
|
|
): Promise<Blob> => {
|
|
const params: any = { format };
|
|
if (startDate) params.startDate = startDate.toISOString();
|
|
if (endDate) params.endDate = endDate.toISOString();
|
|
|
|
const response = await apiClient.get(`/api/v1/analytics/export/${childId}`, {
|
|
params,
|
|
responseType: format === 'json' ? 'json' : 'blob',
|
|
});
|
|
|
|
if (format === 'json') {
|
|
// Convert JSON to Blob
|
|
const jsonStr = JSON.stringify(response.data, null, 2);
|
|
return new Blob([jsonStr], { type: 'application/json' });
|
|
}
|
|
|
|
return response.data;
|
|
},
|
|
|
|
// Compare children
|
|
compareChildren: async (
|
|
childIds: string[],
|
|
metric: ComparisonMetric,
|
|
startDate: string,
|
|
endDate: string,
|
|
): Promise<any> => {
|
|
const response = await apiClient.get('/api/v1/analytics/compare', {
|
|
params: {
|
|
childIds: childIds.join(','),
|
|
metric,
|
|
startDate,
|
|
endDate,
|
|
},
|
|
});
|
|
return response.data.data;
|
|
},
|
|
|
|
// Advanced Analytics Methods
|
|
|
|
// Get circadian rhythm analysis
|
|
getCircadianRhythm: async (childId: string, days: number = 14): Promise<CircadianRhythm> => {
|
|
const response = await apiClient.get(`/api/v1/analytics/advanced/circadian-rhythm/${childId}`, {
|
|
params: { days },
|
|
});
|
|
return response.data.data;
|
|
},
|
|
|
|
// Get anomaly detection
|
|
getAnomalies: async (childId: string, days: number = 30): Promise<AnomalyDetection> => {
|
|
const response = await apiClient.get(`/api/v1/analytics/advanced/anomalies/${childId}`, {
|
|
params: { days },
|
|
});
|
|
const data = response.data.data;
|
|
|
|
// Convert timestamps
|
|
return {
|
|
...data,
|
|
anomalies: data.anomalies.map((a: any) => ({
|
|
...a,
|
|
timestamp: new Date(a.timestamp),
|
|
})),
|
|
};
|
|
},
|
|
|
|
// Get activity clusters
|
|
getClusters: async (
|
|
childId: string,
|
|
activityType: string,
|
|
days: number = 30,
|
|
): Promise<PatternCluster[]> => {
|
|
const response = await apiClient.get(`/api/v1/analytics/advanced/clusters/${childId}`, {
|
|
params: { activityType, days },
|
|
});
|
|
return response.data.data;
|
|
},
|
|
|
|
// Get correlation analysis
|
|
getCorrelations: async (childId: string, days: number = 14): Promise<CorrelationAnalysis> => {
|
|
const response = await apiClient.get(`/api/v1/analytics/advanced/correlations/${childId}`, {
|
|
params: { days },
|
|
});
|
|
return response.data.data;
|
|
},
|
|
|
|
// Get trend analysis
|
|
getTrends: async (childId: string, activityType: string): Promise<TrendAnalysis> => {
|
|
const response = await apiClient.get(`/api/v1/analytics/advanced/trends/${childId}`, {
|
|
params: { type: activityType },
|
|
});
|
|
const data = response.data.data;
|
|
|
|
// Convert dates in predictions
|
|
return {
|
|
...data,
|
|
prediction: {
|
|
...data.prediction,
|
|
next7Days: data.prediction.next7Days.map((p: any) => ({
|
|
...p,
|
|
date: new Date(p.date),
|
|
})),
|
|
},
|
|
};
|
|
},
|
|
|
|
// Get growth percentiles
|
|
calculateGrowthPercentiles: async (
|
|
childId: string,
|
|
measurement: {
|
|
date: Date;
|
|
weight?: number;
|
|
height?: number;
|
|
headCircumference?: number;
|
|
},
|
|
): Promise<GrowthPercentile> => {
|
|
const response = await apiClient.post(
|
|
`/api/v1/analytics/growth/percentiles/${childId}`,
|
|
{
|
|
...measurement,
|
|
date: measurement.date.toISOString(),
|
|
},
|
|
);
|
|
return response.data.data;
|
|
},
|
|
|
|
// Get growth analysis
|
|
getGrowthAnalysis: async (childId: string): Promise<GrowthAnalysis> => {
|
|
const response = await apiClient.get(`/api/v1/analytics/growth/analysis/${childId}`);
|
|
const data = response.data.data;
|
|
|
|
// Convert dates in measurements
|
|
return {
|
|
...data,
|
|
growthCurve: {
|
|
...data.growthCurve,
|
|
measurements: data.growthCurve.measurements.map((m: any) => ({
|
|
...m,
|
|
date: new Date(m.date),
|
|
})),
|
|
},
|
|
};
|
|
},
|
|
|
|
// Get comprehensive analytics dashboard
|
|
getAdvancedDashboard: async (childId: string): Promise<AdvancedAnalyticsDashboard> => {
|
|
const response = await apiClient.get(`/api/v1/analytics/advanced/dashboard/${childId}`);
|
|
const data = response.data.data;
|
|
|
|
// Process all date conversions
|
|
return {
|
|
...data,
|
|
anomalies: {
|
|
...data.anomalies,
|
|
anomalies: data.anomalies.anomalies.map((a: any) => ({
|
|
...a,
|
|
timestamp: new Date(a.timestamp),
|
|
})),
|
|
},
|
|
growthAnalysis: {
|
|
...data.growthAnalysis,
|
|
growthCurve: {
|
|
...data.growthAnalysis.growthCurve,
|
|
measurements: data.growthAnalysis.growthCurve.measurements.map((m: any) => ({
|
|
...m,
|
|
date: new Date(m.date),
|
|
})),
|
|
},
|
|
},
|
|
};
|
|
},
|
|
};
|
|
|
|
export enum ComparisonMetric {
|
|
SLEEP_PATTERNS = 'sleep-patterns',
|
|
FEEDING_FREQUENCY = 'feeding-frequency',
|
|
GROWTH_CURVES = 'growth-curves',
|
|
DIAPER_CHANGES = 'diaper-changes',
|
|
ACTIVITIES = 'activities',
|
|
}
|