Vínculos Zoho en modo creación de tarea (patrón pending)
Vínculos Zoho en modo creación de tarea (patrón pending)
Resumen
Antes de este cambio (commit 18f32fb, 2026-05-17), los vínculos con Zoho Calendar y Zoho Mail solo podían añadirse a tareas ya existentes: el usuario creaba la tarea, la reabriba en modo edición y añadía el evento o email. Esta feature elimina esa fricción extendiendo TaskZohoLinks y TaskModal para soportar un modo creación con estado pendiente.
Motivación
El patrón era incoherente con el flujo de adjuntos (pendingFiles), que ya permitía añadir archivos antes de guardar la tarea. El ticket s68 unifica ambas experiencias.
Arquitectura: patrón pending
El diseño replica exactamente el patrón pendingFiles de attachments:
[TaskModal — create mode]
│
├─ pendingEvent: PendingCalendarEvent | null
├─ pendingEmailLinks: PendingEmailLink[]
│
└─ <TaskZohoLinks taskId={null} ...callbacks />
│ usuario añade evento/email
└─ llama onPendingEventChange / onPendingEmailLinksChange
↑ estado vive en TaskModal
[handleSubmit]
1. POST /api/tasks → saved.id
2. si pendingEvent:
POST /api/zoho/calendar → { uid, calendar_uid, url }
POST /api/tasks/{id}/links/calendar
3. para cada pendingEmailLink:
POST /api/tasks/{id}/links/email
4. si hubo fallos Zoho: mostrar zohoError (la tarea YA existe)
Invariantes de diseño
| Decisión | Motivo |
|---|---|
Máximo 1 pendingEvent por creación | Evitar confusión de multi-evento en flujo create |
Múltiples pendingEmailLinks permitidos | Emails son vínculos ligeros, no crean recursos nuevos en Zoho |
+ Nuevo email deshabilitado en create | El deep-link de compose necesita taskId para contexto útil |
zohoError no descarta la tarea | La tarea ya fue creada; el error Zoho es parcial, no fatal |
Cambios en TaskZohoLinks
Nuevos tipos exportados
export interface PendingCalendarEvent {
title: string;
description?: string;
start: string; // ISO UTC
end: string; // ISO UTC
location?: string;
attendees: string[];
}
export interface PendingEmailLink {
zoho_message_id: string;
thread_id?: string;
subject: string;
from_address: string;
email_date: string;
url: string;
}
Nueva prop taskId: number | null
taskId !== null→ modo edit: comportamiento original (POST/DELETE inmediato al backend).taskId === null→ modo create: las acciones del usuario llamanonPendingEventChange/onPendingEmailLinksChangeen lugar de hacer fetch al backend.
Nuevas props del componente
pendingEvent?: PendingCalendarEvent | null;
onPendingEventChange?: (event: PendingCalendarEvent | null) => void;
pendingEmailLinks?: PendingEmailLink[];
onPendingEmailLinksChange?: (links: PendingEmailLink[]) => void;
Comportamiento visual en create mode
- Items pendientes se renderizan con borde discontinuo (
border: 1px dashed) y badgependienteen itálica. - El botón
+ Crear eventose deshabilita si ya hay unpendingEvent(single-event constraint). - El mini-form muestra el aviso: “El evento se crea cuando pulses ‘Crear’ la tarea abajo.”
Carga de statusMembers en create mode
En edit mode, statusMembers se carga junto con los links del backend. En create mode no hay taskId, pero sí se necesitan los emails Zoho de los assignees para resolver attendees del evento. Se añadió un segundo useEffect que llama GET /api/zoho/status solo cuando isCreate === true.
Optimización de refetch
Se añadió lastFetchedTaskIdRef (useRef) para evitar re-fetch del backend en re-renders del componente en edit mode cuando taskId no cambia.
Función resolveAttendees extraída
La lógica de resolución de emails Zoho a partir de selectedAssignees + statusMembers se extrajo a una función pura resolveAttendees() para poder reutilizarla tanto en el path create como edit.
Cambios en TaskModal
Nuevo estado local
const [pendingEvent, setPendingEvent] = useState<PendingCalendarEvent | null>(null);
const [pendingEmailLinks, setPendingEmailLinks] = useState<PendingEmailLink[]>([]);
const [zohoError, setZohoError] = useState<string | null>(null);
TaskZohoLinks visible en create mode
Antes: {task && !isLocal && <TaskZohoLinks .../>} — solo edit.
Ahora: {!isLocal && <TaskZohoLinks taskId={task ? task.id : null} .../>} — ambos modos.
Ejecución de pendings en handleSubmit
Tras POST /api/tasks exitoso, si !task (create mode), el modal:
- Intenta crear el evento Calendar y vincularlo.
- Intenta vincular cada email pendiente.
- Acumula fallos en
zohoFailures[]. - Si hay fallos: llama
setZohoError(...), llamaonSave(saved)(la card aparece en KanbanMini) y no cierra el modal — el usuario ve el error y puede decidir. - Si todo OK: flujo normal
onSave(saved).
Banner zohoError
Se renderiza un banner inline bajo TaskZohoLinks con color danger y fondo semitransparente cuando zohoError !== null. Mensaje ejemplo:
“Zoho falló en: crear evento Calendar: 403. La tarea SÍ se creó (#42). Cierra el modal y edítala para reintentar.”
Endpoints implicados
| Método | Ruta | Uso |
|---|---|---|
POST | /api/zoho/calendar | Crear evento en Zoho Calendar |
POST | /api/tasks/{id}/links/calendar | Vincular evento creado a la task |
POST | /api/tasks/{id}/links/email | Vincular email a la task |
GET | /api/zoho/status | Resolver emails Zoho de assignees (create mode) |
Véase también
- [[entity—tasks—component—task-zoho-links]]
- [[entity—tasks—component—task-modal]]