CreaRack-SL

Vínculos Zoho en modo creación de tarea (patrón pending)

Funcionalidadactivecreado Sun May 17#tasks#zoho#calendar#mail#frontend#react#typescript#ux

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ónMotivo
Máximo 1 pendingEvent por creaciónEvitar confusión de multi-evento en flujo create
Múltiples pendingEmailLinks permitidosEmails son vínculos ligeros, no crean recursos nuevos en Zoho
+ Nuevo email deshabilitado en createEl deep-link de compose necesita taskId para contexto útil
zohoError no descarta la tareaLa tarea ya fue creada; el error Zoho es parcial, no fatal

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 llaman onPendingEventChange / onPendingEmailLinksChange en 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 badge pendiente en itálica.
  • El botón + Crear evento se deshabilita si ya hay un pendingEvent (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);

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:

  1. Intenta crear el evento Calendar y vincularlo.
  2. Intenta vincular cada email pendiente.
  3. Acumula fallos en zohoFailures[].
  4. Si hay fallos: llama setZohoError(...), llama onSave(saved) (la card aparece en KanbanMini) y no cierra el modal — el usuario ve el error y puede decidir.
  5. Si todo OK: flujo normal onSave(saved).

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étodoRutaUso
POST/api/zoho/calendarCrear evento en Zoho Calendar
POST/api/tasks/{id}/links/calendarVincular evento creado a la task
POST/api/tasks/{id}/links/emailVincular email a la task
GET/api/zoho/statusResolver emails Zoho de assignees (create mode)

Véase también

  • [[entity—tasks—component—task-zoho-links]]
  • [[entity—tasks—component—task-modal]]