Frontend SPA del proyecto QuoteMatic. Construido con React + TypeScript + Vite y conectado a la API REST pública desplegada en https://quotematic.davlos.es.
Este repositorio es la capa visual del producto. El backend ya existe y se mantiene como fuente única de verdad: este frontend no rehace la API ni accede directamente a la base de datos, solo consume sus endpoints.
| Recurso | URL / valor |
|---|---|
| Demo en producción | https://quotematicweb.davlos.es/ |
| API REST | https://quotematic.davlos.es |
| Documentación Swagger | https://quotematic.davlos.es/api-docs/ |
| Repositorio backend | https://github.com/David-LS-Bilbao/QuoteMatic |
| Repositorio frontend | git@github.com:David-LS-Bilbao/QuoteMatic-Web.git |
El frontend está desplegado en https://quotematicweb.davlos.es/ y consume directamente la API de producción en https://quotematic.davlos.es. El backend está configurado con CORS para aceptar el dominio del frontend con credentials: 'include', por lo que la sesión cookie funciona en producción exactamente igual que en local.
Para revisar las zonas autenticadas (/account, /favorites, /my-quotes) y administrativas (/admin/dev-panel, /admin/import) sin tener que registrar un usuario nuevo:
Email: demo-admin@quotematic.local
Password: QuoteMaticDemo2026!
Rol: admin
Esta cuenta es exclusivamente para revisión técnica del proyecto. Para probar el flujo de usuario normal basta con registrarse desde /register (solo email, nombre, contraseña y rango de edad).
Después de la evaluación realizada, esta cuenta demo se eliminará por completo del backend.
- Node.js 20.x LTS (recomendado). Probado contra Vite 8 y React 19.
- npm 10.x (incluido con Node 20).
- Conexión a internet para alcanzar la API de QuoteMatic (no hay backend local, no hace falta levantar nada en paralelo).
# 1. Clonar el repositorio
git clone git@github.com:David-LS-Bilbao/QuoteMatic-Web.git
cd QuoteMatic-Web
# 2. Instalar dependencias
npm install
# 3. Configurar variables de entorno
cp .env.example .env
# El .env.example apunta ya al backend de producción, no hace falta cambiarlo.
# 4. Arrancar el servidor de desarrollo
npm run devVite levantará el frontend en http://localhost:5173 (puerto por defecto). Al cargar / se debe ver una frase aleatoria real recuperada desde la API.
| Comando | Acción |
|---|---|
npm install |
Instala dependencias. |
npm run dev |
Levanta el entorno de desarrollo (Vite). |
npm run lint |
Ejecuta ESLint sobre todo el proyecto. |
npm run build |
Compila TypeScript y genera el build de producción en dist/. |
npm run preview |
Sirve el build de producción para inspección local. |
Antes de cualquier PR, el flujo recomendado es:
npm run lint
npm run buildAmbos comandos deben terminar con cero errores y cero warnings.
El cliente solo necesita una variable. Está en .env.example:
VITE_API_BASE_URL=https://quotematic.davlos.esSi la API se mueve a otro dominio, basta con cambiar este valor. El cliente HTTP (src/services/apiClient.ts) tiene fallback a https://quotematic.davlos.es si la variable no se define.
Toda la lógica de datos vive en el backend desplegado. El frontend consume estos endpoints:
GET /api/quotes/random # Frase aleatoria individual (Home).
GET /api/quotes/random?count=N # Pool aleatorio de N frases (Explore).
GET /api/quotes # Listado paginado (búsqueda por texto).
GET /api/quotes/:id # Frase concreta por id.
GET /api/authors # Catálogo de autores.
GET /api/authors/:id/quotes # Frases de un autor.
GET /api/situations # Catálogo de situaciones (filtros).
GET /api/quote-types # Catálogo de tipos de frase (filtros).POST /api/auth/register
POST /api/auth/login
GET /api/auth/me
POST /api/auth/logout
GET /api/favorites/me
POST /api/favorites/:quoteId
DELETE /api/favorites/:quoteId
GET /api/me/quotes
POST /api/me/quotes
GET /api/me/quotes/:id
PUT /api/me/quotes/:id
DELETE /api/me/quotes/:idPOST /api/quotes/bulk # Importación masiva de frases desde CSV.El backend usa cookies de sesión, no JWT en localStorage. Por eso todas las peticiones desde React se envían con:
credentials: 'include'Esa decisión está centralizada en src/services/apiClient.ts y la documentación detallada está en Swagger.
| Capa | Tecnología |
|---|---|
| UI | React 19 |
| Build / Dev server | Vite 8 |
| Lenguaje | TypeScript |
| Routing | React Router 7 |
| Iconografía | lucide-react |
| Parseo CSV | papaparse (cliente, para importación admin) |
| Cliente HTTP | fetch nativo + apiClient propio |
| Estilos | CSS nativo por capas (sin frameworks) |
| Estado | useState, useReducer puntual y Context para auth/tema |
| Persistencia local | localStorage para tema y filtros de Explore |
Sin Redux, sin Tailwind, sin librerías de componentes externas, sin mocking. Todo se consume contra el backend real.
| Ruta | Tipo | Descripción |
|---|---|---|
/ |
Pública | Home visual con frase aleatoria real desde GET /api/quotes/random. |
/explore |
Pública | Explorador con filtros, búsqueda y pool aleatorio. |
/explore?quote=<id> |
Pública | Carga una frase concreta (deep link desde /authors/:id). |
/authors |
Pública | Catálogo de autores con búsqueda en tiempo real. |
/authors/:authorId |
Pública | Detalle de autor con tabla de frases (responsive: tarjetas apiladas en móvil). |
/about |
Pública | Presentación técnica del proyecto. |
/login |
Pública | Inicio de sesión con cookie. Preserva URL de retorno. |
/register |
Pública | Registro de usuario con ageRange. |
/account |
Protegida | Dashboard de usuario con accesos rápidos y logout. |
/favorites |
Protegida | Frases guardadas por el usuario. |
/my-quotes |
Protegida | CRUD privado de frases. Borrado con modal propio (ConfirmDialog). |
/admin/dev-panel |
Protegida (admin) | Estadísticas del catálogo y accesos rápidos. |
/admin/import |
Protegida (admin) | Importación masiva de frases desde CSV. |
* |
Pública | Página 404 propia. |
Este frontend no aplica un patrón MVC clásico. La organización real es una arquitectura React por capas y features:
pagescomponen pantallas / rutas.componentsagrupa piezas visuales: subcarpetas por feature (home,explore,auth,my-quotes,share,layout) y un núcleo reutilizable encomponents/ui.hooksconcentra estado, efectos y comportamiento.servicesencapsula el acceso HTTP a la API.contextgestiona estado transversal (sesión, tema).typesdefine los contratos TypeScript del dominio y de la API.utilscontiene helpers puros (formateo, share channels, etc.).stylescentraliza tokens, base, layout, utilidades, UI y CSS por feature.
Como referencia rápida para quien viene de MVC:
| MVC aproximado | Equivalente aquí |
|---|---|
| Model | types, respuestas de API y parte de services |
| View | pages, components y styles |
| Controller | hooks, handlers de páginas y context |
La equivalencia es solo orientativa. La estructura encaja mejor en el patrón React moderno: separación clara entre UI, lógica de estado, acceso a datos y contratos, sin forzar MVC.
QuoteMatic-Web/
├── docs/
│ ├── arquitectura-frontend.md
│ ├── auditoria-final-quotematic-web.md
│ ├── auditoria-random-explore.md
│ ├── auditoria-ui-final.md
│ ├── decisiones-tecnicas.md
│ ├── guia-qa-final.md
│ ├── informe-previo.md
│ ├── memoria-feat-admin-csv-import.md
│ ├── memoria-feat-auth-session.md
│ ├── memoria-feat-explore-hero-quote.md
│ ├── memoria-feat-home-random-quote.md
│ ├── memoria-feat-my-private-quotes.md
│ ├── memoria-feat-share-channels.md
│ ├── memoria-feat-ui-design-system.md
│ ├── PROJECT_STATUS.md
│ ├── refactorizacion-estilos-css.md
│ └── refactorizacion-paginas.md
├── public/
├── src/
│ ├── app/
│ │ ├── App.tsx
│ │ └── router.tsx
│ ├── components/
│ │ ├── auth/ProtectedRoute.tsx
│ │ ├── explore/ (ExploreHeader, ExploreFilters, ExploreSummary, ExploreResults)
│ │ ├── home/ (HomeQuoteSpotlight, HomeFeatureGrid, HomeInfoPanel)
│ │ ├── layout/ (AppLayout, Navbar, Footer, UserMenu, PageTransition)
│ │ ├── my-quotes/ (MyQuoteForm, MyQuoteCard)
│ │ ├── share/ (ShareQuoteButton, ShareQuoteActions)
│ │ └── ui/ (Badge, Button, ConfirmDialog, EmptyState, FilterControl, QuoteCard)
│ ├── context/ (AuthProvider, ThemeProvider y sus contextos)
│ ├── hooks/ (useAuth, useFavorites, useMyQuotes, useExploreQuotes, useRandomQuote, useTheme, useAuthors, useAuthorQuotes, useShareQuote, useAdminDashboard, useAdminCsvImport)
│ ├── pages/ (Home, Explore, Authors, AuthorDetail, About, Login, Register, Account, Favorites, MyQuotes, AdminDevPanel, AdminCsvImport, NotFound)
│ ├── services/ (apiClient, authService, quotesService, catalogService, favoritesService, myQuotesService, authorsService, adminBulkQuotesService)
│ ├── styles/
│ │ ├── variables.css
│ │ ├── base.css
│ │ ├── utilities.css
│ │ ├── layout.css
│ │ ├── ui/ (button, badge, quote-card, empty-state, filter-control, confirm-dialog)
│ │ └── features/ (home, explore, auth, authors, favorites, my-quotes, share, admin, admin-csv-import, page-transition, placeholders)
│ ├── types/ (api, auth, author, catalog, favorite, myQuote, quote)
│ ├── utils/ (quoteHelpers, favoriteHelpers, shareChannels, shareQuote)
│ └── main.tsx
├── .env.example
├── package.json
└── README.md
La capa CSS sigue el mismo criterio de capas y features. El punto de entrada es src/styles/index.css, importado desde src/main.tsx.
| Archivo / carpeta | Responsabilidad |
|---|---|
variables.css |
Tokens globales: colores, radios, sombras, layout y transiciones. Dark y light mode. |
base.css |
Reset, estilos de html, body, tipografía global, foco accesible. |
layout.css |
Shell de la app, navbar, footer, user menu y su responsive. |
utilities.css |
Utilidades compartidas (page-section, eyebrow, page-lead). |
ui/ |
Estilos de los componentes reutilizables. |
features/ |
Estilos específicos de cada pantalla o feature. |
La organización sigue ITCSS + Feature Folders: ITCSS dicta el orden de capas de menor a mayor especificidad; Feature Folders agrupa lo propio de cada pantalla en su carpeta. Documentado en docs/refactorizacion-estilos-css.md.
Regla simple de mantenimiento: cualquier estilo nuevo va al archivo de su capa o feature. No se recrea un components.css global sin dueño claro.
La interfaz usa una dirección visual propia llamada Cosmos:
- Tema oscuro y claro con toggle en navbar y persistencia en
localStorage. - Fondos con gradientes radiales cálidos.
- Glassmorphism en navbar, footer, cards y paneles (
backdrop-filter). - Sombras suaves y efectos 3D ligeros en cards.
- Responsive mobile-first.
- Soporte para
prefers-reduced-motionen todas las animaciones. - Compatibilidad con Safari mediante
-webkit-backdrop-filter.
La pantalla /explore añade encima una estética editorial tipo pergamino (textura, comilla decorativa serif y borde envejecido), aplicada solo a la card protagonista para no afectar al QuoteCard reutilizable.
La selección aleatoria de frases en /explore está pensada para evitar repetición de autores y frases. El flujo final:
- Pool aleatorio desde backend: el hook
useExploreQuotespideGET /api/quotes/random?count=20, que devuelve un pool con diversidad real sobre todo el catálogo (no usa el listado paginado, que está ordenado porcreatedAty queda sesgado por imports recientes). - Fisher-Yates local barajado del pool al recibirlo.
- Cascada anti-repetición en
pickQuote:- P1: id no visto + autor no visto + autor distinto del inmediatamente anterior.
- P2: id no visto + autor distinto del inmediatamente anterior.
- P3: id no visto (acepta repetir autor como último recurso).
- Cuando se agotan los ids, se pide un nuevo pool al backend conservando el historial de autores.
- Modo búsqueda: cuando hay
searchactivo, se cae aGET /api/quotes?search=...&page=Npaginado (el endpoint random no soporta búsqueda por texto). Al agotar la página se salta a otra aleatoria del total. - Modo deep link (
/explore?quote=<id>): carga la frase por id; el primer "Otra frase" vuelve al pool random y recuerda el autor de la frase concreta para no repetirlo.
Toda la lógica vive en src/hooks/useExploreQuotes.ts. La auditoría completa que motivó esta versión está en docs/auditoria-random-explore.md.
Funcionalidades completadas y operativas contra producción:
- Home con frase aleatoria real, estados de carga / error / éxito.
- Explorador público con filtros (situación, tipo de frase), búsqueda por texto y pool aleatorio con diversidad de autores.
- Catálogo de autores con búsqueda en tiempo real y detalle con tabla de frases.
- Deep link
/explore?quote=<id>desde detalle de autor. - Autenticación cookie (login, registro, sesión persistente, logout) con
AuthContextyAuthProvider. - Rutas protegidas (
ProtectedRoute) que preservan la URL de retorno tras el login. - Favoritos del usuario (
useFavorites, página/favorites). - CRUD privado de frases (
useMyQuotes, página/my-quotes) con borrado medianteConfirmDialogpropio. - Tema oscuro/claro con toggle en navbar.
- Compartir frase: Web Share API + fallback a portapapeles + canales específicos (WhatsApp, Email, X, Facebook, Discord).
- Panel admin (
/admin/dev-panel) con estadísticas del catálogo en vivo y accesos rápidos. - Importación masiva de frases desde CSV (
/admin/import) con parseo local, validación, vista previa y reporte de resultado. - Documentación técnica completa en
docs/(memorias por sprint + auditorías UI y de random).
Pendiente:
- Tests automáticos (cubierto con QA manual; ver checklist más abajo).
| Requisito | Estado |
|---|---|
| Consumo de API REST real | Cumplido — Home, Explore, Authors, Auth, Favorites, My Quotes y Admin. |
useState |
Cumplido — en todos los hooks de feature y en páginas con estado local. |
useEffect |
Cumplido — cargas iniciales, transiciones, sincronización con URL. |
localStorage |
Cumplido — tema y filtros de Explore persistidos. |
| Más de 5 componentes propios | Ampliamente cumplido (UI + por feature). |
| Hooks personalizados | Cumplido — useAuth, useFavorites, useMyQuotes, useExploreQuotes, useRandomQuote, useTheme, useAuthors, useAuthorQuotes, useShareQuote, useAdminDashboard, useAdminCsvImport. |
| Routing | Cumplido — React Router 7 con rutas públicas, protegidas y por rol. |
| Autenticación y rutas protegidas | Cumplido — sesión cookie + ProtectedRoute con preservación de URL de retorno. |
| Responsive | Cumplido — mobile-first en todas las features (verificado a 360 px). |
| TypeScript | Cumplido — tipado estricto, sin any salvo casos justificados. |
| Documentación | Cumplido — docs/ con memoria por sprint y auditorías finales. |
| Sprint | Rama | Estado | Objetivo |
|---|---|---|---|
| 0 | feat/project-bootstrap |
Completado | Bootstrap Vite + React + TS + Router + ESLint. |
| 1 | feat/ui-design-system |
Completado | Sistema visual Cosmos. |
| 2 | feat/home-random-quote |
Completado | Home conectada a API real. |
| 3 | feat/explore-quotes |
Completado | Explorador público con filtros, búsqueda y persistencia. |
| 4 | feat/auth-session |
Completado | Login, registro, sesión cookie y roles. |
| 5 | feat/favorites |
Completado | Favoritos de usuario. |
| 6 | feat/my-private-quotes |
Completado | CRUD privado de frases. |
| 7 | feat/theme-toggle |
Completado | Tema dark/light con persistencia. |
| 8 | feat/share-quote |
Completado | Web Share API + fallback portapapeles. |
| 9 | feat/authors-catalog |
Completado | Catálogo de autores con búsqueda. |
| 10 | feat/author-detail |
Completado | Detalle de autor con tabla de frases. |
| 11 | feat/admin-dev-panel |
Completado | Panel admin con estadísticas. |
| 12 | feat/admin-csv-import |
Completado | Importación masiva de frases por CSV. |
| 13 | feat/share-channels |
Completado | Canales: WhatsApp, Email, X, Facebook, Discord. |
| 14 | feat/explore-hero-quote |
Completado | Rediseño Explore tipo pergamino y anti-repetición. |
| 15 | feat/explore-scroll-quote-ui |
Completado | UI scroll-stable, autor clicable, ?quote=<id> y menú Enviar compacto. |
| 16 | fix/account-page-functionality |
Completado | Account convertido en dashboard funcional. |
| 17 | fix/final-ui-polish |
Completado | Fixes finales: ConfirmDialog, return-to en login, tabla autor responsive, CSV admin móvil, pool random /api/quotes/random?count=N y estabilidad de transiciones en Explore. |
| 18 | chore/production-deploy |
Completado | Deploy del frontend en https://quotematicweb.davlos.es/. Backend de producción actualizado para incluir el nuevo dominio en CORS_ALLOWED_ORIGINS con credentials: 'include'. |
| 19 | fix/admin-panel-final-polish |
Completado | Sección "Próximas herramientas" del panel admin renombrada a "Herramientas de administración" con pills de estado reales (MVP activo / Disponible / Backlog) y acción directa "Abrir importador" hacia /admin/import. |
Antes de cada PR:
npm run lint # Debe terminar sin errores ni warnings.
npm run build # Debe terminar con build exitoso (tsc -b && vite build).
npm run dev # Verificación manual en navegador.Revisión manual de rutas:
/
/explore
/explore?quote=<id-real-de-alguna-frase>
/authors
/authors/<id-real-de-algun-autor>
/about
/login
/register
/account (con sesión)
/favorites (con sesión)
/my-quotes (con sesión)
/admin/dev-panel (con sesión admin)
/admin/import (con sesión admin)
/ruta-inexistente
Checklist visual y funcional mínimo:
- Sin errores en consola.
- Sin scroll horizontal en ninguna ruta (verificar 360 px y 390 px).
- Home carga una frase real desde la API.
- "Nueva frase" en Home y "Otra frase" en Explore funcionan.
- Explore: 20 clicks de "Otra frase" muestran autores diversos (no repite consecutivos).
- Búsqueda por texto en Explore funciona y mantiene paginación coherente.
- /explore?quote=<id> abre una frase concreta y "Otra frase" limpia la URL.
- Authors: catálogo + búsqueda + click navega a detalle.
- Authors: en móvil la tabla se convierte en lista apilada (sin scroll horizontal raro).
- Compartir: Web Share, Copiar, WhatsApp, Email, X, Facebook y Discord. Feedback desaparece tras 2.5 s.
- Theme toggle persiste tras recargar.
- Login con la cuenta demo redirige al destino solicitado (no siempre a /account).
- Favorites y My Quotes solo accesibles con sesión.
- /my-quotes: borrado abre ConfirmDialog propio; Escape y Cancelar cierran sin borrar.
- /admin/dev-panel muestra estadísticas reales del catálogo.
- /admin/import: CSV válido se parsea, previsualiza e importa; los errores bloquean el botón.
- 404 correcto en ruta inexistente.
Ramas principales:
main = estable / demo
dev = integración
Tipos de rama:
feat/* = nuevas features
fix/* = correcciones de bugs
docs/* = documentación
chore/* = mantenimiento, refactor, auditoría
audit/* = auditorías de solo lectura
Flujo recomendado:
git checkout dev
git pull origin dev
git checkout -b feat/nombre-feature
# ... trabajo ...
npm run lint
npm run build
git status
# PR siempre a `dev`. A `main` solo cuando todo el ciclo está estable.El histórico técnico y las auditorías están en docs/:
| Documento | Contenido |
|---|---|
informe-previo.md |
Brief inicial del proyecto. |
arquitectura-frontend.md |
Visión global de capas y decisiones. |
decisiones-tecnicas.md |
Justificación de stack y patrones. |
refactorizacion-estilos-css.md |
Cómo se aplica ITCSS + Feature Folders. |
refactorizacion-paginas.md |
Movimiento de páginas y rutas. |
memoria-feat-*.md |
Una memoria técnica por sprint completado. |
PROJECT_STATUS.md |
Snapshot del estado del MVP. |
auditoria-final-quotematic-web.md |
Auditoría general previa al cierre. |
auditoria-ui-final.md |
Auditoría UI/UX final (rama audit/final-ui-review). |
auditoria-random-explore.md |
Auditoría del comportamiento random de Explore y propuesta de fix. |
guia-qa-final.md |
Guía de QA manual completa para revisión. |
David López Sotelo
Proyecto individual React del bootcamp. Frontend desarrollado en solitario sobre la API REST QuoteMatic (también de autoría propia, repositorio separado).