diff --git a/composeApp/src/commonMain/kotlin/com/example/mycrib/models/CustomTask.kt b/composeApp/src/commonMain/kotlin/com/example/mycrib/models/CustomTask.kt index 21633e5..f827612 100644 --- a/composeApp/src/commonMain/kotlin/com/example/mycrib/models/CustomTask.kt +++ b/composeApp/src/commonMain/kotlin/com/example/mycrib/models/CustomTask.kt @@ -76,6 +76,7 @@ data class TasksByResidenceResponse( @SerialName("residence_id") val residenceId: String, val summary: TaskSummary, val tasks: List, + @SerialName("completed_tasks") val completedTasks: List = emptyList(), @SerialName("cancelled_tasks") val cancelledTasks: List = emptyList() ) diff --git a/composeApp/src/commonMain/kotlin/com/example/mycrib/ui/screens/ResidenceDetailScreen.kt b/composeApp/src/commonMain/kotlin/com/example/mycrib/ui/screens/ResidenceDetailScreen.kt index 0b8739c..93b9071 100644 --- a/composeApp/src/commonMain/kotlin/com/example/mycrib/ui/screens/ResidenceDetailScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/example/mycrib/ui/screens/ResidenceDetailScreen.kt @@ -1,5 +1,6 @@ package com.mycrib.android.ui.screens +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items @@ -47,6 +48,7 @@ fun ResidenceDetailScreen( var showCompleteDialog by remember { mutableStateOf(false) } var selectedTask by remember { mutableStateOf(null) } var showNewTaskDialog by remember { mutableStateOf(false) } + var showCompletedTasks by remember { mutableStateOf(false) } var showCancelledTasks by remember { mutableStateOf(false) } LaunchedEffect(residenceId) { @@ -392,7 +394,7 @@ fun ResidenceDetailScreen( } is ApiResult.Success -> { val taskData = (tasksState as ApiResult.Success).data - if (taskData.tasks.isEmpty() && taskData.cancelledTasks.isEmpty()) { + if (taskData.tasks.isEmpty() && taskData.completedTasks.isEmpty() && taskData.cancelledTasks.isEmpty()) { item { Card( modifier = Modifier.fillMaxWidth(), @@ -442,6 +444,56 @@ fun ResidenceDetailScreen( ) } + // Completed tasks section + if (taskData.completedTasks.isNotEmpty()) { + item { + Spacer(modifier = Modifier.height(8.dp)) + Row( + modifier = Modifier + .fillMaxWidth() + .clickable { showCompletedTasks = !showCompletedTasks } + .padding(vertical = 8.dp), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + Row(verticalAlignment = Alignment.CenterVertically) { + Icon( + Icons.Default.CheckCircle, + contentDescription = null, + tint = MaterialTheme.colorScheme.tertiary, + modifier = Modifier.size(28.dp) + ) + Spacer(modifier = Modifier.width(8.dp)) + Text( + text = "Completed Tasks (${taskData.completedTasks.size})", + style = MaterialTheme.typography.titleMedium, + fontWeight = FontWeight.Bold, + color = MaterialTheme.colorScheme.tertiary + ) + } + Icon( + if (showCompletedTasks) Icons.Default.KeyboardArrowUp else Icons.Default.KeyboardArrowDown, + contentDescription = null, + tint = MaterialTheme.colorScheme.onSurfaceVariant + ) + } + } + + if (showCompletedTasks) { + items(taskData.completedTasks) { task -> + TaskCard( + task = task, + onCompleteClick = null, + onEditClick = { + onNavigateToEditTask(task) + }, + onCancelClick = null, + onUncancelClick = null + ) + } + } + } + // Cancelled tasks section if (taskData.cancelledTasks.isNotEmpty()) { item { @@ -449,6 +501,7 @@ fun ResidenceDetailScreen( Row( modifier = Modifier .fillMaxWidth() + .clickable { showCancelledTasks = !showCancelledTasks } .padding(vertical = 8.dp), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically @@ -468,9 +521,11 @@ fun ResidenceDetailScreen( color = MaterialTheme.colorScheme.error ) } - TextButton(onClick = { showCancelledTasks = !showCancelledTasks }) { - Text(if (showCancelledTasks) "Hide" else "Show") - } + Icon( + if (showCancelledTasks) Icons.Default.KeyboardArrowUp else Icons.Default.KeyboardArrowDown, + contentDescription = null, + tint = MaterialTheme.colorScheme.onSurfaceVariant + ) } } diff --git a/iosApp/iosApp/Residence/ResidenceDetailView.swift b/iosApp/iosApp/Residence/ResidenceDetailView.swift index ebfbe8c..b694d4e 100644 --- a/iosApp/iosApp/Residence/ResidenceDetailView.swift +++ b/iosApp/iosApp/Residence/ResidenceDetailView.swift @@ -12,6 +12,7 @@ struct ResidenceDetailView: View { @State private var showEditResidence = false @State private var showEditTask = false @State private var selectedTaskForEdit: TaskDetail? + @State private var showCompletedTasks = false @State private var showCancelledTasks = false var body: some View { @@ -37,6 +38,7 @@ struct ResidenceDetailView: View { if let tasksResponse = tasksResponse { TasksSection( tasksResponse: tasksResponse, + showCompletedTasks: $showCompletedTasks, showCancelledTasks: $showCancelledTasks, onEditTask: { task in selectedTaskForEdit = task diff --git a/iosApp/iosApp/Subviews/Task/TasksSection.swift b/iosApp/iosApp/Subviews/Task/TasksSection.swift index b2a62fc..5eca42e 100644 --- a/iosApp/iosApp/Subviews/Task/TasksSection.swift +++ b/iosApp/iosApp/Subviews/Task/TasksSection.swift @@ -3,6 +3,7 @@ import ComposeApp struct TasksSection: View { let tasksResponse: TasksByResidenceResponse + @Binding var showCompletedTasks: Bool @Binding var showCancelledTasks: Bool let onEditTask: (TaskDetail) -> Void let onCancelTask: (TaskDetail) -> Void @@ -24,7 +25,7 @@ struct TasksSection: View { } } - if tasksResponse.tasks.isEmpty && tasksResponse.cancelledTasks.isEmpty { + if tasksResponse.tasks.isEmpty && tasksResponse.completedTasks.isEmpty && tasksResponse.cancelledTasks.isEmpty { EmptyTasksView() } else { ForEach(tasksResponse.tasks, id: \.id) { task in @@ -36,6 +37,38 @@ struct TasksSection: View { ) } + if !tasksResponse.completedTasks.isEmpty { + VStack(alignment: .leading, spacing: 12) { + HStack { + Label("Completed Tasks (\(tasksResponse.completedTasks.count))", systemImage: "checkmark.circle") + .font(.headline) + .foregroundColor(.green) + + Spacer() + + Image(systemName: showCompletedTasks ? "chevron.up" : "chevron.down") + .foregroundColor(.secondary) + .font(.caption) + } + .padding(.top, 8) + .contentShape(Rectangle()) + .onTapGesture { + showCompletedTasks.toggle() + } + + if showCompletedTasks { + ForEach(tasksResponse.completedTasks, id: \.id) { task in + TaskCard( + task: task, + onEdit: { onEditTask(task) }, + onCancel: nil, + onUncancel: nil + ) + } + } + } + } + if !tasksResponse.cancelledTasks.isEmpty { VStack(alignment: .leading, spacing: 12) { HStack { @@ -45,12 +78,15 @@ struct TasksSection: View { Spacer() - Button(showCancelledTasks ? "Hide" : "Show") { - showCancelledTasks.toggle() - } - .font(.subheadline) + Image(systemName: showCancelledTasks ? "chevron.up" : "chevron.down") + .foregroundColor(.secondary) + .font(.caption) } .padding(.top, 8) + .contentShape(Rectangle()) + .onTapGesture { + showCancelledTasks.toggle() + } if showCancelledTasks { ForEach(tasksResponse.cancelledTasks, id: \.id) { task in @@ -101,8 +137,30 @@ struct TasksSection: View { completions: [] ) ], + completedTasks: [ + TaskDetail( + id: 2, + residence: 1, + title: "Fix Leaky Faucet", + description: "Kitchen sink fixed", + category: TaskCategory(id: 2, name: "plumbing", description: "Plumbing tasks"), + priority: TaskPriority(id: 3, name: "high", displayName: "High", description: "High priority"), + frequency: TaskFrequency(id: 6, name: "once", displayName: "One Time"), + status: TaskStatus(id: 3, name: "completed", displayName: "Completed", description: "Task completed"), + dueDate: "2024-11-01", + estimatedCost: "200.00", + actualCost: "185.00", + notes: nil, + createdAt: "2024-10-01T00:00:00Z", + updatedAt: "2024-11-05T00:00:00Z", + nextScheduledDate: nil, + showCompletedButton: false, + completions: [] + ) + ], cancelledTasks: [] ), + showCompletedTasks: .constant(true), showCancelledTasks: .constant(true), onEditTask: { _ in }, onCancelTask: { _ in },