Implement error handling with ApiResultHandler on key screens

Applied the new error handling utilities to ResidencesScreen and
ResidenceDetailScreen, replacing inline error displays with popup dialogs.

Changes:
- ResidencesScreen: Replaced inline error UI with ApiResultHandler
  - Shows error dialog popup instead of error message in center of screen
  - Maintains all functionality while improving UX

- ResidenceDetailScreen: Comprehensive error handling implementation
  - Main residence loading uses ApiResultHandler with custom loading content
  - Generate report operation uses HandleErrors() extension
  - Delete residence operation uses HandleErrors() extension
  - All operations now show retry/cancel dialogs on error

Benefits:
- Consistent error handling UX across screens
- Users can retry failed operations without navigating away
- Cleaner code with less boilerplate error handling
- Error dialogs are non-blocking and dismissible

These screens now demonstrate the pattern for implementing error handling
across the rest of the app using the error handling utilities.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Trey t
2025-11-14 14:33:42 -06:00
parent 1d3a06f492
commit 225bdbc2bc
2 changed files with 40 additions and 112 deletions

View File

@@ -15,7 +15,9 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.mycrib.android.ui.components.AddNewTaskDialog
import com.mycrib.android.ui.components.ApiResultHandler
import com.mycrib.android.ui.components.CompleteTaskDialog
import com.mycrib.android.ui.components.HandleErrors
import com.mycrib.android.ui.components.ManageUsersDialog
import com.mycrib.android.ui.components.common.InfoCard
import com.mycrib.android.ui.components.residence.PropertyDetailItem
@@ -116,6 +118,12 @@ fun ResidenceDetailScreen(
}
// Handle generate report state
// Handle errors for generate report
generateReportState.HandleErrors(
onRetry = { residenceViewModel.generateMaintenanceReport(residenceId) },
errorTitle = "Failed to Generate Report"
)
LaunchedEffect(generateReportState) {
when (generateReportState) {
is ApiResult.Success -> {
@@ -124,15 +132,16 @@ fun ResidenceDetailScreen(
showReportSnackbar = true
residenceViewModel.resetGenerateReportState()
}
is ApiResult.Error -> {
reportMessage = (generateReportState as ApiResult.Error).message
showReportSnackbar = true
residenceViewModel.resetGenerateReportState()
}
else -> {}
}
}
// Handle errors for delete residence
deleteState.HandleErrors(
onRetry = { residenceViewModel.deleteResidence(residenceId) },
errorTitle = "Failed to Delete Property"
)
// Handle delete residence state
LaunchedEffect(deleteState) {
when (deleteState) {
@@ -140,11 +149,6 @@ fun ResidenceDetailScreen(
residenceViewModel.resetDeleteResidenceState()
onNavigateBack()
}
is ApiResult.Error -> {
reportMessage = (deleteState as ApiResult.Error).message
showReportSnackbar = true
residenceViewModel.resetDeleteResidenceState()
}
else -> {}
}
}
@@ -403,63 +407,29 @@ fun ResidenceDetailScreen(
}
}
) { paddingValues ->
when (residenceState) {
is ApiResult.Idle, is ApiResult.Loading -> {
Box(
modifier = Modifier
.fillMaxSize()
.padding(paddingValues),
contentAlignment = Alignment.Center
ApiResultHandler(
state = residenceState,
onRetry = {
residenceViewModel.getResidence(residenceId) { result ->
residenceState = result
}
},
modifier = Modifier.padding(paddingValues),
errorTitle = "Failed to Load Property",
loadingContent = {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
CircularProgressIndicator()
Text(
text = "Loading residence...",
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
CircularProgressIndicator()
Text(
text = "Loading residence...",
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
}
is ApiResult.Error -> {
Box(
modifier = Modifier
.fillMaxSize()
.padding(paddingValues),
contentAlignment = Alignment.Center
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
Icon(
Icons.Default.Error,
contentDescription = null,
modifier = Modifier.size(64.dp),
tint = MaterialTheme.colorScheme.error
)
Text(
text = "Error: ${(residenceState as ApiResult.Error).message}",
color = MaterialTheme.colorScheme.error
)
Button(
onClick = {
residenceViewModel.getResidence(residenceId) { result ->
residenceState = result
}
},
shape = RoundedCornerShape(12.dp)
) {
Text("Retry")
}
}
}
}
is ApiResult.Success -> {
val residence = (residenceState as ApiResult.Success).data
) { residence ->
LazyColumn(
modifier = Modifier
.fillMaxSize()
@@ -720,8 +690,6 @@ fun ResidenceDetailScreen(
}
}
}
else -> {}
}
}
}

View File

@@ -114,50 +114,12 @@ fun ResidencesScreen(
},
floatingActionButtonPosition = FabPosition.End
) { paddingValues ->
when (myResidencesState) {
is ApiResult.Idle, is ApiResult.Loading -> {
Box(
modifier = Modifier
.fillMaxSize()
.padding(paddingValues),
contentAlignment = Alignment.Center
) {
CircularProgressIndicator()
}
}
is ApiResult.Error -> {
Box(
modifier = Modifier
.fillMaxSize()
.padding(paddingValues),
contentAlignment = Alignment.Center
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
Icon(
Icons.Default.Error,
contentDescription = null,
modifier = Modifier.size(64.dp),
tint = MaterialTheme.colorScheme.error
)
Text(
text = "Error: ${(myResidencesState as ApiResult.Error).message}",
color = MaterialTheme.colorScheme.error,
style = MaterialTheme.typography.bodyLarge
)
Button(
onClick = { viewModel.loadMyResidences() },
shape = RoundedCornerShape(12.dp)
) {
Text("Retry")
}
}
}
}
is ApiResult.Success -> {
val response = (myResidencesState as ApiResult.Success).data
ApiResultHandler(
state = myResidencesState,
onRetry = { viewModel.loadMyResidences() },
modifier = Modifier.padding(paddingValues),
errorTitle = "Failed to Load Properties"
) { response ->
if (response.residences.isEmpty()) {
Box(
modifier = Modifier
@@ -436,8 +398,6 @@ fun ResidencesScreen(
}
}
}
else -> {}
}
}
}