You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
taskbot/docs/plan-web-fases.md

11 KiB

Plan de trabajo — Web (SvelteKit) orientado a móvil y acciones de tareas

Contexto y objetivos

  • Reforzar la utilidad móvil, mostrar grupos y responsables correctamente, y simplificar acciones prioritarias (completar/undo) manteniendo seguridad (gating) alineada con el backend.
  • Mantener el diseño sin truncar descripciones; ofrecer orden por fecha o por grupo; y mostrar TODAS las abiertas en /app/groups.
  • Evitar regresiones, con cambios iterativos y fácilmente revertibles.

Decisiones globales

  • No truncar descripciones en TaskItem; envolver en varias líneas. Acciones secundarias se desplazan a una segunda línea/menú contextual en móvil.
  • “Completar” se promueve como acción principal (checkbox/botón destacado). Incluir “Deshacer completar” con ventana configurable (24h por defecto).
  • “Mis tareas” tendrá dos secciones:
    1. Asignadas a mí (todas abiertas).
    2. Sin responsable de mis grupos (todas abiertas).
  • “Grupos” mostrará secciones por grupo, con TODAS las tareas abiertas, expandibles/colapsables, y con toggle “Unassigned first”.
  • Búsqueda por texto desaparece de la UI (se reserva a futuro si fuese necesaria).
  • Orden conmutado por el usuario: Fecha (due asc, NULL al final) | Grupo (agrupación por grupo y dentro por fecha).
  • Seguridad: endurecer PATCH de tareas sin grupo para exigir ser responsable (y opcionalmente creador si el esquema lo permite).
  • Ventana de uncomplete (deshacer completar): configurable por variable de entorno UNCOMPLETE_WINDOW_MIN (1440 por defecto).

Medición de impacto y riesgos

  • N+1 inicial en /app (al agregar “sin responsable” de todos los grupos) tolerable en MVP, mitigado luego con endpoint “overview”.
  • Posible crecimiento de DOM en /app/groups: se mitiga con secciones colapsables.
  • Cambios de gating en PATCH requieren revisar casos edge (tareas personales sin asignados previos).

Archivos ya disponibles para edición (confirmados)

  • UI:
    • apps/web/src/lib/ui/layout/AppShell.svelte
    • apps/web/src/lib/ui/data/TaskItem.svelte
    • apps/web/src/lib/ui/data/GroupCard.svelte
    • apps/web/src/routes/app/+page.svelte
    • apps/web/src/routes/app/+page.server.ts
    • apps/web/src/routes/app/groups/+page.svelte
    • apps/web/src/routes/app/groups/+page.server.ts
  • API:
    • apps/web/src/routes/api/me/tasks/+server.ts
    • apps/web/src/routes/api/me/groups/+server.ts
    • apps/web/src/routes/api/groups/[id]/tasks/+server.ts
    • apps/web/src/routes/api/tasks/[id]/+server.ts (PATCH)
    • apps/web/src/routes/api/tasks/[id]/claim/+server.ts
    • apps/web/src/routes/api/tasks/[id]/unassign/+server.ts
    • apps/web/src/routes/api/tasks/[id]/complete/+server.ts
  • Infra web:
    • apps/web/src/lib/server/env.ts
    • apps/web/src/lib/server/db.ts
  • Núcleo:
    • src/db.ts

Archivos que solicitaremos añadir al chat cuando toque editar

  • Documentación: docs/operations.md (para documentar UNCOMPLETE_WINDOW_MIN y notas UX).
  • Tests: tests/web/* y tests/unit/* (para cubrir uncomplete y cambios de gating).
  • (Opcional) Migraciones: src/db/migrations/* si llegamos a necesitar created_by en gating de PATCH.

Fase 1 — UX base en páginas y TaskItem (sin backend nuevo) — Estado: Completada Objetivos

  • /app: añadir sección “Sin responsable de mis grupos”, quitar búsqueda, añadir conmutador de orden (Fecha | Grupo).
  • /app/groups: mostrar TODAS las tareas por grupo en secciones expandibles/colapsables; toggle “Unassigned first”.
  • TaskItem: mostrar chip de grupo; mantener descripción sin truncar; reorganizar acciones para móvil. Archivos a editar
  • apps/web/src/routes/app/+page.server.ts
    • Cargar /api/me/tasks (open, asignadas a mí).
    • Cargar /api/me/groups; para cada grupo, solicitar /api/groups/:id/tasks?onlyUnassigned=true&limit=0 y reunir la lista “unassigned”.
    • Construir map {groupId → groupName} para la UI.
    • Gestionar query param order=due|group.
  • apps/web/src/routes/app/+page.svelte
    • Renderizar dos secciones (asignadas y sin responsable). Añadir conmutador de orden. Eliminar campo de búsqueda.
  • apps/web/src/routes/app/groups/+page.server.ts
    • En vez de previews, cargar /api/groups/:id/tasks?limit=0 para cada grupo (todas abiertas).
    • Permitir query unassignedFirst=true (por grupo o global).
  • apps/web/src/routes/app/groups/+page.svelte
    • Reemplazar cuadrícula de GroupCard por secciones por grupo (cabecera con +/ para colapsar).
    • Dentro de cada sección, lista de TaskItem reutilizando las mismas acciones.
  • apps/web/src/lib/ui/data/TaskItem.svelte
    • Mostrar chip con el nombre del grupo si group_id != null; “Personal” si no tiene grupo (usar map pasado desde el server o prop groupName).
    • Promover “Completar” visualmente (sin cambiar aún la API).
  • apps/web/src/lib/ui/data/GroupCard.svelte
    • Mantener para posibles resúmenes; la página de grupos dejará de usarla de forma principal. Decisiones
  • Orden “por grupo” inicialmente puede agruparse en cliente para el agregado “sin responsable”; “asignadas” ya vienen por fecha. En fases posteriores, endpoint overview dará orden estable en servidor.

Fase 2 — Backend: Uncomplete (24h configurable) + seguridad de PATCH — Estado: Completada Objetivos

  • Añadir POST /api/tasks/:id/uncomplete (idempotente) con gating simétrico a /complete y ventana configurable.
  • Endurecer PATCH /api/tasks/:id para tareas sin group_id: exigir que el usuario sea asignado (y, si luego confirmamos schema, permitir también si created_by = usuario). Archivos a editar/crear
  • apps/web/src/lib/server/env.ts
    • Añadir export const UNCOMPLETE_WINDOW_MIN = ... (leer de env, default 1440). Exponer helper uncompleteWindowMs si conviene.
  • apps/web/src/routes/api/tasks/[id]/uncomplete/+server.ts (nuevo)
    • Validar sesión, id, existencia de tarea.
    • Gating:
      • Con group_id: miembro activo de grupo allowed.
      • Sin group_id: debe estar asignado a la tarea.
    • Validar ventana: completed=1 y completed_at >= now - UNCOMPLETE_WINDOW_MIN.
    • UPDATE: completed=0, completed_at=NULL (y opcional completed_by=NULL).
    • Respuesta: status ('updated'|'already'|'not_found'|'forbidden'), tarea resultante.
  • apps/web/src/routes/api/tasks/[id]/+server.ts (PATCH)
    • Añadir chequeo cuando group_id IS NULL: exigir que exista asignación del user sobre la tarea (alineado con complete/unassign).
  • UI
    • apps/web/src/lib/ui/data/TaskItem.svelte — añadir acción “Deshacer completar” (usando el nuevo endpoint) y toast con feedback.
    • apps/web/src/routes/app/+page.svelte — en la sección “Completadas (24 h)” añadir botón “Deshacer completar”. Documentación y configuración (solicitaremos permiso para editar)
  • docs/operations.md — documentar UNCOMPLETE_WINDOW_MIN (minutos; default 1440), semántica e idempotencia.

Fase 3 — Navegación móvil (barra de pestañas) y jerarquía de acciones Objetivos

  • Evitar que el header rebose en móvil. Añadir barra inferior con iconos (tabs) para Tareas, Grupos, Integraciones (calendario), Preferencias (alarma). Reducir peso visual de “Integraciones” en móvil.
  • Consolidar “Completar” como acción primaria (checkbox/botón destacado); otras acciones (Reclamar/Soltar/Editar/Fecha) en segunda línea o menú contextual. Archivos a editar/crear
  • apps/web/src/lib/ui/layout/AppShell.svelte
    • Añadir barra inferior sticky (solo en viewport ≤768px), con safe-area, iconos (SVG inline o emojis inicialmente), aria-labels y foco accesible.
    • Mantener header para desktop.
  • (Opcional) apps/web/src/lib/ui/icons/* (nuevos)
    • CalendarIcon.svelte, AlarmIcon.svelte, TasksIcon.svelte, GroupsIcon.svelte (si prefieres SVGs en vez de emojis).
  • apps/web/src/lib/ui/data/TaskItem.svelte
    • Ajustar layout mobile-first: acciones secundarias y espaciado compacto.

Fase 4 — Optimización: endpoint “overview” y orden en servidor Objetivos

  • Evitar N peticiones en /app para el bloque “sin responsable”.
  • Servir orden “por grupo” ya resuelto en servidor. Archivos a crear/editar
  • apps/web/src/routes/api/me/tasks/overview/+server.ts (nuevo)
    • Respuesta: { assigned: Task[], unassigned: Task[] } (open).
    • Params: order=due|group_then_due (por defecto due).
    • Cada Task incluye: id, description, due_date, group_id, group_name, display_code, assignees[].
    • Gating: igual que /api/me/tasks.
  • apps/web/src/routes/api/me/tasks/+server.ts
    • (Opcional) Añadir order=group_then_due y group_name via JOIN; mantener compatibilidad con tests existentes.
  • apps/web/src/routes/app/+page.server.ts
    • Consumir overview para reducir llamadas y aplicar el orden de servidor.

Fase 5 — Responsables: conteo, marca “tú” y popover con wa.me Objetivos

  • Mostrar de forma compacta cuántas personas están asignadas; marcar si el usuario actual está entre ellas; listar números y permitir mensaje directo (wa.me) bajo demanda. Archivos a editar/crear
  • apps/web/src/lib/ui/data/TaskItem.svelte
    • Badge “Responsables: n” + “tú” si corresponde; al pulsar, abrir popover/modal con lista.
  • apps/web/src/lib/ui/feedback/Popover.svelte (nuevo) o reutilizar un modal ligero existente
    • Accesibilidad: rol="dialog", focus trap, cierre con ESC.
  • (Opcional) apps/web/src/lib/utils/phone.ts (nuevo)
    • Helpers para abreviar números y construir URL segura wa.me.

Fase 6 — Pulido y peso visual de “Integraciones” Objetivos

  • Mantener “Integraciones” accesible pero de menor jerarquía en móvil.
  • Ajustar densidad, estados vacíos y mensajes, y recordar colapsado por grupo en localStorage. Archivos a editar
  • apps/web/src/lib/ui/layout/AppShell.svelte (tono/estilo de pestaña Integraciones).
  • apps/web/src/routes/app/groups/+page.svelte (estado colapsado persistente).
  • apps/web/src/routes/app/+page.svelte (estados vacíos claros).

Criterios de aceptación resumidos

  • /app muestra dos secciones con todas las tareas requeridas; sin truncar descripciones; orden conmutado.
  • /app/groups muestra todas las tareas abiertas por grupo; secciones colapsables; “Unassigned first” operativo.
  • Completar y Deshacer completar funcionan desde ambas páginas; ventana de 24h configurable; gating correcto.
  • PATCH no permite editar tareas sin grupo si no eres responsable (o creador, si se habilita).
  • Navegación móvil no rebosa; barra inferior accesible.
  • Asignados: conteo visible, “tú” resaltado y lista en popover con wa.me.

Notas de implementación y buenas prácticas

  • Mantener cabeceras cache-control: no-store en endpoints de listas/acciones.
  • Reutilizar el gating ya presente en claim/unassign/complete; factorizar si conviene (pero sin sobre-ingeniería).
  • Idempotencia en endpoints de mutación (claim, unassign, complete, uncomplete).
  • Evitar dependencias externas para UI; usar SVG inline o emojis como placeholder.
  • Accesibilidad: aria-label en iconos, focus visible, roles correctos en popovers/diálogos.

Siguientes pasos

  1. Implementar Fase 1 (UI base) con los archivos listados.
  2. Implementar Fase 2 (uncomplete + PATCH gating).
  3. Validar en móvil; luego abordar Fase 3 (tabs inferiores).
  4. Optimizar con overview (Fase 4) y cerrar UX de responsables (Fase 5).
  5. Pulido y documentación (Fase 6).