diff --git a/docs/plan-interfaz-web.md b/docs/plan-interfaz-web.md index 5c6fdb0..c5b4f70 100644 --- a/docs/plan-interfaz-web.md +++ b/docs/plan-interfaz-web.md @@ -261,4 +261,192 @@ Implementado: suite web con bun:test y build programático (helpers en tests/web - ics_requests_total{type} - ics_rate_limit_hits_total +## 18) Plan UX/UI (detallado, sin dependencias externas) + +Objetivo +- Disponer de una guía exhaustiva para diseñar e implementar la interfaz web usando SvelteKit/Svelte, sin dependencias externas de UI, asegurando consistencia, accesibilidad y un flujo de trabajo por etapas (vertical slices). + +18.1) Principios de diseño y restricciones +- Sin dependencias externas: no Tailwind ni librerías de componentes. CSS moderno con variables y módulos Svelte. +- Aprovechar SvelteKit: + - SSR por defecto, progressive enhancement en eventos/acciones. + - +page.svelte / +page.server.ts para data loading; endpoints +server.ts ya existentes. + - Stores de Svelte para estado global mínimo (toasts, sesión). +- Mobile-first, responsive fluido; desktop con anchos máximos (contenedor ~960–1200px) y layout en 2 columnas donde aplique. +- Accesibilidad AA: foco visible, roles ARIA en componentes custom, labels asociados, contraste >= 4.5:1. +- Rendimiento: CSS mínimo crítico inline, diferir lo no esencial, listas paginadas; sin icon fonts (usar SVG inline). +- Seguridad: estados de sesión claros; nunca exponer tokens; evitar “copiar URL” en texto plano (usar botón Copy). + +18.2) Lenguaje visual y Design Tokens +- Tipografía: usar fuentes del sistema (Inter/SF Pro/Segoe UI/Roboto/Noto/Sans-Serif fallback). +- Escala tipográfica: 12/14/16/20/24 px; line-height 1.4–1.6. +- Espaciado: 4/8/12/16/24/32 px; grid de 8 px. +- Radios: 6/8 px; sombra suave para elevaciones (header sticky, tarjetas). +- Paleta (light/dark con prefers-color-scheme): + - Neutral: bg, surface, border, text, text-muted. + - Acentos: primary (acciones), danger (rotar/revocar), warning (pronto), success (ok). +- Badges semánticos: + - Overdue: rojo. + - Due soon (≤3 días): ámbar. + - Unassigned: gris/azul neutro. +- Tokens (variables CSS en :root): + - color-bg, color-surface, color-text, color-text-muted, color-border, color-primary, color-danger, color-warning, color-success + - radius-sm/md, shadow-sm/md, space-1..5 +- Modo oscuro: ajustar variables sin duplicar estilos. + +18.3) Accesibilidad (checklist) +- Navegación por teclado completa; focus ring perceptible. +- Contraste verificado para texto y controles (>=4.5:1). +- Labels y aria-describedby en inputs; botones con aria-label si solo icono. +- Estados y errores anunciados (role="status"/"alert" donde aplique). +- Trampas de foco evitadas; orden lógico en DOM. +- Tamaño táctil mínimo 44x44 px. + +18.4) Inventario de componentes (Design System v0) +- Base + - Button (variants: primary/secondary/ghost/danger; tamaños sm/md; con/ sin icono). + - IconButton (solo icono, aria-label). + - TextField (búsqueda), TimeField HH:MM (validación simple). + - SegmentedControl (frecuencia: daily/weekly/weekdays/off). + - Select básico (nativo estilizado). + - Switch/Checkbox (para activar feed C). + - Badge (overdue/soon/default). + - Card (surface + padding + shadow). + - Pagination (prev/next + indicador página). + - Toast/Snackbar (store global; auto-dismiss; role="status"). + - ConfirmDialog (portal sencillo con focus trap básico). + - Skeleton (rectángulos/filas). + - EmptyState y ErrorBanner. +- Datos + - TaskItem (fila) con: [id], descripción, fecha (badge), grupo, asignación (solo lectura). + - GroupCard con nombre, contadores open/unassigned. + - FeedCard con nombre, descripción, botón Copiar y Rotar, estado (revocado/no disponible). +- Utilidades + - CopyToClipboard (navigator.clipboard con fallback). + - RelativeDate / DueBadge (lógica de overdue/soon). + - VisuallyHidden (accesibilidad). + - AppShell (header con usuario/logout, contenedor principal). + +18.5) Patrones de interacción +- Búsqueda: submit explícito o debounce 250–300 ms con actualización de query params; mantener estado al navegar atrás. +- Filtros: chips/segmented con sync a URL (page reset a 1 cuando cambian). +- Paginación: enlaces con URL (page, limit); accesible. +- Formularios: usar fetch desde el cliente con progressive enhancement; validación en cliente (básica) + servidor (autoritativa). +- Copiar: icono “copiar” con feedback de toast y aria-live. +- Confirmaciones peligrosas: diálogo modal con foco dentro y acciones claras. +- Estados: loading (skeletons), vacío (mensaje y CTA contextual), error (retry). + +18.6) IA y flujos por pantalla +- /login + - Objetivo: canjear token con gate de interacción mínima. + - Contenido: mensaje, botón “Continuar”, estado token inválido/expirado con instrucciones /t web. + - Accesibilidad: botón enfocable, mensajes claros. +- /app (Mis tareas) + - Controles: búsqueda texto, chips “Abiertas”, “Pronto (≤3 días)”, selector “Vencen antes de…” (3/7/14 días). + - Lista: TaskItem con fecha badge, grupo, asignación; paginación. + - Estados: vacío, sin resultados, error de carga. + - Mobile: lista de una columna; Desktop: contenido centrado con ancho máx; opcional 2 columnas si hay filtros persistentes. +- /app/groups + - Grid de GroupCard (2–3 col en desktop, 1 en móvil). + - Ver “sin responsable” destacado; posibilidad de prefetch a /api/groups/:id/tasks?onlyUnassigned=1 (aunque MVP sea lectura agregada). + - Estados: sin grupos, error. +- /app/preferences + - Frecuencia (Segmented), Hora (TimeField HH:MM). + - “Próximo recordatorio” calculado por servidor (mostrar string amigable e ISO en tooltip). + - Acciones: Guardar y Revertir; toasts en éxito/error. + - Validación: normalizar hora en cliente (HH:MM) y servidor. +- /app/integrations + - Autogeneración perezosa de feeds B en la carga (el backend garantiza creación si falta). + - Tarjetas: Personal (mis tareas), Grupo (B) por cada grupo activo, Multigrupo (C) opcional con switch. + - Acciones: Copiar (URL oculta, se copia con click), Rotar (confirmación). + - Estados: sin grupos → mostrar solo Personal; feed revocado → indicador y opción recrear. + - Microcopy: guía breve (Google/Apple/Outlook), aviso privacidad. + +18.7) Contratos de datos (UI) +- TaskItem + - id: number + - description: string + - due_date: string | null (YYYY-MM-DD) + - group: { id: string; name: string } | null + - assignees: string[] (ids normalizados) + - flags: { overdue: boolean; dueSoon: boolean } +- TasksList meta + - page: number; limit: number; total: number +- Group + - id: string; name: string + - counts: { open: number; unassigned: number } +- Preferences + - freq: 'daily'|'weekly'|'weekdays'|'off' + - time: string | null (HH:MM) + - nextReminder: { human: string; iso: string | null } +- Feed + - type: 'personal'|'group'|'aggregate' + - groupId?: string + - url?: string (solo UI, nunca persistida) + - created_at?: string; last_used_at?: string | null + - status?: 'active'|'revoked'|'unavailable' + +18.8) Arquitectura front (sin librerías externas) +- Estructura sugerida en apps/web/src + - lib/ui/atoms: Button.svelte, IconButton.svelte, Badge.svelte, Skeleton.svelte, VisuallyHidden.svelte + - lib/ui/inputs: TextField.svelte, TimeField.svelte, SegmentedControl.svelte, Switch.svelte, Select.svelte + - lib/ui/feedback: Toast.svelte (+ store), ConfirmDialog.svelte, EmptyState.svelte, ErrorBanner.svelte + - lib/ui/layout: AppShell.svelte, Card.svelte, Pagination.svelte + - lib/ui/data: TaskItem.svelte, GroupCard.svelte, FeedCard.svelte + - lib/stores: toasts.ts, session.ts (mínimo, p. ej. userId) + - lib/styles: tokens.css (variables), base.css (reset + utilidades mínimas) + - routes/app/*: páginas; usar load con SSR y fetch interno +- Theming y estilos + - tokens.css con variables; base.css con reset ligero (normalize reducido) y utilidades puntuales (sr-only, container, grid). + - Modo oscuro con prefers-color-scheme; clase .theme-dark opcional. +- Iconos + - SVG inline en componentes; set mínimo (copy, rotate, search, warning, check, x). +- Utilidades + - copyToClipboard util con fallback. + - date helpers en lib/utils/date.ts (formatos UI, dueSoon/overdue). + +18.9) Roadmap por etapas (2 semanas sugerido) +- Día 1: Tokens y base.css; AppShell; definición final de contratos de datos por pantalla. + - Entregable: estilos base, header, contenedor, tipografía; documento de contratos de datos firmado. +- Días 2–3: Componentes base (Button, TextField, Segmented, Badge, Card, Toast, Skeleton, EmptyState). + - Entregable: catálogo mínimo interactivo (página de sandbox oculta /app/_sandbox). +- Días 4–5: Página /app (Mis tareas) end-to-end con APIs existentes (GET /api/me/tasks). + - Entregable: búsqueda, filtros, paginación, estados; lighthouse > 90 en móvil. +- Día 6: /app/groups con GroupCard y prefetch de “sin responsable”. + - Entregable: grid responsive con contadores. +- Día 7: /app/preferences (GET/POST) con vista de “próximo recordatorio”. + - Entregable: validación y toasts. +- Días 8–9: /app/integrations UI completa (autogeneración perezosa, Copiar, Rotar con confirmación). + - Backend ICS puede avanzar en paralelo; usar mocks si falta endpoint. +- Día 10: QA accesibilidad y responsive; pulido de microcopy; revisión con 1–2 usuarios internos. + - Entregable: checklist AA, correcciones. + +18.10) Criterios de aceptación UX +- Navegación completa con teclado; foco visible. +- Estados loading/vacío/error presentes y claros en todas las pantallas. +- “Copiar enlace” funciona y anuncia feedback; Rotar pide confirmación y comunica impacto. +- Preferencias reflejan correctamente TZ y el “próximo recordatorio”. +- Rendimiento aceptable: TTI < 2s en 3G rápida para pantallas principales; CSS < 15KB inicial. + +18.11) Validación y métricas (sin dependencias) +- Instrumentación mínima: + - web_ui_interaction_total{type='copy'|'rotate'|'save_prefs'|'search'} mediante el sistema de métricas existente (si expuesto al front vía endpoint). + - Alternativa: logs discretos en servidor al invocar endpoints relevantes. +- Feedback usuario: + - Recoger observaciones de claridad en /app/integrations y /login. + +18.12) Riesgos y mitigaciones +- Falta de librerías UI → más trabajo inicial: mitigado con un Design System v0 bien delimitado. +- Desalineación back/front → trabajar en vertical por pantalla con contratos de datos acordados. +- Accesibilidad ignorada al final → checklist desde el inicio y QA día 10. + +18.13) Notas de implementación (guía sin código) +- Mantener estilos de componentes scopeados por defecto de Svelte. +- Evitar CSS complejo; preferir componentes pequeños y composables. +- Sin icon fonts ni frameworks; SVG inline o componentes Svelte de icono. +- Usar acciones de Svelte (use:) para patrón copy-to-clipboard y focus-trap del modal. +- Sin degradación de SEO relevante (app privada); aún así, SSR establece base de contenido. + +Con esto, el equipo puede trabajar por etapas, validar tempranamente con usuarios y mantener coherencia visual sin dependencias externas. + Fin del documento.