Files
honeyDueKMP/iosApp/iosApp/Subviews/Task/TasksSection.swift
Trey t 4a04aff1e6 Replace status_id with in_progress boolean across mobile apps
- Remove TaskStatus model and status_id foreign key references
- Add in_progress boolean field to task models and forms
- Update TaskApi to use dedicated POST endpoints for task actions:
  - POST /tasks/:id/cancel/ instead of PATCH with is_cancelled
  - POST /tasks/:id/uncancel/
  - POST /tasks/:id/archive/
  - POST /tasks/:id/unarchive/
- Fix iOS TaskViewModel to use error-first pattern for Kotlin-Swift
  generic type bridging issues
- Update iOS callback signatures to pass full TaskResponse instead
  of just taskId to avoid stale closure lookups
- Add in_progress localization strings
- Update widget preview data to use inProgress boolean

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-08 20:47:59 -06:00

168 lines
7.1 KiB
Swift

import SwiftUI
import ComposeApp
struct TasksSection: View {
let tasksResponse: TaskColumnsResponse
let onEditTask: (TaskResponse) -> Void
let onCancelTask: (TaskResponse) -> Void
let onUncancelTask: (Int32) -> Void
let onMarkInProgress: (Int32) -> Void
let onCompleteTask: (TaskResponse) -> Void
let onArchiveTask: (TaskResponse) -> Void
let onUnarchiveTask: (Int32) -> Void
private var hasNoTasks: Bool {
tasksResponse.columns.allSatisfy { $0.tasks.isEmpty }
}
var body: some View {
VStack(alignment: .leading, spacing: 12) {
Text("Tasks")
.font(.title2)
.fontWeight(.bold)
.foregroundColor(Color.appTextPrimary)
if hasNoTasks {
EmptyTasksView()
} else {
GeometryReader { geometry in
ScrollView(.horizontal, showsIndicators: false) {
LazyHStack(spacing: 16) {
// Dynamically create columns from response
ForEach(Array(tasksResponse.columns.enumerated()), id: \.element.name) { index, column in
DynamicTaskColumnView(
column: column,
onEditTask: { task in
onEditTask(task)
},
onCancelTask: { task in
onCancelTask(task)
},
onUncancelTask: { taskId in
onUncancelTask(taskId)
},
onMarkInProgress: { taskId in
onMarkInProgress(taskId)
},
onCompleteTask: { task in
onCompleteTask(task)
},
onArchiveTask: { task in
onArchiveTask(task)
},
onUnarchiveTask: { taskId in
onUnarchiveTask(taskId)
}
)
.frame(width: geometry.size.width - 48)
}
}
.scrollTargetLayout()
.padding(.horizontal, 16)
}
.scrollTargetBehavior(.viewAligned)
}
.frame(height: 500)
}
}
}
}
#Preview {
TasksSection(
tasksResponse: TaskColumnsResponse(
columns: [
TaskColumn(
name: "upcoming_tasks",
displayName: "Upcoming",
buttonTypes: ["edit", "cancel", "uncancel", "mark_in_progress", "complete", "archive"],
icons: ["ios": "calendar", "android": "CalendarToday", "web": "calendar"],
color: "#007AFF",
tasks: [
TaskResponse(
id: 1,
residenceId: 1,
createdById: 1,
createdBy: nil,
assignedToId: nil,
assignedTo: nil,
title: "Clean Gutters",
description: "Remove all debris",
categoryId: 1,
category: TaskCategory(id: 1, name: "maintenance", description: "", icon: "", color: "", displayOrder: 0),
priorityId: 2,
priority: TaskPriority(id: 2, name: "medium", level: 2, color: "", displayOrder: 0),
inProgress: false,
frequencyId: 1,
frequency: TaskFrequency(id: 1, name: "monthly", days: 30, displayOrder: 0),
dueDate: "2024-12-15",
estimatedCost: 150.00,
actualCost: nil,
contractorId: nil,
isCancelled: false,
isArchived: false,
parentTaskId: nil,
completionCount: 0,
kanbanColumn: nil,
completions: [],
createdAt: "2024-01-01T00:00:00Z",
updatedAt: "2024-01-01T00:00:00Z"
)
],
count: 1
),
TaskColumn(
name: "done_tasks",
displayName: "Done",
buttonTypes: ["edit", "archive"],
icons: ["ios": "checkmark.circle", "android": "CheckCircle", "web": "check-circle"],
color: "#34C759",
tasks: [
TaskResponse(
id: 2,
residenceId: 1,
createdById: 1,
createdBy: nil,
assignedToId: nil,
assignedTo: nil,
title: "Fix Leaky Faucet",
description: "Kitchen sink fixed",
categoryId: 2,
category: TaskCategory(id: 2, name: "plumbing", description: "", icon: "", color: "", displayOrder: 0),
priorityId: 3,
priority: TaskPriority(id: 3, name: "high", level: 3, color: "", displayOrder: 0),
inProgress: false,
frequencyId: 6,
frequency: TaskFrequency(id: 6, name: "once", days: nil, displayOrder: 0),
dueDate: "2024-11-01",
estimatedCost: 200.00,
actualCost: nil,
contractorId: nil,
isCancelled: false,
isArchived: false,
parentTaskId: nil,
completionCount: 3,
kanbanColumn: nil,
completions: [],
createdAt: "2024-10-01T00:00:00Z",
updatedAt: "2024-11-05T00:00:00Z"
)
],
count: 1
)
],
daysThreshold: 30,
residenceId: "1", summary: nil
),
onEditTask: { _ in },
onCancelTask: { _ in },
onUncancelTask: { _ in },
onMarkInProgress: { _ in },
onCompleteTask: { _ in },
onArchiveTask: { _ in },
onUnarchiveTask: { _ in }
)
.padding()
}