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:
@@ -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()
|
||||
)
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -346,7 +346,7 @@ fun OnboardingFirstTaskContent(
|
||||
description = null,
|
||||
categoryId = categoryId,
|
||||
priorityId = null,
|
||||
statusId = null,
|
||||
inProgress = false,
|
||||
frequencyId = frequencyId,
|
||||
assignedToId = null,
|
||||
dueDate = today,
|
||||
|
||||
Reference in New Issue
Block a user