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

View File

@@ -449,7 +449,7 @@ fun AddTaskDialog(
categoryId = if (category.id > 0) category.id else null,
frequencyId = if (frequency.id > 0) frequency.id else null,
priorityId = if (priority.id > 0) priority.id else null,
statusId = null,
inProgress = false,
dueDate = dueDate,
estimatedCost = estimatedCost.ifBlank { null }?.toDoubleOrNull()
)

View File

@@ -18,7 +18,6 @@ import com.example.casera.models.TaskDetail
import com.example.casera.models.TaskCategory
import com.example.casera.models.TaskPriority
import com.example.casera.models.TaskFrequency
import com.example.casera.models.TaskStatus
import com.example.casera.models.TaskCompletion
import com.example.casera.util.DateUtils
import org.jetbrains.compose.ui.tooling.preview.Preview
@@ -108,21 +107,15 @@ fun TaskCard(
)
}
// Status badge with semantic colors
if (task.status != null) {
val statusColor = when (task.status.name.lowercase()) {
"completed" -> MaterialTheme.colorScheme.secondary
"in_progress" -> MaterialTheme.colorScheme.tertiary
"pending" -> MaterialTheme.colorScheme.tertiary
"cancelled" -> MaterialTheme.colorScheme.onSurfaceVariant
else -> MaterialTheme.colorScheme.onSurfaceVariant
}
// In Progress badge
if (task.inProgress) {
val statusColor = MaterialTheme.colorScheme.tertiary
Surface(
color = statusColor.copy(alpha = 0.15f),
shape = RoundedCornerShape(12.dp)
) {
Text(
text = task.status.name.replace("_", " ").uppercase(),
text = "IN PROGRESS",
modifier = Modifier.padding(horizontal = 12.dp, vertical = 6.dp),
style = MaterialTheme.typography.labelSmall,
color = statusColor
@@ -604,7 +597,7 @@ fun TaskCardPreview() {
frequency = TaskFrequency(
id = 1, name = "monthly", days = 30
),
status = TaskStatus(id = 1, name = "pending"),
inProgress = false,
dueDate = "2024-12-15",
estimatedCost = 150.00,
createdAt = "2024-01-01T00:00:00Z",

View File

@@ -33,20 +33,18 @@ fun EditTaskScreen(
var selectedCategory by remember { mutableStateOf<TaskCategory?>(task.category) }
var selectedFrequency by remember { mutableStateOf<TaskFrequency?>(task.frequency) }
var selectedPriority by remember { mutableStateOf<TaskPriority?>(task.priority) }
var selectedStatus by remember { mutableStateOf<TaskStatus?>(task.status) }
var inProgress by remember { mutableStateOf(task.inProgress) }
var dueDate by remember { mutableStateOf(task.dueDate ?: "") }
var estimatedCost by remember { mutableStateOf(task.estimatedCost?.toString() ?: "") }
var categoryExpanded by remember { mutableStateOf(false) }
var frequencyExpanded by remember { mutableStateOf(false) }
var priorityExpanded by remember { mutableStateOf(false) }
var statusExpanded by remember { mutableStateOf(false) }
val updateTaskState by viewModel.updateTaskState.collectAsState()
val categories by LookupsRepository.taskCategories.collectAsState()
val frequencies by LookupsRepository.taskFrequencies.collectAsState()
val priorities by LookupsRepository.taskPriorities.collectAsState()
val statuses by LookupsRepository.taskStatuses.collectAsState()
// Validation errors
var titleError by remember { mutableStateOf("") }
@@ -235,36 +233,20 @@ fun EditTaskScreen(
}
}
// Status dropdown
ExposedDropdownMenuBox(
expanded = statusExpanded,
onExpandedChange = { statusExpanded = it }
// In Progress toggle
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = androidx.compose.ui.Alignment.CenterVertically
) {
OutlinedTextField(
value = selectedStatus?.name?.replaceFirstChar { it.uppercase() } ?: "",
onValueChange = {},
readOnly = true,
label = { Text(stringResource(Res.string.tasks_status_label)) },
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = statusExpanded) },
modifier = Modifier
.fillMaxWidth()
.menuAnchor(),
enabled = statuses.isNotEmpty()
Text(
text = stringResource(Res.string.tasks_in_progress_label),
style = MaterialTheme.typography.bodyLarge
)
Switch(
checked = inProgress,
onCheckedChange = { inProgress = it }
)
ExposedDropdownMenu(
expanded = statusExpanded,
onDismissRequest = { statusExpanded = false }
) {
statuses.forEach { status ->
DropdownMenuItem(
text = { Text(status.name.replaceFirstChar { it.uppercase() }) },
onClick = {
selectedStatus = status
statusExpanded = false
}
)
}
}
}
OutlinedTextField(
@@ -301,8 +283,7 @@ fun EditTaskScreen(
Button(
onClick = {
if (validateForm() && selectedCategory != null &&
selectedFrequency != null && selectedPriority != null &&
selectedStatus != null) {
selectedFrequency != null && selectedPriority != null) {
viewModel.updateTask(
taskId = task.id,
request = TaskCreateRequest(
@@ -312,7 +293,7 @@ fun EditTaskScreen(
categoryId = selectedCategory!!.id,
frequencyId = selectedFrequency!!.id,
priorityId = selectedPriority!!.id,
statusId = selectedStatus!!.id,
inProgress = inProgress,
dueDate = dueDate,
estimatedCost = estimatedCost.ifBlank { null }?.toDoubleOrNull()
)
@@ -321,8 +302,7 @@ fun EditTaskScreen(
},
modifier = Modifier.fillMaxWidth(),
enabled = validateForm() && selectedCategory != null &&
selectedFrequency != null && selectedPriority != null &&
selectedStatus != null
selectedFrequency != null && selectedPriority != null
) {
if (updateTaskState is ApiResult.Loading) {
CircularProgressIndicator(

View File

@@ -346,7 +346,7 @@ fun OnboardingFirstTaskContent(
description = null,
categoryId = categoryId,
priorityId = null,
statusId = null,
inProgress = false,
frequencyId = frequencyId,
assignedToId = null,
dueDate = today,