- Remove Preload("Status") from worker handler and repositories
- Update seeds to use in_progress boolean instead of status_id
- Remove task_taskstatus table creation from lookup seeds
- Update documentation to reflect in_progress boolean pattern
Fixes notification worker error:
"Status: unsupported relations for schema Task"
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
14 KiB
Task Kanban Board Categorization Logic
This document describes how tasks are categorized into kanban columns for display in the Casera mobile app.
Overview
Tasks are organized into 6 kanban columns based on their state and due date. The categorization logic is implemented in internal/repositories/task_repo.go in the GetKanbanData and GetKanbanDataForMultipleResidences functions.
Columns Summary
| Column | Name | Color | Button Types | Description |
|---|---|---|---|---|
| 1 | Overdue | #FF3B30 (Red) |
edit, complete, cancel, mark_in_progress | Tasks past their due date |
| 2 | Due Soon | #FF9500 (Orange) |
edit, complete, cancel, mark_in_progress | Tasks due within the threshold (default 30 days) |
| 3 | Upcoming | #007AFF (Blue) |
edit, complete, cancel, mark_in_progress | Tasks due beyond the threshold or with no due date |
| 4 | In Progress | #5856D6 (Purple) |
edit, complete, cancel | Tasks with status "In Progress" |
| 5 | Completed | #34C759 (Green) |
view | Tasks with at least one completion record |
| 6 | Cancelled | #8E8E93 (Gray) |
uncancel, delete | Tasks marked as cancelled |
Categorization Flow
The categorization follows this priority order (first match wins):
┌─────────────────────────────────────────────────────────────────┐
│ START: Process Task │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────┐
│ Is task cancelled? │
│ (is_cancelled = true) │
└─────────────────────────┘
│ │
YES NO
│ │
▼ ▼
┌──────────┐ ┌─────────────────────────┐
│CANCELLED │ │ Has task completions? │
│ column │ │ (len(completions) > 0) │
└──────────┘ └─────────────────────────┘
│ │
YES NO
│ │
▼ ▼
┌──────────┐ ┌─────────────────────────┐
│COMPLETED │ │ Is status "In Progress"?│
│ column │ │ (status.name = "In...) │
└──────────┘ └─────────────────────────┘
│ │
YES NO
│ │
▼ ▼
┌───────────┐ ┌─────────────────┐
│IN PROGRESS│ │ Has due date? │
│ column │ └─────────────────┘
└───────────┘ │ │
YES NO
│ │
▼ ▼
┌──────────────┐ ┌────────┐
│Check due date│ │UPCOMING│
└──────────────┘ │ column │
│ └────────┘
┌────────────┼────────────┐
▼ ▼ ▼
┌─────────────┐ ┌──────────┐ ┌────────────┐
│ due < now │ │due < now │ │due >= now +│
│ │ │+ threshold│ │ threshold │
└─────────────┘ └──────────┘ └────────────┘
│ │ │
▼ ▼ ▼
┌─────────┐ ┌──────────┐ ┌────────┐
│ OVERDUE │ │ DUE SOON │ │UPCOMING│
│ column │ │ column │ │ column │
└─────────┘ └──────────┘ └────────┘
Detailed Rules
1. Cancelled (highest priority)
if task.IsCancelled {
cancelled = append(cancelled, task)
continue
}
- Condition:
is_cancelled = true - Button Types:
uncancel,delete - Rationale: Cancelled tasks can be restored (uncancel) or permanently removed (delete)
2. Completed
if len(task.Completions) > 0 {
completed = append(completed, task)
continue
}
- Condition: Task has at least one
TaskCompletionrecord - Note: A task is considered completed based on having completion records, NOT based on status
- Button Types:
view - Rationale: Completed tasks are read-only for historical purposes; users can view completion details and photos
3. In Progress
if task.InProgress {
inProgress = append(inProgress, task)
continue
}
- Condition: Task's
in_progressboolean istrue - Button Types:
edit,complete,cancel - Rationale: In-progress tasks can be edited, marked complete, or cancelled if work is abandoned
4. Due Date Based Categories (only for tasks not cancelled, completed, or in progress)
Overdue
if task.DueDate.Before(now) {
overdue = append(overdue, task)
}
- Condition:
due_date < current_time - Button Types:
edit,complete,cancel,mark_in_progress - Rationale: Overdue tasks need urgent attention - can complete immediately, start working on them, edit the due date, or cancel if no longer needed
Due Soon
if task.DueDate.Before(threshold) {
dueSoon = append(dueSoon, task)
}
- Condition:
current_time <= due_date < (current_time + days_threshold) - Default threshold: 30 days
- Button Types:
edit,complete,cancel,mark_in_progress - Rationale: Due soon tasks are approaching their deadline - users can complete early, start working, reschedule, or cancel
Upcoming
upcoming = append(upcoming, task)
- Condition:
due_date >= (current_time + days_threshold)ORdue_date IS NULL - Button Types:
edit,complete,cancel,mark_in_progress - Rationale: Future tasks may need to be completed early (ahead of schedule), started, rescheduled, or cancelled
Button Types Reference
| Button Type | Action | Description |
|---|---|---|
edit |
Update task | Modify task details (title, description, due date, category, etc.) |
complete |
Create completion | Mark task as done with optional notes and photos |
cancel |
Cancel task | Mark task as cancelled (soft delete, can be uncancelled) |
mark_in_progress |
Start work | Change status to "In Progress" to indicate work has begun |
view |
View details | View task and completion details (read-only) |
uncancel |
Restore task | Restore a cancelled task to its previous state |
delete |
Hard delete | Permanently remove task (only for cancelled tasks) |
Column Metadata
Each column includes metadata for the mobile clients:
{
Name: "overdue_tasks", // Internal identifier
DisplayName: "Overdue", // User-facing label
ButtonTypes: []string{"edit", "complete", "cancel", "mark_in_progress"},
Icons: map[string]string{ // Platform-specific icons
"ios": "exclamationmark.triangle",
"android": "Warning"
},
Color: "#FF3B30", // Display color
Tasks: []Task{...}, // Tasks in this column
Count: int, // Number of tasks
}
Icons by Column
| Column | iOS Icon | Android Icon |
|---|---|---|
| Overdue | exclamationmark.triangle |
Warning |
| Due Soon | clock |
Schedule |
| Upcoming | calendar |
Event |
| In Progress | hammer |
Build |
| Completed | checkmark.circle |
CheckCircle |
| Cancelled | xmark.circle |
Cancel |
Days Threshold Parameter
The daysThreshold parameter (default: 30) determines the boundary between "Due Soon" and "Upcoming":
- Tasks due within
daysThresholddays from now → Due Soon - Tasks due beyond
daysThresholddays from now → Upcoming
This can be customized per request via query parameter.
Sorting
Tasks within each column are sorted by:
due_date ASC NULLS LAST(earliest due date first, tasks without due dates at end)priority_id DESC(higher priority first)created_at DESC(newest first)
Excluded Tasks
The following tasks are excluded from the kanban board entirely:
- Archived tasks:
is_archived = true
API Response Example
{
"columns": [
{
"name": "overdue_tasks",
"display_name": "Overdue",
"button_types": ["edit", "complete", "cancel", "mark_in_progress"],
"icons": {
"ios": "exclamationmark.triangle",
"android": "Warning"
},
"color": "#FF3B30",
"tasks": [...],
"count": 2
},
{
"name": "due_soon_tasks",
"display_name": "Due Soon",
"button_types": ["edit", "complete", "cancel", "mark_in_progress"],
"icons": {
"ios": "clock",
"android": "Schedule"
},
"color": "#FF9500",
"tasks": [...],
"count": 5
},
{
"name": "upcoming_tasks",
"display_name": "Upcoming",
"button_types": ["edit", "complete", "cancel", "mark_in_progress"],
"icons": {
"ios": "calendar",
"android": "Event"
},
"color": "#007AFF",
"tasks": [...],
"count": 10
},
{
"name": "in_progress_tasks",
"display_name": "In Progress",
"button_types": ["edit", "complete", "cancel"],
"icons": {
"ios": "hammer",
"android": "Build"
},
"color": "#5856D6",
"tasks": [...],
"count": 3
},
{
"name": "completed_tasks",
"display_name": "Completed",
"button_types": ["view"],
"icons": {
"ios": "checkmark.circle",
"android": "CheckCircle"
},
"color": "#34C759",
"tasks": [...],
"count": 15
},
{
"name": "cancelled_tasks",
"display_name": "Cancelled",
"button_types": ["uncancel", "delete"],
"icons": {
"ios": "xmark.circle",
"android": "Cancel"
},
"color": "#8E8E93",
"tasks": [...],
"count": 1
}
],
"days_threshold": 30,
"residence_id": "123"
}
Design Decisions
Why "In Progress" doesn't have "mark_in_progress"
Tasks already in the "In Progress" column are already marked as in progress, so this action doesn't make sense.
Why "Completed" only has "view"
Completed tasks represent historical records. Users can view them but shouldn't be able to uncomplete or modify them. If a task was completed incorrectly, the user should delete the completion record through the completion detail view.
Why "Cancelled" has "delete" but others don't
Hard deletion is only available for cancelled tasks as a final cleanup action. For active tasks, users should cancel first (soft delete), then delete if truly needed. This prevents accidental data loss.
Why all active columns have "cancel"
Users should always be able to abandon a task they no longer need, regardless of its due date or progress state.
Code Location
- Repository:
internal/repositories/task_repo.goGetKanbanData()- Single residenceGetKanbanDataForMultipleResidences()- Multiple residences (all user's properties)
- Service:
internal/services/task_service.goListTasks()- All tasks for userGetTasksByResidence()- Tasks for specific residence
- Response DTOs:
internal/dto/responses/task.goKanbanBoardResponseKanbanColumnResponse