Replace status_id with in_progress boolean field

- Remove task_statuses lookup table and StatusID foreign key
- Add InProgress boolean field to Task model
- Add database migration (005_replace_status_with_in_progress)
- Update all handlers, services, and repositories
- Update admin frontend to display in_progress as checkbox/boolean
- Remove Task Statuses tab from admin lookups page
- Update tests to use InProgress instead of StatusID
- Task categorization now uses InProgress for kanban column assignment

🤖 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:48:16 -06:00
parent cb250f108b
commit c5b0225422
43 changed files with 353 additions and 753 deletions

View File

@@ -33,7 +33,7 @@ KANBAN COLUMNS (in priority order):
----------------------------------
1. CANCELLED: Task.IsCancelled = true
2. COMPLETED: NextDueDate = nil AND has completions (one-time task done)
3. IN_PROGRESS: Status.Name = "In Progress"
3. IN_PROGRESS: InProgress = true
4. OVERDUE: NextDueDate < now
5. DUE_SOON: NextDueDate < now + daysThreshold (default 30)
6. UPCOMING: Everything else (NextDueDate >= threshold or no due date)
@@ -72,6 +72,14 @@ func daysAgo(n int) time.Time {
return time.Now().UTC().AddDate(0, 0, -n)
}
// isTaskCompleted checks if a task is permanently completed (one-time task done).
// A task is completed when it has completions AND NextDueDate is nil.
func isTaskCompleted(task *models.Task) bool {
if len(task.Completions) == 0 {
return false
}
return task.NextDueDate == nil
}
// ============================================================================
// isTaskCompleted FUNCTION TESTS
@@ -157,7 +165,7 @@ func TestGetButtonTypesForTask_CompletedOneTimeTask(t *testing.T) {
func TestGetButtonTypesForTask_InProgressTask(t *testing.T) {
task := &models.Task{
NextDueDate: ptr(daysFromNow(10)),
Status: &models.TaskStatus{Name: "In Progress"},
InProgress: true,
}
buttons := GetButtonTypesForTask(task, 30)
@@ -237,7 +245,7 @@ func TestGetIOSCategoryForTask_CompletedTask(t *testing.T) {
func TestGetIOSCategoryForTask_InProgressTask(t *testing.T) {
task := &models.Task{
NextDueDate: ptr(daysFromNow(10)),
Status: &models.TaskStatus{Name: "In Progress"},
InProgress: true,
}
category := GetIOSCategoryForTask(task)
@@ -285,7 +293,7 @@ func TestDetermineKanbanColumn_CompletedOneTimeTask(t *testing.T) {
func TestDetermineKanbanColumn_InProgressTask(t *testing.T) {
task := &models.Task{
NextDueDate: ptr(daysAgo(5)), // Even overdue
Status: &models.TaskStatus{Name: "In Progress"},
InProgress: true,
}
column := responses.DetermineKanbanColumn(task, 30)
@@ -902,7 +910,7 @@ func TestEdgeCase_CancelledAndOverdue(t *testing.T) {
func TestEdgeCase_InProgressAndOverdue(t *testing.T) {
task := &models.Task{
NextDueDate: ptr(daysAgo(5)),
Status: &models.TaskStatus{Name: "In Progress"},
InProgress: true,
}
column := responses.DetermineKanbanColumn(task, 30)
@@ -1011,7 +1019,7 @@ func TestButtonTypes_ConsistencyWithKanbanColumn(t *testing.T) {
name: "In Progress task",
task: &models.Task{
NextDueDate: ptr(daysFromNow(10)),
Status: &models.TaskStatus{Name: "In Progress"},
InProgress: true,
},
expectedColumn: "in_progress_tasks",
expectedButtons: []string{"edit", "complete", "cancel"},
@@ -1062,7 +1070,7 @@ func TestPriorityOrder_CancelledBeatsEverything(t *testing.T) {
task := &models.Task{
IsCancelled: true,
NextDueDate: ptr(daysAgo(10)),
Status: &models.TaskStatus{Name: "In Progress"},
InProgress: true,
Completions: []models.TaskCompletion{{CompletedAt: daysAgo(1)}},
}
@@ -1074,7 +1082,7 @@ func TestPriorityOrder_CompletedBeatsInProgress(t *testing.T) {
// One-time task with In Progress status but completed
task := &models.Task{
NextDueDate: nil,
Status: &models.TaskStatus{Name: "In Progress"},
InProgress: true,
Completions: []models.TaskCompletion{{CompletedAt: daysAgo(1)}},
}
@@ -1086,7 +1094,7 @@ func TestPriorityOrder_InProgressBeatsDateBased(t *testing.T) {
// Overdue task that's in progress
task := &models.Task{
NextDueDate: ptr(daysAgo(10)),
Status: &models.TaskStatus{Name: "In Progress"},
InProgress: true,
}
column := responses.DetermineKanbanColumn(task, 30)