diff --git a/maternal-web/app/family/page.tsx b/maternal-web/app/family/page.tsx index a0130cc..a5dca0b 100644 --- a/maternal-web/app/family/page.tsx +++ b/maternal-web/app/family/page.tsx @@ -21,8 +21,12 @@ import { ListItemText, Card, CardContent, + Select, + MenuItem, + FormControl, + InputLabel, } from '@mui/material'; -import { PersonAdd, ContentCopy, People, Delete, GroupAdd } from '@mui/icons-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'; @@ -32,10 +36,12 @@ import { JoinFamilyDialog } from '@/components/family/JoinFamilyDialog'; import { RemoveMemberDialog } from '@/components/family/RemoveMemberDialog'; 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, selectedIndex, setSelectedIndex, userFamilies, hasMultipleFamilies } = useSelectedFamily(); const [family, setFamily] = useState(null); const [members, setMembers] = useState([]); const [loading, setLoading] = useState(true); @@ -46,9 +52,7 @@ export default function FamilyPage() { const [memberToRemove, setMemberToRemove] = useState(null); const [actionLoading, setActionLoading] = useState(false); const [snackbar, setSnackbar] = useState({ open: false, message: '' }); - - // Get familyId from user - const familyId = user?.families?.[0]?.familyId; + const [familyNames, setFamilyNames] = useState>({}); useEffect(() => { if (familyId) { @@ -57,7 +61,7 @@ export default function FamilyPage() { setLoading(false); setError(t('messages.noFamilyFound')); } - }, [familyId]); + }, [familyId, selectedIndex]); const fetchFamilyData = async () => { if (!familyId) return; @@ -71,6 +75,12 @@ export default function FamilyPage() { ]); 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')); @@ -90,6 +100,37 @@ export default function FamilyPage() { } }; + 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')); @@ -200,6 +241,40 @@ export default function FamilyPage() { + {/* Family Selector - show if user has multiple families */} + {hasMultipleFamilies && ( + + + + + Select Family + + + + + )} + {error && ( setError('')}> {error} @@ -222,7 +297,7 @@ export default function FamilyPage() { {t('shareCode.description')} - + + )} diff --git a/maternal-web/hooks/useSelectedFamily.ts b/maternal-web/hooks/useSelectedFamily.ts new file mode 100644 index 0000000..aa68430 --- /dev/null +++ b/maternal-web/hooks/useSelectedFamily.ts @@ -0,0 +1,47 @@ +import { useState, useEffect } from 'react'; +import { useAuth } from '@/lib/auth/AuthContext'; + +const SELECTED_FAMILY_KEY = 'selectedFamilyIndex'; + +/** + * Hook to manage selected family across the app + * Stores selection in localStorage for persistence + */ +export function useSelectedFamily() { + const { user } = useAuth(); + const userFamilies = user?.families || []; + + // Initialize from localStorage or default to 0 + const [selectedIndex, setSelectedIndex] = useState(() => { + if (typeof window === 'undefined') return 0; + const stored = localStorage.getItem(SELECTED_FAMILY_KEY); + return stored ? parseInt(stored, 10) : 0; + }); + + // Ensure selectedIndex is within bounds + useEffect(() => { + if (selectedIndex >= userFamilies.length && userFamilies.length > 0) { + setSelectedIndex(0); + } + }, [userFamilies.length, selectedIndex]); + + // Save to localStorage when changed + const setSelectedFamily = (index: number) => { + setSelectedIndex(index); + if (typeof window !== 'undefined') { + localStorage.setItem(SELECTED_FAMILY_KEY, index.toString()); + } + }; + + const familyId = userFamilies[selectedIndex]?.familyId || null; + const familyRole = userFamilies[selectedIndex]?.role || null; + + return { + familyId, + familyRole, + selectedIndex, + setSelectedIndex: setSelectedFamily, + userFamilies, + hasMultipleFamilies: userFamilies.length > 1, + }; +} diff --git a/maternal-web/package.json b/maternal-web/package.json index d932da4..221a777 100644 --- a/maternal-web/package.json +++ b/maternal-web/package.json @@ -3,7 +3,7 @@ "version": "0.1.0", "private": true, "scripts": { - "dev": "next dev -p 3005 -H 0.0.0.0", + "dev": "next dev -p 3030 -H 0.0.0.0", "build": "next build", "start": "next start", "lint": "next lint",