package com.keeply.pantry.ui import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.Add import androidx.compose.material.icons.outlined.Edit import androidx.compose.material.icons.outlined.Groups import androidx.compose.material.icons.outlined.Logout import androidx.compose.material.icons.outlined.MailOutline import androidx.compose.material.icons.outlined.Shield import androidx.compose.material3.AlertDialog import androidx.compose.material3.Button import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedButton import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.dp import com.keeply.pantry.KeeplyViewModel import com.keeply.pantry.data.Household import com.keeply.pantry.data.HouseholdMember import com.keeply.pantry.data.formatDate import com.keeply.pantry.ui.theme.Ink import com.keeply.pantry.ui.theme.KeeplyGreen import com.keeply.pantry.ui.theme.KeeplyGreenSoft import com.keeply.pantry.ui.theme.Muted import com.keeply.pantry.ui.theme.Warning import com.keeply.pantry.ui.theme.WarningSoft @Composable fun HouseholdsScreen(viewModel: KeeplyViewModel) { val households = viewModel.households.data.orEmpty() val isSiteAdmin = viewModel.user?.isSiteAdmin == true var editing by remember { mutableStateOf(null) } var editorOpen by remember { mutableStateOf(false) } var inviting by remember { mutableStateOf(null) } var leaving by remember { mutableStateOf(null) } LazyColumn( Modifier.fillMaxSize().padding(horizontal = 18.dp), contentPadding = androidx.compose.foundation.layout.PaddingValues(top = 24.dp, bottom = 80.dp), verticalArrangement = Arrangement.spacedBy(15.dp), ) { item { PageHeader( eyebrow = "Pantries work better together", title = "Households", description = "Manage shared spaces and the people who keep them running.", action = if (isSiteAdmin) { { Button(onClick = { editing = null; viewModel.clearActionError(); editorOpen = true }) { Icon(Icons.Outlined.Add, null, Modifier.size(18.dp)); Spacer(Modifier.width(6.dp)); Text("New") } } } else null, ) } if (!isSiteAdmin) { item { Surface(color = KeeplyGreenSoft, shape = RoundedCornerShape(13.dp), modifier = Modifier.fillMaxWidth()) { Row(Modifier.padding(15.dp), verticalAlignment = Alignment.CenterVertically) { Icon(Icons.Outlined.Shield, null, tint = KeeplyGreen) Spacer(Modifier.width(11.dp)) Column { Text("Your household access is managed safely", fontWeight = FontWeight.SemiBold) Text("Household admins can update details and add existing Keeply users.", color = Muted, style = MaterialTheme.typography.bodySmall) } } } } } when { viewModel.households.loading -> item { LoadingPane("Loading households…") } viewModel.households.error != null -> item { EmptyPane("Households are unavailable", viewModel.households.error.orEmpty(), "Try again") { viewModel.loadHouseholds(true) } } households.isEmpty() -> item { EmptyPane( "No households yet", if (isSiteAdmin) "Create a household, then add the people who share it." else "A site administrator can create a household and add you to it.", if (isSiteAdmin) "Create household" else null, if (isSiteAdmin) ({ editing = null; editorOpen = true }) else null, ) } else -> items(households, key = { it.id }) { household -> HouseholdCard( household = household, canManage = isSiteAdmin || household.isCurrentUserHouseholdAdmin, onEdit = { editing = household; viewModel.clearActionError(); editorOpen = true }, onInvite = { inviting = household; viewModel.clearActionError() }, onLeave = { leaving = household; viewModel.clearActionError() }, ) } } } if (editorOpen) { HouseholdEditorDialog( household = editing, working = viewModel.working, error = viewModel.actionError, onSave = { name, description -> viewModel.saveHousehold(editing, name, description) { editorOpen = false } }, onDismiss = { editorOpen = false }, ) } inviting?.let { household -> InviteMemberDialog( household = household, working = viewModel.working, error = viewModel.actionError, onInvite = { email -> viewModel.inviteMember(household, email) { inviting = null } }, onDismiss = { inviting = null }, ) } leaving?.let { household -> ConfirmActionDialog( title = "Leave this household?", message = "You will lose access to ${household.name} and its shared search results.", confirmLabel = "Leave household", working = viewModel.working, onConfirm = { viewModel.leaveHousehold(household) { leaving = null } }, onDismiss = { leaving = null }, ) } } @Composable private fun HouseholdCard( household: Household, canManage: Boolean, onEdit: () -> Unit, onInvite: () -> Unit, onLeave: () -> Unit, ) { KeeplyCard { Column { Row(Modifier.fillMaxWidth().padding(17.dp), verticalAlignment = Alignment.Top) { Surface(Modifier.size(48.dp), shape = RoundedCornerShape(13.dp), color = KeeplyGreenSoft) { androidx.compose.foundation.layout.Box(contentAlignment = Alignment.Center) { Icon(Icons.Outlined.Groups, null, tint = KeeplyGreen) } } Spacer(Modifier.width(13.dp)) Column(Modifier.weight(1f)) { SectionEyebrow("Established ${formatDate(household.createdAt)}") Spacer(Modifier.height(4.dp)) Text(household.name, style = MaterialTheme.typography.titleLarge, color = Ink) Text(household.description ?: "A shared place for a well-kept kitchen.", color = Muted, style = MaterialTheme.typography.bodySmall) } if (household.isCurrentUserHouseholdAdmin) { Surface(color = WarningSoft, contentColor = Warning, shape = RoundedCornerShape(50)) { Text("You manage this", Modifier.padding(horizontal = 8.dp, vertical = 5.dp), style = MaterialTheme.typography.labelSmall) } } } FieldDivider() Row(Modifier.fillMaxWidth().padding(horizontal = 17.dp, vertical = 12.dp), verticalAlignment = Alignment.CenterVertically) { Text("${household.members.size} ${if (household.members.size == 1) "member" else "members"}", fontWeight = FontWeight.SemiBold, modifier = Modifier.weight(1f)) if (canManage) TextButton(onClick = onInvite) { Icon(Icons.Outlined.MailOutline, null, Modifier.size(17.dp)); Spacer(Modifier.width(5.dp)); Text("Add member") } } Column(Modifier.padding(horizontal = 17.dp), verticalArrangement = Arrangement.spacedBy(7.dp)) { if (household.members.isEmpty()) Text("No members have been added yet.", color = Muted, style = MaterialTheme.typography.bodySmall) household.members.forEach { MemberRow(it) } } Spacer(Modifier.height(13.dp)) FieldDivider() Row(Modifier.fillMaxWidth().padding(horizontal = 17.dp, vertical = 10.dp), verticalAlignment = Alignment.CenterVertically) { Text("Managed by ${household.adminEmail}", color = Muted, style = MaterialTheme.typography.bodySmall, maxLines = 1, overflow = TextOverflow.Ellipsis, modifier = Modifier.weight(1f)) if (canManage) OutlinedButton(onClick = onEdit) { Icon(Icons.Outlined.Edit, null, Modifier.size(16.dp)); Spacer(Modifier.width(5.dp)); Text("Edit") } if (!household.isCurrentUserHouseholdAdmin) TextButton(onClick = onLeave) { Icon(Icons.Outlined.Logout, null, Modifier.size(16.dp)); Spacer(Modifier.width(5.dp)); Text("Leave") } } } } } @Composable private fun MemberRow(member: HouseholdMember) { Surface(color = Color(0xFFFBFCFA), shape = RoundedCornerShape(11.dp), modifier = Modifier.fillMaxWidth()) { Row(Modifier.padding(10.dp), verticalAlignment = Alignment.CenterVertically) { InitialsAvatar(member.initials) Spacer(Modifier.width(10.dp)) Column(Modifier.weight(1f)) { Text(member.displayName, fontWeight = FontWeight.SemiBold, maxLines = 1, overflow = TextOverflow.Ellipsis) Text(member.email, color = Muted, style = MaterialTheme.typography.bodySmall, maxLines = 1, overflow = TextOverflow.Ellipsis) } if (member.isHouseholdAdmin) Text("Household admin", color = Warning, style = MaterialTheme.typography.labelSmall) } } } @Composable private fun HouseholdEditorDialog( household: Household?, working: Boolean, error: String?, onSave: (String, String?) -> Unit, onDismiss: () -> Unit, ) { var name by remember(household?.id) { mutableStateOf(household?.name.orEmpty()) } var description by remember(household?.id) { mutableStateOf(household?.description.orEmpty()) } AlertDialog( onDismissRequest = { if (!working) onDismiss() }, title = { Column { SectionEyebrow("Shared pantry"); Spacer(Modifier.height(4.dp)); Text(if (household == null) "Create a household" else "Edit household") } }, text = { Column(verticalArrangement = Arrangement.spacedBy(12.dp)) { OutlinedTextField(name, { name = it }, label = { Text("Household name") }, placeholder = { Text("e.g. The Morgan household") }, singleLine = true, modifier = Modifier.fillMaxWidth()) OutlinedTextField(description, { description = it }, label = { Text("Description (optional)") }, minLines = 3, modifier = Modifier.fillMaxWidth()) ErrorText(error) } }, confirmButton = { Button(onClick = { onSave(name.trim(), description.trim().ifBlank { null }) }, enabled = !working && name.isNotBlank()) { if (working) CircularProgressIndicator(Modifier.size(17.dp), color = Color.White, strokeWidth = 2.dp) else Text(if (household == null) "Create household" else "Save changes") } }, dismissButton = { TextButton(onClick = onDismiss, enabled = !working) { Text("Cancel") } }, ) } @Composable private fun InviteMemberDialog( household: Household, working: Boolean, error: String?, onInvite: (String) -> Unit, onDismiss: () -> Unit, ) { var email by remember(household.id) { mutableStateOf("") } AlertDialog( onDismissRequest = { if (!working) onDismiss() }, title = { Column { SectionEyebrow(household.name); Spacer(Modifier.height(4.dp)); Text("Add a household member") } }, text = { Column(verticalArrangement = Arrangement.spacedBy(12.dp)) { Text("Invite an existing Keeply user using the email address they use to sign in.", color = Muted) OutlinedTextField( email, { email = it }, label = { Text("Email address") }, singleLine = true, keyboardOptions = androidx.compose.foundation.text.KeyboardOptions(keyboardType = KeyboardType.Email), modifier = Modifier.fillMaxWidth(), ) ErrorText(error) } }, confirmButton = { Button(onClick = { onInvite(email.trim()) }, enabled = !working && email.isNotBlank()) { if (working) CircularProgressIndicator(Modifier.size(17.dp), color = Color.White, strokeWidth = 2.dp) else Text("Add member") } }, dismissButton = { TextButton(onClick = onDismiss, enabled = !working) { Text("Cancel") } }, ) }