3. Web Cliente — Next.js (Frontend del Comensal)

3.0 Interfaz visual / Vistas principales

A continuación se muestran las capturas de pantalla principales de la Web Cliente.


3.1 Introducción

La Web Cliente es la cara pública del restaurante: el portal donde los comensales descubren el menú, exploran promociones, arman su pedido, gestionan su perfil y siguen sus órdenes en tiempo real. Está construida como una Single-Page Application (SPA) progresiva sobre Next.js 16 con el Pages Router, lo que significa que navegar entre secciones es rápido y fluido, con renderizado híbrido que combina la velocidad del cliente con SEO optimizado desde el servidor.

El diseño sigue una estética moderna de food-tech, con una paleta de colores cálida centrada en el naranja ámbar de marca (#F5A623 / brand-500) y un azul oscuro corporativo (#2C3E50 / navy-600) que transmite confianza. La interfaz es completamente responsive: se adapta perfectamente desde un teléfono móvil de 320px hasta monitores de escritorio de 1920px, manteniendo la misma experiencia visual coherente en todos los tamaños de pantalla.


3.1.1 Requisitos técnicos

Para usar la Web Cliente, el usuario solo necesita:

Requisito Detalle
Navegador Cualquier navegador moderno: Chrome, Firefox, Safari, Edge (últimas 2 versiones)
Dispositivo Móvil, tablet, laptop o desktop
Conexión Internet estable (Wi-Fi, 4G/5G)
Cookies Habilitadas para mantener la sesión y el carrito
Notificaciones Opcional: permitir notificaciones del navegador para recibir actualizaciones de pedido

3.1.2 Cómo acceder

El cliente abre su navegador y escribe la URL del restaurante, por ejemplo:

https://a.masquetapas.bar

La URL exacta depende del restaurante (configurada en NEXT_PUBLIC_BASE_URL). Al cargar, la página muestra el logo del restaurante, un spinner de carga naranja (brand-500), y en menos de 2 segundos aparece la Home completa con banners, categorías y productos populares.

Nota importante: La web envía automáticamente un header X-localization: es en todas las peticiones al backend, indicando que el idioma activo es español. Si el restaurante soporta otros idiomas, este header cambia dinámicamente según la selección del usuario en el selector de idioma (ubicado típicamente en el footer o en el menú lateral).


3.2 Estructura visual global

3.2.1 Layout principal (MainLayout)

Cada página de la web se envuelve en el MainLayout, que consiste en:

  • TopBar (barra superior): una franja delgada de color navy-700 (#2C3E50 oscurecido) que puede mostrar anuncios flash, horarios de apertura, o un mensaje promocional (por ejemplo, "Envío gratis en pedidos +20€"). En móvil, esta barra se oculta o se convierte en un marquee scrollable.
  • NavBar (barra de navegación): el elemento más visible de la interfaz. Es una barra horizontal fija en la parte superior (sticky) con fondo blanco en modo claro o navy-800 en modo oscuro. Contiene:
  • Logo del restaurante a la izquierda, clickeable para volver a la home.
  • Barra de búsqueda centrada: un input redondeado con borde gris claro, icono de lupa (HiSearch), placeholder "Buscar productos...". Al escribir, aparece un dropdown con sugerencias e historial reciente.
  • Icono de carrito a la derecha: un icono de bolsa de compra (HiShoppingBag) con un badge circular naranja (brand-500) superpuesto en la esquina superior derecha que muestra el número de items. Si el carrito está vacío, el badge desaparece.
  • Icono de perfil: un icono de usuario (HiUser) que lleva a /auth/login si no está logueado, o a /profile si sí lo está. Cuando está logueado, muestra un avatar circular pequeño (40x40px) con la foto de perfil.
  • MobileBottomNav (solo en móvil): una barra de navegación fija en la parte inferior con 5 iconos: Inicio, Buscar, Carrito (con badge), Órdenes, Perfil. El icono activo está resaltado en naranja (brand-500), los demás en gris (#7F8C8D).
  • Footer (pie de página): en desktop aparece al final de cada página. Contiene enlaces a páginas legales (Términos, Privacidad, Reembolso, Cancelación), información del restaurante, redes sociales, y un selector de idioma. El fondo es navy-800 con texto blanco/gris claro.
  • Sidebar (menú lateral): en desktop, un menú hamburguesa (icono de 3 líneas) a la izquierda de la NavBar abre un drawer lateral izquierdo con categorías del menú, enlaces rápidos, y opciones de cuenta.

3.2.2 Modo oscuro (Dark Mode)

La web soporta modo oscuro mediante la estrategia class. Un toggle en el footer o menú de perfil añade/elimina la clase dark en la etiqueta <html>. Cuando está activo:
- Fondo principal: navy-900 (casi negro azulado)
- Texto: blanco o gris muy claro
- Tarjetas: navy-800 con bordes sutiles
- Badges y botones de acción: siguen siendo naranja (brand-500) para mantener la identidad de marca
- La preferencia se guarda en localStorage vía Redux Persist, por lo que al volver a la web se recuerda la elección del usuario.


3.3 Páginas principales — Descripción visual detallada

3.3.1 Home (/)

Cómo se ve:

La Home es la vitrina digital del restaurante. Es una página larga, scrollable, con fondo blanco (o navy-900 en dark mode). Al cargar, aparece un skeleton loader con bloques grisáceos animados hasta que los datos llegan del backend.

Secciones visuales de arriba a abajo:

  1. BannerSlider: un carrusel horizontal de banners promocionales a ancho completo, implementado con Swiper. Cada banner es una imagen de alta resolución con texto superpuesto (título grande en blanco, subtítulo en ámbar, botón CTA). Los banners se deslizan automáticamente cada 5 segundos. En la parte inferior hay dots indicadores pequeños: el activo es un círculo naranja (brand-500), los inactivos son gris claro. El usuario puede deslizar con el dedo en móvil o arrastrar en desktop. En los laterales (solo desktop), flechas de navegación semi-transparentes.

  2. Selector de sucursal / Categorías: debajo del banner, una fila horizontal scrollable de chips redondeados con iconos (pizza, hamburguesa, sushi, etc.). Cada chip tiene borde gris y texto oscuro. Al hacer clic, el chip se vuelve naranja (brand-500 con texto blanco) y filtra los productos. Si hay múltiples sucursales, aparece un selector desplegable con el nombre de la sucursal actual y un icono de mapa (HiMapPin).

  3. Menú del día / Set Menu: una sección destacada con fondo ligeramente amarillo/naranja (brand-50) que muestra el combo del día. Tarjeta grande con imagen, título "Menú del día", precio tachado y precio promocional en verde (#16A34A), y un botón naranja grande "Añadir al menú".

  4. Productos populares / Recomendados: grid de 2-4 columnas según el ancho de pantalla. Cada celda contiene un ProductCard.

  5. Mapa de sucursales: una sección intermedia con un mapa embebido (Google Maps o similar) mostrando la ubicación de las sucursales cercanas. Marcadores con el logo del restaurante. Al hacer clic en un marcador, aparece un popup con nombre, dirección, horario y botón "Ver menú".

  6. Sección de confianza: iconos con texto breve ("Envío rápido", "Pago seguro", "Ingredientes frescos") en una fila horizontal.

Qué hace: Es la página de entrada donde el cliente descubre el restaurante, ve promociones actuales, y comienza su navegación hacia productos o categorías.

Flujo paso a paso:
1. El cliente entra a https://a.masquetapas.bar.
2. Ve el BannerSlider y puede deslizar para ver promociones.
3. Hace clic en una categoría o desplaza hacia abajo para ver productos.
4. Hace clic en un ProductCard para ver detalles del producto.
5. O usa la barra de búsqueda para encontrar algo específico.

Conexión con backend:
- GET /api/v1/config — carga configuración del restaurante (nombre, logo, colores, horarios, modo mantenimiento).
- GET /api/v1/products/popular — lista de productos populares.
- GET /api/v1/products/latest — novedades.
- GET /api/v1/branches — lista de sucursales para el mapa.
- GET /api/v1/categories — categorías activas.

SEO/SSR: La Home usa getServerSideProps para renderizar meta tags OG (Open Graph) en el servidor: título del restaurante, descripción, imagen de logo, y URL canónica. Esto optimiza el compartir en redes sociales y el indexado en Google. Si el backend devuelve maintenance_mode: true, la página muestra una pantalla de mantenimiento con un icono de engranaje y el mensaje configurado desde el panel de administración.


3.3.2 Ficha de producto (/products/[id])

Cómo se ve:

Es una página vertical con scroll. En la parte superior, una imagen grande del producto (ocupa todo el ancho en móvil, 50% en desktop). La imagen puede ampliarse con zoom (React Zoom Pan Pinch) haciendo clic o pellizcando en móvil. Debajo de la imagen:

  • Título del producto en fuente Rubik, tamaño grande (24-32px), color navy-800.
  • Badge de promoción (si aplica): una etiqueta ovalada naranja con texto blanco "-20%" o "2x1".
  • Precio: el precio base en fuente grande, negrita. Si hay descuento, el precio original tachado en gris y el precio final en verde (#16A34A).
  • Descripción: texto en gris (#7F8C8D), Inter 14px, explicando ingredientes, sabor, presentación.
  • Selector de variaciones: si el producto tiene opciones (ej: tamaño "Pequeño / Mediano / Grande"), aparece un grupo de botones tipo "radio button" alineados horizontalmente, con el seleccionado en naranja y los demás con borde gris.
  • Selector de addons: checkboxes o chips con precios adicionales (ej: "+ Queso extra: +1.50€"). Cada addon tiene un pequeño toggle o checkbox cuadrado.
  • Alérgenos: una fila de iconos pequeños (gluten, lactosa, frutos secos, etc.) con tooltips al pasar el mouse. En móvil, un botón "Ver alérgenos" abre un modal con la lista completa.
  • Reviews / Valoraciones: una sección con estrellas doradas (1-5), el promedio grande, y comentarios de otros clientes. Cada review muestra avatar, nombre, fecha, estrellas y texto. Un botón "Escribir reseña" (solo si está logueado y compró el producto).
  • Botón flotante de acción: en móvil, un botón grande naranja fijo en la parte inferior que dice "Añadir al carrito — X.XX€". En desktop, el mismo botón pero debajo de los selectores. Al hacer clic, el botón muestra un spinner breve y luego una animación de "check" verde, mientras arriba el badge del carrito se incrementa.

Qué hace: Permite al cliente ver todos los detalles de un producto, personalizarlo con variaciones y addons, y añadirlo al carrito.

Flujo paso a paso:
1. El cliente llega desde la Home o desde una categoría.
2. Ve la imagen grande, lee la descripción.
3. Selecciona variación (ej: "Mediano"). El precio se actualiza dinámicamente.
4. Marca addons opcionales. El precio total se recalcula en tiempo real.
5. Hace clic en "Añadir al carrito".
6. Aparece un toast de confirmación naranja en la esquina superior: " Pizza añadida al carrito".
7. El cliente puede seguir comprando o ir al carrito.

Conexión con backend:
- GET /api/v1/products/details/{id} — detalle completo del producto.
- GET /api/v1/customer/review/list?product_id={id} — reviews del producto.
- POST /api/v1/customer/cart/add — añadir item al carrito.


3.3.3 Listado por categoría (/categories/[slug])

Cómo se ve:

Página con header mostrando el nombre de la categoría en grande (ej: " Hamburguesas") y un breadcrumb "Inicio > Categorías > Hamburguesas". Debajo, un grid de ProductCards igual al de la home, pero filtrado. En móvil, al hacer scroll hacia abajo se carga más contenido automáticamente (paginación o infinite scroll). Un botón flotante circular naranja en la esquina inferior derecha permite filtrar/ordenar (precio, popularidad, novedades).

Qué hace: Muestra todos los productos de una categoría específica.

Conexión con backend:
- GET /api/v1/categories/products/{id} — productos de la categoría.


3.3.4 Carrito (/cart)

Cómo se ve:

Página dividida en dos columnas en desktop (60/40), una sola columna en móvil.

Columna izquierda / Lista de items:

Cada item del carrito se muestra como una tarjeta horizontal con:
- Imagen del producto a la izquierda (80x80px, redondeada).
- Nombre del producto en negrita, con las variaciones/addons debajo en texto más pequeño gris (ej: "Mediano, Queso extra, Salsa BBQ").
- Precio unitario y precio total del item (cantidad × unitario).
- Controles de cantidad: un grupo de 3 elementos centrados verticalmente: botón "−" (gris, redondeado), número en el medio, botón "+" (naranja). Al llegar a 1, el botón "−" se desactiva o cambia a un icono de para eliminar.
- Botón de eliminar: un icono de X o papelera en la esquina superior derecha de la tarjeta. Al hacer clic, la tarjeta se desvanece con una animación de Framer Motion y se elimina.

Columna derecha / Resumen:

Una tarjeta "sticky" que se mantiene visible al hacer scroll:
- Subtotal: suma de todos los items.
- Descuento por cupón: si hay un cupón aplicado, muestra el monto en verde con un icono de ticket.
- Envío: costo de envío (o "Gratis" en verde si aplica promoción).
- Total: en fuente grande y negrita.
- Input de cupón: un campo de texto con placeholder "Código de cupón" y un botón naranja "Aplicar" al lado. Si el cupón es válido, aparece un check verde y el descuento se aplica. Si es inválido, un mensaje rojo debajo.
- Botón "Proceder al checkout": un botón enorme, ancho completo, naranja (brand-500), con texto blanco y negrita. Si el carrito está vacío, este botón está deshabilitado (gris) y dice "Carrito vacío".

En móvil, la lista ocupa toda la pantalla y el resumen aparece como un drawer inferior que se desliza hacia arriba al hacer clic en un botón flotante "Ver resumen", o bien la página es una sola columna con el resumen fijo al final.

También existe el CartDrawer (modal lateral): cuando el usuario añade un producto desde la home o hace clic en el icono de carrito en la NavBar, se abre un drawer desde la derecha que muestra una versión compacta del carrito con los últimos items añadidos, un resumen rápido, y el botón "Checkout". El fondo se oscurece (backdrop) para enfocar la atención. El drawer se cierra arrastrándolo hacia la derecha o haciendo clic en el backdrop.

Qué hace: Permite revisar, modificar cantidades, eliminar productos, aplicar cupones, y proceder al pago.

Flujo paso a paso:
1. El cliente añade productos desde la home o ficha de producto.
2. Hace clic en el icono de carrito (badge naranja con número).
3. Se abre el CartDrawer o navega a /cart.
4. Revisa la lista: puede aumentar/disminuir cantidades o eliminar items.
5. Escribe un código de cupón y hace clic en "Aplicar".
6. El sistema valida el cupón y actualiza el total.
7. Hace clic en "Proceder al checkout" y va a /checkout.

Conexión con backend:
- GET /api/v1/customer/cart/list — carga el carrito actual (si está logueado, desde el servidor; si es guest, desde el servidor usando el Guest ID).
- POST /api/v1/customer/cart/add — añadir item.
- POST /api/v1/customer/cart/update — actualizar cantidad.
- POST /api/v1/customer/cart/remove — eliminar item.
- POST /api/v1/customer/coupon/apply — validar y aplicar cupón.

Persistencia: El carrito se sincroniza con el backend en cada cambio. Si el usuario no está logueado, se envía un Guest ID (generado automáticamente y almacenado en Redux Persist), permitiendo que el carrito persista entre sesiones. Cuando el usuario se registra o loguea, el backend fusiona el carrito del guest con el de su cuenta.


3.3.5 Checkout (/checkout)

Cómo se ve:

Es una de las páginas más importantes visualmente. Tiene un layout de 2 columnas en desktop (60/40), una sola columna en móvil. El fondo es blanco muy limpio, con mucho espacio en blanco para no distraer al cliente del objetivo: completar la compra.

Columna izquierda — Formulario de checkout:

  1. Selector de tipo de orden: dos grandes tarjetas clickeables una al lado de la otra:
  2. Delivery: tarjeta con icono de moto, título "A domicilio", descripción "Recibe tu pedido donde quieras". Seleccionada: borde naranja, fondo brand-50, icono naranja.
  3. Takeaway: tarjeta con icono de tienda/bolsa, título "Para recoger", descripción "Pasa a recoger cuando esté listo". Seleccionada: borde naranja, fondo brand-50.
  4. Si se selecciona Delivery, aparecen las secciones de dirección. Si se selecciona Takeaway, se muestra un selector de sucursal para recoger.

  5. Dirección de entrega (solo si Delivery):

  6. Si el usuario tiene direcciones guardadas, aparecen como tarjetas con: nombre de la dirección (ej: "Casa", "Trabajo"), dirección completa, icono de mapa. La seleccionada tiene un borde naranja y un check. Un botón "+ Añadir nueva dirección".
  7. Si no hay direcciones, aparece el formulario de nueva dirección directamente.
  8. El formulario de dirección tiene: campo de texto con autocomplete de Google Places (React auto-complete): al escribir "Calle Ma", aparece un dropdown con sugerencias. Al seleccionar, los campos de ciudad, código postal, país se autocompletan. También hay un mapa embebido pequeño que muestra el pin de la ubicación seleccionada.
  9. Campos: Dirección (línea 1), Número / Piso / Puerta (línea 2), Ciudad, Código postal, País, Notas para el repartidor (ej: "Timbre 3B, dejar en portería").
  10. Un checkbox "Guardar esta dirección para futuros pedidos".

  11. Notas del pedido: un textarea opcional para instrucciones generales (ej: "Sin cebolla, por favor").

  12. Método de pago: un acordeón o lista de opciones:

  13. Tarjeta de crédito/débito: logo de Visa/Mastercard. Seleccionado abre el iframe de MONEI (integración de pago) donde se ingresan los datos de tarjeta de forma segura. El iframe está estilizado con los colores del restaurante.
  14. Efectivo al recibir: solo disponible si el restaurante lo permite. Icono de billetes.
  15. Transferencia bancaria: datos de cuenta (solo si aplica).
  16. Billetera / Wallet: si el usuario tiene saldo, muestra el saldo disponible y un checkbox para usarlo.
  17. Cada opción tiene un radio button a la izquierda, con el seleccionado relleno de naranja.

  18. Resumen de items: una lista compacta de los productos en el carrito (nombre, variaciones, cantidad, precio) sin controles de edición (solo lectura). Un enlace "Editar carrito" vuelve a /cart.

Columna derecha — Resumen de orden (sticky):

Una tarjeta blanca con sombra sutil que se mantiene fija al hacer scroll:
- Título "Resumen de tu pedido".
- Lista de items con subtotal.
- Envío: calculado según la distancia (usando la API de distancia del backend).
- Descuento por cupón (si aplica).
- Total final: fuente muy grande, negrita, color navy-800.
- Botón "Pagar y confirmar pedido": ancho completo, alto considerable (48-56px), naranja (brand-500), texto blanco, negrita. Al hacer clic, muestra un spinner de carga y se desactiva para evitar doble clic.
- Debajo del botón, texto pequeño en gris: "Al confirmar, aceptas los Términos y Condiciones y la Política de Cancelación" con enlaces clickeables.

En móvil, todo es una sola columna. El resumen de orden está al final, fijo en la parte inferior de la pantalla (sticky bottom bar) mostrando solo el total y un botón "Pagar" que al hacer clic expande el formulario completo.

Qué hace: Es el paso final antes de pagar. Recopila dirección, método de pago, y confirma la orden.

Flujo paso a paso:
1. El cliente llega desde el carrito.
2. Selecciona Delivery o Takeaway.
3. Si Delivery, selecciona o crea una dirección. El sistema calcula automáticamente la distancia y el costo de envío llamando al backend.
4. Selecciona método de pago. Si es tarjeta, ingresa los datos en el iframe MONEI.
5. Revisa el resumen y hace clic en "Pagar y confirmar".
6. El backend procesa el pago. Si es exitoso, redirige a /order-success. Si falla, muestra un error en rojo con opción de reintentar.

Conexión con backend:
- GET /api/v1/customer/address/list — direcciones guardadas.
- POST /api/v1/customer/address/store — guardar nueva dirección.
- POST /api/v1/monei/create-payment — crear intención de pago con MONEI.
- POST /api/v1/customer/order/place — colocar la orden final.
- POST /api/v1/monei/retry-payment — reintentar pago fallido.
- GET /api/v1/mapapi/distance-api — calcular distancia/envío.
- GET /api/v1/mapapi/place-api-autocomplete — autocomplete de direcciones.


3.3.6 Confirmación de orden (/order-success)

Cómo se ve:

Una página festiva y tranquilizadora. Fondo blanco o un gradiente sutil naranja/blanco. En el centro:
- Un icono grande de check verde (#16A34A) animado (escala de 0 a 1 con Framer Motion).
- "¡Pedido confirmado!" en fuente grande, negrita, color navy-800.
- Número de orden: en fuente monoespaciada, naranja (brand-500), destacado. Ej: "ORD-123456".
- Resumen breve: tipo de orden, dirección (si delivery), método de pago, total pagado.
- Timeline simplificada: 5 pasos horizontales con iconos: Pedido recibido, ⏳ Confirmado, ⏳ Preparando, ⏳ En camino, ⏳ Entregado. El primero está verde, los demás gris.
- Botón "Seguir mi pedido": naranja, lleva a /orders/tracking/[id].
- Botón "Volver al inicio": secundario, borde naranja, texto naranja.
- Un confetti animado (opcional) cae desde la parte superior durante 3 segundos.

Qué hace: Confirma al usuario que su pago y orden fueron procesados correctamente, y le da acceso directo al tracking.


3.3.7 Mis pedidos (/orders)

Cómo se ve:

Página con tabs o filtros en la parte superior: Todos | Activos | Completados | Cancelados. Cada tab al hacer clic muestra una línea naranja debajo (indicador activo). Debajo, una lista vertical de tarjetas de orden.

Cada tarjeta de orden contiene:
- Fila superior: número de orden en naranja (brand-500), fecha en gris pequeño, estado con badge de color (verde = entregado, amarillo = en proceso, rojo = cancelado).
- Fila media: pequeñas imágenes de los productos (3-4 thumbnails redondeados, si hay más muestra "+3").
- Fila inferior: total del pedido en negrita, botón "Ver detalle" (outline naranja), y si está activo, botón "Seguir" (naranja sólido).
- Al hacer clic en la tarjeta, se expande o navega a /orders/[id].

En móvil, las tarjetas son a ancho completo. En desktop, hay 2-3 por fila.

Qué hace: Permite al cliente ver todo su historial de compras, filtrar por estado, y acceder a detalles o tracking.

Conexión con backend:
- GET /api/v1/customer/order/list — lista de órdenes del usuario.


3.3.8 Detalle de orden (/orders/[id])

Cómo se ve:

Página con header mostrando el número de orden y un breadcrumb. Layout de 2 columnas en desktop.

Columna izquierda:
- Timeline vertical: una línea vertical con círculos conectados. Cada círculo representa un estado:
- Pedido recibido: círculo verde, check, texto abajo con fecha/hora.
- ⏳ Confirmado: círculo verde si completado, gris vacío si no.
- ⏳ Preparando: círculo verde si completado, gris si no, amarillo con spinner si es el estado actual.
- En camino: icono de moto, verde si completado, amarillo si actual, gris si no.
- Entregado: círculo verde, icono de casa.
- Cada estado tiene una fecha/hora debajo en texto pequeño gris.
- Mapa pequeño: muestra la ruta desde el restaurante hasta la dirección del cliente (si está en delivery y en camino).
- Info del repartidor: si está en camino, aparece una tarjeta con avatar circular del repartidor, nombre, teléfono (botón para llamar), y un mini-rating con estrellas.

Columna derecha / Resumen:
- Lista de productos con imagen, nombre, variaciones, cantidad, precio unitario, total por línea.
- Subtotal, envío, descuento, total.
- Dirección de entrega completa (si aplica).
- Método de pago usado.
- Botones de acción:
- "Seguir pedido" (si está activo).
- "Cancelar pedido" (solo si el estado lo permite, ej: antes de "Preparando"). Muestra un modal de confirmación: "¿Estás seguro?" con explicación de la política de cancelación.
- "Reordenar" (si está completado): añade los mismos items al carrito.

Qué hace: Muestra el estado completo de una orden específica, permite cancelarla si aún es posible, y ver la ruta del repartidor.

Conexión con backend:
- GET /api/v1/customer/order/track — detalle y estado de la orden.
- POST /api/v1/customer/order/cancel — cancelar orden.


3.3.9 Tracking en tiempo real (/orders/tracking/[id])

Cómo se ve:

Esta es la página más dinámica de la web. Ocupa toda la pantalla en móvil.

  • Mapa a ancho completo: ocupa el 60-70% de la pantalla. Es un mapa interactivo (Google Maps) centrado en la ubicación del cliente. Un pin azul marca la dirección del cliente. Un pin naranja (o icono de moto) marca la ubicación actual del repartidor. Una línea azul conecta ambos puntos mostrando la ruta.
  • Panel inferior flotante (en móvil, un drawer que se desliza desde abajo; en desktop, un panel lateral derecho):
  • Estado actual grande: " Tu pedido está en camino" con animación de pulso en el icono.
  • Tiempo estimado: "Llegada estimada: 18:45" (calculado por el backend según distancia y tráfico).
  • Avatar del repartidor: circular, con nombre, estrellas, y botones "Llamar" y "Chat".
  • Timeline simplificada: solo los 5 pasos, con el actual resaltado en naranja y animado.
  • Botón "Ver detalle": lleva a /orders/[id].

El mapa se actualiza automáticamente cada 5-10 segundos mientras la orden esté "En camino". El pin del repartidor se mueve suavemente en el mapa.

Qué hace: Permite al cliente seguir la ubicación exacta del repartidor y saber cuándo llegará su pedido.

Conexión con backend:
- Socket.IO client: conexión persistente al backend para recibir actualizaciones en tiempo real de la ubicación del repartidor y cambios de estado.
- GET /api/v1/customer/order/track — datos iniciales del tracking.


3.3.10 Perfil (/profile)

Cómo se ve:

Página personal del cliente. En móvil, una sola columna. En desktop, una columna centrada de 600px máximo.

  • Header del perfil: un banner con color degradado navy-700 a navy-800. Sobre el banner, centrado:
  • Avatar circular grande (120x120px): la foto de perfil del usuario, o un icono genérico si no tiene. Borde blanco grueso de 4px.
  • Nombre completo en blanco, fuente Rubik, tamaño 24px.
  • Email y teléfono debajo en texto más pequeño, gris claro.
  • Botón "Editar perfil": pequeño, blanco con texto naranja, posicionado cerca del avatar.
  • Resumen de actividad: una fila de 3-4 tarjetas pequeñas horizontales:
  • Pedidos realizados: número grande.
  • Total gastado: número grande con €.
  • Puntos de fidelidad: número grande.
  • Cupones activos: número grande.
  • Menú grid de opciones: un grid de 3-4 columnas (2 en móvil) con tarjetas clickeables. Cada tarjeta tiene:
  • Un icono grande centrado (React Icons: HiShoppingBag, HiMapPin, HiWallet, HiTicket, HiHeart, HiBell, HiChat, HiGift, HiStar, HiCog, HiTrash).
  • Título debajo del icono (ej: "Mis órdenes", "Direcciones", "Billetera", "Cupones", "Favoritos", "Notificaciones", "Chat", "Referidos", "Lealtad", "Ajustes", "Borrar cuenta").
  • Un badge pequeño naranja en la esquina superior derecha si hay notificaciones pendientes (ej: 3 notificaciones nuevas).
  • Al pasar el mouse, la tarjeta se eleva ligeramente (sombra más pronunciada) y el icono cambia a color naranja (brand-500).
  • Las tarjetas de "Borrar cuenta" tienen un icono rojo y texto rojo, separadas del resto por un espacio adicional para indicar que es una acción destructiva.

Qué hace: Es el centro de control del cliente. Desde aquí puede acceder a todas las funciones secundarias de su cuenta.

Conexión con backend:
- GET /api/v1/customer/profile — datos del perfil.
- GET /api/v1/customer/order/list — conteo de pedidos.
- GET /api/v1/customer/wallet/balance — saldo y puntos.
- GET /api/v1/customer/notification/list — notificaciones no leídas.


3.3.11 Editar perfil (/profile/edit)

Cómo se ve:

Formulario de edición con campos alineados verticalmente. Cada campo tiene un label arriba en gris oscuro, el input debajo con borde gris redondeado, y un mensaje de error en rojo si la validación falla (validación con Formik + Yup).

Campos visibles:
- Avatar: imagen actual con un botón de cámara superpuesto en la esquina inferior derecha del círculo. Al hacer clic, abre un selector de archivo para subir nueva imagen.
- Nombre completo: input de texto.
- Email: input de email, deshabilitado si el email ya está verificado (con un check verde al lado).
- Teléfono: input con React Phone Number Input, que muestra un dropdown de banderas de países a la izquierda y el número formateado. El prefijo se detecta automáticamente según el país del restaurante.
- Fecha de nacimiento: selector de fecha (datepicker).
- Género: radio buttons (Masculino, Femenino, Otro, Prefiero no decir).
- Botón "Guardar cambios": naranja, ancho completo, con spinner durante la petición.
- Botón "Cambiar contraseña": outline naranja, abre un modal con 3 campos (actual, nueva, confirmar).

Al guardar con éxito, aparece un toast verde en la esquina: "Perfil actualizado correctamente ".

Conexión con backend:
- POST /api/v1/customer/profile/update — actualizar datos.
- POST /api/v1/auth/forgot-password — cambiar contraseña (usa el mismo endpoint con flujo OTP).


3.3.12 Billetera / Wallet (/wallet)

Cómo se ve:

Página con tarjeta de saldo prominente en la parte superior. La tarjeta es un rectángulo redondeado con gradiente de navy-700 a navy-800, texto blanco:
- Saldo actual: número grande, blanco, negrita. Ej: "€ 47.50".
- Puntos de fidelidad: debajo, en color ámbar (brand-300). Ej: " 1.250 puntos".
- Bonus disponible: si hay bonos, aparece un badge naranja.
- Botón "Recargar saldo" (naranja) si el restaurante permite recargas.

Debajo de la tarjeta, una tabla o lista de transacciones:
- Filtros: "Todas | Recargas | Pagos | Recompensas | Reembolsos".
- Cada fila: fecha, descripción (ej: "Pedido #12345", "Recarga con tarjeta", "Bonus de fidelidad"), monto (verde si es entrada, rojo si es salida), estado (completada, pendiente).
- Scroll infinito o paginación con botones "Anterior / Siguiente".

En móvil, cada transacción es una tarjeta individual con los datos apilados verticalmente.

Conexión con backend:
- GET /api/v1/customer/wallet/balance — saldo y puntos.
- GET /api/v1/customer/wallet/transactions — historial.


3.3.13 Cupones (/coupons)

Cómo se ve:

Lista de tarjetas de cupón, cada una con diseño de "ticket" (bordes con semicírculos en los lados simulando perforación):
- Código del cupón: en fuente monoespaciada, naranja, grande. Ej: "TAPAS20".
- Descripción: "20% de descuento en tu próximo pedido".
- Válido hasta: fecha límite en gris.
- Términos: texto pequeño (ej: "Mínimo €15, no acumulable").
- Estado: badge verde "Activo", gris "Usado", rojo "Expirado".
- Botón "Copiar": un icono de copiar al portapapeles. Al hacer clic, cambia a un check verde y aparece un toast: "Código copiado ".
- Cupones usados/expirados aparecen al final de la lista, atenuados (opacity 0.6).

Conexión con backend:
- GET /api/v1/customer/coupon/list — cupones del usuario.


3.3.14 Favoritos / Wishlist (/wishlist)

Cómo se ve:

Grid de ProductCards similar a la home, pero cada tarjeta tiene un icono de corazón rojo (HiHeart) en la esquina superior derecha. Al hacer clic en el corazón, el producto se elimina de favoritos con una animación de desvanecimiento. Si la lista está vacía, se muestra una ilustración de un corazón roto o una bolsa de compras vacía, con texto "No tienes favoritos aún" y un botón naranja "Explorar menú" que lleva a la home.

Conexión con backend:
- GET /api/v1/customer/wishlist/list — lista de favoritos.
- POST /api/v1/customer/wishlist/toggle — añadir/quitar de favoritos.


3.3.15 Notificaciones (/notifications)

Cómo se ve:

Lista vertical de notificaciones tipo "timeline". Cada notificación es una tarjeta horizontal con:
- Icono circular a la izquierda con fondo de color según tipo: naranja (pedido), verde (éxito), rojo (alerta), azul (promoción).
- Contenido: título en negrita, descripción en gris, timestamp pequeño ("Hace 2 horas").
- Estado: una pequeña esquina naranja si no está leída. Al hacer clic, se marca como leída y la esquina desaparece.
- Un botón "Eliminar" (icono X) al pasar el mouse.
- Botón "Marcar todas como leídas" en la parte superior derecha.

Conexión con backend:
- GET /api/v1/customer/notification/list — notificaciones.
- POST /api/v1/customer/notification/read — marcar como leída.
- Recibe actualizaciones en tiempo real vía Socket.IO y FCM (Firebase Cloud Messaging) cuando la web está en segundo plano o minimizada.


3.3.16 Chat (/chat)

Cómo se ve:

Interfaz de chat tipo WhatsApp o Messenger. Ocupa toda la pantalla en móvil, dos columnas en desktop (lista de chats a la izquierda, conversación a la derecha).

  • Lista de conversaciones (izquierda / móvil inicial): cada item muestra avatar, nombre del interlocutor ("Soporte" o nombre del repartidor), último mensaje preview, timestamp, y un badge naranja con número de no leídos si aplica.
  • Conversación activa (derecha / móvil):
  • Header: avatar, nombre, estado ("En línea", "Entregando tu pedido #12345").
  • Burbujas de mensaje: mensajes propios alineados a la derecha con fondo naranja (brand-500) y texto blanco. Mensajes del otro alineados a la izquierda con fondo gris muy claro y texto oscuro. Cada burbuja tiene timestamp pequeño debajo y doble check si está leído.
  • Input inferior: un campo de texto redondeado con placeholder "Escribe un mensaje...", icono de clip (adjuntos), y botón de enviar (icono de avión de papel naranja). El input se mantiene fijo en la parte inferior.
  • Scroll automático: la conversación siempre se desplaza al último mensaje al entrar o al recibir uno nuevo.

Conexión con backend:
- GET /api/v1/customer/message/list — historial de mensajes.
- POST /api/v1/customer/message/send — enviar mensaje.
- Actualizaciones en tiempo real vía Socket.IO.


3.3.17 Direcciones (/address)

Cómo se ve:

Lista de tarjetas de dirección. Cada tarjeta tiene:
- Icono de mapa (HiMapPin) a la izquierda, naranja.
- Nombre de la dirección: "Casa", "Trabajo", o "Otra".
- Dirección completa en texto gris.
- Badge "Predeterminada" verde si es la dirección por defecto.
- Botones: "Editar" (icono lápiz), "Eliminar" (icono basura rojo), "Hacer predeterminada" (si no lo es).
- Botón flotante "+ Añadir dirección" (naranja, circular, esquina inferior derecha en móvil; o botón normal arriba en desktop).

Formulario de nueva dirección (modal o página separada):
- Campo de dirección con autocomplete de Google Places (mientras escribe, aparece dropdown con sugerencias). Al seleccionar una, los campos de ciudad, código postal, país se llenan automáticamente.
- Campos manuales: Nombre de la dirección (ej: "Casa"), Calle y número, Piso/Puerta, Ciudad, Código postal, País, Notas.
- Mapa embebido pequeño mostrando el pin de la ubicación seleccionada. El pin se puede arrastrar para ajustar la ubicación exacta.
- Checkbox "Establecer como dirección predeterminada".
- Botón "Guardar dirección" naranja.

Conexión con backend:
- GET /api/v1/customer/address/list — direcciones.
- POST /api/v1/customer/address/store — crear.
- POST /api/v1/customer/address/update/{id} — editar.
- DELETE /api/v1/customer/address/delete/{id} — eliminar.
- GET /api/v1/mapapi/place-api-autocomplete — autocomplete de Google Places.


3.3.18 Sucursales (/branches y /branches/[id])

Cómo se ve:

/branches: grid de tarjetas de sucursal. Cada tarjeta muestra:
- Imagen de la sucursal (o imagen genérica del restaurante).
- Nombre de la sucursal en negrita.
- Dirección breve en gris.
- Badge de estado: "Abierto" (verde) o "Cerrado" (rojo) según horario.
- Horario de hoy: "Hoy: 10:00 - 22:00".
- Botón "Ver menú" naranja.
- Rating con estrellas y número de reviews.

/branches/[id]: página detallada de una sucursal.
- Imagen grande de cabecera.
- Nombre, dirección completa, teléfono (botón para llamar).
- Mapa con la ubicación exacta.
- Horarios semanales: una tabla con días de la semana y horarios de apertura/cierre.
- Menú de la sucursal: grid de ProductCards de los productos disponibles en esa sucursal específica.
- Reviews: valoraciones de clientes que visitaron esa sucursal.
- Botón "Ordenar desde aquí" que establece esta sucursal como la activa y redirige a la home.

Conexión con backend:
- GET /api/v1/branches — lista de sucursales.
- GET /api/v1/branches/{id} — detalle de sucursal.


Cómo se ve:

Página centrada en la búsqueda. En la parte superior, un input grande a ancho completo con icono de lupa (HiSearch), placeholder "Buscar hamburguesas, pizzas, postres...", y un botón "X" para limpiar. Al enfocar el input, aparece un dropdown debajo con:
- Historial reciente: búsquedas anteriores del usuario con icono de reloj (HiClock) y una X para eliminar cada una.
- Sugerencias populares: tags clickeables ("Pizza", "Hamburguesa", "Postres").

Al escribir 3 o más caracteres, aparece un dropdown de resultados en vivo con productos coincidentes: imagen miniatura, nombre, precio. Al hacer clic en un resultado, va a la ficha de producto. Al presionar Enter, se muestra una página de resultados en grid.

Resultados de búsqueda: grid de ProductCards con filtros laterales (en desktop) o un botón de filtros flotante (en móvil). Filtros disponibles: categoría, rango de precio, ordenar por (relevancia, precio menor/mayor, popularidad, novedades).

Si no hay resultados, una ilustración de lupa con texto "No encontramos resultados para 'xyz'" y sugerencias.

Conexión con backend:
- GET /api/v1/products?search={query} — búsqueda de productos.
- Los filtros y historial se guardan en el estado global de Redux (search slice) para persistencia entre sesiones.


3.3.20 Autenticación (/auth/*)

Login (/auth/login)

Cómo se ve:

Página limpia, centrada, con un card de ancho máximo 420px. Arriba del card, el logo del restaurante centrado.

  • Tabs: "Iniciar sesión" | "Crear cuenta". El activo tiene una línea naranja debajo.
  • Selector de método: dos iconos grandes clickeables: " Email" o " Teléfono". El seleccionado tiene borde naranja.
  • Si selecciona Email: input de email y input de contraseña (con icono de ojo para mostrar/ocultar).
  • Si selecciona Teléfono: input de teléfono con React Phone Number Input (dropdown de bandera + número). Al introducir el número, aparece un botón naranja "Enviar código OTP".
  • Botón "Iniciar sesión": naranja, ancho completo. Al hacer clic, muestra spinner.
  • Enlace "¿Olvidaste tu contraseña?": debajo del botón, texto naranja, lleva a /auth/forgot-password.
  • Separador: línea horizontal con "o" en el medio.
  • Botón "Continuar como invitado": outline gris, permite navegar sin loguearse (se genera Guest ID).

Flujo de login con OTP:
1. El cliente introduce su teléfono.
2. Hace clic en "Enviar código OTP".
3. El backend envía un SMS con el código.
4. La web redirige a /auth/verify-otp.

Verificación OTP (/auth/verify-otp)

Cómo se ve:

Página centrada. Muestra:
- "Ingresa el código de 6 dígitos enviado a +34 612 345 678".
- 6 inputs de un dígito alineados horizontalmente, cuadrados, grandes. Al escribir en uno, el foco salta automáticamente al siguiente. Al borrar, vuelve al anterior.
- Contador regresivo: "Reenviar código en 00:45". Cuando llega a 0, aparece un botón de texto "Reenviar código".
- Botón "Verificar": naranja, se activa cuando los 6 dígitos están llenos.
- Si el código es incorrecto, los inputs se iluminan en rojo y vibran brevemente.

Registro (/auth/register)

Cómo se ve:

Formulario en card centrado. Campos:
- Nombre completo (input texto).
- Email (input email, validación en tiempo real).
- Teléfono (React Phone Number Input).
- Contraseña (input con requisitos visibles: mínimo 8 caracteres, 1 mayúscula, 1 número). Una barra de fuerza debajo cambia de rojo a naranja a verde según la fortaleza.
- Confirmar contraseña (debe coincidir, validación Yup).
- Checkbox de términos y condiciones (enlace a /terms en texto naranja).
- Botón "Crear cuenta" naranja. Al éxito, redirige a /auth/verify-otp para verificar el teléfono.

Recuperar contraseña (/auth/forgot-password)

Cómo se ve:

Formulario simple:
- Input de email o teléfono.
- Botón "Enviar enlace de recuperación" naranja.
- Al enviar, muestra un mensaje de éxito: "Revisa tu email/SMS para continuar" y un botón "Volver al login".

Conexión con backend:
- POST /api/v1/auth/login — login con email/contraseña.
- POST /api/v1/auth/verify-otp — verificar OTP.
- POST /api/v1/auth/registration — registro.
- POST /api/v1/auth/forgot-password — recuperar contraseña.


3.3.21 Programa de referidos (/refer)

Cómo se ve:

Página promocional con:
- Ilustración o icono grande de regalo (HiGift) en naranja.
- Título: "Invita a tus amigos".
- Descripción: "Comparte tu código y gana €5 por cada amigo que haga su primer pedido. Tu amigo también recibe €5 de descuento."
- Código de referido: un campo de texto con el código único del usuario (ej: "JUAN-1234") y un botón "Copiar" al lado.
- Botones de compartir: WhatsApp, Facebook, Twitter, Email, o "Copiar enlace".
- Tabla de amigos referidos: nombre, fecha, estado ("Se registró", "Hizo primer pedido", "Recompensa recibida"), monto ganado.
- Total ganado: badge grande naranja con el monto acumulado.


3.3.22 Puntos de fidelidad (/loyalty)

Cómo se ve:

Página similar a la wallet pero centrada en puntos:
- Tarjeta de puntos: gradiente naranja (brand-500 a brand-600), texto blanco, muestra el total de puntos acumulados (ej: " 1.250 puntos").
- Barra de progreso: una barra horizontal que muestra cuántos puntos faltan para el siguiente nivel/recompensa. Ej: "1.250 / 2.000 puntos para una comida gratis". La barra está llena en proporción naranja, el resto gris claro.
- Nivel actual: "Nivel Plata " con beneficios listados (descuentos exclusivos, envío gratis, etc.).
- Historial de puntos: tabla con fecha, acción ("Pedido completado", "Review escrita", "Amigo referido"), puntos ganados/perdidos, color verde para ganados, rojo para canjeados.
- Catálogo de recompensas: grid de tarjetas con recompensas canjeables: "Cupón €5" (500 pts), "Envío gratis" (300 pts), "Producto gratis" (1000 pts). Cada tarjeta tiene un botón "Canjear" deshabilitado si no hay suficientes puntos.


3.3.23 Páginas legales (/about-us, /terms, /privacy-policy, /refund-policy, /cancellation-policy)

Cómo se ve:

Páginas de texto con formato simple y profesional. Layout centrado con ancho máximo 800px para legibilidad.
- Header con el título de la página (ej: "Términos y Condiciones") en fuente grande, negrita, color navy-800.
- Contenido: texto formateado con secciones, subtítulos, listas numeradas y viñetas. Fondo blanco, texto gris oscuro (#2C3E50 en navy-600), interlineado amplio para facilitar lectura.
- Enlace de contacto: al final de cada página, un bloque con "¿Tienes dudas? Contáctanos" con un botón naranja que abre el chat o el email.
- About Us: puede incluir una foto del equipo o del restaurante, historia breve, misión/visión, valores.


3.3.24 Menú del día (/set-menu)

Cómo se ve:

Página dedicada al menú del día (daily menu). Header con título "Menú del día" y fecha actual. El contenido muestra:
- Entrante: 1 opción de 3 posibles, cada una con imagen, nombre, descripción breve. Radio button para seleccionar.
- Plato principal: 1 opción de 3 posibles, mismo formato.
- Postre: 1 opción de 2 posibles.
- Bebida: 1 opción de varias.
- Precio total del menú: destacado en grande, tachado el precio normal, mostrado el precio menú en verde.
- Botón "Añadir menú al carrito": naranja, grande. Al hacer clic, añade todos los items seleccionados como un combo al carrito.
- Badge de disponibilidad: si el menú del día ya no está disponible (pasada la hora límite), aparece un mensaje rojo "El menú de hoy ya no está disponible. Vuelve mañana." y el botón se desactiva.


3.3.25 QR (/qr)

Cómo se ve:

Página simple centrada en la experiencia QR. Puede ser:
- Escaneo: un área de cámara con marco de escaneo (esquinas naranjas) para leer un código QR de mesa o promoción. Al escanear, redirige a la página correspondiente (ej: menú de la mesa, o aplica un cupón automáticamente).
- Mi QR: si el usuario tiene un código QR personal (para fidelidad o promociones), se muestra un código QR grande centrado con instrucciones "Muestra este código en caja para acumular puntos o canjear recompensas". Debajo, el nombre del cliente y su número de fidelidad.


3.4 Flujos principales detallados (con descripción visual)

3.4.1 Flujo de registro completo

Este es el recorrido que sigue un nuevo cliente desde que llega a la web hasta que tiene su cuenta activa.

Paso 1 — Llegada a la Home:
El cliente abre https://a.masquetapas.bar. Ve la Home completa con banners, categorías y productos. En la NavBar, el icono de perfil ( ) está gris sin avatar. Hace clic en él.

Paso 2 — Login/Registro:
La web navega a /auth/login. Aparece una tarjeta centrada con el logo del restaurante arriba. El cliente hace clic en la pestaña "Crear cuenta". El formulario de registro aparece con campos limpios y bordes grises.

Paso 3 — Completar datos:
El cliente escribe su nombre, email, selecciona su país en el dropdown de banderas del campo de teléfono, introduce su número, escribe una contraseña que cumple los requisitos (la barra de fuerza cambia a verde). Marca el checkbox de términos. Hace clic en "Crear cuenta".

Paso 4 — Verificación OTP:
La web muestra un spinner y luego redirige a /auth/verify-otp. Aparece un mensaje: "Hemos enviado un código a +34 612 345 678". Los 6 cuadros de entrada están vacíos y esperando. El cliente recibe el SMS, escribe los 6 dígitos (el cursor salta automáticamente entre cuadros). El botón "Verificar" se activa (pasa de gris a naranja). Hace clic.

Paso 5 — Éxito y perfil:
Si el código es correcto, aparece un toast verde: " Cuenta verificada. ¡Bienvenido!" La web redirige automáticamente a /profile. Ahora el icono de perfil en la NavBar muestra un avatar circular con las iniciales del nombre. El menú grid muestra todas las opciones disponibles. El cliente puede empezar a comprar.

Paso 6 — Guest → Usuario:
Si el cliente había añadido productos al carrito antes de registrarse (como invitado), el backend automáticamente fusiona el carrito del guest con el de su nueva cuenta. Los items aparecen en el carrito sin perderse.


3.4.2 Flujo de compra completo

Este es el flujo más importante: desde descubrir un producto hasta pagar y confirmar.

Paso 1 — Descubrimiento en la Home:
El cliente está en /. Ve el BannerSlider, desplaza hacia abajo y ve la sección de "Recomendados para ti". Un ProductCard le llama la atención: imagen apetitosa, precio visible, badge naranja "Popular", botón "+" naranja en la esquina inferior derecha de la tarjeta. Hace clic en la tarjeta.

Paso 2 — Ficha de producto:
La web navega a /products/[id]. La página carga con una imagen grande del producto. El cliente lee la descripción, ve que tiene variaciones: "Pequeño €8", "Mediano €10", "Grande €12". Hace clic en "Mediano" — el botón se vuelve naranja con texto blanco, los demás quedan con borde gris. El precio total se actualiza a €10. Marca un addon: "+ Queso extra €1.50". El precio ahora muestra €11.50. Hace clic en el botón naranja grande "Añadir al carrito — €11.50".

Paso 3 — Carrito:
Aparece un toast naranja flotante arriba: " Hamburguesa Mediana añadida al carrito". El badge del carrito en la NavBar cambia de 0 a 1. El cliente hace clic en el icono del carrito. Se abre el CartDrawer desde la derecha: un panel blanco que ocupa el 80% del ancho en móvil, 400px fijo en desktop. Muestra el item con imagen, nombre, variaciones, precio. El cliente decide que quiere otra. Cierra el drawer (arrastra a la derecha o clic en el backdrop oscuro) y vuelve a la home.

Paso 4 — Añadir más productos:
El cliente añade 2 productos más usando el botón "+" rápido en los ProductCards (desde la home, sin entrar a la ficha). El badge del carrito ahora marca 3. Hace clic en el carrito y va a /cart.

Paso 5 — Revisar carrito:
En /cart, ve una lista de 3 items. Cada uno tiene controles +/-. Para el segundo item, hace clic en "+" — la cantidad pasa a 2, el precio total del item se actualiza. El subtotal general en la columna derecha se recalcula instantáneamente. Escribe un código de cupón "TAPAS20" en el input y hace clic en "Aplicar". Aparece un check verde y una línea de descuento en el resumen: "-€3.20". El total se reduce. Hace clic en "Proceder al checkout".

Paso 6 — Checkout:
La web navega a /checkout. El cliente ve dos tarjetas grandes: "Delivery" y "Takeaway". Selecciona "Delivery" — la tarjeta se ilumina con borde naranja. Aparece la sección de dirección. Tiene una dirección guardada "Casa" que aparece como tarjeta seleccionada (borde naranja, check verde). Hace clic en "Notas para el pedido" y escribe "Dejar en portería, timbre 3B".

Paso 7 — Método de pago:
Desplaza hacia abajo a "Método de pago". Selecciona "Tarjeta de crédito/débito". Aparece un iframe de MONEI embebido en la página: un formulario seguro con campos de número de tarjeta, fecha de expiración, CVC, y nombre del titular. El iframe tiene los colores del restaurante (fondo blanco, botones naranja). El cliente introduce los datos de su tarjeta.

Paso 8 — Confirmar y pagar:
En la columna derecha (o al final en móvil), revisa el resumen: lista de 3 items, subtotal, envío €2.50, descuento -€3.20, Total: €28.30. Hace clic en el botón grande naranja "Pagar y confirmar pedido". El botón se desactiva y muestra un spinner blanco. El backend procesa el pago vía MONEI.

Paso 9 — Confirmación:
El pago es exitoso. La web redirige a /order-success. La página muestra un check verde animado, confetti cayendo, y el mensaje "¡Pedido confirmado!". El número de orden "ORD-78432" aparece en naranja grande. Hay dos botones: "Seguir mi pedido" (naranja) y "Volver al inicio" (outline). El cliente hace clic en "Seguir mi pedido".

Paso 10 — Tracking:
La web navega a /orders/tracking/78432. Muestra un mapa grande con el pin del restaurante y la ruta hacia su dirección. En el panel inferior, la timeline muestra: Pedido recibido, ⏳ Confirmado, ⏳ Preparando, ⏳ En camino, ⏳ Entregado. Recibirá actualizaciones automáticas por Socket.IO cada vez que el estado cambie.


3.4.3 Flujo de tracking en tiempo real

Este flujo ocurre después de que el pedido está confirmado y el cliente quiere seguir su progreso.

Paso 1 — Acceder al tracking:
El cliente puede llegar al tracking desde: /order-success haciendo clic en "Seguir mi pedido", desde /orders haciendo clic en "Seguir" en un pedido activo, o desde /orders/[id] haciendo clic en "Seguir pedido".

Paso 2 — Vista del tracking:
La página /orders/tracking/[id] carga. En móvil, ocupa toda la pantalla. El 70% superior es un mapa interactivo. El 30% inferior es un panel blanco con esquinas redondeadas superiores que se desliza hacia arriba (drawer). En desktop, el mapa ocupa el 60% izquierdo y el panel de info está a la derecha.

Paso 3 — Estados iniciales:
Al cargar, la timeline muestra: Pedido recibido (completado), ⏳ Confirmado (estado actual, amarillo con spinner), y los demás gris. El panel dice "⏳ Esperando confirmación del restaurante".

Paso 4 — Actualización en vivo:
Minutos después, el restaurante confirma el pedido. El backend envía un evento Socket.IO. La web recibe la actualización y la timeline cambia: Pedido recibido, Confirmado, ⏳ Preparando (ahora amarillo con animación). El mensaje del panel cambia a " ‍ El restaurante está preparando tu pedido". Un toast aparece brevemente: "Tu pedido ha sido confirmado ".

Paso 5 — En camino:
Cuando el repartidor recoge el pedido, el estado cambia a "En camino". Ahora el mapa muestra dos pins: el destino (casa del cliente, pin azul) y la ubicación actual del repartidor (pin naranja con icono de moto). La ruta entre ambos se dibuja con una línea azul. El panel muestra: " Tu pedido está en camino", tiempo estimado "18:45", foto circular del repartidor (ej: "Carlos M."), 4.8 , y botones "Llamar" y "Chat".

Paso 6 — Movimiento en el mapa:
Cada 5-10 segundos, el backend envía nuevas coordenadas del repartidor. El pin naranja se mueve suavemente en el mapa (animación de transición). El tiempo estimado se actualiza si el tráfico cambia.

Paso 7 — Entrega:
Cuando el repartidor marca el pedido como entregado, la timeline completa todos los círculos en verde. El mapa desaparece o se minimiza. El panel muestra: " Pedido entregado. ¡Buen provecho!". Aparece un botón "Escribir reseña" para valorar el pedido y al repartidor.


3.4.4 Flujo de direcciones

Este flujo permite al cliente gestionar sus direcciones de entrega.

Paso 1 — Acceder a direcciones:
El cliente está en /profile. Hace clic en la tarjeta del menú grid con icono de mapa (HiMapPin) y título "Direcciones". La web navega a /address.

Paso 2 — Lista de direcciones:
La página muestra una lista vertical. Si tiene direcciones guardadas, cada una aparece como tarjeta con icono de pin naranja, nombre (ej: "Casa"), dirección completa, y badge verde "Predeterminada" en la principal. Si no tiene direcciones, aparece una ilustración de un mapa vacío con texto "No tienes direcciones guardadas" y un botón naranja "Añadir primera dirección".

Paso 3 — Añadir nueva dirección:
El cliente hace clic en "+ Añadir dirección". En móvil, se abre una pantalla completa. En desktop, un modal centrado. El formulario tiene un campo "Dirección" con autocomplete de Google Places (usando la API /api/v1/mapapi/place-api-autocomplete). Al escribir "Calle Mayor", aparece un dropdown con sugerencias: "Calle Mayor, Madrid, España", "Calle Mayor, 28013 Madrid", etc.

Paso 4 — Autocompletar:
El cliente selecciona una sugerencia. Los campos de Ciudad, Código postal y País se llenan automáticamente. Añade manualmente: Nombre de la dirección "Casa", Número "15", Piso "3B", y Notas "Timbre 3B, dejar en portería si no contesto". Un mapa pequeño debajo muestra un pin en la ubicación seleccionada. El cliente puede arrastrar el pin para ajustar la ubicación exacta si la dirección es imprecisa.

Paso 5 — Guardar:
Marca el checkbox "Establecer como dirección predeterminada". Hace clic en "Guardar dirección" naranja. Aparece un spinner breve, luego un toast verde: "Dirección guardada ". La web vuelve a la lista de direcciones donde ahora aparece la nueva tarjeta con el badge "Predeterminada".

Paso 6 — Editar o eliminar:
El cliente puede hacer clic en el icono de lápiz para editar una dirección existente (se abre el mismo formulario con datos precargados), o en el icono de basura para eliminar. Al eliminar, aparece un modal de confirmación: "¿Eliminar esta dirección?" con dos botones: "Cancelar" (gris) y "Eliminar" (rojo). Si la dirección eliminada era la predeterminada, la web marca automáticamente la siguiente como predeterminada.


3.5 Componentes UI globales (descripción visual detallada)

3.5.1 NavBar (Barra de navegación)

La NavBar es el ancla visual de toda la web. Está fija en la parte superior (sticky) y siempre visible al hacer scroll.

  • Desktop: altura de 64-72px. Fondo blanco (o navy-800 en dark mode) con sombra sutil debajo (shadow-sm).
  • Logo: a la izquierda, imagen SVG o PNG del restaurante, altura ~40px, clickeable para ir a /.
  • Barra de búsqueda: centrada, ancho ~400-500px. Input con bordes redondeados (radius 8px), borde gris claro (#E5E7EB), placeholder "Buscar productos, categorías...". Icono de lupa (HiSearch) a la izquierda dentro del input. Al enfocar, el borde cambia a naranja (brand-500).
  • Iconos de acción: a la derecha, en fila horizontal:
    • Carrito: icono de bolsa (HiShoppingBag), tamaño 24px. Si hay items, un badge circular naranja (brand-500) de 18px de diámetro superpuesto en la esquina superior derecha con el número en blanco y fuente pequeña. Si hay más de 9 items, muestra "9+".
    • Perfil: si no logueado, icono de usuario (HiUser) gris. Si logueado, avatar circular de 36-40px con la foto de perfil o iniciales sobre fondo naranja. Al pasar el mouse, un pequeño dropdown aparece con opciones: "Mi perfil", "Mis órdenes", "Cerrar sesión".
    • / Tema: icono de luna o sol para toggle de dark mode (puede estar en el footer o en este dropdown).
  • Menú hamburguesa: a la izquierda del logo (solo si hay sidebar), 3 líneas horizontales grises que se transforman en X al abrir el sidebar.

  • Móvil: la barra de búsqueda se colapsa en un icono de lupa. Al hacer clic, se expande a ancho completo y empuja los demás iconos. El logo está centrado. Los iconos de carrito y perfil están a la derecha.

3.5.2 MobileBottomNav (Barra de navegación inferior)

Solo visible en móvil y tablet (<768px). Es una barra fija en la parte inferior, altura ~64px, fondo blanco con borde superior gris muy claro o sombra superior.

Contiene 5 iconos distribuidos equitativamente:
1. Inicio (HiHome): va a /. Icono de casa.
2. Buscar (HiMagnifyingGlass): va a /search. Icono de lupa.
3. Carrito (HiShoppingBag): va a /cart. Mismo badge naranja que en la NavBar.
4. Órdenes (HiClipboardDocumentList): va a /orders. Icono de lista.
5. Perfil (HiUser): va a /profile o /auth/login.

El icono correspondiente a la página actual está resaltado: color naranja (brand-500) y con una línea naranja de 2-3px en la parte superior del icono (indicador activo). Los demás iconos están en gris (#7F8C8D). Cada icono tiene un label debajo en fuente muy pequeña (10-11px): "Inicio", "Buscar", "Carrito", "Órdenes", "Perfil".

Al hacer clic en cualquier icono, hay una animación de escala breve (pulse) para dar feedback táctil.

3.5.3 BannerSlider (Carrusel de banners)

Implementado con Swiper, ocupa todo el ancho de la pantalla menos el padding del layout.

  • Cada slide: es una imagen de alta resolución (aspect ratio 16:9 o 2:1) con un gradiente oscuro superpuesto desde la izquierda (para que el texto sea legible). Sobre la imagen:
  • Título: fuente Rubik, 24-32px, blanco, negrita, alineado a la izquierda.
  • Subtítulo: Inter, 14-16px, blanco o ámbar (brand-300).
  • Botón CTA: "Ver oferta" o "Ordenar ahora", fondo naranja (brand-500), texto blanco, redondeado, posicionado abajo a la izquierda.
  • Dots de navegación: alineados horizontalmente en el centro, debajo del banner. 8px de diámetro. Inactivo: gris claro (#E5E7EB). Activo: naranja (brand-500).
  • Flechas de navegación: solo en desktop, a los lados. Círculos de 40px con fondo blanco semi-transparente y icono de flecha. Aparecen al pasar el mouse sobre el banner.
  • Autoplay: se desliza automáticamente cada 5 segundos. Al hacer hover o touch, se pausa.
  • Transición: deslizamiento suave (slide) o fade con duración 500ms.

3.5.4 ProductCard (Tarjeta de producto)

Es el componente más repetido de la web. Aparece en grids de 2 (móvil) a 4-5 (desktop) columnas.

  • Contenedor: rectángulo redondeado (radius 12-16px), fondo blanco, sombra sutil (shadow-sm). En dark mode, fondo navy-800 con borde sutil.
  • Imagen: ocupa la mitad superior del card, ancho completo, altura fija (~150-200px), object-fit cover. Redondeada solo en las esquinas superiores. Si no hay imagen, muestra un placeholder gris con icono de plato.
  • Badge de promoción: si aplica, una etiqueta posicionada en la esquina superior izquierda de la imagen. Fondo naranja (brand-500), texto blanco, redondeado, pequeño. Ej: "-15%", "Nuevo", "Popular".
  • Contenido inferior (padding 12-16px):
  • Nombre del producto: Rubik, 14-16px, negrita, color navy-800, una línea (truncado con ellipsis si es muy largo).
  • Descripción corta: Inter, 12-13px, gris (#7F8C8D), 2 líneas máximo, truncado.
  • Precio: Inter, 14px, negrita. Si hay descuento, precio original tachado en gris claro y precio final en verde (#16A34A).
  • Fila inferior: rating con estrellas doradas ( ) y número de reviews (ej: "4.5 (128)"), alineado a la izquierda. A la derecha, un botón circular naranja (brand-500) de 32-36px con un icono de "+" blanco. Al hacer clic, añade 1 unidad al carrito inmediatamente.
  • Hover / Active: al pasar el mouse en desktop, el card se eleva (sombra más grande, shadow-md), la imagen hace un zoom sutil (scale 1.05). En móvil, al tocar, hay un feedback de opacidad breve.
  • Wishlist: si el producto está en favoritos, el corazón (HiHeart) aparece en la esquina superior derecha, relleno de rojo. Si no, aparece al hacer "long press" o en un icono secundario.

3.5.5 CartDrawer (Drawer del carrito)

Se abre desde la derecha de la pantalla cuando el usuario hace clic en el icono de carrito o añade un producto.

  • Backdrop: el fondo de la página se oscurece a un negro semi-transparente (rgba(0,0,0,0.5)). Al hacer clic en el backdrop, el drawer se cierra.
  • Panel: ancho 100% en móvil (deslizable hacia abajo desde arriba o hacia la izquierda desde la derecha), 400-450px en desktop/tablet. Fondo blanco, con sombra grande en el borde izquierdo. Se desliza con animación de 300ms (ease-out).
  • Header: "Tu carrito (3 items)" con botón de cerrar (X) a la derecha.
  • Lista de items: scrollable. Cada item es compacto: imagen 60x60px a la izquierda, nombre y variaciones en el medio, controles +/- de cantidad a la derecha. Los controles son más pequeños que en la página de carrito completa.
  • Footer del drawer: siempre visible en la parte inferior del panel:
  • Subtotal en texto gris.
  • Total en fuente grande, negrita.
  • Botón naranja ancho completo: "Checkout — €28.50".
  • Vacio: si el carrito está vacío, muestra una ilustración de una bolsa de compras vacía, texto "Tu carrito está vacío" y un botón "Explorar el menú".

3.5.6 CheckoutSection (Sección de checkout)

Este es el componente compuesto que aparece en la página /checkout.

  • Selector de tipo de orden: dos tarjetas grandes una al lado de la otra en desktop, apiladas en móvil. Cada tarjeta tiene:
  • Icono grande centrado (moto o tienda), 40-48px, color gris o naranja según selección.
  • Título: "A domicilio" o "Para recoger", 16px, negrita.
  • Descripción: texto pequeño gris.
  • Radio button a la derecha o dentro del card.
  • Seleccionada: borde 2px naranja, fondo brand-50, icono naranja.
  • No seleccionada: borde 1px gris claro, fondo blanco, icono gris.

  • Dirección de entrega: cuando Delivery está seleccionado, aparece una sección desplegable o visible:

  • Tarjetas de direcciones guardadas: cada una tiene un radio button a la izquierda, texto de dirección, y badge "Predeterminada" si aplica. La seleccionada tiene borde naranja.
  • Botón "+ Añadir nueva dirección" con icono de +, naranja, outline.
  • Formulario de nueva dirección: inputs con bordes redondeados, validación en tiempo real (bordes rojos si inválido, verdes si válido). El campo de dirección principal tiene el dropdown de autocomplete con sugerencias de Google Places.
  • Mapa embebido: 200-250px de alto, redondeado, con un pin centrado.

  • Método de pago: lista de opciones tipo acordeón o lista de radio buttons:

  • Cada opción tiene un radio button a la izquierda, logo del método (Visa, Mastercard, efectivo, etc.), nombre del método, y posiblemente un badge ("Recomendado" en naranja).
  • Al seleccionar "Tarjeta", se expande o aparece debajo el iframe de MONEI: un contenedor seguro con borde gris y fondo blanco donde el usuario ingresa los datos de tarjeta. El iframe tiene su propio estilo pero está integrado visualmente.
  • Al seleccionar "Efectivo", aparece un mensaje informativo: "Paga al recibir tu pedido. Asegúrate de tener cambio exacto."
  • Al seleccionar "Wallet", muestra el saldo disponible y un checkbox "Usar mi saldo (€12.50)".

  • Resumen de orden: panel sticky en desktop o fixed bottom en móvil:

  • Lista compacta de items (imagen miniatura 40x40, nombre, cantidad, precio).
  • Separador horizontal.
  • Subtotal, envío (con icono de moto), descuento (con icono de ticket), total.
  • Total: fuente 20-24px, negrita, navy-800.
  • Botón "Pagar y confirmar pedido": ancho completo, altura 48-56px, fondo naranja, texto blanco 16px negrita. Hover: oscurece ligeramente (brand-600).

3.5.7 OrderTracking (Componente de tracking)

Aparece en /orders/tracking/[id].

  • Mapa: ocupa la mayor parte de la pantalla. Es un mapa de Google Maps con controles de zoom (esquina inferior derecha). Marcadores personalizados:
  • Destino: pin azul estándar de Google o un pin con el logo del restaurante.
  • Repartidor: un icono de moto o una foto circular del repartidor con borde naranja. Se mueve en tiempo real.
  • Ruta: línea azul o naranja conectando ambos puntos, mostrando la ruta de conducción.

  • Panel de info (overlay sobre el mapa):

  • Estado actual: un icono grande animado (pulse) con el estado actual. Texto grande debajo.
  • ETA: "Llegada estimada: 18:45" en color naranja (brand-500).
  • Info del repartidor: tarjeta horizontal con avatar circular 48px, nombre, rating con estrellas doradas, número de teléfono (botón para llamar directo), botón "Chat" (icono de burbuja).
  • Timeline vertical: 5 pasos con círculos conectados por una línea vertical. Completados: círculo verde con check. Actual: círculo naranja con spinner o icono animado. Futuros: círculo gris vacío. Cada paso tiene un label y una hora debajo (cuando aplica).

  • Botón flotante: "Ver detalle completo" (naranja, circular o pill-shaped) que lleva a /orders/[id].


3.6 Módulos específicos con descripción visual

3.6.1 Carrito (módulo completo)

El carrito es uno de los módulos más complejos de la web. Funciona a través de dos vistas: el CartDrawer (vista rápida) y la página completa (/cart).

Visual del item en carrito:

Cada item es una fila horizontal con separador gris claro debajo:
- Imagen: 80x80px en la página completa, 60x60px en el drawer. Redondeada (radius 8px). object-fit cover.
- Info central:
- Nombre: Rubik, 14-16px, negrita, navy-800. Una línea, ellipsis si es largo.
- Variaciones y addons: Inter, 12px, gris (#7F8C8D). Ej: "Mediano, Queso extra, Salsa BBQ". Si hay muchos addons, trunca con "...".
- Precio unitario: Inter, 12px, gris. Ej: "€10.00 c/u".
- Controles de cantidad (alineados a la derecha):
- Botón "−": cuadrado o redondeado, 32x32px, fondo gris muy claro, texto gris oscuro. Deshabilitado si cantidad = 1 (o cambia a icono de basura ).
- Número: Inter, 14px, negrita, centrado, ancho 32px. No editable directamente.
- Botón "+": cuadrado o redondeado, 32x32px, fondo naranja (brand-500), texto blanco.
- Precio total del item: Inter, 14px, negrita, navy-800. Alineado a la derecha de los controles. Ej: "€20.00".
- Botón eliminar: icono de X (HiXMark) o basura (HiTrash), 20px, gris. Aparece al pasar el mouse (desktop) o siempre visible (móvil). Al hacer clic, el item se desvanece con una animación de Framer Motion (opacity → 0, translateX → 100px) y desaparece.

Resumen del carrito:

  • Subtotal: suma de (cantidad × precio) de todos los items. Inter, 14px, gris.
  • Descuento por cupón: si aplica, aparece una línea nueva con icono de ticket (HiTicket). Texto verde (#16A34A). Ej: "Cupón TAPAS20: -€3.20".
  • Envío: calculado según la distancia desde la sucursal a la dirección. Si es gratis, texto verde "Gratis". Si no, texto gris. Ej: "Envío: €2.50".
  • Total: Inter, 18-20px, negrita, navy-800. La suma final.
  • Input de cupón: input de texto con borde gris, placeholder "Código de cupón (ej: TAPAS20)", botón "Aplicar" naranja al lado. Si el cupón es válido, aparece un icono de check verde y el descuento se añade. Si es inválido, el borde del input se vuelve rojo y aparece un mensaje de error en rojo debajo: "Cupón no válido o expirado".
  • Botón "Proceder al checkout": ancho completo, fondo naranja, texto blanco, 16px negrita. Altura 48-52px. Si el carrito está vacío, el botón está deshabilitado (fondo gris claro, texto gris, cursor not-allowed) y dice "Carrito vacío".

Comportamiento del módulo:

  • Cada cambio de cantidad (±) actualiza el total en tiempo real (sin recargar la página), calculado en el frontend pero validado contra el backend.
  • Si el cliente no está logueado, todos los cambios se sincronizan con el backend usando el Guest ID.
  • Si el cliente cierra la web y vuelve días después, el carrito se restaura desde el backend (si está logueado) o desde el estado persistido (si es guest, aunque el backend también lo persiste por Guest ID).
  • Si un producto del carrito deja de estar disponible (se agota), aparece un badge rojo "Agotado" sobre el item y el botón de checkout se deshabilita hasta que el cliente lo elimine.

3.6.2 Checkout (módulo completo)

El checkout está diseñado para minimizar distracciones y maximizar la tasa de conversión.

Selector de tipo de orden:

Dos grandes tarjetas clickeables, una al lado de la otra en desktop (50/50), apiladas en móvil.
- Delivery: icono de moto de entrega (40px), título "A domicilio", descripción "Recibe tu pedido en la puerta de tu casa". Tarjeta con borde redondeado, padding amplio.
- Takeaway: icono de tienda/bolsa (40px), título "Para recoger", descripción "Pasa a recoger cuando esté listo".
- Selección visual: la tarjeta seleccionada gana un borde 2px naranja (brand-500), fondo brand-50, y el icono se vuelve naranja. La no seleccionada tiene borde 1px gris claro y icono gris. Hay una transición suave de 200ms.
- Si se cambia de Delivery a Takeaway, la sección de dirección se oculta con animación de colapso (altura → 0, opacity → 0) y aparece un selector de sucursal para recoger.

Dirección de entrega:

  • Direcciones guardadas: si el usuario tiene direcciones, aparecen como tarjetas horizontales con radio button a la izquierda. Cada tarjeta tiene:
  • Icono de pin naranja (HiMapPin) 20px.
  • Nombre de la dirección en negrita (ej: "Casa", "Trabajo").
  • Dirección completa en gris, 12px.
  • Badge "Predeterminada" verde si aplica.
  • La tarjeta seleccionada tiene borde naranja. Las demás, borde gris.
  • Formulario de nueva dirección: se despliega al hacer clic en "+ Añadir nueva dirección".
  • Campo de dirección: es el más importante. Tiene un input de texto con icono de lupa. Al escribir 3 o más caracteres, aparece un dropdown de sugerencias debajo con direcciones de Google Places. Cada sugerencia muestra el texto principal y el subtexto de ciudad/país. Al seleccionar, los campos de Ciudad, Código postal y País se autocompletan instantáneamente.
  • Campos adicionales: Dirección línea 2 (número, piso, puerta), Ciudad, Código postal, País. Todos con bordes redondeados, validación en tiempo real (borde rojo si vacío y requerido).
  • Mapa embebido: un iframe o componente de mapa de 200px de alto, redondeado, mostrando el pin de la dirección seleccionada. El usuario puede hacer clic en el mapa para mover el pin y ajustar la ubicación exacta.
  • Notas: textarea opcional con placeholder "Notas para el repartidor (ej: timbre 3B, dejar en portería)".
  • Checkbox: "Guardar esta dirección para futuros pedidos".

Método de pago:

Cada método es una fila horizontal con radio button, logo, nombre, y descripción:
- Tarjeta: logos de Visa y Mastercard (iconos SVG). Al seleccionar, aparece debajo el iframe de MONEI: un contenedor con borde gris y fondo blanco. Dentro, campos seguros para número de tarjeta (con icono de tarjeta), fecha MM/AA, CVC (con icono de escudo), y nombre del titular. El iframe tiene un botón propio de "Pagar" que está integrado visualmente con el formulario general.
- Efectivo: icono de billetes ( ). Al seleccionar, aparece un mensaje informativo amarillo claro: "Paga al recibir tu pedido. Por favor, asegúrate de tener el importe exacto o cambio para agilizar la entrega."
- Wallet: icono de cartera ( ). Muestra el saldo actual (ej: "Saldo disponible: €12.50"). Si el saldo es suficiente para cubrir el total, aparece un checkbox "Usar mi saldo". Si no, el método está deshabilitado con texto gris "Saldo insuficiente".
- Transferencia: si aplica, muestra datos bancarios del restaurante (IBAN, titular) en un bloque con fondo gris muy claro y texto monoespaciado.

El radio button seleccionado tiene un círculo exterior naranja y un punto interior naranja. Los demás tienen círculo gris vacío.

Botón de confirmación:

  • "Pagar y confirmar pedido" es el CTA principal. Ancho completo, altura 48-56px, fondo naranja (brand-500), texto blanco, 16px, negrita. Hover: brand-600. Active: scale 0.98 brevemente.
  • Durante el procesamiento, el texto se reemplaza por un spinner blanco y el botón se desactiva para evitar doble clic.
  • Debajo, texto legal pequeño: "Al confirmar, aceptas nuestros Términos y Condiciones y Política de Cancelación." Los enlaces abren en nueva pestaña.

3.6.3 Wallet (módulo completo)

La billetera es el centro financiero del cliente.

Tarjeta de saldo:

  • Posicionada en la parte superior de la página. Es un rectángulo grande, redondeado (radius 16px), con gradiente de navy-700 (#2C3E50 oscurecido) a navy-800.
  • Icono de billetera (HiWallet) en la esquina superior izquierda, blanco, 24px.
  • Texto "Saldo actual": Inter, 12px, blanco semi-transparente, en la parte superior.
  • Monto: fuente grande (32-40px), blanco, negrita. Ej: "€ 47.50". Si el saldo es cero, muestra "€ 0.00" en gris claro.
  • Puntos de fidelidad: debajo del saldo, texto ámbar (brand-300). Icono de estrella (HiStar) + número. Ej: " 1.250 puntos".
  • Bonus: si hay bonos activos, un badge naranja (brand-500) en la esquina superior derecha con texto "Bonus activo".
  • Botón "Recargar saldo" (solo si el restaurante permite recargas): pequeño, naranja, redondeado, posicionado en la esquina inferior derecha de la tarjeta.

Historial de transacciones:

  • Tabs de filtro: "Todas" | "Recargas" | "Pagos" | "Recompensas" | "Reembolsos". Cada tab tiene un indicador naranja debajo cuando está activo.
  • Lista de transacciones: cada item es una fila horizontal con:
  • Icono circular: color según tipo. Verde (#16A34A) para entradas, rojo para salidas, naranja para bonos, azul para reembolsos.
  • Descripción: "Pedido #12345", "Recarga con tarjeta", "Bonus de fidelidad", "Reembolso por cancelación".
  • Fecha: "15 Jun 2026, 14:30" en gris pequeño.
  • Monto: alineado a la derecha. Verde y con + para entradas. Rojo y con − para salidas. Negrita.
  • Estado: badge pequeño. "Completada" verde, "Pendiente" amarillo, "Fallida" rojo.
  • En móvil, cada transacción es una tarjeta vertical con los datos apilados.
  • Paginación: botones "Anterior" y "Siguiente" en la parte inferior, con números de página. Deshabilitados si no hay más páginas.

Puntos de fidelidad:

  • Sección separada debajo del historial o en una pestaña.
  • Barra de progreso: una barra horizontal gris clara de fondo. La parte llena es naranja (brand-500) con ancho proporcional a los puntos actuales vs. el objetivo. Ej: 1.250 / 2.000 = 62.5% llena.
  • Texto de progreso: "1.250 / 2.000 puntos para tu siguiente recompensa".
  • Catálogo de recompensas: grid de 2-3 columnas. Cada recompensa es una tarjeta con:
  • Icono o imagen representativa.
  • Nombre: "Cupón €5", "Envío gratis", "Producto gratis".
  • Costo en puntos: "500 pts".
  • Botón "Canjear": naranja si hay puntos suficientes, gris deshabilitado si no.

3.6.4 Chat (módulo completo)

El chat permite comunicación directa entre el cliente y el soporte o el repartidor de su pedido actual.

Lista de conversaciones (vista inicial en móvil / panel izquierdo en desktop):

  • Cada conversación es una fila horizontal con:
  • Avatar circular: 48px. Foto del repartidor o icono de auriculares para soporte.
  • Nombre: "Soporte ZEUMAX" o "Carlos M. (Repartidor)".
  • Último mensaje: preview de 1 línea, truncado. "Tu pedido está en camino ".
  • Timestamp: "14:30" o "Ayer".
  • Badge no leídos: círculo naranja con número blanco, 20px, posicionado encima del avatar o al final de la fila.
  • La conversación activa tiene fondo brand-50 y borde izquierdo naranja de 3px.

Vista de conversación:

  • Header: fijo en la parte superior. Avatar 40px, nombre, estado ("En línea" con punto verde, "Entregando tu pedido #12345"). Botón de llamada (teléfono) y más opciones (⋮).
  • Área de mensajes: ocupa todo el espacio restante. Fondo blanco muy claro o navy-50 en dark mode. Scrollable.
  • Mensajes propios (cliente): alineados a la derecha. Burbuja con fondo naranja (brand-500), texto blanco, bordes redondeados (radius 16px, esquina inferior derecha recta). Max-width 70% del ancho. Timestamp pequeño debajo en gris, alineado a la derecha. Doble check ( ) si leído.
  • Mensajes del otro (soporte/repartidor): alineados a la izquierda. Burbuja con fondo gris muy claro (#F3F4F6), texto navy-800, bordes redondeados (radius 16px, esquina inferior izquierda recta). Max-width 70%. Timestamp debajo en gris.
  • Mensajes de sistema: centrados, fondo transparente, texto gris pequeño, sin burbuja. Ej: "Carlos ha aceptado tu pedido".
  • Fecha separadora: cada día nuevo tiene un label centrado con texto "Hoy", "Ayer", "15 Jun 2026" en una cápsula gris clara.
  • Input inferior: fijo en la parte inferior. Fondo blanco, borde superior gris claro.
  • Input de texto: redondeado, fondo gris muy claro, placeholder "Escribe un mensaje...". Altura auto-creciente (hasta 4 líneas) si el mensaje es largo.
  • Botón de clip/attachment: icono de clip (HiPaperClip), gris, para adjuntar imágenes (si está habilitado).
  • Botón de enviar: icono de avión de papel (HiPaperAirplane), naranja (brand-500), 24px. Se activa cuando hay texto. Al hacer clic, el mensaje se envía y aparece inmediatamente en el área de mensajes con un spinner pequeño hasta confirmar recepción del backend.
  • Scroll automático: al entrar a la conversación y al recibir/enviar mensajes, el scroll se posiciona al final automáticamente.

3.6.5 Perfil (módulo completo)

El perfil es la puerta de entrada a todas las funciones personales del cliente.

Header del perfil:

  • Banner: un rectángulo de 120-150px de alto con gradiente horizontal de navy-700 a navy-800. Puede tener una textura sutil o un patrón geométrico muy discreto.
  • Avatar: centrado horizontalmente, posicionado para que la mitad inferior esté sobre el banner y la mitad superior sobre el contenido blanco de abajo. Tamaño 120x120px, circular, borde blanco sólido de 4px. Si no hay foto, muestra las iniciales del nombre sobre fondo naranja (brand-500) o un icono de usuario genérico.
  • Botón de cámara: superpuesto en la esquina inferior derecha del avatar. Un círculo pequeño (32px) naranja con icono de cámara blanco. Al hacer clic, abre un selector de archivo para cambiar la foto.
  • Nombre: centrado, debajo del avatar. Rubik, 20-24px, negrita, navy-800.
  • Contacto: email y teléfono centrados, Inter, 13-14px, gris (#7F8C8D).
  • Botón "Editar perfil": pill-shaped, borde naranja, texto naranja, pequeño. Posicionado cerca del avatar o debajo del nombre.

Resumen de actividad:

  • Una fila horizontal de 3-4 tarjetas pequeñas, igual ancho cada una:
  • Pedidos: icono de bolsa (HiShoppingBag), número grande, texto "Pedidos".
  • Gasto: icono de moneda (HiCurrencyEuro), número grande con €, texto "Total gastado".
  • Puntos: icono de estrella (HiStar), número grande, texto "Puntos".
  • Cupones: icono de ticket (HiTicket), número grande, texto "Cupones".
  • Cada tarjeta tiene fondo blanco, borde redondeado, sombra sutil. En dark mode, fondo navy-800.

Menú grid de opciones:

  • Grid de 3 columnas en desktop, 2 columnas en móvil. Gap de 12-16px.
  • Cada celda es una tarjeta clickeable:
  • Icono: centrado, 28-32px. Color gris por defecto.
  • Título: centrado, 13-14px, Inter, navy-800.
  • Badge: si hay notificaciones pendientes, un círculo naranja con número blanco en la esquina superior derecha del card.
  • Opciones del menú (de izquierda a derecha, arriba a abajo):
  • Mis órdenes/orders
  • Direcciones/address
  • Billetera/wallet
  • Cupones/coupons
  • Favoritos/wishlist
  • Notificaciones/notifications
  • Chat/chat
  • Referidos/refer
  • Lealtad/loyalty
  • Ajustes/profile/edit (o página de ajustes)
  • Borrar cuenta → acción destructiva. Separada visualmente por un espacio mayor. Icono rojo, texto rojo.
  • Hover/Active: al pasar el mouse, la tarjeta se eleva (shadow-md), el icono se vuelve naranja (brand-500), y el fondo cambia ligeramente a brand-50.

3.6.6 Búsqueda (módulo completo)

La búsqueda es un módulo de descubrimiento que aparece en la página dedicada /search y como dropdown en la NavBar.

Barra de búsqueda:

  • Input: ancho completo, altura 48-56px, redondeado (radius 12px), borde gris claro. Icono de lupa (HiMagnifyingGlass) a la izquierda dentro del input. Placeholder "Buscar hamburguesas, pizzas, postres...". Al enfocar, el borde cambia a naranja y el icono se vuelve naranja.
  • Botón de limpiar: icono X (HiXMark) a la derecha del input, aparece solo cuando hay texto. Gris, 20px.
  • Botón de filtrar: icono de ajustes (HiAdjustmentsHorizontal) a la derecha del input. Abre un panel lateral o modal de filtros.

Dropdown de sugerencias (cuando el input está enfocado):

  • Historial reciente: sección con título "Búsquedas recientes" en gris pequeño. Cada item tiene:
  • Icono de reloj (HiClock) gris, 16px.
  • Texto de la búsqueda anterior.
  • Icono X a la derecha para eliminar del historial.
  • Sugerencias populares: sección con título "Popular ahora" en gris. Tags clickeables redondeados con borde gris: "Pizza", "Hamburguesa", "Sushi", "Postres", "Bebidas".

Dropdown de resultados en vivo (cuando se escriben 3+ caracteres):

  • Lista vertical de 5-8 resultados máximo. Cada resultado:
  • Imagen miniatura 40x40px, redondeada.
  • Nombre del producto en negrita.
  • Precio a la derecha.
  • Categoría o descripción breve en gris.
  • Al final: "Ver todos los resultados para 'pizza'" en naranja, que lleva a la página de resultados completa.

Página de resultados:

  • Grid de ProductCards: mismo componente que en la home. 2 columnas en móvil, 3-4 en desktop.
  • Barra de filtros: en desktop, un panel lateral izquierdo con filtros. En móvil, un botón flotante "Filtros" que abre un drawer inferior con:
  • Categorías: checkboxes con nombres de categorías.
  • Rango de precio: slider dual (min-max) o inputs numéricos.
  • Ordenar por: radio buttons — "Relevancia", "Precio: menor a mayor", "Precio: mayor a menor", "Más populares", "Nuevos primero".
  • Botón "Aplicar filtros" naranja, ancho completo.
  • Contador de resultados: "24 resultados para 'pizza'" en texto gris, arriba del grid.
  • Sin resultados: ilustración de lupa grande, gris claro. Texto "No encontramos resultados para 'xyz'". Sugerencias: "Prueba con: 'pizza', 'hamburguesa', 'postre'" (tags clickeables).

3.7 Estado global y persistencia

La Web Cliente utiliza Redux Toolkit con Redux Persist para gestionar el estado global y mantenerlo entre sesiones del navegador. Esto significa que muchas de las preferencias y datos del cliente se recuerdan automáticamente, incluso si cierra la pestaña o el navegador.

3.7.1 ¿Qué se guarda localmente?

Slice Qué se guarda ¿Dónde? Persistencia
cart Items del carrito, cupón aplicado, descuento, ownerId localStorage + backend Sí (si guest, por Guest ID; si auth, por user ID)
wishlist IDs de productos favoritos localStorage + backend Sí (sincronizado si está logueado)
profile Datos del usuario, token JWT, estado de login localStorage Sí (token en localStorage, datos en memoria)
theme Modo dark o light localStorage Sí (se recuerda la preferencia)
config Configuración del restaurante (nombre, logo, horarios, colores) localStorage + backend Sí (refrescada periódicamente desde /api/v1/config)
branch Sucursal actual seleccionada, lista de sucursales localStorage Sí (se recuerda la última sucursal usada)
language Idioma activo (es, en, etc.) localStorage Sí (se recuerda la selección)
guest Guest ID para usuarios no logueados localStorage Sí (crítico para persistir el carrito sin login)
search Historial de búsquedas, filtros activos localStorage Sí (historial de búsquedas recientes)
notification IDs de notificaciones leídas localStorage Sí (para saber cuáles son nuevas)
checkout Tipo de orden preferido, método de pago, dirección, notas localStorage Sí (se recuerdan las preferencias de checkout)

3.7.2 ¿Cómo funciona la persistencia?

  1. Redux Persist intercepta cada cambio en los slices marcados y lo guarda en localStorage del navegador.
  2. Al recargar la página, Redux Persist lee localStorage y restaura el estado antes de que la aplicación se renderice.
  3. El carrito es especialmente importante: si el usuario no está logueado, la web genera un Guest ID (un UUID v4) que se almacena en localStorage y se envía en cada petición al backend. El backend usa este Guest ID para asociar el carrito a un "usuario temporal".
  4. Cuando el usuario se registra o inicia sesión, el backend automáticamente fusiona el carrito del Guest ID con el carrito de su cuenta real. El cliente no pierde nada.
  5. El tema (dark/light) se guarda en localStorage como theme: 'dark' | 'light'. Al cargar la web, la clase dark se aplica al <html> antes de que el contenido se pinte, evitando el "flash" de modo claro.

3.7.3 Sincronización con el servidor

No todo queda solo en el navegador. Los slices más importantes se sincronizan con el backend en cada cambio:

  • Carrito: cada vez que se añade, quita o modifica un item, se llama a POST /api/v1/customer/cart/update. Si el usuario está logueado, se usa su token. Si no, se usa el Guest ID.
  • Wishlist: cada toggle de favorito llama a POST /api/v1/customer/wishlist/toggle. La lista se sincroniza al cargar la página.
  • Perfil: los datos se cargan desde GET /api/v1/customer/profile al iniciar sesión. El token JWT se almacena en localStorage y se envía en el header Authorization: Bearer <token> de cada petición.
  • Config: se carga desde GET /api/v1/config al inicio y se refresca periódicamente. Esto permite que el restaurante cambie horarios, promociones o active el modo mantenimiento sin necesidad de redeployar la web.

Nota para el usuario: Si el cliente usa la web en modo incógnito o limpia las cookies/localStorage, perderá el carrito y las preferencias locales. Sin embargo, si estaba logueado, al volver a iniciar sesión el carrito se recupera desde el servidor.


3.8 Notificaciones en tiempo real

La Web Cliente mantiene al cliente informado de sus pedidos mediante dos tecnologías complementarias:

3.8.1 Socket.IO (WebSockets)

La web establece una conexión persistente con el backend usando Socket.IO client. Esta conexión se mantiene abierta mientras la pestaña está activa.

Qué se recibe en tiempo real:
- Cambios de estado del pedido: cuando el restaurante confirma, prepara, envía o entrega un pedido, el backend emite un evento y la web recibe la actualización instantáneamente. Esto actualiza la timeline en /orders/[id], el tracking en /orders/tracking/[id], y el badge de notificaciones.
- Ubicación del repartidor: durante el estado "En camino", el repartidor envía su geolocalización cada 5-10 segundos. El backend la retransmite a la web, y el pin naranja en el mapa se mueve suavemente.
- Mensajes de chat: cuando llega un nuevo mensaje en /chat, aparece instantáneamente sin necesidad de recargar.
- Notificaciones generales: promociones flash, alertas del sistema, etc.

Visualmente: cuando llega una notificación en tiempo real, aparece un toast en la esquina superior derecha de la pantalla:
- Fondo blanco o navy-800 según tema.
- Icono circular según tipo: naranja para pedidos, verde para éxito, rojo para alertas.
- Título breve: "Tu pedido está en camino ".
- Se desvanece automáticamente después de 4-5 segundos (React Hot Toast).

3.8.2 Firebase Cloud Messaging (FCM) — Notificaciones push

Cuando la web está en segundo plano (pestaña minimizada, otra pestaña activa, o en móvil con la web añadida a la pantalla de inicio como PWA), las notificaciones importantes llegan como push notifications del sistema operativo.

Qué se recibe por push:
- Pedido confirmado
- Pedido en camino
- Pedido entregado
- Mensajes nuevos de chat
- Promociones especiales (configurables desde el backend)

Visualmente: en el sistema operativo del cliente (Windows, macOS, Android, iOS) aparece una notificación nativa con:
- Logo del restaurante como icono.
- Título de la notificación.
- Cuerpo del mensaje.
- Al hacer clic en la notificación, se abre la web directamente en la página relevante (ej: /orders/tracking/12345).

Importante: Para que las notificaciones push funcionen, el cliente debe haber aceptado el permiso cuando la web se lo solicitó (aparece un diálogo del navegador: "a.masquetapas.bar quiere mostrar notificaciones"). Si el cliente bloquea el permiso, solo recibirá notificaciones dentro de la web cuando esté activa (Socket.IO).


3.9 SEO y Server-Side Rendering (SSR)

Aunque la Web Cliente es principalmente una SPA, Next.js proporciona capacidades de Server-Side Rendering (SSR) que mejoran el SEO y la experiencia de compartir en redes sociales.

3.9.1 Meta tags dinámicos en la Home

La página / usa getServerSideProps para obtener la configuración del restaurante desde el backend antes de enviar HTML al navegador. Esto permite inyectar meta tags en el <head>:

<title>Restaurante Más que Tapas - Pedir comida online</title>
<meta name="description" content="Descubre el mejor menú de tapas y comida española..." />
<meta property="og:title" content="Más que Tapas" />
<meta property="og:description" content="Pide online y recibe en casa..." />
<meta property="og:image" content="https://a.masquetapas.bar/logo-og.png" />
<meta property="og:url" content="https://a.masquetapas.bar" />
<meta name="twitter:card" content="summary_large_image" />

Esto significa que:
- Al compartir el enlace en WhatsApp, Facebook o Twitter, aparece una tarjeta previa con imagen, título y descripción del restaurante.
- Google indexa correctamente el título y la descripción del restaurante.

3.9.2 Modo mantenimiento

El backend puede activar un modo mantenimiento desde el panel de administración. Cuando getServerSideProps detecta maintenance_mode: true, renderiza una página especial:

  • Página de mantenimiento: fondo navy-800, icono grande de engranaje ( ) en gris, título "Estamos en mantenimiento" en blanco, descripción configurada desde el backend (ej: "Volvemos a las 15:00 con mejoras en el sistema"), y un spinner o contador regresivo si aplica.
  • No hay NavBar, ni carrito, ni footer. Es una página aislada.
  • Se envía un código HTTP 503 (Service Unavailable) para indicar a los motores de búsqueda que el sitio está temporalmente fuera de servicio.

3.9.3 Otros beneficios de SSR

  • Velocidad percibida: el usuario ve contenido real (logo, título, estructura) antes de que JavaScript cargue completamente.
  • Accesibilidad: los lectores de pantalla y bots ven contenido estructurado desde el primer HTML.
  • Social sharing: cada página principal (producto, categoría) puede tener meta tags específicos si se implementa SSR dinámico para esas rutas.

3.10 Notas importantes para el usuario final

3.10.1 Responsive design (Adaptable a todos los dispositivos)

La Web Cliente está diseñada con Tailwind CSS siguiendo un enfoque mobile-first, lo que significa que primero se diseña para móvil y luego se expande a tablet y desktop.

  • Móvil (< 768px): una sola columna, navegación inferior (MobileBottomNav), drawers laterales/inferiores, botones grandes para dedos, texto legible sin zoom.
  • Tablet (768px - 1024px): dos columnas en grids, sidebar colapsable, NavBar compacta.
  • Desktop (> 1024px): layout de 2-3 columnas, sidebar fija, NavBar expandida con búsqueda centrada, mapas más grandes, hover effects.

En todos los dispositivos, la experiencia es consistente: los mismos colores, los mismos componentes, la misma información. No hay funciones exclusivas de desktop ni de móvil.

3.10.2 Header de sucursal (branch-id)

En cada petición que la web hace al backend, incluye automáticamente un header HTTP:

branch-id: 123

Este header le dice al backend qué sucursal está usando el cliente. Esto es crucial porque:
- El menú puede variar entre sucursales (algunas tienen productos exclusivos).
- Los precios pueden ser diferentes por zona.
- Las promociones pueden ser específicas de una sucursal.
- El horario de apertura y disponibilidad de delivery depende de la sucursal.

El cliente selecciona la sucursal en la Home (dropdown o selector) o automáticamente se detecta la más cercana por geolocalización. La selección se guarda en el estado global (branch slice) y se persiste en localStorage.

3.10.3 Usuarios invitados (Guest)

No es necesario tener una cuenta para empezar a comprar. La web genera automáticamente un Guest ID (un identificador único aleatorio) y lo guarda en localStorage.

Qué puede hacer un guest:
- Navegar el menú completo.
- Añadir productos al carrito.
- Ver el carrito y el resumen.
- Proceder al checkout.
- Guardar direcciones (aunque puede introducir una nueva en el checkout).
- Ver historial de pedidos anteriores.
- Acumular puntos de fidelidad.
- Usar la wallet.

Cuándo se requiere login:
- Al llegar al checkout, si el cliente quiere pagar con tarjeta guardada o usar wallet, se le solicita login.
- Después de completar el pedido como guest, la web muestra un mensaje: "¿Quieres guardar tu pedido? Crea una cuenta en 30 segundos" con un botón naranja que inicia el flujo de registro.
- Al registrarse, el carrito del guest se fusiona automáticamente con el de la nueva cuenta.

3.10.4 Seguridad del pago

Los pagos con tarjeta se procesan a través de MONEI, un proveedor de pagos seguro certificado PCI DSS. Esto significa:
- Los datos de la tarjeta nunca tocan los servidores del restaurante. Se ingresan directamente en el iframe seguro de MONEI.
- La web muestra el iframe de MONEI con los colores del restaurante, pero la comunicación con el banco es directa y encriptada (HTTPS/TLS).
- Si un pago falla, el cliente puede reintentar sin perder su carrito ni su dirección (botón "Reintentar pago" en rojo con el error específico).

3.10.5 Política de cancelación

Los pedidos pueden cancelarse desde /orders/[id] haciendo clic en "Cancelar pedido", pero solo si el estado lo permite:
- Cancelable: "Pedido recibido", "Confirmado" (antes de que empiece la preparación).
- No cancelable: "Preparando", "En camino", "Entregado".
- Al intentar cancelar un pedido no cancelable, el botón está deshabilitado (gris) con un tooltip: "No es posible cancelar un pedido que ya está en preparación".
- Si el pedido ya fue pagado y se cancela a tiempo, el monto se reembolsa automáticamente a la wallet o a la tarjeta original según el método de pago.

3.10.6 Compatibilidad con PWA (Progressive Web App)

La Web Cliente puede instalarse como una aplicación en la pantalla de inicio de móviles y tablets:
- En Android/Chrome: aparece el prompt "Añadir a pantalla de inicio".
- En iOS/Safari: el usuario puede usar "Compartir > Añadir a pantalla de inicio".
- Una vez instalada, se abre en pantalla completa sin la barra de direcciones del navegador, como una app nativa.
- Funciona offline en páginas previamente visitadas (gracias al service worker de Next.js), aunque no puede hacer pedidos sin conexión.


3.11 Glosario de términos técnicos para el usuario

Término Significado para el usuario
SPA Single-Page Application: la web no recarga la página completa al navegar; solo cambia el contenido, como una app móvil.
PWA Progressive Web App: se puede "instalar" en el teléfono como si fuera una app nativa, sin ir a la App Store.
Dark Mode Modo oscuro: fondo oscuro con texto claro, más cómodo para la vista en ambientes con poca luz.
Guest ID Identificador temporal que permite usar el carrito sin crear cuenta. Se pierde si se borran las cookies.
Redux Sistema que guarda el estado de la app (carrito, perfil, tema) para que no se pierda al cambiar de página.
Socket.IO Conexión en tiempo real para recibir actualizaciones instantáneas (estado del pedido, ubicación del repartidor).
FCM Firebase Cloud Messaging: sistema de notificaciones push que llegan incluso cuando la web está cerrada.
SSR Server-Side Rendering: parte de la página se genera en el servidor para que Google y redes sociales la vean mejor.
MONEI Pasarela de pago segura que procesa las tarjetas sin que el restaurante vea los datos.
OTP One-Time Password: código de 6 dígitos enviado por SMS para verificar el teléfono.
TanStack Query Sistema que gestiona las llamadas al servidor, caché de datos, y estados de carga (spinners).
Axios Librería que hace las peticiones HTTP al backend de forma estructurada y segura.

Documento generado para el sistema ZEUMAX (ZEUMAX).
Tecnología: Next.js 16 + React 19 + Tailwind CSS 3.4 + Redux Toolkit + TanStack Query v5 + Axios + Socket.IO client.
URL de ejemplo: https://a.masquetapas.bar
Idioma: Español (X-localization: es).
Última actualización: Documento inicial del manual de usuario.