Add skeleton loading states across all tracking pages
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

- Replace CircularProgress spinners with content-aware skeleton screens
- Add FormSkeleton for form loading states (feeding, sleep, diaper pages)
- Add ActivityListSkeleton for recent activities loading
- Improves perceived performance with layout-matching placeholders

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-10-01 20:36:11 +00:00
parent b00e75f679
commit 8276db39a2
6 changed files with 372 additions and 29 deletions

View File

@@ -18,6 +18,7 @@ import { AppShell } from '@/components/layouts/AppShell/AppShell';
import { ProtectedRoute } from '@/components/common/ProtectedRoute';
import { ErrorBoundary } from '@/components/common/ErrorBoundary';
import { ChartErrorFallback } from '@/components/common/ErrorFallbacks';
import { StatGridSkeleton, ChartSkeleton } from '@/components/common/LoadingSkeletons';
import {
TrendingUp,
Hotel,
@@ -102,15 +103,14 @@ export default function AnalyticsPage() {
return (
<ProtectedRoute>
<AppShell>
<Box
sx={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
minHeight: '60vh',
}}
>
<CircularProgress />
<Box>
<Typography variant="h4" gutterBottom fontWeight="600" sx={{ mb: 3 }}>
Analytics & Insights
</Typography>
<StatGridSkeleton count={3} />
<Box sx={{ mt: 4 }}>
<ChartSkeleton height={350} />
</Box>
</Box>
</AppShell>
</ProtectedRoute>

View File

@@ -8,6 +8,7 @@ import { EmailVerificationBanner } from '@/components/common/EmailVerificationBa
import { ErrorBoundary } from '@/components/common/ErrorBoundary';
import { DataErrorFallback } from '@/components/common/ErrorFallbacks';
import { NetworkStatusIndicator } from '@/components/common/NetworkStatusIndicator';
import { StatGridSkeleton } from '@/components/common/LoadingSkeletons';
import {
Restaurant,
Hotel,
@@ -147,12 +148,11 @@ export default function HomePage() {
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 ? (
{loading ? (
<StatGridSkeleton count={3} />
) : (
<Paper sx={{ p: 3 }}>
{!dailySummary ? (
<Box sx={{ textAlign: 'center', py: 4 }}>
<Typography variant="body2" color="text.secondary">
{children.length === 0
@@ -198,8 +198,9 @@ export default function HomePage() {
</Box>
</Grid>
</Grid>
)}
</Paper>
)}
</Paper>
)}
</ErrorBoundary>
{/* Next Predicted Activity */}

View File

@@ -27,6 +27,7 @@ import {
ToggleButton,
FormLabel,
} from '@mui/material';
import { FormSkeleton, ActivityListSkeleton } from '@/components/common/LoadingSkeletons';
import {
ArrowBack,
Refresh,
@@ -305,8 +306,17 @@ export default function DiaperTrackPage() {
return (
<ProtectedRoute>
<AppShell>
<Box sx={{ display: 'flex', justifyContent: 'center', py: 8 }}>
<CircularProgress />
<Box>
<Typography variant="h4" fontWeight="600" sx={{ mb: 3 }}>
Track Diaper Change
</Typography>
<Paper sx={{ p: 3, mb: 3 }}>
<FormSkeleton />
</Paper>
<Typography variant="h6" fontWeight="600" sx={{ mb: 2 }}>
Recent Diaper Changes
</Typography>
<ActivityListSkeleton count={3} />
</Box>
</AppShell>
</ProtectedRoute>
@@ -545,9 +555,7 @@ export default function DiaperTrackPage() {
</Box>
{diapersLoading ? (
<Box sx={{ display: 'flex', justifyContent: 'center', py: 4 }}>
<CircularProgress size={30} />
</Box>
<ActivityListSkeleton count={3} />
) : recentDiapers.length === 0 ? (
<Box sx={{ textAlign: 'center', py: 4 }}>
<Typography variant="body2" color="text.secondary">

View File

@@ -49,6 +49,7 @@ import { useAuth } from '@/lib/auth/AuthContext';
import { trackingApi, Activity } from '@/lib/api/tracking';
import { childrenApi, Child } from '@/lib/api/children';
import { VoiceInputButton } from '@/components/voice/VoiceInputButton';
import { FormSkeleton, ActivityListSkeleton } from '@/components/common/LoadingSkeletons';
import { motion } from 'framer-motion';
import { formatDistanceToNow } from 'date-fns';
@@ -308,8 +309,17 @@ function FeedingTrackPage() {
return (
<ProtectedRoute>
<AppShell>
<Box sx={{ display: 'flex', justifyContent: 'center', py: 8 }}>
<CircularProgress />
<Box>
<Typography variant="h4" fontWeight="600" sx={{ mb: 3 }}>
Track Feeding
</Typography>
<Paper sx={{ p: 3, mb: 3 }}>
<FormSkeleton />
</Paper>
<Typography variant="h6" fontWeight="600" sx={{ mb: 2 }}>
Recent Feedings
</Typography>
<ActivityListSkeleton count={3} />
</Box>
</AppShell>
</ProtectedRoute>

View File

@@ -24,6 +24,7 @@ import {
Chip,
Snackbar,
} from '@mui/material';
import { FormSkeleton, ActivityListSkeleton } from '@/components/common/LoadingSkeletons';
import {
ArrowBack,
Refresh,
@@ -320,8 +321,17 @@ export default function SleepTrackPage() {
return (
<ProtectedRoute>
<AppShell>
<Box sx={{ display: 'flex', justifyContent: 'center', py: 8 }}>
<CircularProgress />
<Box>
<Typography variant="h4" fontWeight="600" sx={{ mb: 3 }}>
Track Sleep
</Typography>
<Paper sx={{ p: 3, mb: 3 }}>
<FormSkeleton />
</Paper>
<Typography variant="h6" fontWeight="600" sx={{ mb: 2 }}>
Recent Sleep Activities
</Typography>
<ActivityListSkeleton count={3} />
</Box>
</AppShell>
</ProtectedRoute>
@@ -536,9 +546,7 @@ export default function SleepTrackPage() {
</Box>
{sleepsLoading ? (
<Box sx={{ display: 'flex', justifyContent: 'center', py: 4 }}>
<CircularProgress size={30} />
</Box>
<ActivityListSkeleton count={3} />
) : recentSleeps.length === 0 ? (
<Box sx={{ textAlign: 'center', py: 4 }}>
<Typography variant="body2" color="text.secondary">