From e272e4568978f94aef1cf490cfdd45653f25d56b Mon Sep 17 00:00:00 2001 From: Trey t Date: Wed, 5 Nov 2025 21:35:52 -0600 Subject: [PATCH] wip --- .../com/example/mycrib/models/CustomTask.kt | 16 ++- .../com/example/mycrib/network/TaskApi.kt | 7 +- .../ui/screens/ResidenceDetailScreen.kt | 132 +++++++++--------- .../Residence/ResidenceDetailView.swift | 10 +- .../iosApp/Subviews/Task/TasksSection.swift | 72 +++++----- 5 files changed, 129 insertions(+), 108 deletions(-) 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 f827612..894224a 100644 --- a/composeApp/src/commonMain/kotlin/com/example/mycrib/models/CustomTask.kt +++ b/composeApp/src/commonMain/kotlin/com/example/mycrib/models/CustomTask.kt @@ -74,10 +74,18 @@ data class TaskDetail( @Serializable 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() + @SerialName("days_threshold") val daysThreshold: Int, + val summary: CategorizedTaskSummary, + @SerialName("upcoming_tasks") val upcomingTasks: List, + @SerialName("in_progress_tasks") val inProgressTasks: List, + @SerialName("done_tasks") val doneTasks: List +) + +@Serializable +data class CategorizedTaskSummary( + val upcoming: Int, + @SerialName("in_progress") val inProgress: Int, + val done: Int ) @Serializable diff --git a/composeApp/src/commonMain/kotlin/com/example/mycrib/network/TaskApi.kt b/composeApp/src/commonMain/kotlin/com/example/mycrib/network/TaskApi.kt index e15e393..d709e53 100644 --- a/composeApp/src/commonMain/kotlin/com/example/mycrib/network/TaskApi.kt +++ b/composeApp/src/commonMain/kotlin/com/example/mycrib/network/TaskApi.kt @@ -94,10 +94,15 @@ class TaskApi(private val client: HttpClient = ApiClient.httpClient) { } } - suspend fun getTasksByResidence(token: String, residenceId: Int): ApiResult { + suspend fun getTasksByResidence( + token: String, + residenceId: Int, + days: Int = 30 + ): ApiResult { return try { val response = client.get("$baseUrl/tasks/by-residence/$residenceId/") { header("Authorization", "Token $token") + parameter("days", days) } if (response.status.isSuccess()) { 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 3f2355a..90a4865 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 @@ -48,8 +48,8 @@ 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) } + var showInProgressTasks by remember { mutableStateOf(false) } + var showDoneTasks by remember { mutableStateOf(false) } LaunchedEffect(residenceId) { residenceViewModel.getResidence(residenceId) { result -> @@ -394,7 +394,7 @@ fun ResidenceDetailScreen( } is ApiResult.Success -> { val taskData = (tasksState as ApiResult.Success).data - if (taskData.tasks.isEmpty() && taskData.completedTasks.isEmpty() && taskData.cancelledTasks.isEmpty()) { + if (taskData.upcomingTasks.isEmpty() && taskData.inProgressTasks.isEmpty() && taskData.doneTasks.isEmpty()) { item { Card( modifier = Modifier.fillMaxWidth(), @@ -427,7 +427,8 @@ fun ResidenceDetailScreen( } } } else { - items(taskData.tasks) { task -> + // Upcoming tasks section + items(taskData.upcomingTasks) { task -> TaskCard( task = task, onCompleteClick = { @@ -444,14 +445,69 @@ fun ResidenceDetailScreen( ) } - // Completed tasks section - if (taskData.completedTasks.isNotEmpty()) { + // In Progress tasks section + if (taskData.inProgressTasks.isNotEmpty()) { item { Spacer(modifier = Modifier.height(8.dp)) Row( modifier = Modifier .fillMaxWidth() - .clickable { showCompletedTasks = !showCompletedTasks } + .clickable { showInProgressTasks = !showInProgressTasks } + .padding(vertical = 8.dp), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + Row(verticalAlignment = Alignment.CenterVertically) { + Icon( + Icons.Default.PlayCircle, + contentDescription = null, + tint = MaterialTheme.colorScheme.tertiary, + modifier = Modifier.size(28.dp) + ) + Spacer(modifier = Modifier.width(8.dp)) + Text( + text = "In Progress (${taskData.inProgressTasks.size})", + style = MaterialTheme.typography.titleMedium, + fontWeight = FontWeight.Bold, + color = MaterialTheme.colorScheme.tertiary + ) + } + Icon( + if (showInProgressTasks) Icons.Default.KeyboardArrowUp else Icons.Default.KeyboardArrowDown, + contentDescription = null, + tint = MaterialTheme.colorScheme.onSurfaceVariant + ) + } + } + + if (showInProgressTasks) { + items(taskData.inProgressTasks) { task -> + TaskCard( + task = task, + onCompleteClick = { + selectedTask = task + showCompleteDialog = true + }, + onEditClick = { + onNavigateToEditTask(task) + }, + onCancelClick = { + residenceViewModel.cancelTask(task.id) + }, + onUncancelClick = null + ) + } + } + } + + // Done tasks section + if (taskData.doneTasks.isNotEmpty()) { + item { + Spacer(modifier = Modifier.height(8.dp)) + Row( + modifier = Modifier + .fillMaxWidth() + .clickable { showDoneTasks = !showDoneTasks } .padding(vertical = 8.dp), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically @@ -460,77 +516,27 @@ fun ResidenceDetailScreen( Icon( Icons.Default.CheckCircle, contentDescription = null, - tint = MaterialTheme.colorScheme.tertiary, + tint = MaterialTheme.colorScheme.primary, modifier = Modifier.size(28.dp) ) Spacer(modifier = Modifier.width(8.dp)) Text( - text = "Completed Tasks (${taskData.completedTasks.size})", + text = "Done (${taskData.doneTasks.size})", style = MaterialTheme.typography.titleMedium, fontWeight = FontWeight.Bold, - color = MaterialTheme.colorScheme.tertiary + color = MaterialTheme.colorScheme.primary ) } Icon( - if (showCompletedTasks) Icons.Default.KeyboardArrowUp else Icons.Default.KeyboardArrowDown, + if (showDoneTasks) 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 { - Spacer(modifier = Modifier.height(8.dp)) - Row( - modifier = Modifier - .fillMaxWidth() - .clickable { showCancelledTasks = !showCancelledTasks } - .padding(vertical = 8.dp), - horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically - ) { - Row(verticalAlignment = Alignment.CenterVertically) { - Icon( - Icons.Default.Cancel, - contentDescription = null, - tint = MaterialTheme.colorScheme.error, - modifier = Modifier.size(28.dp) - ) - Spacer(modifier = Modifier.width(8.dp)) - Text( - text = "Cancelled Tasks (${taskData.cancelledTasks.size})", - style = MaterialTheme.typography.titleMedium, - fontWeight = FontWeight.Bold, - color = MaterialTheme.colorScheme.error - ) - } - Icon( - if (showCancelledTasks) Icons.Default.KeyboardArrowUp else Icons.Default.KeyboardArrowDown, - contentDescription = null, - tint = MaterialTheme.colorScheme.onSurfaceVariant - ) - } - } - - if (showCancelledTasks) { - items(taskData.cancelledTasks) { task -> + if (showDoneTasks) { + items(taskData.doneTasks) { task -> TaskCard( task = task, onCompleteClick = null, diff --git a/iosApp/iosApp/Residence/ResidenceDetailView.swift b/iosApp/iosApp/Residence/ResidenceDetailView.swift index b694d4e..ad69307 100644 --- a/iosApp/iosApp/Residence/ResidenceDetailView.swift +++ b/iosApp/iosApp/Residence/ResidenceDetailView.swift @@ -12,8 +12,8 @@ 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 + @State private var showInProgressTasks = false + @State private var showDoneTasks = false var body: some View { ZStack { @@ -38,8 +38,8 @@ struct ResidenceDetailView: View { if let tasksResponse = tasksResponse { TasksSection( tasksResponse: tasksResponse, - showCompletedTasks: $showCompletedTasks, - showCancelledTasks: $showCancelledTasks, + showInProgressTasks: $showInProgressTasks, + showDoneTasks: $showDoneTasks, onEditTask: { task in selectedTaskForEdit = task showEditTask = true @@ -134,7 +134,7 @@ struct ResidenceDetailView: View { tasksError = nil let taskApi = TaskApi(client: ApiClient_iosKt.createHttpClient()) - taskApi.getTasksByResidence(token: token, residenceId: residenceId) { result, error in + taskApi.getTasksByResidence(token: token, residenceId: residenceId, days: 30) { result, error in if let successResult = result as? ApiResultSuccess { self.tasksResponse = successResult.data self.isLoadingTasks = false diff --git a/iosApp/iosApp/Subviews/Task/TasksSection.swift b/iosApp/iosApp/Subviews/Task/TasksSection.swift index 5eca42e..df309d5 100644 --- a/iosApp/iosApp/Subviews/Task/TasksSection.swift +++ b/iosApp/iosApp/Subviews/Task/TasksSection.swift @@ -3,8 +3,8 @@ import ComposeApp struct TasksSection: View { let tasksResponse: TasksByResidenceResponse - @Binding var showCompletedTasks: Bool - @Binding var showCancelledTasks: Bool + @Binding var showInProgressTasks: Bool + @Binding var showDoneTasks: Bool let onEditTask: (TaskDetail) -> Void let onCancelTask: (TaskDetail) -> Void let onUncancelTask: (TaskDetail) -> Void @@ -19,16 +19,17 @@ struct TasksSection: View { Spacer() HStack(spacing: 8) { - TaskPill(count: tasksResponse.summary.total, label: "Total", color: .blue) - TaskPill(count: tasksResponse.summary.pending, label: "Pending", color: .orange) - TaskPill(count: tasksResponse.summary.completed, label: "Done", color: .green) + TaskPill(count: Int32(tasksResponse.summary.upcoming), label: "Upcoming", color: .blue) + TaskPill(count: Int32(tasksResponse.summary.inProgress), label: "In Progress", color: .orange) + TaskPill(count: Int32(tasksResponse.summary.done), label: "Done", color: .green) } } - if tasksResponse.tasks.isEmpty && tasksResponse.completedTasks.isEmpty && tasksResponse.cancelledTasks.isEmpty { + if tasksResponse.upcomingTasks.isEmpty && tasksResponse.inProgressTasks.isEmpty && tasksResponse.doneTasks.isEmpty { EmptyTasksView() } else { - ForEach(tasksResponse.tasks, id: \.id) { task in + // Upcoming tasks + ForEach(tasksResponse.upcomingTasks, id: \.id) { task in TaskCard( task: task, onEdit: { onEditTask(task) }, @@ -37,31 +38,32 @@ struct TasksSection: View { ) } - if !tasksResponse.completedTasks.isEmpty { + // In Progress tasks section + if !tasksResponse.inProgressTasks.isEmpty { VStack(alignment: .leading, spacing: 12) { HStack { - Label("Completed Tasks (\(tasksResponse.completedTasks.count))", systemImage: "checkmark.circle") + Label("In Progress (\(tasksResponse.inProgressTasks.count))", systemImage: "play.circle") .font(.headline) - .foregroundColor(.green) + .foregroundColor(.orange) Spacer() - Image(systemName: showCompletedTasks ? "chevron.up" : "chevron.down") + Image(systemName: showInProgressTasks ? "chevron.up" : "chevron.down") .foregroundColor(.secondary) .font(.caption) } .padding(.top, 8) .contentShape(Rectangle()) .onTapGesture { - showCompletedTasks.toggle() + showInProgressTasks.toggle() } - if showCompletedTasks { - ForEach(tasksResponse.completedTasks, id: \.id) { task in + if showInProgressTasks { + ForEach(tasksResponse.inProgressTasks, id: \.id) { task in TaskCard( task: task, onEdit: { onEditTask(task) }, - onCancel: nil, + onCancel: { onCancelTask(task) }, onUncancel: nil ) } @@ -69,32 +71,33 @@ struct TasksSection: View { } } - if !tasksResponse.cancelledTasks.isEmpty { + // Done tasks section + if !tasksResponse.doneTasks.isEmpty { VStack(alignment: .leading, spacing: 12) { HStack { - Label("Cancelled Tasks (\(tasksResponse.cancelledTasks.count))", systemImage: "xmark.circle") + Label("Done (\(tasksResponse.doneTasks.count))", systemImage: "checkmark.circle") .font(.headline) - .foregroundColor(.red) + .foregroundColor(.green) Spacer() - Image(systemName: showCancelledTasks ? "chevron.up" : "chevron.down") + Image(systemName: showDoneTasks ? "chevron.up" : "chevron.down") .foregroundColor(.secondary) .font(.caption) } .padding(.top, 8) .contentShape(Rectangle()) .onTapGesture { - showCancelledTasks.toggle() + showDoneTasks.toggle() } - if showCancelledTasks { - ForEach(tasksResponse.cancelledTasks, id: \.id) { task in + if showDoneTasks { + ForEach(tasksResponse.doneTasks, id: \.id) { task in TaskCard( task: task, onEdit: { onEditTask(task) }, onCancel: nil, - onUncancel: { onUncancelTask(task) } + onUncancel: nil ) } } @@ -109,14 +112,13 @@ struct TasksSection: View { TasksSection( tasksResponse: TasksByResidenceResponse( residenceId: "1", - summary: TaskSummary( - total: 3, - completed: 1, - pending: 2, - inProgress: 0, - overdue: 1 + daysThreshold: 30, + summary: CategorizedTaskSummary( + upcoming: 3, + inProgress: 1, + done: 2 ), - tasks: [ + upcomingTasks: [ TaskDetail( id: 1, residence: 1, @@ -137,7 +139,8 @@ struct TasksSection: View { completions: [] ) ], - completedTasks: [ + inProgressTasks: [], + doneTasks: [ TaskDetail( id: 2, residence: 1, @@ -157,11 +160,10 @@ struct TasksSection: View { showCompletedButton: false, completions: [] ) - ], - cancelledTasks: [] + ] ), - showCompletedTasks: .constant(true), - showCancelledTasks: .constant(true), + showInProgressTasks: .constant(true), + showDoneTasks: .constant(true), onEditTask: { _ in }, onCancelTask: { _ in }, onUncancelTask: { _ in }