import { useEffect, useState } from 'react' import { householdsApi } from '../../api/client.js' import { useAuth } from '../../context/AuthContext.jsx' import { formatDate } from '../../utils/searchUtils.js' import './AdminPage.css' const EMPTY_HOUSEHOLD_FORM = { name: '', description: '', } const EMPTY_USER_FORM = { firstName: '', lastName: '', email: '', password: '', confirmPassword: '', } function formatPersonName(person) { const fullName = [person.firstName, person.lastName] .filter(Boolean) .join(' ') return fullName || person.email || 'Unnamed person' } function AdminPage() { const { isAuthenticated, isSiteAdmin, register } = useAuth() const [loading, setLoading] = useState(false) const [errorMessage, setErrorMessage] = useState('') const [statusMessage, setStatusMessage] = useState('') const [households, setHouseholds] = useState([]) const [selectedHouseholdId, setSelectedHouseholdId] = useState('') const [editingHouseholdId, setEditingHouseholdId] = useState('') const [householdForm, setHouseholdForm] = useState(EMPTY_HOUSEHOLD_FORM) const [inviteEmail, setInviteEmail] = useState('') const [userForm, setUserForm] = useState(EMPTY_USER_FORM) const [createdUser, setCreatedUser] = useState(null) async function loadHouseholds(preferredSelectionId = '') { const response = await householdsApi.getHouseholds() const nextHouseholds = Array.isArray(response) ? response : [] setHouseholds(nextHouseholds) setSelectedHouseholdId(currentSelectionId => { const targetSelectionId = preferredSelectionId || currentSelectionId if (nextHouseholds.some(household => household.id === targetSelectionId)) { return targetSelectionId } return nextHouseholds[0]?.id ?? '' }) setEditingHouseholdId(currentEditingId => ( nextHouseholds.some(household => household.id === currentEditingId) ? currentEditingId : '' )) } useEffect(() => { let cancelled = false async function loadInitialHouseholds() { if (!isAuthenticated) { setHouseholds([]) setSelectedHouseholdId('') setEditingHouseholdId('') setHouseholdForm(EMPTY_HOUSEHOLD_FORM) setInviteEmail('') setUserForm(EMPTY_USER_FORM) setCreatedUser(null) setErrorMessage('') setStatusMessage('') return } setLoading(true) setErrorMessage('') try { const response = await householdsApi.getHouseholds() if (cancelled) return const nextHouseholds = Array.isArray(response) ? response : [] setHouseholds(nextHouseholds) setSelectedHouseholdId(nextHouseholds[0]?.id ?? '') } catch (error) { if (!cancelled) { setErrorMessage(error.message) } } finally { if (!cancelled) { setLoading(false) } } } loadInitialHouseholds() return () => { cancelled = true } }, [isAuthenticated]) const selectedHousehold = households.find(household => household.id === selectedHouseholdId) ?? null const editingHousehold = households.find(household => household.id === editingHouseholdId) ?? null const canManageSelectedHousehold = Boolean(selectedHousehold && (isSiteAdmin || selectedHousehold.isCurrentUserHouseholdAdmin)) const canSubmitHouseholdForm = editingHouseholdId ? Boolean(editingHousehold && (isSiteAdmin || editingHousehold.isCurrentUserHouseholdAdmin)) : isSiteAdmin const createdUserRoles = Array.isArray(createdUser?.roles) ? createdUser.roles : [] function resetHouseholdEditor() { setEditingHouseholdId('') setHouseholdForm(EMPTY_HOUSEHOLD_FORM) } function resetUserForm() { setUserForm(EMPTY_USER_FORM) } async function handleHouseholdSubmit(event) { event.preventDefault() const name = householdForm.name.trim() if (!name) { setErrorMessage('Household name is required.') return } if (!canSubmitHouseholdForm) { setErrorMessage(editingHouseholdId ? 'You can only edit households you administer.' : 'Only site admins can create households.') return } setLoading(true) setErrorMessage('') setStatusMessage('') try { const payload = { name, description: householdForm.description.trim() || null, } const result = editingHouseholdId ? await householdsApi.updateHousehold(editingHouseholdId, payload) : await householdsApi.createHousehold(payload) await loadHouseholds(result?.id ?? editingHouseholdId) resetHouseholdEditor() setStatusMessage(editingHouseholdId ? 'Household updated.' : 'Household created.') } catch (error) { setErrorMessage(error.message) } finally { setLoading(false) } } async function handleInviteSubmit(event) { event.preventDefault() const email = inviteEmail.trim() if (!selectedHouseholdId) { setErrorMessage('Select a household before sending an invite.') return } if (!email) { setErrorMessage('Invite email is required.') return } if (!canManageSelectedHousehold) { setErrorMessage('You can only invite users to households you administer.') return } setLoading(true) setErrorMessage('') setStatusMessage('') try { await householdsApi.inviteHouseholdMember(selectedHouseholdId, { email }) await loadHouseholds(selectedHouseholdId) setInviteEmail('') setStatusMessage('Invitation sent.') } catch (error) { setErrorMessage(error.message) } finally { setLoading(false) } } async function handleUserSubmit(event) { event.preventDefault() const email = userForm.email.trim() if (!isSiteAdmin) { setErrorMessage('Only site admins can create users from the admin page.') return } if (!email) { setErrorMessage('User email is required.') return } if (!userForm.password) { setErrorMessage('A password is required to create a user.') return } if (userForm.password !== userForm.confirmPassword) { setErrorMessage('Passwords do not match.') return } setLoading(true) setErrorMessage('') setStatusMessage('') try { const result = await register({ email, password: userForm.password, confirmPassword: userForm.confirmPassword, firstName: userForm.firstName.trim() || null, lastName: userForm.lastName.trim() || null, }) setCreatedUser(result?.user ?? null) resetUserForm() setStatusMessage(result?.message || 'User created.') } catch (error) { setErrorMessage(error.message) } finally { setLoading(false) } } async function handleLeaveHousehold(householdId) { const confirmed = window.confirm('Leave this household?') if (!confirmed) return setLoading(true) setErrorMessage('') setStatusMessage('') try { await householdsApi.leaveHousehold(householdId) await loadHouseholds(selectedHouseholdId === householdId ? '' : selectedHouseholdId) if (selectedHouseholdId === householdId) { setInviteEmail('') resetHouseholdEditor() } setStatusMessage('Household left.') } catch (error) { setErrorMessage(error.message) } finally { setLoading(false) } } return ( <>

Admin


{!isAuthenticated ? (
Sign in on the dashboard before using the household administration endpoints.
) : ( <> {errorMessage &&
{errorMessage}
} {statusMessage &&
{statusMessage}
} {loading &&
Processing admin request...
}

Household Summary

Households {households.length}
Managed Households {households.filter(household => household.isCurrentUserHouseholdAdmin || isSiteAdmin).length}
Members {households.reduce((count, household) => count + (household.members?.length ?? 0), 0)}

{editingHouseholdId ? 'Edit Household' : 'Create Household'}

{editingHouseholdId && ( )}
{!isSiteAdmin && !editingHouseholdId && (

Only site admins can create households. Household admins can still edit an existing household from the list.

)}
setHouseholdForm(current => ({ ...current, name: event.target.value }))} placeholder="Main Household" required />