'use client'; import { useState, useEffect } from 'react'; import { Box, Typography, Grid, Paper, Button, Avatar, Chip, CircularProgress, Alert, IconButton, Divider, Snackbar, Container, List, ListItem, ListItemAvatar, ListItemText, Card, CardContent, Select, MenuItem, FormControl, InputLabel, } from '@mui/material'; import { PersonAdd, ContentCopy, People, Delete, GroupAdd, Home } from '@mui/icons-material'; import { useAuth } from '@/lib/auth/AuthContext'; import { AppShell } from '@/components/layouts/AppShell/AppShell'; import { ProtectedRoute } from '@/components/common/ProtectedRoute'; import { familiesApi, Family, FamilyMember, InviteMemberData, JoinFamilyData } from '@/lib/api/families'; import { InviteMemberDialog } from '@/components/family/InviteMemberDialog'; import { JoinFamilyDialog } from '@/components/family/JoinFamilyDialog'; import { RemoveMemberDialog } from '@/components/family/RemoveMemberDialog'; import { RoleInvitesSection } from '@/components/family/RoleInvitesSection'; import { motion } from 'framer-motion'; import { useTranslation } from '@/hooks/useTranslation'; import { useSelectedFamily } from '@/hooks/useSelectedFamily'; export default function FamilyPage() { const { t } = useTranslation('family'); const { user, refreshUser } = useAuth(); const { familyId, familyRole, selectedIndex, setSelectedIndex, userFamilies, hasMultipleFamilies } = useSelectedFamily(); const [family, setFamily] = useState(null); const [members, setMembers] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(''); const [inviteDialogOpen, setInviteDialogOpen] = useState(false); const [joinDialogOpen, setJoinDialogOpen] = useState(false); const [removeDialogOpen, setRemoveDialogOpen] = useState(false); const [memberToRemove, setMemberToRemove] = useState(null); const [actionLoading, setActionLoading] = useState(false); const [snackbar, setSnackbar] = useState({ open: false, message: '' }); const [familyNames, setFamilyNames] = useState>({}); // Check if current user is a parent (has admin permissions) const isParent = familyRole === 'parent'; useEffect(() => { if (familyId) { fetchFamilyData(); } else { setLoading(false); setError(t('messages.noFamilyFound')); } }, [familyId, selectedIndex]); const fetchFamilyData = async () => { if (!familyId) return; try { setLoading(true); setError(''); const [familyData, membersData] = await Promise.all([ familiesApi.getFamily(familyId), familiesApi.getFamilyMembers(familyId), ]); setFamily(familyData); setMembers(membersData); // Cache the family name setFamilyNames(prev => ({ ...prev, [familyId]: familyData.name })); } catch (err: any) { console.error('Failed to fetch family data:', err); setError(err.response?.data?.message || t('messages.failedToLoad')); } finally { setLoading(false); } }; const handleCopyCode = async () => { if (!family?.shareCode) return; try { await navigator.clipboard.writeText(family.shareCode); setSnackbar({ open: true, message: t('messages.shareCodeCopied') }); } catch (err) { setSnackbar({ open: true, message: t('messages.shareCodeCopyFailed') }); } }; const handleGenerateShareCode = async () => { if (!familyId) return; try { setActionLoading(true); const result = await familiesApi.generateShareCode(familyId); // Update family with new code if (family) { setFamily({ ...family, shareCode: result.code }); } // Copy to clipboard await navigator.clipboard.writeText(result.code); const expiryDate = new Date(result.expiresAt).toLocaleDateString(); setSnackbar({ open: true, message: `New share code generated: ${result.code} (expires ${expiryDate}). Copied to clipboard!`, }); } catch (err: any) { console.error('Failed to generate share code:', err); setSnackbar({ open: true, message: err.response?.data?.message || 'Failed to generate share code', }); } finally { setActionLoading(false); } }; const handleInviteMember = async (data: InviteMemberData) => { if (!familyId) { throw new Error(t('messages.noFamilyId')); } try { setActionLoading(true); await familiesApi.inviteMember(familyId, data); setSnackbar({ open: true, message: t('messages.invitationSent') }); await fetchFamilyData(); setInviteDialogOpen(false); } catch (err: any) { console.error('Failed to invite member:', err); throw new Error(err.response?.data?.message || t('messages.failedToInvite')); } finally { setActionLoading(false); } }; const handleJoinFamily = async (data: JoinFamilyData) => { try { setActionLoading(true); await familiesApi.joinFamily(data); setSnackbar({ open: true, message: t('messages.joinedFamily') }); await refreshUser(); await fetchFamilyData(); setJoinDialogOpen(false); } catch (err: any) { console.error('Failed to join family:', err); throw new Error(err.response?.data?.message || t('messages.failedToJoin')); } finally { setActionLoading(false); } }; const handleRemoveClick = (member: FamilyMember) => { setMemberToRemove(member); setRemoveDialogOpen(true); }; const handleRemoveConfirm = async () => { if (!familyId || !memberToRemove) return; try { setActionLoading(true); await familiesApi.removeMember(familyId, memberToRemove.userId); setSnackbar({ open: true, message: t('messages.memberRemoved') }); await fetchFamilyData(); setRemoveDialogOpen(false); setMemberToRemove(null); } catch (err: any) { console.error('Failed to remove member:', err); setError(err.response?.data?.message || t('messages.failedToRemove')); } finally { setActionLoading(false); } }; const getRoleColor = (role: string): 'primary' | 'secondary' | 'default' | 'success' | 'warning' | 'info' => { switch (role) { case 'parent': return 'primary'; case 'caregiver': return 'secondary'; case 'viewer': return 'info'; default: return 'default'; } }; const isCurrentUser = (userId: string) => { return user?.id === userId; }; return ( {t('pageTitle')} {t('pageSubtitle')} {isParent && ( )} {/* Family Selector - show if user has multiple families */} {hasMultipleFamilies && ( Select Family )} {error && ( setError('')}> {error} )} {loading ? ( ) : ( {/* Family Share Code */} {family && ( {t('shareCode.title')} {t('shareCode.description')} {isParent && ( )} )} {/* Family Members */} {t('members.title', { count: members.length })} {members.length === 0 ? ( {t('members.noMembers')} {t('members.noMembersDescription')} {isParent && ( )} ) : ( {members.map((member, index) => { const memberName = member.user?.name || t('placeholders.unknownUser'); return ( {memberName[0]} {memberName} {isCurrentUser(member.userId) && ( )} } secondary={member.user?.email || t('placeholders.noEmail')} primaryTypographyProps={{ fontWeight: 500 }} /> {isParent && !isCurrentUser(member.userId) && ( handleRemoveClick(member)} color="error" aria-label={t('members.removeAriaLabel', { name: memberName })} > )} ); })} )} {/* Role-Based Invites - Only visible to parents */} {isParent && family && ( setSnackbar({ open: true, message })} onError={(message) => setError(message)} /> )} )} setInviteDialogOpen(false)} onSubmit={handleInviteMember} isLoading={actionLoading} /> setJoinDialogOpen(false)} onSubmit={handleJoinFamily} isLoading={actionLoading} /> setRemoveDialogOpen(false)} onConfirm={handleRemoveConfirm} memberName={memberToRemove?.user?.name || ''} isLoading={actionLoading} /> setSnackbar({ ...snackbar, open: false })} message={snackbar.message} /> ); }