@ -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.