diff --git a/app/src/main/java/com/m57/hermescontrol/ui/kanban/KanbanScreen.kt b/app/src/main/java/com/m57/hermescontrol/ui/kanban/KanbanScreen.kt index 821f66a3..ac3f87df 100644 --- a/app/src/main/java/com/m57/hermescontrol/ui/kanban/KanbanScreen.kt +++ b/app/src/main/java/com/m57/hermescontrol/ui/kanban/KanbanScreen.kt @@ -145,23 +145,25 @@ fun KanbanScreen( Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { Text("No boards found.") } + } else if (state.columns.isEmpty()) { + Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { + Text("No columns found for this board.") + } } else { - // Columns list: To Do, In Progress, Done - val columns = listOf("todo", "in_progress", "done") - val columnNames = - mapOf( - "todo" to "To Do", - "in_progress" to "In Progress", - "done" to "Done", - ) - LazyRow( modifier = Modifier.fillMaxSize(), contentPadding = PaddingValues(16.dp), horizontalArrangement = Arrangement.spacedBy(16.dp), ) { - items(columns) { col -> - val colTasks = state.tasks.filter { it.status.lowercase() == col } + items(state.columns) { column -> + val colName = column.name + val colTasks = + state.tasks.filter { + it.status.equals(colName, ignoreCase = true) + } + val columnIndex = state.columns.indexOf(column) + val prevColumn = state.columns.getOrNull(columnIndex - 1) + val nextColumn = state.columns.getOrNull(columnIndex + 1) Column( modifier = @@ -170,7 +172,7 @@ fun KanbanScreen( .fillMaxSize(), ) { Text( - text = "${columnNames[col]} (${colTasks.size})", + text = "${colName.replaceFirstChar { it.uppercase() }} (${colTasks.size})", style = MaterialTheme.typography.titleMedium, fontWeight = FontWeight.Bold, modifier = Modifier.padding(bottom = 8.dp), @@ -181,30 +183,16 @@ fun KanbanScreen( verticalArrangement = Arrangement.spacedBy(8.dp), ) { items(colTasks) { task -> - val onMoveLeft: (() -> Unit)? = - if (col != "todo") { - { - val prevStatus = if (col == "done") "in_progress" else "todo" - viewModel.moveTask(task, prevStatus) - } - } else { - null - } - - val onMoveRight: (() -> Unit)? = - if (col != "done") { - { - val nextStatus = if (col == "todo") "in_progress" else "done" - viewModel.moveTask(task, nextStatus) - } - } else { - null - } - TaskCard( task = task, - onMoveLeft = onMoveLeft, - onMoveRight = onMoveRight, + onMoveLeft = + prevColumn?.let { + { viewModel.moveTask(task, it.name) } + }, + onMoveRight = + nextColumn?.let { + { viewModel.moveTask(task, it.name) } + }, ) } } @@ -219,7 +207,7 @@ fun KanbanScreen( AddTaskDialog( onDismiss = { showAddTaskDialog = false }, onConfirm = { title, desc -> - viewModel.createTask(title, desc, "todo") + viewModel.createTask(title, desc, state.columns.firstOrNull()?.name ?: "todo") showAddTaskDialog = false }, ) diff --git a/app/src/main/java/com/m57/hermescontrol/ui/kanban/KanbanViewModel.kt b/app/src/main/java/com/m57/hermescontrol/ui/kanban/KanbanViewModel.kt index cddf044f..6967cbae 100644 --- a/app/src/main/java/com/m57/hermescontrol/ui/kanban/KanbanViewModel.kt +++ b/app/src/main/java/com/m57/hermescontrol/ui/kanban/KanbanViewModel.kt @@ -3,6 +3,7 @@ package com.m57.hermescontrol.ui.kanban import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.m57.hermescontrol.data.model.KanbanBoard +import com.m57.hermescontrol.data.model.KanbanColumn import com.m57.hermescontrol.data.model.KanbanTask import com.m57.hermescontrol.data.remote.ApiClient import kotlinx.coroutines.Dispatchers @@ -17,6 +18,7 @@ data class KanbanUiState( val isLoading: Boolean = false, val boards: List = emptyList(), val selectedBoard: KanbanBoard? = null, + val columns: List = emptyList(), val tasks: List = emptyList(), val errorMessage: String? = null, val toastMessage: String? = null, @@ -75,13 +77,15 @@ class KanbanViewModel : ViewModel() { ApiClient.hermesApi.getKanbanBoard() } if (response.isSuccessful) { - val allTasks = - response - .body() - ?.columns - ?.flatMap { it.tasks } - .orEmpty() - _uiState.update { it.copy(isLoading = false, tasks = allTasks) } + val body = response.body() + val allTasks = body?.columns?.flatMap { it.tasks }.orEmpty() + _uiState.update { + it.copy( + isLoading = false, + columns = body?.columns.orEmpty(), + tasks = allTasks, + ) + } } else { _uiState.update { it.copy( diff --git a/app/src/main/java/com/m57/hermescontrol/ui/system/SystemViewModel.kt b/app/src/main/java/com/m57/hermescontrol/ui/system/SystemViewModel.kt index 4b39f230..1f380e37 100644 --- a/app/src/main/java/com/m57/hermescontrol/ui/system/SystemViewModel.kt +++ b/app/src/main/java/com/m57/hermescontrol/ui/system/SystemViewModel.kt @@ -33,26 +33,21 @@ class SystemViewModel : ViewModel() { withContext(Dispatchers.IO) { ApiClient.hermesApi.getSystemStats() } - val doctorResponse = - withContext(Dispatchers.IO) { - ApiClient.hermesApi.runDoctor() - } - if (statsResponse.isSuccessful && doctorResponse.isSuccessful) { + if (statsResponse.isSuccessful) { _uiState.update { it.copy( isLoading = false, stats = statsResponse.body(), - doctorReport = doctorResponse.body(), ) } + // Doctor is optional; many servers do not expose it. + loadDoctorReport() } else { _uiState.update { it.copy( isLoading = false, - errorMessage = - "Failed to load system data: HTTP " + - "${statsResponse.code()} / ${doctorResponse.code()}", + errorMessage = "Failed to load system stats: HTTP ${statsResponse.code()}", ) } } @@ -60,13 +55,29 @@ class SystemViewModel : ViewModel() { _uiState.update { it.copy( isLoading = false, - errorMessage = "Failed to load system data: ${e.message}", + errorMessage = "Failed to load system stats: ${e.message}", ) } } } } + private fun loadDoctorReport() { + viewModelScope.launch { + try { + val doctorResponse = + withContext(Dispatchers.IO) { + ApiClient.hermesApi.runDoctor() + } + if (doctorResponse.isSuccessful) { + _uiState.update { it.copy(doctorReport = doctorResponse.body()) } + } + } catch (_: Exception) { + // Ignore optional doctor failures. + } + } + } + fun triggerBackup() { viewModelScope.launch { try {