Skip to content

David-LS-Bilbao/QuoteMatic-Web

Repository files navigation

QuoteMatic Web

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.


Demo y entorno de evaluación

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.

Credenciales de prueba (solo evaluación)

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.


Requisitos previos

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

Instalación local

# 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 dev

Vite levantará el frontend en http://localhost:5173 (puerto por defecto). Al cargar / se debe ver una frase aleatoria real recuperada desde la API.


Scripts disponibles

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 build

Ambos comandos deben terminar con cero errores y cero warnings.


Variables de entorno

El cliente solo necesita una variable. Está en .env.example:

VITE_API_BASE_URL=https://quotematic.davlos.es

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


API consumida

Toda la lógica de datos vive en el backend desplegado. El frontend consume estos endpoints:

Públicos

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

Autenticados (sesión cookie)

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/:id

Admin (sesión cookie + rol admin)

POST /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.


Stack técnico

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.


Rutas implementadas

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.

Arquitectura del proyecto

Este frontend no aplica un patrón MVC clásico. La organización real es una arquitectura React por capas y features:

  • pages componen pantallas / rutas.
  • components agrupa piezas visuales: subcarpetas por feature (home, explore, auth, my-quotes, share, layout) y un núcleo reutilizable en components/ui.
  • hooks concentra estado, efectos y comportamiento.
  • services encapsula el acceso HTTP a la API.
  • context gestiona estado transversal (sesión, tema).
  • types define los contratos TypeScript del dominio y de la API.
  • utils contiene helpers puros (formateo, share channels, etc.).
  • styles centraliza 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.

Estructura de carpetas

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

Arquitectura CSS

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.


Design System Cosmos

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-motion en 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.


Comportamiento random de Explore

La selección aleatoria de frases en /explore está pensada para evitar repetición de autores y frases. El flujo final:

  1. Pool aleatorio desde backend: el hook useExploreQuotes pide GET /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 por createdAt y queda sesgado por imports recientes).
  2. Fisher-Yates local barajado del pool al recibirlo.
  3. 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.
  4. Modo búsqueda: cuando hay search activo, se cae a GET /api/quotes?search=...&page=N paginado (el endpoint random no soporta búsqueda por texto). Al agotar la página se salta a otra aleatoria del total.
  5. 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.


Estado del MVP

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 AuthContext y AuthProvider.
  • 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 mediante ConfirmDialog propio.
  • 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).

Requisitos de bootcamp cubiertos

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.

Plan de sprints

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.

QA recomendado

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.

Git workflow

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.

Documentación adicional

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.

Autor

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

About

Aplicación web full stack para gestión de frases, usuarios y contenido motivacional.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors

Languages