Files
honeyDueAPI/docs/TASK_KANBAN_LOGIC.md
Trey t 3419b66097 Add landing page, redesign emails, and return updated task on completion
- Integrate landing page into Go app (served at root /)
- Add STATIC_DIR config for static file serving
- Redesign all email templates with modern dark theme styling
- Add app icon to email headers
- Return updated task with kanban_column in completion response
- Update task DTO to include kanban column for client-side state updates

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-02 21:33:17 -06:00

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 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

if task.Status != nil && task.Status.Name == "In Progress" {
    inProgress = append(inProgress, task)
    continue
}
  • Condition: Task's status name is exactly "In Progress"
  • 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) 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:

{
    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

{
  "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