'use client'; import { useState, useEffect } from 'react'; import { Box, Typography, Button, Paper, TextField, FormControl, InputLabel, Select, MenuItem, IconButton, Alert, CircularProgress, Card, CardContent, Dialog, DialogTitle, DialogContent, DialogContentText, DialogActions, Chip, Snackbar, ToggleButtonGroup, ToggleButton, FormLabel, } from '@mui/material'; import { FormSkeleton, ActivityListSkeleton } from '@/components/common/LoadingSkeletons'; import { ArrowBack, Refresh, Save, Delete, BabyChangingStation, Warning, CheckCircle, ChildCare, Add, } from '@mui/icons-material'; import { useRouter, useSearchParams } from 'next/navigation'; import { AppShell } from '@/components/layouts/AppShell/AppShell'; import { ProtectedRoute } from '@/components/common/ProtectedRoute'; import { useAuth } from '@/lib/auth/AuthContext'; import { trackingApi, Activity } from '@/lib/api/tracking'; import { childrenApi, Child } from '@/lib/api/children'; import { motion } from 'framer-motion'; import { useLocalizedDate } from '@/hooks/useLocalizedDate'; import { useTranslation } from '@/hooks/useTranslation'; import { useDispatch, useSelector } from 'react-redux'; import { fetchChildren, selectChild, selectSelectedChild, childrenSelectors } from '@/store/slices/childrenSlice'; import { AppDispatch, RootState } from '@/store/store'; import ChildSelector from '@/components/common/ChildSelector'; interface DiaperData { diaperType: 'wet' | 'dirty' | 'both' | 'dry'; conditions: string[]; hasRash: boolean; rashSeverity?: 'mild' | 'moderate' | 'severe'; } export default function DiaperTrackPage() { const router = useRouter(); const searchParams = useSearchParams(); const { user } = useAuth(); const { t } = useTranslation('tracking'); const { formatDistanceToNow, format } = useLocalizedDate(); const dispatch = useDispatch(); // Redux state const children = useSelector((state: RootState) => childrenSelectors.selectAll(state)); const selectedChild = useSelector(selectSelectedChild); const familyId = user?.families?.[0]?.familyId; // Local state const [selectedChildIds, setSelectedChildIds] = useState([]); // Diaper state const [timestamp, setTimestamp] = useState( format(new Date(), "yyyy-MM-dd'T'HH:mm") ); const [diaperType, setDiaperType] = useState<'wet' | 'dirty' | 'both' | 'dry'>('wet'); const [conditions, setConditions] = useState(['normal']); const [hasRash, setHasRash] = useState(false); const [rashSeverity, setRashSeverity] = useState<'mild' | 'moderate' | 'severe'>('mild'); // Common state const [notes, setNotes] = useState(''); const [recentDiapers, setRecentDiapers] = useState([]); const [loading, setLoading] = useState(false); const [diapersLoading, setDiapersLoading] = useState(false); const [error, setError] = useState(null); const [successMessage, setSuccessMessage] = useState(null); // Delete confirmation dialog const [deleteDialogOpen, setDeleteDialogOpen] = useState(false); const [activityToDelete, setActivityToDelete] = useState(null); const availableConditions = [ 'normal', 'soft', 'hard', 'watery', 'mucus', 'blood', ]; // Load children from Redux useEffect(() => { if (familyId && children.length === 0) { dispatch(fetchChildren(familyId)); } }, [familyId, dispatch, children.length]); // Sync selectedChildIds with Redux selectedChild useEffect(() => { if (selectedChild?.id) { setSelectedChildIds([selectedChild.id]); } }, [selectedChild]); // Load recent diapers when child is selected useEffect(() => { if (selectedChild?.id) { loadRecentDiapers(); } }, [selectedChild?.id]); // Pre-fill form from URL parameters (voice command) useEffect(() => { const type = searchParams.get('type'); const timestampParam = searchParams.get('timestamp'); const notesParam = searchParams.get('notes'); const rashParam = searchParams.get('rash'); const colorParam = searchParams.get('color'); const consistencyParam = searchParams.get('consistency'); if (type) { // Map backend type values to frontend diaperType const typeMap: Record = { 'wet': 'wet', 'dirty': 'dirty', 'both': 'both', 'dry': 'dry', }; if (type in typeMap) { setDiaperType(typeMap[type]); } } if (timestampParam) { try { const date = new Date(timestampParam); setTimestamp(format(date, "yyyy-MM-dd'T'HH:mm")); } catch (e) { console.warn('[Diaper] Invalid timestamp from URL:', timestampParam); } } if (notesParam) { setNotes(notesParam); } if (rashParam) { setHasRash(rashParam === 'true'); } // Map color and consistency to conditions const newConditions: string[] = []; if (colorParam && availableConditions.includes(colorParam)) { newConditions.push(colorParam); } if (consistencyParam && availableConditions.includes(consistencyParam)) { newConditions.push(consistencyParam); } if (newConditions.length > 0) { setConditions(newConditions); } }, [searchParams]); const loadRecentDiapers = async () => { if (!selectedChild?.id) return; try { setDiapersLoading(true); const activities = await trackingApi.getActivities(selectedChild.id, 'diaper'); // Sort by timestamp descending and take last 10 const sorted = activities.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime() ).slice(0, 10); setRecentDiapers(sorted); } catch (err: any) { console.error('Failed to load recent diapers:', err); } finally { setDiapersLoading(false); } }; const setTimeNow = () => { setTimestamp(format(new Date(), "yyyy-MM-dd'T'HH:mm")); }; const handleConditionToggle = (condition: string) => { setConditions((prev) => { if (prev.includes(condition)) { // Remove condition, but ensure at least one remains if (prev.length === 1) return prev; return prev.filter((c) => c !== condition); } else { return [...prev, condition]; } }); }; const handleSubmit = async () => { if (!selectedChild?.id) { setError('Please select a child'); return; } // Validation if (!timestamp) { setError('Please enter timestamp'); return; } if (conditions.length === 0) { setError('Please select at least one condition'); return; } try { setLoading(true); setError(null); const data: DiaperData = { diaperType, conditions, hasRash, }; if (hasRash) { data.rashSeverity = rashSeverity; } await trackingApi.createActivity(selectedChild.id, { type: 'diaper', timestamp, data, notes: notes || undefined, }); setSuccessMessage(t('diaper.success')); // Reset form resetForm(); // Reload recent diapers await loadRecentDiapers(); } catch (err: any) { console.error('Failed to save diaper:', err); setError(err.response?.data?.message || 'Failed to save diaper change'); } finally { setLoading(false); } }; const resetForm = () => { setTimestamp(format(new Date(), "yyyy-MM-dd'T'HH:mm")); setDiaperType('wet'); setConditions(['normal']); setHasRash(false); setRashSeverity('mild'); setNotes(''); }; const handleDeleteClick = (activityId: string) => { setActivityToDelete(activityId); setDeleteDialogOpen(true); }; const handleDeleteConfirm = async () => { if (!activityToDelete) return; try { setLoading(true); await trackingApi.deleteActivity(activityToDelete); setSuccessMessage(t('diaper.deleted')); setDeleteDialogOpen(false); setActivityToDelete(null); await loadRecentDiapers(); } catch (err: any) { console.error('Failed to delete diaper:', err); setError(err.response?.data?.message || 'Failed to delete diaper change'); } finally { setLoading(false); } }; const getDiaperTypeColor = (type: string) => { switch (type) { case 'wet': return '#2196f3'; // blue case 'dirty': return '#795548'; // brown case 'both': return '#ff9800'; // orange case 'dry': return '#4caf50'; // green default: return '#757575'; // grey } }; const getDiaperTypeIcon = (type: string) => { switch (type) { case 'wet': return '💧'; case 'dirty': return '💩'; case 'both': return '💧💩'; case 'dry': return '✨'; default: return '🍼'; } }; const getDiaperDetails = (activity: Activity) => { const data = activity.data as DiaperData; const typeLabel = t(`diaper.types.${data.diaperType}`); const conditionsLabel = data.conditions?.map(c => t(`diaper.conditions.${c}`)).join(', ') || ''; let details = typeLabel; if (conditionsLabel) { details += ` - ${conditionsLabel}`; } if (data.hasRash) { details += ` - ${t('diaper.rash.title')} (${t(`diaper.rash.severities.${data.rashSeverity}`)})`; } return details; }; const getRashSeverityColor = (severity: string) => { switch (severity) { case 'mild': return 'warning'; case 'moderate': return 'error'; case 'severe': return 'error'; default: return 'default'; } }; const childrenLoading = useSelector((state: RootState) => state.children.loading); if (childrenLoading && children.length === 0) { return ( {t('diaper.title')} {t('diaper.title')} ); } if (!familyId || children.length === 0) { return ( {t('common.noChildrenAdded')} {t('common.noChildrenMessage')} ); } return ( router.back()} sx={{ mr: 2 }}> {t('diaper.title')} {error && ( setError(null)}> {error} )} {/* Child Selector */} {children.length > 0 && ( { setSelectedChildIds(childIds); if (childIds.length > 0) { dispatch(selectChild(childIds[0])); } }} mode="single" label={t('common.selectChild')} required /> )} {/* Main Form */} {/* Icon Header */} {/* Timestamp */} {t('diaper.time')} setTimestamp(e.target.value)} InputLabelProps={{ shrink: true }} /> {/* Diaper Type */} {t('diaper.type')} { if (value !== null) { setDiaperType(value); } }} fullWidth > 💧 {t('diaper.types.wet')} 💩 {t('diaper.types.dirty')} 💧💩 {t('diaper.types.both')} {t('diaper.types.dry')} {/* Condition Selector */} {t('diaper.conditions.title')} {availableConditions.map((condition) => ( handleConditionToggle(condition)} color={conditions.includes(condition) ? 'primary' : 'default'} variant={conditions.includes(condition) ? 'filled' : 'outlined'} sx={{ cursor: 'pointer' }} /> ))} {/* Rash Indicator */} {t('diaper.rash.title')} {/* Rash Severity */} {hasRash && ( {t('diaper.rash.alert')} {t('diaper.rash.severity')} )} {/* Notes Field */} setNotes(e.target.value)} sx={{ mb: 3 }} placeholder={t('diaper.placeholders.notes')} /> {/* Submit Button */} {/* Recent Diapers */} {t('diaper.recentDiapers')} {diapersLoading ? ( ) : recentDiapers.length === 0 ? ( {t('noEntries')} ) : ( {recentDiapers.map((activity, index) => { const data = activity.data as DiaperData; // Skip activities with invalid data structure if (!data || !data.diaperType) { console.warn('[Diaper] Activity missing diaperType:', activity); return null; } return ( {getDiaperTypeIcon(data.diaperType)} {t('diaper.title')} {data.hasRash && ( } label={`${t('diaper.rash.title')}: ${t(`diaper.rash.severities.${data.rashSeverity}`)}`} size="small" color={getRashSeverityColor(data.rashSeverity || 'mild') as any} /> )} {getDiaperDetails(activity)} {activity.notes && ( {activity.notes} )} handleDeleteClick(activity.id)} disabled={loading} > ); })} )} {/* Delete Confirmation Dialog */} setDeleteDialogOpen(false)} > {t('deleteEntry')} {t('confirmDelete')} {/* Success Snackbar */} setSuccessMessage(null)} message={successMessage} /> ); }