Files
maternal-app/maternal-web/app/page.tsx
Andrei dad20f6d08
Some checks failed
CI/CD Pipeline / Build Application (push) Has been cancelled
CI/CD Pipeline / Lint and Test (push) Has been cancelled
CI/CD Pipeline / E2E Tests (push) Has been cancelled
fix: Remove NetworkStatusIndicator from main page (Redux not integrated)
The NetworkStatusIndicator component requires Redux Provider to be set up,
which is not yet integrated in the app. Removed it from the main page to
prevent "could not find react-redux context value" errors.

NetworkStatusIndicator will be added back once Redux Provider is properly
configured in the app root.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-01 19:38:58 +00:00

223 lines
8.2 KiB
TypeScript

'use client';
import { useState, useEffect } from 'react';
import { Box, Typography, Button, Paper, Grid, CircularProgress } from '@mui/material';
import { AppShell } from '@/components/layouts/AppShell/AppShell';
import { ProtectedRoute } from '@/components/common/ProtectedRoute';
import { EmailVerificationBanner } from '@/components/common/EmailVerificationBanner';
import { ErrorBoundary } from '@/components/common/ErrorBoundary';
import { DataErrorFallback } from '@/components/common/ErrorFallbacks';
import {
Restaurant,
Hotel,
BabyChangingStation,
Insights,
SmartToy,
Analytics,
} from '@mui/icons-material';
import { motion } from 'framer-motion';
import { useAuth } from '@/lib/auth/AuthContext';
import { useRouter } from 'next/navigation';
import { trackingApi, DailySummary } from '@/lib/api/tracking';
import { childrenApi, Child } from '@/lib/api/children';
import { format } from 'date-fns';
export default function HomePage() {
const { user } = useAuth();
const router = useRouter();
const [children, setChildren] = useState<Child[]>([]);
const [selectedChild, setSelectedChild] = useState<Child | null>(null);
const [dailySummary, setDailySummary] = useState<DailySummary | null>(null);
const [loading, setLoading] = useState(true);
const familyId = user?.families?.[0]?.familyId;
// Load children and daily summary
useEffect(() => {
const loadData = async () => {
if (!familyId) {
setLoading(false);
return;
}
try {
// Load children
const childrenData = await childrenApi.getChildren(familyId);
setChildren(childrenData);
if (childrenData.length > 0) {
const firstChild = childrenData[0];
setSelectedChild(firstChild);
// Load today's summary for first child
const today = format(new Date(), 'yyyy-MM-dd');
const summary = await trackingApi.getDailySummary(firstChild.id, today);
setDailySummary(summary);
}
} catch (error) {
console.error('Failed to load data:', error);
} finally {
setLoading(false);
}
};
loadData();
}, [familyId]);
const quickActions = [
{ icon: <Restaurant />, label: 'Feeding', color: '#FFB6C1', path: '/track/feeding' },
{ icon: <Hotel />, label: 'Sleep', color: '#B6D7FF', path: '/track/sleep' },
{ icon: <BabyChangingStation />, label: 'Diaper', color: '#FFE4B5', path: '/track/diaper' },
{ icon: <SmartToy />, label: 'AI Assistant', color: '#FFD3B6', path: '/ai-assistant' },
{ icon: <Analytics />, label: 'Analytics', color: '#D4B5FF', path: '/analytics' },
];
const formatSleepHours = (minutes: number) => {
const hours = Math.floor(minutes / 60);
const mins = minutes % 60;
if (hours > 0 && mins > 0) {
return `${hours}h ${mins}m`;
} else if (hours > 0) {
return `${hours}h`;
} else {
return `${mins}m`;
}
};
return (
<ProtectedRoute>
<AppShell>
<Box>
<EmailVerificationBanner />
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5 }}
>
<Typography variant="h4" gutterBottom fontWeight="600" sx={{ mb: 1 }}>
Welcome Back{user?.name ? `, ${user.name}` : ''}! 👋
</Typography>
<Typography variant="body1" color="text.secondary" sx={{ mb: 4 }}>
Track your child's activities and get AI-powered insights
</Typography>
{/* Quick Actions */}
<Typography variant="h6" gutterBottom fontWeight="600" sx={{ mb: 2 }}>
Quick Actions
</Typography>
<Grid container spacing={2} sx={{ mb: 4 }}>
{quickActions.map((action, index) => (
<Grid item xs={6} sm={2.4} key={action.label}>
<motion.div
initial={{ opacity: 0, scale: 0.9 }}
animate={{ opacity: 1, scale: 1 }}
transition={{ duration: 0.3, delay: index * 0.1 }}
>
<Paper
onClick={() => router.push(action.path)}
sx={{
p: 3,
textAlign: 'center',
cursor: 'pointer',
bgcolor: action.color,
color: 'white',
transition: 'transform 0.2s',
'&:hover': {
transform: 'scale(1.05)',
},
}}
>
<Box sx={{ fontSize: 48, mb: 1 }}>{action.icon}</Box>
<Typography variant="body1" fontWeight="600">
{action.label}
</Typography>
</Paper>
</motion.div>
</Grid>
))}
</Grid>
{/* Today's Summary */}
<Typography variant="h6" gutterBottom fontWeight="600" sx={{ mb: 2 }}>
Today's Summary{selectedChild ? ` - ${selectedChild.name}` : ''}
</Typography>
<ErrorBoundary
isolate
fallback={<DataErrorFallback error={new Error('Failed to load summary')} />}
>
<Paper sx={{ p: 3 }}>
{loading ? (
<Box sx={{ display: 'flex', justifyContent: 'center', py: 4 }}>
<CircularProgress />
</Box>
) : !dailySummary ? (
<Box sx={{ textAlign: 'center', py: 4 }}>
<Typography variant="body2" color="text.secondary">
{children.length === 0
? 'Add a child to start tracking'
: 'No activities tracked today'}
</Typography>
</Box>
) : (
<Grid container spacing={3}>
<Grid item xs={4}>
<Box textAlign="center">
<Restaurant sx={{ fontSize: 32, color: 'primary.main', mb: 1 }} />
<Typography variant="h5" fontWeight="600">
{dailySummary.feedingCount || 0}
</Typography>
<Typography variant="body2" color="text.secondary">
Feedings
</Typography>
</Box>
</Grid>
<Grid item xs={4}>
<Box textAlign="center">
<Hotel sx={{ fontSize: 32, color: 'info.main', mb: 1 }} />
<Typography variant="h5" fontWeight="600">
{dailySummary.sleepTotalMinutes
? formatSleepHours(dailySummary.sleepTotalMinutes)
: '0m'}
</Typography>
<Typography variant="body2" color="text.secondary">
Sleep
</Typography>
</Box>
</Grid>
<Grid item xs={4}>
<Box textAlign="center">
<BabyChangingStation sx={{ fontSize: 32, color: 'warning.main', mb: 1 }} />
<Typography variant="h5" fontWeight="600">
{dailySummary.diaperCount || 0}
</Typography>
<Typography variant="body2" color="text.secondary">
Diapers
</Typography>
</Box>
</Grid>
</Grid>
)}
</Paper>
</ErrorBoundary>
{/* Next Predicted Activity */}
<Box sx={{ mt: 4 }}>
<Paper sx={{ p: 3, bgcolor: 'primary.light' }}>
<Typography variant="body2" color="text.secondary" gutterBottom>
Next Predicted Activity
</Typography>
<Typography variant="h6" fontWeight="600" gutterBottom>
Nap time in 45 minutes
</Typography>
<Typography variant="body2" color="text.secondary">
Based on your child's sleep patterns
</Typography>
</Paper>
</Box>
</motion.div>
</Box>
</AppShell>
</ProtectedRoute>
);
}