import { useEffect, useState } from 'react'
import { View, Text, ScrollView, StyleSheet, Alert } from 'react-native'
import { useAuth } from '../context/AuthContext.jsx'
import { householdsApi, usersApi } from '../api/client.js'
import { formatDate } from '../utils/searchUtils.js'
import { StatusBanner, Panel, SectionHeading, InputField, Btn, BtnRow, EmptyState, EntityRow, EntityMeta, EntityActions, FormNote } from '../components/ui.jsx'
import { colors, spacing, fontSize, radius } from '../theme.js'
const EMPTY_HOUSEHOLD_FORM = { name: '', description: '' }
const EMPTY_USER_FORM = { firstName: '', lastName: '', email: '', password: '', confirmPassword: '' }
function formatPersonName(p) {
const full = [p.firstName, p.lastName].filter(Boolean).join(' ')
return full || p.email || 'Unnamed person'
}
export default function AdminScreen() {
const { isAuthenticated, isSiteAdmin, register } = useAuth()
const [loading, setLoading] = useState(false)
const [error, setError] = useState('')
const [status, setStatus] = 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)
const [householdHistory, setHouseholdHistory] = useState([])
const [historyLoading, setHistoryLoading] = useState(false)
const [historyError, setHistoryError] = useState('')
async function loadHouseholds(preferredId = '') {
const r = await householdsApi.getHouseholds()
const next = Array.isArray(r) ? r : []
setHouseholds(next)
setSelectedHouseholdId(id => { const t = preferredId || id; return next.some(h => h.id === t) ? t : next[0]?.id ?? '' })
setEditingHouseholdId(id => next.some(h => h.id === id) ? id : '')
}
useEffect(() => {
let cancelled = false
async function init() {
if (!isAuthenticated) { setHouseholds([]); return }
setLoading(true); setError('')
try {
const r = await householdsApi.getHouseholds()
if (cancelled) return
const next = Array.isArray(r) ? r : []
setHouseholds(next); setSelectedHouseholdId(next[0]?.id ?? '')
} catch (e) { if (!cancelled) setError(e.message) }
finally { if (!cancelled) setLoading(false) }
}
init()
return () => { cancelled = true }
}, [isAuthenticated])
useEffect(() => {
let cancelled = false
async function loadHistory() {
if (!isAuthenticated || !selectedHouseholdId) { setHouseholdHistory([]); return }
setHistoryLoading(true); setHistoryError('')
try { const r = await householdsApi.getHouseholdHistory(selectedHouseholdId); if (!cancelled) setHouseholdHistory(Array.isArray(r) ? r : []) }
catch (e) { if (!cancelled) { setHouseholdHistory([]); setHistoryError(e.message) } }
finally { if (!cancelled) setHistoryLoading(false) }
}
loadHistory()
return () => { cancelled = true }
}, [isAuthenticated, selectedHouseholdId])
const selectedHousehold = households.find(h => h.id === selectedHouseholdId) ?? null
const editingHousehold = households.find(h => h.id === editingHouseholdId) ?? null
const canManageSelected = Boolean(selectedHousehold && (isSiteAdmin || selectedHousehold.isCurrentUserHouseholdAdmin))
const canSubmitForm = editingHouseholdId ? Boolean(editingHousehold && (isSiteAdmin || editingHousehold.isCurrentUserHouseholdAdmin)) : isSiteAdmin
async function submitHousehold() {
const name = householdForm.name.trim()
if (!name) { setError('Household name is required.'); return }
if (!canSubmitForm) { setError(editingHouseholdId ? 'You can only edit households you administer.' : 'Only site admins can create households.'); return }
setLoading(true); setError(''); setStatus('')
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)
setEditingHouseholdId(''); setHouseholdForm(EMPTY_HOUSEHOLD_FORM)
setStatus(editingHouseholdId ? 'Household updated.' : 'Household created.')
} catch (e) { setError(e.message) }
finally { setLoading(false) }
}
async function submitInvite() {
const email = inviteEmail.trim()
if (!selectedHouseholdId) { setError('Select a household first.'); return }
if (!email) { setError('Email is required.'); return }
if (!canManageSelected) { setError('You can only invite users to households you administer.'); return }
setLoading(true); setError(''); setStatus('')
try { await householdsApi.inviteHouseholdMember(selectedHouseholdId, { email }); await loadHouseholds(selectedHouseholdId); setInviteEmail(''); setStatus('Invitation sent.') }
catch (e) { setError(e.message) }
finally { setLoading(false) }
}
async function submitCreateUser() {
if (!isSiteAdmin) { setError('Only site admins can create users.'); return }
const email = userForm.email.trim()
if (!email) { setError('Email is required.'); return }
if (!userForm.password) { setError('Password is required.'); return }
if (userForm.password !== userForm.confirmPassword) { setError('Passwords do not match.'); return }
setLoading(true); setError(''); setStatus('')
try {
const result = await register({ email, password: userForm.password, confirmPassword: userForm.confirmPassword, firstName: userForm.firstName.trim() || null, lastName: userForm.lastName.trim() || null })
try {
const users = await usersApi.getUsers()
const match = Array.isArray(users) ? users.find(u => u.email?.toLowerCase() === email.toLowerCase()) : null
setCreatedUser(match ?? { email, firstName: userForm.firstName.trim(), lastName: userForm.lastName.trim(), roles: [] })
} catch { setCreatedUser({ email, firstName: userForm.firstName.trim(), lastName: userForm.lastName.trim(), roles: [] }) }
setUserForm(EMPTY_USER_FORM); setStatus(result?.message || 'User created.')
} catch (e) { setError(e.message) }
finally { setLoading(false) }
}
async function handleLeave(id) {
Alert.alert('Leave household?', undefined, [
{ text: 'Cancel' },
{ text: 'Leave', style: 'destructive', onPress: async () => {
setLoading(true); setError(''); setStatus('')
try { await householdsApi.leaveHousehold(id); await loadHouseholds(selectedHouseholdId === id ? '' : selectedHouseholdId); setStatus('Household left.') }
catch (e) { setError(e.message) }
finally { setLoading(false) }
}},
])
}
if (!isAuthenticated) return (
Sign in to access admin features.
)
const totalMembers = households.reduce((n, h) => n + (h.members?.length ?? 0), 0)
const managedCount = households.filter(h => h.isCurrentUserHouseholdAdmin || isSiteAdmin).length
return (
{error ? {error} : null}
{status ? {status} : null}
{loading ? Processing... : null}
Households{households.length}
Managed{managedCount}
Members{totalMembers}
{ setEditingHouseholdId(''); setHouseholdForm(EMPTY_HOUSEHOLD_FORM) }} /> : null} />
{!isSiteAdmin && !editingHouseholdId && Only site admins can create households.}
setHouseholdForm(f => ({ ...f, name: v }))} placeholder="Main Household" />
setHouseholdForm(f => ({ ...f, description: v }))} placeholder="Shared pantry access" multiline />
{ setEditingHouseholdId(''); setHouseholdForm(EMPTY_HOUSEHOLD_FORM) }} />
{selectedHousehold?.name || 'None selected'}} />
{!selectedHousehold ? Select a household to invite a member. : (
<>
{!canManageSelected && You can only invite users to households you administer.}
setInviteEmail('')} />
>
)}
{isSiteAdmin ? 'Creates a new account via the register endpoint.' : 'Only site admins can create users.'}
setUserForm(f => ({ ...f, firstName: v }))} placeholder="Jordan" editable={isSiteAdmin} />
setUserForm(f => ({ ...f, lastName: v }))} placeholder="Lee" editable={isSiteAdmin} />
setUserForm(f => ({ ...f, email: v }))} keyboardType="email-address" autoCapitalize="none" placeholder="new@example.com" editable={isSiteAdmin} />
setUserForm(f => ({ ...f, password: v }))} secureTextEntry placeholder="Password" editable={isSiteAdmin} />
setUserForm(f => ({ ...f, confirmPassword: v }))} secureTextEntry placeholder="Repeat password" editable={isSiteAdmin} />
setUserForm(EMPTY_USER_FORM)} disabled={!isSiteAdmin} />
{createdUser && (
{formatPersonName(createdUser)}
{createdUser.email}
{createdUser.id && ID: {createdUser.id}}
{(Array.isArray(createdUser.roles) && createdUser.roles.length > 0
? createdUser.roles
: ['No roles assigned']
).map((r, i) => {r})}
)}
{selectedHousehold?.name || 'None selected'}} />
{historyLoading ? Loading history... : null}
{historyError ? {historyError} : null}
{!selectedHousehold ? Select a household to see its history. :
householdHistory.length === 0 ? No history returned. :
householdHistory.map(entry => (
{entry.action}
{formatDate(entry.changedAt) || 'Unknown'} by {entry.changedByEmail || 'Unknown'}
{entry.description || 'No description.'}
{entry.affectedUserEmail && Affected: {entry.affectedUserEmail}}
))
}
{households.length} total} />
{households.length === 0 ? No households yet. : households.map(h => {
const canManage = isSiteAdmin || h.isCurrentUserHouseholdAdmin
return (
{h.name}
{h.isCurrentUserHouseholdAdmin && Household admin}
{h.description || 'No description.'}
Created: {formatDate(h.createdAt) || 'Not available'}
{(h.members ?? []).map(member => (
{formatPersonName(member)}
{member.email}
))}
setSelectedHouseholdId(h.id)} />
{canManage && { setSelectedHouseholdId(h.id); setEditingHouseholdId(h.id); setHouseholdForm({ name: h.name ?? '', description: h.description ?? '' }) }} />}
handleLeave(h.id)} />
)
})}
)
}
const s = StyleSheet.create({
container: { flex: 1, backgroundColor: colors.bg },
content: { padding: spacing.md },
authMsg: { padding: spacing.lg, textAlign: 'center', color: colors.textMuted, fontSize: fontSize.md },
statsGrid: { flexDirection: 'row', gap: spacing.sm },
statCard: { flex: 1, backgroundColor: colors.surfaceMuted, borderRadius: radius.sm, padding: spacing.sm, alignItems: 'center' },
statLabel: { fontSize: fontSize.xs, color: colors.textMuted },
statValue: { fontSize: fontSize.xl, fontWeight: '700', color: colors.text },
strongText: { fontSize: fontSize.md, fontWeight: '600', color: colors.text },
subtleText: { fontSize: fontSize.sm, color: colors.textMuted },
householdHeader: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' },
badgeRow: { flexDirection: 'row', gap: spacing.xs },
adminBadge: { backgroundColor: colors.accent + '22', borderRadius: radius.sm, paddingHorizontal: spacing.xs, paddingVertical: 2 },
badgeText: { fontSize: fontSize.xs, color: colors.accent },
memberRow: { paddingVertical: spacing.xs, borderTopWidth: 1, borderColor: colors.border, marginTop: spacing.xs },
memberName: { fontSize: fontSize.sm, fontWeight: '600', color: colors.text },
createdUserCard: { marginTop: spacing.sm, padding: spacing.sm, borderWidth: 1, borderColor: colors.border, borderRadius: radius.sm },
roleList: { flexDirection: 'row', flexWrap: 'wrap', gap: spacing.xs, marginTop: spacing.xs },
roleBadge: { backgroundColor: colors.chipNeutral, borderRadius: radius.sm, paddingHorizontal: spacing.xs, paddingVertical: 2 },
roleBadgeText: { fontSize: fontSize.xs, color: colors.textSoft },
})