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
estimatedMinutesde 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,dosageyunit(ml/Log/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
endConditionesFIXED_COUNT.
- Name
endDate- Type
- string
- Description
Fecha de fin cuando
endConditionesSPECIFIC_DATE.
- Name
dependencies- Type
- array
- Description
Dependencias con otros items (
itemId+type:MUST_COMPLETEoMUST_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
idniblockId, 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"
}
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
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
}
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).
Si falta name o items está vacío, la respuesta es 400 Validation Error. En modo real, Tenant-id es obligatorio.
Request
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"
}
}
Obtener un plan
Retorna un plan de tareas por su planId. Si no existe, la respuesta es 404 Not Found.
Request
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"
}
}
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
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"
}
}
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
curl -X DELETE https://api.cannahub.tech/api/task-plans/plan_01HQ8PLAN001 \
-H "Authorization: Bearer {token}" \
-H "Tenant-id: {tenantId}"
Response
{
"success": true
}
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
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"
}
}
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,varianceen %).
- 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
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."
]
}
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
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
}
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
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"
}
}
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
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
}
Obtener una asignación
Retorna una asignación por su assignmentId, o 404 Not Found si no existe.
Request
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"]
}
}
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
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"
}
}
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 estrue, 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 esfalse, se eliminan todas las tareas vinculadas.
Ante una falla parcial en el borrado de tareas, la respuesta es HTTP 207 con el detalle por ID de lo que se eliminó, desvinculó o falló.
Request
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": []
}
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
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
}
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
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
}
}
Obtener un bloque
Retorna un bloque por su blockId, o 404 Not Found si no existe.
Request
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": []
}
}
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
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
}
}
Eliminar un bloque
Elimina un bloque personalizado. Los bloques built-in no pueden eliminarse (respuesta 403 Forbidden).
Request
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
}
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
previewcon 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-itemsodry-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
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
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,
}