Files
honeyDueAPI/docs/TASK_KANBAN_LOGIC.md
Trey t 0c86611a10 Add IsFree subscription toggle to bypass all tier limitations
- Add IsFree boolean field to UserSubscription model
- When IsFree is true, user sees limitations_enabled=false regardless of global setting
- CheckLimit() bypasses all limit checks for IsFree users
- Add admin endpoint GET /api/admin/subscriptions/user/:user_id
- Add IsFree toggle to admin user detail page under Subscription card
- Add database migration 004_subscription_is_free
- Add integration tests for IsFree functionality
- Add task kanban categorization documentation

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-01 18:05:41 -06:00

9.5 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

Column Name Color Description
1 Overdue #FF3B30 (Red) Tasks past their due date
2 Due Soon #FF9500 (Orange) Tasks due within the threshold (default 30 days)
3 Upcoming #007AFF (Blue) Tasks due beyond the threshold or with no due date
4 In Progress #5856D6 (Purple) Tasks with status "In Progress"
5 Completed #34C759 (Green) Tasks with at least one completion record
6 Cancelled #8E8E93 (Gray) 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
  • Actions Available: uncancel, 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
  • Actions Available: view

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"
  • Actions Available: edit, complete

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
  • Actions Available: edit, cancel, mark_in_progress

Due Soon

if task.DueDate.Before(threshold) {
    dueSoon = append(dueSoon, task)
}
  • Condition: current_time <= due_date < (current_time + days_threshold)
  • Default threshold: 30 days
  • Actions Available: edit, complete, mark_in_progress

Upcoming

upcoming = append(upcoming, task)
  • Condition: due_date >= (current_time + days_threshold) OR due_date IS NULL
  • Actions Available: edit, cancel

Column Metadata

Each column includes metadata for the mobile clients:

{
    Name:        "overdue_tasks",          // Internal identifier
    DisplayName: "Overdue",                // User-facing label
    ButtonTypes: []string{"edit", "cancel", "mark_in_progress"},  // Available actions
    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
}

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", "cancel", "mark_in_progress"],
      "icons": {
        "ios": "exclamationmark.triangle",
        "android": "Warning"
      },
      "color": "#FF3B30",
      "tasks": [...],
      "count": 2
    },
    // ... other columns
  ],
  "days_threshold": 30,
  "residence_id": "123"
}

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