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

172 lines
11 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 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).