Planes de tareas

Un plan de tareas (o protocolo de cultivo) es una plantilla reutilizable que describe qué tareas ejecutar a lo largo de un ciclo de cultivo: riegos, dosificación de nutrientes, control de plagas (IPM), entrenamiento, control ambiental y cosecha. Cada plan se compone de items posicionados por dayOffset y, opcionalmente, de bloques reutilizables, objetivos ambientales por semana y un cronograma de nutrientes.

Cuando un plan se asigna a un cultivo se crea una TaskPlanAssignment, y a partir de ella se generan tareas concretas con fechas reales calculadas desde startDate. En esta página veremos cómo consultar, crear, actualizar, duplicar y asignar planes, cómo administrar bloques, y cómo disparar la generación de tareas.

Todas las rutas son endpoints BFF propios de la webapp (Strapi como backend), autenticadas con Authorization: Bearer {token} y con el header Tenant-id para el aislamiento por club.

The task plan model

El modelo TaskPlan es la plantilla de nivel superior. Agrupa los items, el equipo sugerido, los metadatos de estilo de cultivo (V2) y las métricas agregadas de uso.

Properties

  • Name
    id
    Type
    string
    Description

    Identificador único del plan.

  • Name
    name
    Type
    string
    Description

    Nombre del plan.

  • Name
    description
    Type
    string
    Description

    Descripción del plan.

  • Name
    targetPhases
    Type
    CropStatus[]
    Description

    Fases de cultivo a las que aplica el plan (por ejemplo VEGETATING, FLOWERING).

  • Name
    items
    Type
    TaskPlanItem[]
    Description

    Definición de las tareas de la plantilla. Ver el modelo de item más abajo.

  • Name
    suggestedTeam
    Type
    SuggestedTeamMember[]
    Description

    Composición de equipo sugerida.

    • Name
      role
      Type
      TeamMemberRole
      Description

      Rol del miembro del equipo.

    • Name
      count
      Type
      number
      Description

      Cantidad de personas sugeridas para ese rol.

  • Name
    estimatedTotalHours
    Type
    number
    Description

    Horas totales estimadas del plan (calculadas a partir de estimatedMinutes de los items).

  • Name
    tags
    Type
    string[]
    Description

    Etiquetas de clasificación / búsqueda.

  • Name
    status
    Type
    TaskPlanStatus
    Description

    Estado del plan. Puede ser:

    • DRAFT: borrador, no disponible para asignar
    • PUBLISHED: publicado y disponible
    • ARCHIVED: archivado
  • Name
    isTemplate
    Type
    boolean
    Description

    Indica si el plan es una plantilla reutilizable.

  • Name
    usageCount
    Type
    number
    Description

    Cantidad de veces que el plan fue asignado.

  • Name
    createdAt
    Type
    string
    Description

    Timestamp de creación.

  • Name
    updatedAt
    Type
    string
    Description

    Timestamp de última actualización.

  • Name
    growStyle
    Type
    GrowStyle
    Description

    Estilo de cultivo (V2). Puede ser:

    • INDOOR_PHOTO
    • INDOOR_AUTO
    • OUTDOOR
    • GREENHOUSE
  • Name
    medium
    Type
    GrowMedium
    Description

    Medio de cultivo (V2). Puede ser:

    • SOIL
    • COCO
    • HYDRO_DWC
    • HYDRO_RDWC
    • AEROPONIC
    • ROCKWOOL
  • Name
    blocks
    Type
    TaskBlock[]
    Description

    Bloques reutilizables incluidos en el plan (V2).

  • Name
    environmentTargets
    Type
    EnvironmentTarget[]
    Description

    Objetivos ambientales por semana (V2).

    • Name
      weekNumber
      Type
      number
      Description

      Número de semana.

    • Name
      temperature
      Type
      object
      Description

      Rango { min, max } de temperatura.

    • Name
      humidity
      Type
      object
      Description

      Rango { min, max } de humedad.

    • Name
      vpd
      Type
      object
      Description

      Rango { min, max } de VPD.

    • Name
      co2
      Type
      object
      Description

      Rango { min, max } de CO₂.

    • Name
      lightHours
      Type
      number
      Description

      Horas de luz por día.

    • Name
      lightIntensity
      Type
      number
      Description

      Intensidad lumínica (PPFD).

  • Name
    nutrientSchedule
    Type
    NutrientScheduleEntry[]
    Description

    Cronograma de nutrientes por semana (V2).

    • Name
      weekNumber
      Type
      number
      Description

      Número de semana.

    • Name
      products
      Type
      NutrientProduct[]
      Description

      Productos con name, dosage y unit (ml/L o g/L).

    • Name
      ecTarget
      Type
      object
      Description

      Rango { min, max } de EC objetivo.

    • Name
      phTarget
      Type
      object
      Description

      Rango { min, max } de pH objetivo.

    • Name
      waterVolume
      Type
      number
      Description

      Litros de agua por planta.

    • Name
      notes
      Type
      string
      Description

      Notas del riego / mezcla.

  • Name
    estimatedDurationDays
    Type
    number
    Description

    Duración estimada del plan en días (V2).

  • Name
    version
    Type
    number
    Description

    Versión del plan.

  • Name
    avgAdherenceRate
    Type
    number
    Description

    Tasa media de adherencia histórica (0-100).

  • Name
    avgYieldGPerPlant
    Type
    number
    Description

    Rendimiento medio histórico (gramos por planta).

  • Name
    phaseWindows
    Type
    PlanPhaseWindow[]
    Description

    Bandas de fase personalizadas para la línea de tiempo (Gantt).

Ejemplo de plan de tareas

{
  "id": "plan_01HQ8PLAN001",
  "name": "Protocolo Indoor Fotoperiódica — Coco",
  "description": "Ciclo completo de 12 semanas para fotoperiódicas en coco.",
  "targetPhases": ["VEGETATING", "FLOWERING"],
  "items": [
    {
      "id": "item_01HQ8ITEM001",
      "taskType": "WATERING",
      "title": "Riego con solución vegetativa",
      "dayOffset": 0,
      "estimatedMinutes": 30,
      "priority": "MEDIUM",
      "assignedRole": "GROWER",
      "order": 0,
      "weekNumber": 1
    }
  ],
  "suggestedTeam": [
    { "role": "GROWER", "count": 2 },
    { "role": "TRIMMER", "count": 3 }
  ],
  "estimatedTotalHours": 24.5,
  "tags": ["indoor", "coco", "fotoperiódica"],
  "status": "PUBLISHED",
  "isTemplate": true,
  "usageCount": 7,
  "growStyle": "INDOOR_PHOTO",
  "medium": "COCO",
  "estimatedDurationDays": 84,
  "environmentTargets": [
    {
      "weekNumber": 1,
      "temperature": { "min": 22, "max": 26 },
      "humidity": { "min": 60, "max": 70 },
      "vpd": { "min": 0.8, "max": 1.1 },
      "lightHours": 18
    }
  ],
  "nutrientSchedule": [
    {
      "weekNumber": 1,
      "products": [
        { "name": "Grow A", "dosage": 2, "unit": "ml/L" },
        { "name": "Grow B", "dosage": 2, "unit": "ml/L" }
      ],
      "ecTarget": { "min": 1.0, "max": 1.2 },
      "phTarget": { "min": 5.8, "max": 6.2 }
    }
  ],
  "createdAt": "2025-09-01T10:00:00Z",
  "updatedAt": "2025-09-05T14:30:00Z"
}

The task plan item model

Cada TaskPlanItem describe una tarea de la plantilla, posicionada por dayOffset desde el inicio del plan. Al generar tareas concretas, cada item se materializa en una o más tareas con fechas reales.

Properties

  • Name
    id
    Type
    string
    Description

    Identificador único del item.

  • Name
    taskType
    Type
    TaskType
    Description

    Tipo de tarea (por ejemplo riego, nutrición, IPM, cosecha).

  • Name
    title
    Type
    string
    Description

    Título de la tarea.

  • Name
    description
    Type
    string
    Description

    Descripción de la tarea.

  • Name
    dayOffset
    Type
    number
    Description

    Días desde el inicio del plan (base 0).

  • Name
    estimatedMinutes
    Type
    number
    Description

    Minutos estimados de duración.

  • Name
    priority
    Type
    TaskPriority
    Description

    Prioridad de la tarea.

  • Name
    assignedRole
    Type
    TeamMemberRole
    Description

    Rol responsable de la tarea.

  • Name
    recurring
    Type
    object
    Description

    Configuración de recurrencia.

    • Name
      interval
      Type
      number
      Description

      Días entre repeticiones.

    • Name
      endCondition
      Type
      enum
      Description

      Condición de fin. Puede ser:

      • END_OF_PHASE
      • FIXED_COUNT
      • SPECIFIC_DATE
    • Name
      fixedCount
      Type
      number
      Description

      Cantidad de repeticiones cuando endCondition es FIXED_COUNT.

    • Name
      endDate
      Type
      string
      Description

      Fecha de fin cuando endCondition es SPECIFIC_DATE.

  • Name
    dependencies
    Type
    array
    Description

    Dependencias con otros items (itemId + type: MUST_COMPLETE o MUST_START).

  • Name
    fertilizerId
    Type
    string
    Description

    Referencia al fertilizante.

  • Name
    fertilizerName
    Type
    string
    Description

    Nombre del fertilizante.

  • Name
    fertilizerDosage
    Type
    number
    Description

    Dosis de fertilizante.

  • Name
    fertilizerUnit
    Type
    string
    Description

    Unidad de la dosis.

  • Name
    notes
    Type
    string
    Description

    Notas del item.

  • Name
    order
    Type
    number
    Description

    Orden dentro de un mismo dayOffset.

  • Name
    weekNumber
    Type
    number
    Description

    Semana calculada (Math.floor(dayOffset / 7) + 1).

  • Name
    blockId
    Type
    string
    Description

    Bloque al que pertenece el item.

  • Name
    photoRequired
    Type
    boolean
    Description

    Requiere foto al completar.

  • Name
    checklistItems
    Type
    ChecklistItem[]
    Description

    Checklist asociado (id, label, required, order).

  • Name
    triggerConditions
    Type
    TriggerCondition[]
    Description

    Condiciones de sensor que disparan la tarea (sensorType, operator, threshold, action).

Ejemplo de item

{
  "id": "item_01HQ8ITEM002",
  "taskType": "FEEDING",
  "title": "Fertirriego semana 3",
  "description": "EC 1.4 — pH 5.9",
  "dayOffset": 14,
  "estimatedMinutes": 45,
  "priority": "HIGH",
  "assignedRole": "GROWER",
  "order": 0,
  "weekNumber": 3,
  "fertilizerName": "Bloom A+B",
  "fertilizerDosage": 3,
  "fertilizerUnit": "ml/L",
  "recurring": {
    "interval": 2,
    "endCondition": "END_OF_PHASE"
  },
  "photoRequired": true,
  "checklistItems": [
    { "id": "chk_1", "label": "Verificar EC", "required": true, "order": 0 }
  ]
}

The task block model

Un TaskBlock es un conjunto reutilizable de items agrupados por categoría (nutrición, IPM, ambiente, etc.). Existen bloques built-in del sistema y bloques personalizados por club.

Properties

  • Name
    id
    Type
    string
    Description

    Identificador único del bloque.

  • Name
    name
    Type
    string
    Description

    Nombre del bloque.

  • Name
    description
    Type
    string
    Description

    Descripción del bloque.

  • Name
    category
    Type
    BlockCategory
    Description

    Categoría del bloque. Puede ser:

    • NUTRIENT
    • IPM
    • ENVIRONMENT
    • TRAINING
    • HARVEST
    • GENERAL
  • Name
    items
    Type
    array
    Description

    Items del bloque (sin id ni blockId, se completan al usarse).

  • Name
    color
    Type
    string
    Description

    Color hex para visualización en la línea de tiempo.

  • Name
    tags
    Type
    string[]
    Description

    Etiquetas del bloque.

  • Name
    isBuiltIn
    Type
    boolean
    Description

    Indica si es un bloque del sistema. Los bloques built-in no se pueden modificar ni eliminar.

  • Name
    usageCount
    Type
    number
    Description

    Cantidad de veces utilizado.

Ejemplo de bloque

{
  "id": "block-ipm-preventivo",
  "name": "IPM Preventivo Semanal",
  "description": "Control preventivo de plagas cada 7 días.",
  "category": "IPM",
  "color": "#8BC34A",
  "tags": ["ipm", "preventivo"],
  "isBuiltIn": true,
  "usageCount": 34,
  "items": [
    {
      "taskType": "PEST_CONTROL",
      "title": "Aplicación de aceite de neem",
      "dayOffset": 0,
      "estimatedMinutes": 20,
      "priority": "MEDIUM",
      "assignedRole": "GROWER",
      "order": 0
    }
  ]
}

The task plan assignment model

Una TaskPlanAssignment vincula un plan con un cultivo concreto. Registra los trabajadores asignados, el progreso y los IDs de las tareas generadas.

Properties

  • Name
    id
    Type
    string
    Description

    Identificador único de la asignación.

  • Name
    taskPlanId
    Type
    string
    Description

    ID del plan asignado.

  • Name
    taskPlanName
    Type
    string
    Description

    Nombre del plan (denormalizado).

  • Name
    cropId
    Type
    string
    Description

    ID del cultivo.

  • Name
    cropName
    Type
    string
    Description

    Nombre del cultivo.

  • Name
    cropPhase
    Type
    CropStatus
    Description

    Fase del cultivo al momento de la asignación.

  • Name
    startDate
    Type
    string
    Description

    Fecha de inicio (ISO). Base para calcular las fechas de las tareas.

  • Name
    status
    Type
    TaskPlanAssignmentStatus
    Description

    Estado de la asignación. Puede ser:

    • ACTIVE
    • PAUSED
    • COMPLETED
    • CANCELLED
  • Name
    workerAssignments
    Type
    WorkerAssignment[]
    Description

    Trabajadores asignados por rol (role, workerId, workerName).

  • Name
    autoAssign
    Type
    boolean
    Description

    Si las tareas generadas se auto-asignan a los trabajadores.

  • Name
    generatedTaskIds
    Type
    string[]
    Description

    IDs de las tareas concretas generadas desde el plan.

  • Name
    completedTaskCount
    Type
    number
    Description

    Cantidad de tareas completadas.

  • Name
    totalTaskCount
    Type
    number
    Description

    Cantidad total de tareas.

  • Name
    progress
    Type
    number
    Description

    Progreso en porcentaje (0-100).

  • Name
    pausedAt
    Type
    string
    Description

    Timestamp de pausa.

  • Name
    completedAt
    Type
    string
    Description

    Timestamp de finalización.

  • Name
    createdAt
    Type
    string
    Description

    Timestamp de creación.

  • Name
    updatedAt
    Type
    string
    Description

    Timestamp de última actualización.

Ejemplo de asignación

{
  "id": "assign_01HQ8ASSIGN001",
  "taskPlanId": "plan_01HQ8PLAN001",
  "taskPlanName": "Protocolo Indoor Fotoperiódica — Coco",
  "cropId": "crop_01HQ8CROP001",
  "cropName": "Gorilla Glue #4 — Sala A",
  "cropPhase": "VEGETATING",
  "startDate": "2025-11-01",
  "status": "ACTIVE",
  "workerAssignments": [
    { "role": "GROWER", "workerId": "usr_grower_1", "workerName": "Luciana Pérez" }
  ],
  "autoAssign": true,
  "generatedTaskIds": ["gen_task_001", "gen_task_002"],
  "completedTaskCount": 12,
  "totalTaskCount": 53,
  "progress": 23,
  "createdAt": "2025-11-01T09:00:00Z",
  "updatedAt": "2025-11-20T16:40:00Z"
}

GET/api/task-plans

Listar planes de tareas

Retorna una lista paginada de planes de tareas del club, con filtros opcionales.

Query params

  • Name
    status
    Type
    TaskPlanStatus
    Description

    Filtra por estado (DRAFT, PUBLISHED, ARCHIVED).

  • Name
    targetPhase
    Type
    CropStatus
    Description

    Filtra por fase objetivo incluida en targetPhases.

  • Name
    isTemplate
    Type
    boolean
    Description

    Filtra por plantillas (true / false).

  • Name
    search
    Type
    string
    Description

    Busca por nombre, descripción o tags.

  • Name
    page
    Type
    integer
    Description

    Número de página (por defecto 1).

  • Name
    limit
    Type
    integer
    Description

    Cantidad por página (por defecto 50).

Request

GET
/api/task-plans
curl -G https://api.cannahub.tech/api/task-plans \
  -H "Authorization: Bearer {token}" \
  -H "Tenant-id: {tenantId}" \
  -d status=PUBLISHED \
  -d limit=50

Response

{
  "taskPlans": [
    {
      "id": "plan_01HQ8PLAN001",
      "name": "Protocolo Indoor Fotoperiódica — Coco",
      "targetPhases": ["VEGETATING", "FLOWERING"],
      "status": "PUBLISHED",
      "isTemplate": true,
      "usageCount": 7,
      "estimatedTotalHours": 24.5,
      "tags": ["indoor", "coco"],
      "createdAt": "2025-09-01T10:00:00Z",
      "updatedAt": "2025-09-05T14:30:00Z"
    }
  ],
  "count": 1
}

POST/api/task-plans

Crear un plan de tareas

Crea un nuevo plan de tareas. Requiere name y al menos un item en items. Las horas totales se calculan del lado del servidor a partir de estimatedMinutes.

Atributos requeridos

  • Name
    name
    Type
    string
    Description

    Nombre del plan.

  • Name
    items
    Type
    Omit<TaskPlanItem, 'id'>[]
    Description

    Al menos un item de plantilla.

  • Name
    targetPhases
    Type
    CropStatus[]
    Description

    Fases objetivo del plan.

  • Name
    suggestedTeam
    Type
    SuggestedTeamMember[]
    Description

    Equipo sugerido.

Atributos opcionales

  • Name
    description
    Type
    string
    Description

    Descripción del plan.

  • Name
    tags
    Type
    string[]
    Description

    Etiquetas.

  • Name
    status
    Type
    TaskPlanStatus
    Description

    Estado inicial (por defecto DRAFT).

  • Name
    isTemplate
    Type
    boolean
    Description

    Marca el plan como plantilla.

  • Name
    estimatedDurationDays
    Type
    number
    Description

    Duración estimada en días (V2).

  • Name
    growStyle
    Type
    GrowStyle
    Description

    Estilo de cultivo (V2).

  • Name
    medium
    Type
    GrowMedium
    Description

    Medio de cultivo (V2).

  • Name
    phaseWindows
    Type
    PlanPhaseWindow[]
    Description

    Bandas de fase personalizadas (V2).

  • Name
    blocks
    Type
    TaskBlock[]
    Description

    Bloques incluidos (V2).

  • Name
    environmentTargets
    Type
    EnvironmentTarget[]
    Description

    Objetivos ambientales por semana (V2).

  • Name
    nutrientSchedule
    Type
    NutrientScheduleEntry[]
    Description

    Cronograma de nutrientes (V2).

Request

POST
/api/task-plans
curl -X POST https://api.cannahub.tech/api/task-plans \
  -H "Authorization: Bearer {token}" \
  -H "Tenant-id: {tenantId}" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Protocolo Autofloreciente Indoor",
    "targetPhases": ["VEGETATING", "FLOWERING"],
    "suggestedTeam": [{ "role": "GROWER", "count": 1 }],
    "items": [
      {
        "taskType": "WATERING",
        "title": "Riego inicial",
        "dayOffset": 0,
        "estimatedMinutes": 30,
        "priority": "MEDIUM",
        "assignedRole": "GROWER",
        "order": 0
      }
    ]
  }'

Response (201)

{
  "taskPlan": {
    "id": "plan_1730467200000",
    "name": "Protocolo Autofloreciente Indoor",
    "targetPhases": ["VEGETATING", "FLOWERING"],
    "items": [
      {
        "id": "item_1730467200000_0",
        "taskType": "WATERING",
        "title": "Riego inicial",
        "dayOffset": 0,
        "estimatedMinutes": 30,
        "priority": "MEDIUM",
        "assignedRole": "GROWER",
        "order": 0
      }
    ],
    "suggestedTeam": [{ "role": "GROWER", "count": 1 }],
    "estimatedTotalHours": 0.5,
    "tags": [],
    "status": "DRAFT",
    "isTemplate": false,
    "usageCount": 0,
    "createdAt": "2025-11-01T12:00:00Z",
    "updatedAt": "2025-11-01T12:00:00Z"
  }
}

GET/api/task-plans/:planId

Obtener un plan

Retorna un plan de tareas por su planId. Si no existe, la respuesta es 404 Not Found.

Request

GET
/api/task-plans/:planId
curl https://api.cannahub.tech/api/task-plans/plan_01HQ8PLAN001 \
  -H "Authorization: Bearer {token}" \
  -H "Tenant-id: {tenantId}"

Response

{
  "taskPlan": {
    "id": "plan_01HQ8PLAN001",
    "name": "Protocolo Indoor Fotoperiódica — Coco",
    "targetPhases": ["VEGETATING", "FLOWERING"],
    "status": "PUBLISHED",
    "isTemplate": true,
    "usageCount": 7,
    "estimatedTotalHours": 24.5,
    "tags": ["indoor", "coco"],
    "items": [],
    "suggestedTeam": [{ "role": "GROWER", "count": 2 }],
    "createdAt": "2025-09-01T10:00:00Z",
    "updatedAt": "2025-09-05T14:30:00Z"
  }
}

PATCH/api/task-plans/:planId

Actualizar un plan

Actualiza campos de un plan existente. Acepta cualquier subconjunto de los atributos de creación más status. Si se envían items, reemplazan la lista completa (los items sin id reciben uno nuevo).

Atributos opcionales

  • Name
    name
    Type
    string
    Description
  • Name
    description
    Type
    string
    Description
  • Name
    targetPhases
    Type
    CropStatus[]
    Description
  • Name
    items
    Type
    TaskPlanItem[]
    Description
  • Name
    suggestedTeam
    Type
    SuggestedTeamMember[]
    Description
  • Name
    tags
    Type
    string[]
    Description
  • Name
    status
    Type
    TaskPlanStatus
    Description
  • Name
    growStyle
    Type
    GrowStyle
    Description
  • Name
    medium
    Type
    GrowMedium
    Description
  • Name
    environmentTargets
    Type
    EnvironmentTarget[]
    Description
  • Name
    nutrientSchedule
    Type
    NutrientScheduleEntry[]
    Description
  • Name
    blocks
    Type
    TaskBlock[]
    Description
  • Name
    phaseWindows
    Type
    PlanPhaseWindow[]
    Description
  • Name
    estimatedDurationDays
    Type
    number
    Description

Request

PATCH
/api/task-plans/:planId
curl -X PATCH https://api.cannahub.tech/api/task-plans/plan_01HQ8PLAN001 \
  -H "Authorization: Bearer {token}" \
  -H "Tenant-id: {tenantId}" \
  -H "Content-Type: application/json" \
  -d '{ "status": "PUBLISHED", "tags": ["indoor", "coco", "premium"] }'

Response

{
  "taskPlan": {
    "id": "plan_01HQ8PLAN001",
    "name": "Protocolo Indoor Fotoperiódica — Coco",
    "status": "PUBLISHED",
    "tags": ["indoor", "coco", "premium"],
    "updatedAt": "2025-11-01T13:00:00Z"
  }
}

DELETE/api/task-plans/:planId

Eliminar un plan

Elimina un plan de tareas. Retorna { "success": true } en caso de éxito, o 404 Not Found si el plan no existe.

Request

DELETE
/api/task-plans/:planId
curl -X DELETE https://api.cannahub.tech/api/task-plans/plan_01HQ8PLAN001 \
  -H "Authorization: Bearer {token}" \
  -H "Tenant-id: {tenantId}"

Response

{
  "success": true
}

POST/api/task-plans/:planId/duplicate

Duplicar un plan

Crea una copia del plan indicado. La copia se crea en estado DRAFT, con isTemplate: false, usageCount: 0 y el nombre sufijado con (copia). Cada item recibe un nuevo id.

Request

POST
/api/task-plans/:planId/duplicate
curl -X POST https://api.cannahub.tech/api/task-plans/plan_01HQ8PLAN001/duplicate \
  -H "Authorization: Bearer {token}" \
  -H "Tenant-id: {tenantId}"

Response (201)

{
  "taskPlan": {
    "id": "plan_1730470800000",
    "name": "Protocolo Indoor Fotoperiódica — Coco (copia)",
    "status": "DRAFT",
    "isTemplate": false,
    "usageCount": 0,
    "createdAt": "2025-11-01T14:00:00Z",
    "updatedAt": "2025-11-01T14:00:00Z"
  }
}

GET/api/task-plans/:planId/analytics

Analíticas de un plan

Retorna métricas agregadas de un plan: adherencia, desvío de horas, resumen por asignación/ciclo y sugerencias de optimización.

Response

  • Name
    planId
    Type
    string
    Description

    ID del plan.

  • Name
    adherence
    Type
    object
    Description

    Adherencia de las tareas.

    • Name
      onTime
      Type
      number
      Description

      Tareas completadas a tiempo.

    • Name
      delayed
      Type
      number
      Description

      Tareas demoradas.

    • Name
      skipped
      Type
      number
      Description

      Tareas omitidas.

    • Name
      total
      Type
      number
      Description

      Total de tareas.

    • Name
      rate
      Type
      number
      Description

      Tasa de adherencia (0-100).

  • Name
    hoursDeviation
    Type
    object
    Description

    Desvío de horas estimadas vs. reales (estimated, actual, variance en %).

  • Name
    assignments
    Type
    array
    Description

    Resumen por asignación (id, cropName, startDate, completedAt?, adherenceRate, yieldGPerPlant?).

  • Name
    suggestions
    Type
    string[]
    Description

    Sugerencias de optimización generadas.

Request

GET
/api/task-plans/:planId/analytics
curl https://api.cannahub.tech/api/task-plans/plan_01HQ8PLAN001/analytics \
  -H "Authorization: Bearer {token}" \
  -H "Tenant-id: {tenantId}"

Response

{
  "planId": "plan_01HQ8PLAN001",
  "adherence": {
    "onTime": 42,
    "delayed": 8,
    "skipped": 3,
    "total": 53,
    "rate": 79
  },
  "hoursDeviation": {
    "estimated": 24.5,
    "actual": 28.2,
    "variance": 15.1
  },
  "assignments": [
    {
      "id": "assign-1",
      "cropName": "Gorilla Glue #4 - Sala A",
      "startDate": "2025-11-01",
      "completedAt": "2025-12-20",
      "adherenceRate": 85,
      "yieldGPerPlant": 142
    }
  ],
  "suggestions": [
    "Las tareas de la Semana 3 se retrasan con frecuencia. Considera adelantar 1-2 días.",
    "La EC objetivo de semana 5 (1.4) se supera frecuentemente. Revisa la dosificación."
  ]
}

GET/api/task-plans/assignments

Listar asignaciones

Retorna una lista paginada de asignaciones, con filtros opcionales.

Query params

  • Name
    cropId
    Type
    string
    Description

    Filtra por cultivo.

  • Name
    taskPlanId
    Type
    string
    Description

    Filtra por plan.

  • Name
    status
    Type
    TaskPlanAssignmentStatus
    Description

    Filtra por estado (ACTIVE, PAUSED, COMPLETED, CANCELLED).

  • Name
    page
    Type
    integer
    Description

    Página (por defecto 1).

  • Name
    limit
    Type
    integer
    Description

    Cantidad por página (por defecto 50).

Request

GET
/api/task-plans/assignments
curl -G https://api.cannahub.tech/api/task-plans/assignments \
  -H "Authorization: Bearer {token}" \
  -H "Tenant-id: {tenantId}" \
  -d cropId=crop_01HQ8CROP001

Response

{
  "assignments": [
    {
      "id": "assign_01HQ8ASSIGN001",
      "taskPlanId": "plan_01HQ8PLAN001",
      "taskPlanName": "Protocolo Indoor Fotoperiódica — Coco",
      "cropId": "crop_01HQ8CROP001",
      "cropName": "Gorilla Glue #4 — Sala A",
      "cropPhase": "VEGETATING",
      "startDate": "2025-11-01",
      "status": "ACTIVE",
      "progress": 23,
      "completedTaskCount": 12,
      "totalTaskCount": 53
    }
  ],
  "count": 1
}

POST/api/task-plans/assignments

Crear una asignación

Asigna un plan a un cultivo. Requiere taskPlanId, cropId y startDate. Al crear la asignación en modo real, el servidor genera automáticamente las tareas concretas del plan (operación idempotente: se omite si ya existen tareas para la asignación).

Atributos requeridos

  • Name
    taskPlanId
    Type
    string
    Description

    Plan a asignar.

  • Name
    cropId
    Type
    string
    Description

    Cultivo destino.

  • Name
    startDate
    Type
    string
    Description

    Fecha de inicio (ISO).

Atributos opcionales

  • Name
    cropName
    Type
    string
    Description

    Nombre del cultivo (denormalizado).

  • Name
    cropPhase
    Type
    CropStatus
    Description

    Fase del cultivo.

  • Name
    workerAssignments
    Type
    WorkerAssignment[]
    Description

    Trabajadores asignados por rol.

  • Name
    autoAssign
    Type
    boolean
    Description

    Auto-asignar tareas a los trabajadores.

Request

POST
/api/task-plans/assignments
curl -X POST https://api.cannahub.tech/api/task-plans/assignments \
  -H "Authorization: Bearer {token}" \
  -H "Tenant-id: {tenantId}" \
  -H "Content-Type: application/json" \
  -d '{
    "taskPlanId": "plan_01HQ8PLAN001",
    "cropId": "crop_01HQ8CROP001",
    "startDate": "2025-11-01",
    "autoAssign": true
  }'

Response (201)

{
  "assignment": {
    "id": "assign_1730467200000",
    "taskPlanId": "plan_01HQ8PLAN001",
    "taskPlanName": "Protocolo Indoor Fotoperiódica — Coco",
    "cropId": "crop_01HQ8CROP001",
    "cropName": "Gorilla Glue #4 — Sala A",
    "cropPhase": "VEGETATING",
    "startDate": "2025-11-01",
    "status": "ACTIVE",
    "workerAssignments": [],
    "autoAssign": true,
    "generatedTaskIds": ["gen_task_001", "gen_task_002"],
    "completedTaskCount": 0,
    "totalTaskCount": 53,
    "progress": 0,
    "createdAt": "2025-11-01T12:00:00Z",
    "updatedAt": "2025-11-01T12:00:00Z"
  }
}

POST/api/task-plans/assignments/batch

Asignar a varios cultivos

Asigna un mismo plan a múltiples cultivos en una sola llamada. Requiere taskPlanId, un arreglo no vacío cropIds y startDate.

Atributos requeridos

  • Name
    taskPlanId
    Type
    string
    Description

    Plan a asignar.

  • Name
    cropIds
    Type
    string[]
    Description

    IDs de los cultivos (no vacío).

  • Name
    startDate
    Type
    string
    Description

    Fecha de inicio común (ISO).

Atributos opcionales

  • Name
    workerAssignments
    Type
    WorkerAssignment[]
    Description

    Trabajadores asignados por rol.

  • Name
    autoAssign
    Type
    boolean
    Description

    Auto-asignar tareas.

Request

POST
/api/task-plans/assignments/batch
curl -X POST https://api.cannahub.tech/api/task-plans/assignments/batch \
  -H "Authorization: Bearer {token}" \
  -H "Tenant-id: {tenantId}" \
  -H "Content-Type: application/json" \
  -d '{
    "taskPlanId": "plan_01HQ8PLAN001",
    "cropIds": ["crop_01", "crop_02", "crop_03"],
    "startDate": "2025-11-01"
  }'

Response (201)

{
  "assignments": [
    {
      "id": "assign_batch_1730467200000_0",
      "taskPlanId": "plan_01HQ8PLAN001",
      "cropId": "crop_01",
      "startDate": "2025-11-01",
      "status": "ACTIVE",
      "progress": 0
    }
  ],
  "count": 3
}

GET/api/task-plans/assignments/:assignmentId

Obtener una asignación

Retorna una asignación por su assignmentId, o 404 Not Found si no existe.

Request

GET
/api/task-plans/assignments/:assignmentId
curl https://api.cannahub.tech/api/task-plans/assignments/assign_01HQ8ASSIGN001 \
  -H "Authorization: Bearer {token}" \
  -H "Tenant-id: {tenantId}"

Response

{
  "assignment": {
    "id": "assign_01HQ8ASSIGN001",
    "taskPlanId": "plan_01HQ8PLAN001",
    "cropId": "crop_01HQ8CROP001",
    "cropName": "Gorilla Glue #4 — Sala A",
    "status": "ACTIVE",
    "progress": 23,
    "completedTaskCount": 12,
    "totalTaskCount": 53,
    "generatedTaskIds": ["gen_task_001", "gen_task_002"]
  }
}

PATCH/api/task-plans/assignments/:assignmentId

Actualizar una asignación

Actualiza una asignación. Cambiar status a PAUSED o COMPLETED registra automáticamente pausedAt / completedAt.

Atributos opcionales

  • Name
    status
    Type
    TaskPlanAssignmentStatus
    Description

    Nuevo estado.

  • Name
    workerAssignments
    Type
    WorkerAssignment[]
    Description

    Trabajadores asignados.

  • Name
    autoAssign
    Type
    boolean
    Description

    Auto-asignación de tareas.

  • Name
    completedTaskCount
    Type
    number
    Description

    Cantidad de tareas completadas.

  • Name
    progress
    Type
    number
    Description

    Progreso (0-100).

Request

PATCH
/api/task-plans/assignments/:assignmentId
curl -X PATCH https://api.cannahub.tech/api/task-plans/assignments/assign_01HQ8ASSIGN001 \
  -H "Authorization: Bearer {token}" \
  -H "Tenant-id: {tenantId}" \
  -H "Content-Type: application/json" \
  -d '{ "status": "PAUSED" }'

Response

{
  "assignment": {
    "id": "assign_01HQ8ASSIGN001",
    "status": "PAUSED",
    "pausedAt": "2025-11-10T09:30:00Z",
    "updatedAt": "2025-11-10T09:30:00Z"
  }
}

DELETE/api/task-plans/assignments/:assignmentId

Eliminar una asignación

Elimina una asignación y cascadea sobre sus tareas generadas.

Query params

  • Name
    keepCompleted
    Type
    boolean
    Description

    Por defecto true. Cuando es true, las tareas completadas sobreviven al borrado y se desvinculan de la asignación (pasan a ser entradas manuales); solo se eliminan las tareas pendientes / en progreso / canceladas. Cuando es false, se eliminan todas las tareas vinculadas.

Request

DELETE
/api/task-plans/assignments/:assignmentId
curl -X DELETE "https://api.cannahub.tech/api/task-plans/assignments/assign_01HQ8ASSIGN001?keepCompleted=true" \
  -H "Authorization: Bearer {token}" \
  -H "Tenant-id: {tenantId}"

Response

{
  "success": true,
  "keepCompleted": true,
  "deleted": {
    "openTaskIds": ["gen_task_003", "gen_task_004"],
    "completedTaskIds": []
  },
  "detachedTaskIds": ["gen_task_001", "gen_task_002"],
  "failed": []
}

GET/api/task-plans/blocks

Listar bloques

Retorna la lista de bloques disponibles: los built-in del sistema más los personalizados del club.

Query params

  • Name
    category
    Type
    BlockCategory
    Description

    Filtra por categoría (NUTRIENT, IPM, ENVIRONMENT, TRAINING, HARVEST, GENERAL).

  • Name
    search
    Type
    string
    Description

    Busca por nombre, descripción o tags.

  • Name
    page
    Type
    integer
    Description

    Página (por defecto 1).

  • Name
    limit
    Type
    integer
    Description

    Cantidad por página (por defecto 50).

Request

GET
/api/task-plans/blocks
curl -G https://api.cannahub.tech/api/task-plans/blocks \
  -H "Authorization: Bearer {token}" \
  -H "Tenant-id: {tenantId}" \
  -d category=IPM

Response

{
  "blocks": [
    {
      "id": "block-ipm-preventivo",
      "name": "IPM Preventivo Semanal",
      "category": "IPM",
      "color": "#8BC34A",
      "tags": ["ipm", "preventivo"],
      "isBuiltIn": true,
      "usageCount": 34,
      "items": []
    }
  ],
  "count": 1
}

POST/api/task-plans/blocks

Crear un bloque

Crea un bloque personalizado del club. Requiere name, category y color.

Atributos requeridos

  • Name
    name
    Type
    string
    Description

    Nombre del bloque.

  • Name
    category
    Type
    BlockCategory
    Description

    Categoría del bloque.

  • Name
    color
    Type
    string
    Description

    Color hex para la línea de tiempo.

Atributos opcionales

  • Name
    description
    Type
    string
    Description

    Descripción.

  • Name
    items
    Type
    array
    Description

    Items del bloque.

  • Name
    tags
    Type
    string[]
    Description

    Etiquetas.

Request

POST
/api/task-plans/blocks
curl -X POST https://api.cannahub.tech/api/task-plans/blocks \
  -H "Authorization: Bearer {token}" \
  -H "Tenant-id: {tenantId}" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Defoliación Semana 5",
    "category": "TRAINING",
    "color": "#4CAF50",
    "items": []
  }'

Response (201)

{
  "block": {
    "id": "block-custom-1730467200000",
    "name": "Defoliación Semana 5",
    "description": "",
    "category": "TRAINING",
    "items": [],
    "color": "#4CAF50",
    "tags": [],
    "isBuiltIn": false,
    "usageCount": 0
  }
}

GET/api/task-plans/blocks/:blockId

Obtener un bloque

Retorna un bloque por su blockId, o 404 Not Found si no existe.

Request

GET
/api/task-plans/blocks/:blockId
curl https://api.cannahub.tech/api/task-plans/blocks/block-ipm-preventivo \
  -H "Authorization: Bearer {token}" \
  -H "Tenant-id: {tenantId}"

Response

{
  "block": {
    "id": "block-ipm-preventivo",
    "name": "IPM Preventivo Semanal",
    "category": "IPM",
    "color": "#8BC34A",
    "isBuiltIn": true,
    "items": []
  }
}

PATCH/api/task-plans/blocks/:blockId

Actualizar un bloque

Actualiza un bloque personalizado. Los bloques built-in no pueden modificarse (respuesta 403 Forbidden).

Atributos opcionales

  • Name
    name
    Type
    string
    Description
  • Name
    description
    Type
    string
    Description
  • Name
    category
    Type
    BlockCategory
    Description
  • Name
    items
    Type
    array
    Description
  • Name
    color
    Type
    string
    Description
  • Name
    tags
    Type
    string[]
    Description

Request

PATCH
/api/task-plans/blocks/:blockId
curl -X PATCH https://api.cannahub.tech/api/task-plans/blocks/block-custom-1730467200000 \
  -H "Authorization: Bearer {token}" \
  -H "Tenant-id: {tenantId}" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Defoliación (revisada)", "color": "#2E7D32" }'

Response

{
  "block": {
    "id": "block-custom-1730467200000",
    "name": "Defoliación (revisada)",
    "color": "#2E7D32",
    "category": "TRAINING",
    "isBuiltIn": false
  }
}

DELETE/api/task-plans/blocks/:blockId

Eliminar un bloque

Elimina un bloque personalizado. Los bloques built-in no pueden eliminarse (respuesta 403 Forbidden).

Request

DELETE
/api/task-plans/blocks/:blockId
curl -X DELETE https://api.cannahub.tech/api/task-plans/blocks/block-custom-1730467200000 \
  -H "Authorization: Bearer {token}" \
  -H "Tenant-id: {tenantId}"

Response

{
  "success": true
}

POST/api/task-plans/generate-tasks

Generar tareas desde una asignación

Genera tareas concretas a partir de una asignación existente. Requiere assignmentId. La operación es idempotente: si la asignación ya tiene tareas, no se generan nuevas (salvo que se use force).

Body

  • Name
    assignmentId
    Type
    string
    Description

    ID de la asignación desde la cual generar tareas.

  • Name
    force
    Type
    boolean
    Description

    Re-genera aunque ya existan tareas. Retorna un diff (deletedTaskIds, preservedTaskIds).

  • Name
    dryRun
    Type
    boolean
    Description

    No modifica nada; retorna solo el preview con los conteos (toCreate, toDelete, toPreserve).

Response

  • Name
    generatedCount
    Type
    number
    Description

    Cantidad de tareas generadas.

  • Name
    taskIds
    Type
    string[]
    Description

    IDs de las tareas resultantes.

  • Name
    skippedReason
    Type
    enum
    Description

    Motivo por el que no se generó nada: already-generated, no-items o dry-run.

  • Name
    deletedTaskIds
    Type
    string[]
    Description

    Presente con force=true: tareas eliminadas en la regeneración.

  • Name
    preservedTaskIds
    Type
    string[]
    Description

    Presente con force=true: tareas preservadas.

  • Name
    preview
    Type
    object
    Description

    Presente con dryRun=true: { toCreate, toDelete, toPreserve }.

Request

POST
/api/task-plans/generate-tasks
curl -X POST https://api.cannahub.tech/api/task-plans/generate-tasks \
  -H "Authorization: Bearer {token}" \
  -H "Tenant-id: {tenantId}" \
  -H "Content-Type: application/json" \
  -d '{ "assignmentId": "assign_01HQ8ASSIGN001" }'

Response (201)

{
  "generatedCount": 53,
  "taskIds": ["gen_task_001", "gen_task_002", "gen_task_003"]
}

Flujo de asignación y generación

Loading diagram...

React Query Hooks

Cannahub incluye query keys y hooks de React Query para los planes de tareas y sus asignaciones.

Query Keys

// features/Club/TaskPlans/hooks/keys.ts
export const taskPlanKeys = {
  all: ['task-plans'] as const,
  lists: () => [...taskPlanKeys.all, 'list'] as const,
  list: (filters?: TaskPlanFilters) => [...taskPlanKeys.lists(), filters] as const,
  details: () => [...taskPlanKeys.all, 'detail'] as const,
  detail: (id: string) => [...taskPlanKeys.details(), id] as const,
  assignments: () => [...taskPlanKeys.all, 'assignments'] as const,
  assignmentList: (filters?: AssignmentFilters) =>
    [...taskPlanKeys.assignments(), 'list', filters] as const,
  assignment: (id: string) => [...taskPlanKeys.assignments(), id] as const,
}

Was this page helpful?