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>
This commit is contained in:
Trey t
2025-12-08 20:47:59 -06:00
parent a067228597
commit 4a04aff1e6
33 changed files with 314 additions and 376 deletions
+1
View File
@@ -251,6 +251,7 @@ enum L10n {
// Task Card Actions
static var inProgress: String { String(localized: "tasks_in_progress") }
static var inProgressLabel: String { String(localized: "tasks_in_progress_label") }
static var complete: String { String(localized: "tasks_complete") }
static var edit: String { String(localized: "tasks_edit") }
static var cancel: String { String(localized: "tasks_cancel") }
@@ -93,6 +93,10 @@ final class WidgetActionProcessor {
let data = success.data {
// Update widget with fresh data
WidgetDataManager.shared.saveTasks(from: data)
// Update summary from response (no extra API call needed)
if let summary = data.summary {
DataManager.shared.setTotalSummary(summary: summary)
}
}
} catch {
print("WidgetActionProcessor: Error refreshing tasks: \(error)")
+6 -10
View File
@@ -223,14 +223,15 @@ final class WidgetDataManager {
let title: String
let description: String?
let priority: String?
let status: String?
let inProgress: Bool
let dueDate: String?
let category: String?
let residenceName: String?
let isOverdue: Bool
enum CodingKeys: String, CodingKey {
case id, title, description, priority, status, category
case id, title, description, priority, category
case inProgress = "in_progress"
case dueDate = "due_date"
case residenceName = "residence_name"
case isOverdue = "is_overdue"
@@ -273,7 +274,7 @@ final class WidgetDataManager {
title: task.title,
description: task.description_,
priority: task.priority?.name ?? "",
status: task.status?.name,
inProgress: task.inProgress,
dueDate: task.dueDate,
category: task.category?.name ?? "",
residenceName: "", // No longer available in API, residence lookup needed
@@ -325,14 +326,9 @@ final class WidgetDataManager {
func getUpcomingTasks() -> [WidgetTask] {
let allTasks = loadTasks()
// Filter for pending/in-progress tasks (non-archived, non-completed)
let upcoming = allTasks.filter { task in
let status = task.status?.lowercased() ?? ""
return status == "pending" || status == "in_progress" || status == "in progress"
}
// All loaded tasks are already filtered (archived and completed columns are excluded during save)
// Sort by due date (earliest first), with overdue at top
return upcoming.sorted { task1, task2 in
return allTasks.sorted { task1, task2 in
// Overdue tasks first
if task1.isOverdue != task2.isOverdue {
return task1.isOverdue