Ir al contenido

Misiones

El módulo de Misiones cubre el ciclo completo de una misión (tarea de desarrollo/evaluación asignada a un colaborador): verla, avanzarla, entregarla, y — si el usuario autenticado es responsable de otras personas — asignarla a un miembro de su equipo y evaluarla (aprobar/rechazar tareas o la misión completa, calificarla y agendar feedback).

Acciones cubiertas:

  • Ver mis misiones (listado + resumen de estados) y el detalle de una misión, incluyendo tareas, archivos de soporte, entregas y feedbacks.
  • Crear una misión para mí mismo o para un miembro de mi equipo, con tareas opcionales y vínculo opcional a un proceso/objetivo de evaluación.
  • Avanzar el estado de mi misión (marcarla leída, iniciarla) y enviarla a revisión adjuntando una entrega (comentario + archivo/link) a nivel de misión o de tarea.
  • Eliminar una misión propia o de mi equipo.
  • Evaluar misiones de mi equipo: aprobar o rechazar tareas/misión con calificación (1-5), rechazar con comentario, agendar y marcar feedback como realizado.
  • Consultar la actividad (bitácora de auditoría) de una misión.

Prerrequisitos:

  • El controlador exige JWT y contexto de tenant (@RequiresTenancy()) en todas las rutas; no requiere RequiresOrganizationalInfo para las rutas de este documento (a diferencia del listado team/all, fuera de alcance — ver nota abajo).

:::

Este documento no cubre GET /missions/team/all, POST /v2/missions/search, POST /v2/missions/search/creators ni POST /v2/missions/search/export (tablero/búsqueda administrativa fuera del alcance de autoservicio) ni los endpoints de “planes de acción” legacy bajo /me/mission/* (action-plans, team/:username, team/summary/all), que pertenecen a un módulo distinto (src/mission/, migración de planes de acción legacy) y no son consumidos por la pantalla de Misiones de referencia.

sequenceDiagram
    participant App as App móvil
    participant API as th-core-api

    Note over App,API: Carga inicial de Mis misiones
    App->>API: GET /missions/summary
    API-->>App: contadores por estado + avgCsat/avgCes
    App->>API: GET /missions?filter.status=$btw:1,6
    API-->>App: listado paginado de mis misiones

    Note over App,API: Abrir el detalle de una misión
    App->>API: PATCH /missions/:missionId (status=READ) si estaba ASSIGNED
    App->>API: GET /missions/:missionId
    API-->>App: misión completa (tareas, archivos, entregas, feedbacks)
    App->>API: GET /missions/:missionId/activity
    API-->>App: bitácora de eventos

    Note over App,API: Asignar una misión nueva (a mí o a un miembro de mi equipo)
    App->>API: POST /missions o POST /missions/:userId
    API-->>App: misión creada (status ASSIGNED)

    Note over App,API: Avanzar y entregar mi misión
    App->>API: PATCH /missions/:missionId (status=IN_PROGRESS)
    Note over App: bloqueado si startDate es futura
    App->>API: POST /missions/:missionId/deliveries o /tasks/:taskId/deliveries
    App->>API: PATCH /missions/tasks/:taskId (taskStatus=SENT)
    App->>API: PATCH /missions/:missionId (status=COMPLETED, missionDifficulty)
    API-->>App: misión en revisión

    Note over App,API: Evaluar la misión de un miembro del equipo
    App->>API: PATCH /missions/tasks/:taskId (taskQuality) luego (taskStatus=APPROVED|REJECTED)
    App->>API: PATCH /missions/:missionId/deliveries/:deliveryId (rejectionComment) si se rechaza
    App->>API: PATCH /missions/:missionId (status calculado: TO_BE_CORRECTED, APPROVED o FINALIZED)
    API-->>App: misión evaluada

    Note over App,API: Agendar y cerrar feedback
    App->>API: POST /missions/:missionId/feedbacks
    App->>API: PATCH /missions/feedbacks/:feedbackId (feedbackStatus=DONE)
    Note over App: si la misión estaba APPROVED, se envía además PATCH status=FINALIZED

    Note over App,API: Eliminar
    App->>API: DELETE /missions/:missionId

Resumen de estados de mis misiones (usuario autenticado).

No recibe query params.

Response:

Campo Tipo Descripción
assigned number Misiones en estado ASSIGNED (1).
read number Misiones en estado READ (2). Se calcula en el backend pero no está documentado en el Swagger del endpoint — verificado en getMissionSummaryForUser.
inProgress number Misiones en estado IN_PROGRESS (3).
toBeCorrected number Misiones en estado TO_BE_CORRECTED (5).
toBeReviewed number Misiones en estado COMPLETED (4), es decir enviadas y pendientes de revisión.
avgCsat number Promedio de missionQuality (1-5) sobre misiones FINALIZED calificadas (0 si no hay ninguna).
avgCes number Promedio de missionDifficulty (1-5) sobre misiones FINALIZED calificadas (0 si no hay ninguna).
{
"assigned": 2,
"read": 0,
"inProgress": 1,
"toBeCorrected": 0,
"toBeReviewed": 3,
"avgCsat": 4.5,
"avgCes": 3.8
}

Sin errores específicos adicionales a los comunes.

Igual que el anterior, para un miembro del equipo. Misma respuesta y estructura.

Path param: userId (string, usr_usuario del colaborador).

Listado paginado de mis misiones (usuario autenticado). Usa nestjs-paginate.

Query params (ver también Convenciones para el formato general de paginación):

Param Tipo Descripción
page, limit number Paginación (defaultLimit 20, maxLimit 100).
sortBy string Formato campo:ASC|DESC. Columnas ordenables: id, userId, status, missionProgress, assignedDate, readDate, startDate, endDate, deadlineDate, finishedDate, processType, processId, objectiveId. Orden por defecto: createdAt:DESC.
search string Búsqueda libre sobre missionTitle, missionDescription, createdBy, userId.
filter.status string Filtro por estado. La pantalla de referencia usa $btw:1,6 (rango ASSIGNED..APPROVED) para excluir FINALIZED y DELETED de la vista activa. Columnas filtrables: id, userId, status, missionProgress, missionQuality, missionDifficulty, createdBy, missionTitle, missionDescription, processType, processId, objectiveId, assignedDate, readDate, startDate, endDate, deadlineDate, finishedDate.
select string Selección de columnas (JSON:API).

Response: { data, meta, links } (JSON:API paginado). Cada ítem trae los campos base de la misión más createdByFullName y missionsTasksSummaryno trae missionsTasks, supportFiles, feedbacks ni deliveries completos (para eso usar el detalle).

{
"data": [
{
"id": 8937,
"userId": "jperez",
"processType": null,
"processId": null,
"objectiveId": null,
"missionTitle": "Actualizar manual de onboarding",
"missionDescription": "Revisar y actualizar el manual con los cambios de política 2026",
"assignedDate": "2026-07-14T09:12:00.000Z",
"readDate": null,
"startDate": "2026-07-15T00:00:00.000Z",
"endDate": "2026-07-25T00:00:00.000Z",
"status": 1,
"missionProgress": 0,
"missionQuality": null,
"missionDifficulty": null,
"createdBy": "jperez",
"createdByFullName": "Juan Pérez",
"deadlineDate": "2026-07-25T00:00:00.000Z",
"finishedDate": null,
"createdAt": "2026-07-14T09:12:00.000Z",
"updatedAt": "2026-07-14T09:12:00.000Z",
"missionsTasksSummary": { "pending": 2, "sent": 0, "approved": 0, "rejected": 0, "total": 2 }
}
],
"meta": {
"itemsPerPage": 20,
"totalItems": 1,
"currentPage": 1,
"totalPages": 1,
"sortBy": [["createdAt", "DESC"]]
},
"links": { "current": "..." }
}

Sin errores específicos adicionales a los comunes.

Igual que el anterior, para un miembro del equipo (misma paginación y forma de respuesta).

Path param: userId (string).

Detalle completo de una misión: tareas (con archivos de soporte y entregas), archivos de soporte de la misión, entregas, feedbacks y objetivo vinculado.

Path param: missionId (number).

Response (campos relevantes para la UI, alineados con el tipo ApiMission de referencia):

Campo Tipo Descripción
id, userId, missionTitle, missionDescription Datos base.
status number Ver tabla de estados en Lógica de cliente.
missionProgress number | null null si no tiene tareas; si tiene, (tareas aprobadas / total) * 100 redondeado.
assignedDate, readDate, startDate, endDate, deadlineDate, finishedDate date Ver nota sobre deadlineDate vs endDate en Lógica de cliente.
missionQuality number | null Calificación 1-5 (CSAT) cargada por el evaluador al aprobar.
missionDifficulty number | null Calificación 1-5 (CES) cargada por el propio ejecutor al enviar.
createdBy string Username de quien creó la misión.
createdByFullName string | null Nombre formateado, calculado en el backend.
processName string Nombre del proceso de evaluación vinculado, si aplica (adjuntado por el repositorio, fuera del alcance de este documento).
objective.name string Nombre del objetivo vinculado, si aplica.
supportFiles[] { id, type: 'link'|'attached', fileUrl, fileComment } Archivos de soporte de la misión.
missionsTasks[] Ver MissionTaskDto abajo, con taskSupportFiles[] y deliveries[] anidados.
feedbacks[] Ver FeedbackDto abajo.
deliveries[] Entregas a nivel de misión (referenceType: "MISSION").
{
"id": 8937,
"userId": "jperez",
"missionTitle": "Actualizar manual de onboarding",
"missionDescription": "Revisar y actualizar el manual con los cambios de política 2026",
"assignedDate": "2026-07-14T09:12:00.000Z",
"readDate": "2026-07-14T10:00:00.000Z",
"startDate": "2026-07-15T00:00:00.000Z",
"endDate": "2026-07-25T00:00:00.000Z",
"deadlineDate": "2026-07-25T00:00:00.000Z",
"finishedDate": null,
"status": 3,
"missionProgress": 50,
"missionQuality": null,
"missionDifficulty": null,
"createdBy": "lgomez",
"createdByFullName": "Laura Gómez",
"processName": null,
"objective": null,
"supportFiles": [
{ "id": 401, "type": "link", "fileUrl": "https://drive.example.com/manual-2025.pdf", "fileComment": "Versión anterior" }
],
"missionsTasks": [
{
"id": 5201,
"taskDescription": "Revisar sección de beneficios",
"taskStatus": 3,
"taskQuality": 4,
"taskDifficulty": 2,
"taskSupportFiles": [],
"deliveries": []
},
{
"id": 5202,
"taskDescription": "Actualizar sección de políticas de vacaciones",
"taskStatus": 1,
"taskQuality": null,
"taskDifficulty": null,
"taskSupportFiles": [],
"deliveries": []
}
],
"feedbacks": [],
"deliveries": []
}

Errores específicos:

Código Cuándo ocurre
404 La misión no existe ("Mission not found").

Crea una misión para el usuario autenticado (self-service).

Body (CreateMissionDto):

Campo Tipo Obligatorio Descripción
missionTitle string Título.
missionDescription string No* El DTO sólo exige @IsString() (no @IsNotEmpty); el formulario de referencia tampoco la valida como obligatoria.
startDate, endDate date Pese a required: false en el Swagger, el DTO valida @IsNotEmpty @IsDateString — enviarlas siempre.
processType 'PROCESS' | 'EVALUATION' No Si se omite y se envía processId, el backend infiere EVALUATION.
processId, objectiveId string No Vínculo opcional a un proceso/objetivo de evaluación. La búsqueda de procesos/objetivos (GET /evaluation/type, GET /processes, GET /processes/{id}/objectives) es fuera del alcance de este documento.
supportFiles[] { type: 'link'|'attached', fileUrl, fileComment? } No Archivos de soporte a nivel de misión.
missionsTasks[] { taskDescription, taskFiles?/taskSupportFiles? } No Tareas iniciales. taskFiles es alias de taskSupportFiles (mismo formato { type: 'link'|'attached', fileUrl, fileComment? }, string — no confundir con el type numérico de POST /missions/{missionId}/tasks, ver caution abajo).

Response (MissionResponseDto): misión creada, status = 1 (ASSIGNED) siempre, missionProgress = 0 si se enviaron tareas o null si no.

{
"id": 8940,
"userId": "jperez",
"processType": null,
"processId": null,
"objectiveId": null,
"missionTitle": "Preparar demo del sprint",
"missionDescription": "Armar la demo para el review del viernes",
"assignedDate": "2026-07-16T14:00:00.000Z",
"readDate": null,
"startDate": "2026-07-17T00:00:00.000Z",
"endDate": "2026-07-24T00:00:00.000Z",
"status": 1,
"missionProgress": null,
"missionQuality": null,
"missionDifficulty": null,
"createdBy": "jperez",
"deadlineDate": "2026-07-24T00:00:00.000Z",
"finishedDate": null
}

Sin errores específicos adicionales a los comunes (400 si processId no es numérico).

Crea una misión para el userId indicado (usada por el responsable para asignar una misión a un miembro de su equipo). Mismo body, DTO y forma de respuesta que POST /missions. Ver caution de autorización en Resumen.

Path param: userId (string).

Actualiza estado, fecha fin y/o calificaciones de una misión.

Body (UpdateMissionDto):

Campo Tipo Obligatorio Descripción
status number No Ver tabla de transiciones en Lógica de cliente. Si se envía 3 (IN_PROGRESS) y startDate es futura, responde 400.
endDate date No Sólo actualiza endDate; no actualiza deadlineDate (ver caution en Lógica de cliente).
missionQuality number No Calificación 1-5 (CSAT), la carga el evaluador al aprobar.
missionDifficulty number No Calificación 1-5 (CES), la carga el ejecutor al enviar.

Response (MissionResponseDto): misión actualizada, incluye missionsTasks/supportFiles/feedbacks (no incluye deliveries, ese campo no está en MissionResponseDto).

Errores específicos:

Código Cuándo ocurre
400 Se intenta pasar a IN_PROGRESS (status=3) antes de la startDate ("Cannot set mission to in-progress before start date").
404 La misión no existe.

Elimina (soft delete) una misión propia o de un miembro del equipo. Marca status = 8 (DELETED) y aplica soft delete (queda fuera de listados por defecto).

Path param: missionId (number).

Response:

{ "id": "8940", "deleted": true }

Sin errores específicos adicionales a los comunes.

Agrega una tarea a una misión existente. En la UI de referencia las tareas normalmente se cargan dentro de missionsTasks al crear la misión (POST /missions / POST /missions/{userId}); este endpoint permite agregarlas después.

Body (CreateTaskDto):

Campo Tipo Obligatorio Descripción
taskDescription string
taskFiles[] { type: TaskFileType (1=link, 2=attached), fileUrl, fileComment? } No type es numérico aquí (enum TaskFileType), a diferencia de MissionFileInputDto (string 'link'|'attached') usado en la creación de misión y en entregas. Verificar el tipo esperado según el endpoint.
missionQuality, missionDifficulty number (1-5) No Presentes en el DTO, pero ver caution abajo — no persisten en la tarea.

Response (MissionTaskDto): la tarea creada. También recalcula missionProgress de la misión.

{
"id": 5203,
"taskDescription": "Grabar la demo",
"taskStatus": 1,
"taskQuality": null,
"taskDifficulty": null,
"taskFiles": []
}

Sin errores específicos adicionales a los comunes.

Actualiza estado y/o calificaciones de una tarea. Es el único camino real para fijar taskQuality/taskDifficulty (ver caution arriba).

Path param: taskId (number).

Body (UpdateTaskDto):

Campo Tipo Obligatorio Descripción
taskStatus number No Ver TaskStatus en Lógica de cliente.
taskQuality number (1-5) No Calificación del evaluador al aprobar la tarea.
taskDifficulty number (1-5) No Autocalificación del ejecutor al marcarla como enviada.

Response (MissionTaskDto): tarea actualizada. También recalcula missionProgress de la misión.

Sin errores específicos adicionales a los comunes (404 si la tarea no existe).

Elimina (soft delete) una tarea y recalcula el progreso de la misión.

Response:

{ "id": "5203", "deleted": true }

Sin errores específicos adicionales a los comunes.

Crea una entrega a nivel de misión (para misiones sin tareas). Sólo aplica cuando la misión no tiene missionsTasks.

Body (CreateDeliveryDto):

Campo Tipo Obligatorio Descripción
deliveryComment string Comentario de la entrega.
deliveryFiles[] { type: 'link'|'attached', fileUrl, fileComment? } No
rejectionComment string No Normalmente no se envía al crear; se agrega después vía PATCH cuando el evaluador rechaza.

Response (DeliveryDto): status = 1 (SENT) siempre al crear.

{
"id": 3010,
"missionId": 8937,
"taskId": null,
"referenceType": "MISSION",
"status": 1,
"deliveryComment": "Listo, adjunto el documento actualizado",
"rejectionComment": null,
"deliveryFiles": [
{ "id": 601, "type": "attached", "fileUrl": "deliveries/manual-v2.pdf", "fileComment": "manual-v2.pdf" }
],
"createdAt": "2026-07-20T16:00:00.000Z",
"updatedAt": "2026-07-20T16:00:00.000Z"
}

Errores específicos:

Código Cuándo ocurre
400 Falta deliveryComment.
404 La misión no existe.

POST /missions/{missionId}/tasks/{taskId}/deliveries

Sección titulada «POST /missions/{missionId}/tasks/{taskId}/deliveries»

Igual al anterior, pero para una tarea puntual dentro de una misión con tareas. referenceType = "TASK".

Path params: missionId, taskId (number).

Errores específicos: además de los del endpoint anterior, 404 si la tarea no pertenece a la misión.

PATCH /missions/{missionId}/deliveries/{deliveryId}

Sección titulada «PATCH /missions/{missionId}/deliveries/{deliveryId}»

Actualiza una entrega: cambia su estado (con transiciones validadas) y/o edita comentario/archivos/comentario de rechazo.

Path params: missionId, deliveryId (number).

Body (UpdateDeliveryDto):

Campo Tipo Obligatorio Enum Descripción
status number No 1 SENT, 2 RECEIVED, 3 APPROVED, 4 REJECTED Transiciones válidas: SENT→RECEIVED, RECEIVED→APPROVED|REJECTED. Cualquier otra combinación responde 400.
deliveryComment string No Sólo se persiste si el estado actual (antes del patch) es SENT.
deliveryFiles[] No Ídem: sólo se agregan archivos si el estado actual es SENT.
rejectionComment string No Se puede actualizar en cualquier estado, incluso sin cambiar status — así es como el flujo de referencia adjunta el motivo de rechazo de una misión sin tareas (sin mover la entrega a REJECTED).

Errores específicos:

Código Cuándo ocurre
400 Transición de estado inválida, o se intenta pasar a REJECTED (referencia MISSION) sin rejectionComment (ni uno previo en la entrega).
404 La entrega no existe o no pertenece a la misión.

Agenda un feedback (presencial o virtual) para una misión.

Body (CreateFeedbackDto):

Campo Tipo Obligatorio Enum Descripción
feedbackType number 1 inPerson, 2 virtual
feedbackScheduledDate date Fecha/hora agendada.
feedbackComment string No
feedbackMeetUrl string No Link de la reunión virtual, si feedbackType = 2.

Response (FeedbackDto): feedbackStatus = 1 (PLANNED) siempre al crear.

{
"id": 701,
"missionId": 8937,
"feedbackType": "inPerson",
"feedbackStatus": 1,
"feedbackScheduledDate": "2026-07-22T15:00:00.000Z",
"feedbackComment": "Revisar el resultado final juntos",
"feedbackMeetUrl": null
}

Sin errores específicos adicionales a los comunes.

Variante en lote: crea el mismo feedback para varias misiones a la vez.

Body (CreateBulkFeedbackDto): igual a CreateFeedbackDto pero sin missionId, con missionIds: number[] obligatorio en su lugar.

Response: array de FeedbackDto (uno por misión).

Actualiza estado y/o fecha de un feedback.

Body (UpdateFeedbackDto):

Campo Tipo Obligatorio Descripción
feedbackStatus number No 1 PLANNED, 2 DONE.
feedbackScheduledDate date No

Response (FeedbackDto): feedback actualizado.

Sin errores específicos adicionales a los comunes (404 si el feedback no existe).

Bitácora de auditoría de la misión (creación, cambios de estado, tareas, feedbacks), orden occurredAt DESC.

Response: array de eventos, sin sobre.

Campo Tipo Descripción
entityType 'mission' | 'task' | 'feedback' Entidad afectada.
entityId number Id de esa entidad.
eventType enum missionCreated, missionUpdated, taskAdded, taskUpdated, taskDeleted, feedbackScheduled, feedbackUpdated, statusChanged.
previousStatus, newStatus string | null Estado anterior/nuevo (como string), sólo en statusChanged/taskUpdated.
performedById, performedByName string | null Quién ejecutó la acción (null = sistema).
occurredAt date
comments string[] Comentario asociado (p. ej. motivo de rechazo), si aplica.
metadata object Datos adicionales según el evento (p. ej. deliveryId, taskId).
[
{
"id": 9001,
"missionId": 8937,
"entityType": "mission",
"entityId": 8937,
"eventType": "statusChanged",
"previousStatus": "1",
"newStatus": "3",
"performedById": "jperez",
"performedByName": "jperez",
"occurredAt": "2026-07-17T09:00:00.000Z",
"comments": [],
"metadata": {}
}
]

Sin errores específicos adicionales a los comunes (404 si la misión no existe).

Valor Significado backend Texto sugerido (vista del ejecutor) Texto sugerido (vista del evaluador) Color sugerido
1 ASSIGNED Asignada Asignada neutral
2 READ Leída Leída neutral
3 IN_PROGRESS En progreso En progreso info
4 COMPLETED Completada / en revisión Por revisar success
5 TO_BE_CORRECTED Por corregir Por corregir error
6 APPROVED Aprobada (falta feedback) Aprobada success
7 FINALIZED Finalizada Finalizada success
8 DELETED — (excluir de listados y contadores)
Valor Significado Texto sugerido Color sugerido
1 PENDING Pendiente neutral
2 SENT Enviada info
3 APPROVED Aprobada success
4 REJECTED Rechazada error
Valor Significado Transición siguiente permitida
1 SENT → RECEIVED
2 RECEIVED → APPROVED o REJECTED
3 APPROVED (final)
4 REJECTED (final)

En el flujo de referencia, el evaluador no siempre mueve la entrega por sus estados formales: para rechazar una misión sin tareas sólo adjunta rejectionComment a la última entrega (sin status) y cambia el status de la misión a TO_BE_CORRECTED. Tratar DeliveryStatusCode como informativo/de auditoría, no como el disparador único del rechazo.

Valor Significado Texto sugerido
1 PLANNED Planeada
2 DONE Realizada
Valor Significado
1 / inPerson Presencial
2 / virtual Virtual (requiere feedbackMeetUrl)

Qué mostrar/habilitar según el estado (vista del ejecutor — “Mis misiones”)

Sección titulada «Qué mostrar/habilitar según el estado (vista del ejecutor — “Mis misiones”)»
  • Botón de acción de la tabla: “Detalles” si status es ASSIGNED, READ, COMPLETED o APPROVED; “Continuar” en cualquier otro caso (IN_PROGRESS, TO_BE_CORRECTED).
  • Al abrir el detalle desde ASSIGNED: el cliente dispara automáticamente PATCH { status: READ } antes de mostrar el detalle (no requiere acción explícita del usuario).
  • Footer “Iniciar misión”: visible sólo si status es ASSIGNED o READ. Dispara PATCH { status: IN_PROGRESS }; si la misión aún no llegó a su startDate, mostrar el error del backend en vez de reintentar.
  • Footer “Enviar”: visible sólo si status es IN_PROGRESS, TO_BE_CORRECTED o COMPLETED.
    • Si la misión tiene tareas: sólo se pueden seleccionar/enviar tareas en estado PENDING (1) o REJECTED (4); las SENT/APPROVED aparecen ya marcadas y deshabilitadas. Antes de habilitar el envío hay que validar que cada tarea seleccionada tenga comentario de entrega. Al seleccionar una tarea se abre el modal de dificultad (1-5, taskDifficulty) — es obligatorio elegir una calificación para poder confirmar la selección.
    • Si la misión no tiene tareas: se requiere comentario de entrega antes de habilitar el envío; al confirmar se abre el modal de dificultad de la misión (missionDifficulty, obligatorio) antes de mandar el PATCH { status: COMPLETED, missionDifficulty }.
  • Sección de corrección (status = TO_BE_CORRECTED): mostrar el rejectionComment de la última entrega/tarea rechazada antes de permitir un nuevo envío.

Qué mostrar/habilitar según el estado (vista del evaluador — misiones de un miembro del equipo)

Sección titulada «Qué mostrar/habilitar según el estado (vista del evaluador — misiones de un miembro del equipo)»
  • Botones “Aprobar” / “Rechazar” a nivel de misión: sólo para misiones sin tareas, y sólo si status no es ASSIGNED, READ, IN_PROGRESS, APPROVED ni TO_BE_CORRECTED (en la práctica, COMPLETED). Aprobar exige seleccionar una calificación (missionQuality, 1-5) antes de habilitar el botón de confirmar; el nuevo estado es APPROVED si la misión tiene algún feedback PLANNED, o FINALIZED si no tiene ninguno.
  • Rechazar misión sin tareas: requiere comentario obligatorio; el flujo agrega el comentario a la última entrega (PATCH deliveries/{id} { rejectionComment }) y cambia la misión a TO_BE_CORRECTED.
  • Aprobar/rechazar por tarea (misiones con tareas, tarea en SENT): el botón “Calificar” (estrella) es obligatorio antes de habilitar “Aprobar”/“Rechazar” (ambos deshabilitados hasta que task.taskQuality tenga valor). Rechazar exige comentario. Al enviar todas las revisiones seleccionadas, el nuevo estado de la misión se calcula así:
    • alguna tarea quedó REJECTEDTO_BE_CORRECTED.
    • todas quedaron APPROVEDAPPROVED (si hay feedback PLANNED) o FINALIZED (si no hay ninguno).
    • quedan tareas PENDING/SENT sin revisar → IN_PROGRESS.
  • Botón “Eliminar misión”: siempre visible en el detalle (con o sin tareas), abre modal de confirmación simple.
  • Feedback “Marcar como realizado”: visible sólo en feedbacks con feedbackStatus = PLANNED. Dispara PATCH feedbacks/{id} { feedbackStatus: DONE }; si la misión estaba en APPROVED, además dispara PATCH mission { status: FINALIZED } para cerrarla.

La UI de referencia muestra siempre deadlineDate como “fecha límite/fin”, nunca el campo endDate crudo. deadlineDate se fija una sola vez, al crear la misión (copia de endDate en ese momento); PATCH /missions/{missionId} sólo puede modificar endDate, no deadlineDate. Si se expone edición de fecha fin en la app, considerar que el valor mostrado en el resto de la UI de referencia no se actualizaría.

  • Antes de “Iniciar misión”: la misión debe estar en ASSIGNED/READ y su startDate no puede ser futura (el backend rechaza con 400 si lo es).
  • Antes de “Enviar” una misión sin tareas: comentario de entrega no vacío + calificación de dificultad seleccionada.
  • Antes de enviar tareas: comentario de entrega no vacío por cada tarea seleccionada + calificación de dificultad por tarea.
  • Antes de aprobar (misión o tarea): calificación de calidad (missionQuality/taskQuality) seleccionada.
  • Antes de rechazar (misión o tarea): comentario de rechazo no vacío.
  • Antes de agendar feedback presencial: hora seleccionada (startTime); para virtual, no se exige el link (feedbackMeetUrl es opcional en el DTO), pero si se ingresa se envía.
  • Sin misiones activas: GET /missions con filter.status=$btw:1,6 devuelve data: [] — mostrar estado vacío (“Aún no tienes misiones activas”).
  • Misión sin tareas y sin entregas: missionsTasks y deliveries llegan como arrays vacíos; no mostrar el bloque de progreso (sólo aplica si missionsTasks.length > 0).
  • missionProgress = null: ocurre cuando la misión no tiene tareas; no mostrar la barra de progreso o mostrarla en 0 sin dato.
  • Feedback sin feedbackMeetUrl: ocultar el botón de “unirse a la reunión” si el campo viene vacío/null.
  • processName/objective ausentes: mostrar “Independiente” (sin vínculo a proceso) en vez de un campo vacío.