# 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) ```go 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 ```go if len(task.Completions) > 0 { completed = append(completed, task) continue } ``` - **Condition**: Task has at least one `TaskCompletion` record - **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 ```go if task.InProgress { inProgress = append(inProgress, task) continue } ``` - **Condition**: Task's `in_progress` boolean is `true` - **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 ```go 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 ```go 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 ```go upcoming = append(upcoming, task) ``` - **Condition**: `due_date >= (current_time + days_threshold)` OR `due_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: ```go { 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 `daysThreshold` days from now → **Due Soon** - Tasks due beyond `daysThreshold` days from now → **Upcoming** This can be customized per request via query parameter. ## Sorting Tasks within each column are sorted by: 1. `due_date ASC NULLS LAST` (earliest due date first, tasks without due dates at end) 2. `priority_id DESC` (higher priority first) 3. `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 ```json { "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.go` - `GetKanbanData()` - Single residence - `GetKanbanDataForMultipleResidences()` - Multiple residences (all user's properties) - **Service**: `internal/services/task_service.go` - `ListTasks()` - All tasks for user - `GetTasksByResidence()` - Tasks for specific residence - **Response DTOs**: `internal/dto/responses/task.go` - `KanbanBoardResponse` - `KanbanColumnResponse`