diff --git a/android-app/app/src/main/java/com/korexlabs/dhinspeccion/ui/MobileHomeScreen.kt b/android-app/app/src/main/java/com/korexlabs/dhinspeccion/ui/MobileHomeScreen.kt index 3caec53..42226e5 100644 --- a/android-app/app/src/main/java/com/korexlabs/dhinspeccion/ui/MobileHomeScreen.kt +++ b/android-app/app/src/main/java/com/korexlabs/dhinspeccion/ui/MobileHomeScreen.kt @@ -11,79 +11,29 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.LazyRow import androidx.compose.foundation.lazy.items -import androidx.compose.material3.AssistChip +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Assignment +import androidx.compose.material.icons.filled.Refresh import androidx.compose.material3.Button -import androidx.compose.material3.Card import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.ElevatedCard +import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedButton +import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import com.korexlabs.dhinspeccion.MainViewModel -import com.korexlabs.dhinspeccion.data.DhRepository -import com.korexlabs.dhinspeccion.data.MobileInspectionOpenRepository -import com.korexlabs.dhinspeccion.data.MobilePlanningAsset import com.korexlabs.dhinspeccion.data.VisitSummary -import kotlinx.coroutines.launch @Composable fun MobileHomeScreen(model: MainViewModel) { val session = model.session ?: return - val context = LocalContext.current - val scope = rememberCoroutineScope() - val openRepository = remember(context) { MobileInspectionOpenRepository(context) } - - var showOpen by rememberSaveable { mutableStateOf(false) } - var areas by remember { mutableStateOf>(emptyList()) } - var operators by remember { mutableStateOf>(emptyList()) } - var selectedAreaId by rememberSaveable { mutableStateOf(null) } - var selectedCompanyId by rememberSaveable { mutableStateOf(null) } - var loadingContext by remember { mutableStateOf(false) } - var opening by remember { mutableStateOf(false) } - var localError by remember { mutableStateOf(null) } - - fun loadAreas() { - scope.launch { - loadingContext = true - localError = null - runCatching { openRepository.areas().data } - .onSuccess { areas = it } - .onFailure { localError = DhRepository.humanError(it) } - loadingContext = false - } - } - - fun chooseArea(areaId: String) { - selectedAreaId = areaId - selectedCompanyId = null - operators = emptyList() - scope.launch { - loadingContext = true - localError = null - runCatching { openRepository.operators(areaId).data } - .onSuccess { operators = it } - .onFailure { localError = DhRepository.humanError(it) } - loadingContext = false - } - } - - LaunchedEffect(showOpen) { - if (showOpen && areas.isEmpty()) loadAreas() - } Column(Modifier.fillMaxSize().padding(top = 28.dp)) { Row( @@ -91,16 +41,18 @@ fun MobileHomeScreen(model: MainViewModel) { verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.SpaceBetween, ) { - Column { + Column(Modifier.weight(1f)) { Text("Mis inspecciones", style = MaterialTheme.typography.headlineSmall, fontWeight = FontWeight.Bold) Text(session.displayName, style = MaterialTheme.typography.bodySmall) } Row { - OutlinedButton(onClick = { model.loadVisits() }, enabled = !model.busy && !opening) { + OutlinedButton(onClick = { model.loadVisits() }, enabled = !model.busy) { + Icon(Icons.Filled.Refresh, contentDescription = null) + Spacer(Modifier.width(6.dp)) Text("Actualizar") } Spacer(Modifier.width(8.dp)) - OutlinedButton(onClick = { model.logout() }, enabled = !opening) { Text("Salir") } + OutlinedButton(onClick = { model.logout() }) { Text("Salir") } } } @@ -110,80 +62,24 @@ fun MobileHomeScreen(model: MainViewModel) { ) { model.error?.let { Text(it, color = MaterialTheme.colorScheme.error) } model.notice?.let { Text(it, color = MaterialTheme.colorScheme.primary) } - localError?.let { Text(it, color = MaterialTheme.colorScheme.error) } - Button( - onClick = { - showOpen = !showOpen - localError = null - }, + Surface( modifier = Modifier.fillMaxWidth(), - enabled = !opening, + shape = MaterialTheme.shapes.large, + color = MaterialTheme.colorScheme.secondaryContainer, ) { - Text(if (showOpen) "Cancelar nueva inspección" else "Abrir inspección") - } - - if (showOpen) { - Card(Modifier.fillMaxWidth()) { - Column( - Modifier.fillMaxWidth().padding(14.dp), - verticalArrangement = Arrangement.spacedBy(10.dp), - ) { - Text("Abrir inspección en campo", style = MaterialTheme.typography.titleMedium, fontWeight = FontWeight.Bold) + Row( + Modifier.padding(14.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(10.dp), + ) { + Icon(Icons.Filled.Assignment, contentDescription = null, tint = MaterialTheme.colorScheme.secondary) + Column(Modifier.weight(1f)) { + Text("Trabajo planificado desde oficina", fontWeight = FontWeight.Bold) Text( - "Elegí el Área y la Operadora vigente. La inspección se crea autoasignada a vos y queda iniciada con la fecha y hora del servidor.", + "Acá aparecen las inspecciones asignadas desde el tablero. La visita se inicia únicamente al llegar al lugar.", style = MaterialTheme.typography.bodySmall, ) - - Text("1. Área", fontWeight = FontWeight.SemiBold) - if (loadingContext && areas.isEmpty()) CircularProgressIndicator() - LazyRow(horizontalArrangement = Arrangement.spacedBy(8.dp)) { - items(areas, key = { it.id }) { area -> - AssistChip( - onClick = { chooseArea(area.id) }, - label = { Text(if (area.id == selectedAreaId) "✓ ${area.name}" else area.name) }, - ) - } - } - - if (selectedAreaId != null) { - Text("2. Operadora", fontWeight = FontWeight.SemiBold) - if (loadingContext && operators.isEmpty()) { - CircularProgressIndicator() - } else if (operators.isEmpty()) { - Text("No hay una Operadora vigente para el Área seleccionada.", color = MaterialTheme.colorScheme.error) - } - LazyRow(horizontalArrangement = Arrangement.spacedBy(8.dp)) { - items(operators, key = { it.id }) { company -> - AssistChip( - onClick = { selectedCompanyId = company.id }, - label = { Text(if (company.id == selectedCompanyId) "✓ ${company.name}" else company.name) }, - ) - } - } - } - - Button( - onClick = { - val areaId = selectedAreaId ?: return@Button - val companyId = selectedCompanyId ?: return@Button - scope.launch { - opening = true - localError = null - runCatching { openRepository.open(areaId, companyId) } - .onSuccess { opened -> - showOpen = false - model.openVisit(opened.id) - } - .onFailure { localError = DhRepository.humanError(it) } - opening = false - } - }, - modifier = Modifier.fillMaxWidth(), - enabled = selectedAreaId != null && selectedCompanyId != null && !opening && !loadingContext, - ) { - Text(if (opening) "Abriendo…" else "Abrir inspección ahora") - } } } } @@ -192,8 +88,16 @@ fun MobileHomeScreen(model: MainViewModel) { if (model.busy && model.visits.isEmpty()) { Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { CircularProgressIndicator() } } else if (model.visits.isEmpty()) { - Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { - Text("No tenés inspecciones asignadas. Podés abrir una nueva desde esta tablet.") + Box(Modifier.fillMaxSize().padding(24.dp), contentAlignment = Alignment.Center) { + Column(horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.spacedBy(10.dp)) { + Icon(Icons.Filled.Assignment, contentDescription = null, tint = MaterialTheme.colorScheme.secondary) + Text("No tenés inspecciones asignadas", fontWeight = FontWeight.Bold) + Text( + "Cuando una inspección sea planificada y asignada desde el tablero, aparecerá en esta pantalla.", + style = MaterialTheme.typography.bodyMedium, + ) + Button(onClick = { model.loadVisits() }, enabled = !model.busy) { Text("Actualizar lista") } + } } } else { LazyColumn( @@ -211,21 +115,32 @@ fun MobileHomeScreen(model: MainViewModel) { @Composable private fun MobileVisitCard(visit: VisitSummary, onOpen: () -> Unit) { - Card(onClick = onOpen, modifier = Modifier.fillMaxWidth()) { - Column(Modifier.padding(14.dp), verticalArrangement = Arrangement.spacedBy(5.dp)) { + ElevatedCard(onClick = onOpen, modifier = Modifier.fillMaxWidth()) { + Column(Modifier.padding(15.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) { Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) { Text(visit.code, fontWeight = FontWeight.Bold) - Text(visit.status) + StatusPill(mobileVisitStatusLabel(visit.status)) } + Text(visit.scopeAsset?.name ?: visit.operationalArea?.name ?: "Alcance sin informar", fontWeight = FontWeight.SemiBold) Text(visit.operatorCompany?.name ?: "Operadora sin definir") - Text(visit.operationalArea?.name ?: "Área sin definir", style = MaterialTheme.typography.bodySmall) + visit.operationalArea?.name?.takeIf { it != visit.scopeAsset?.name }?.let { + Text("Área · $it", style = MaterialTheme.typography.bodySmall) + } visit.plannedStartAt?.let { - Text("Prevista: ${it.replace('T', ' ').take(16)}", style = MaterialTheme.typography.bodySmall) + Text("Planificada · ${it.replace('T', ' ').take(16)}", style = MaterialTheme.typography.bodySmall) } Text( - "Inventario: ${visit.assetCount} · Equipo inspector: ${visit.memberCount}", + "Inventario previsto: ${visit.assetCount} · Equipo inspector: ${visit.memberCount}", style = MaterialTheme.typography.bodySmall, ) } } } + +internal fun mobileVisitStatusLabel(status: String): String = when (status) { + "PLANNED" -> "Planificada" + "IN_PROGRESS" -> "En curso" + "CLOSED" -> "Cerrada" + "CANCELLED" -> "Cancelada" + else -> "Estado no reconocido" +}