🍽️ Estudio Técnico: Restaurant Template Storefront
Documento: Análisis y Diseño Técnico
Nombre del Proyecto: Restaurant23
URL Producción: https://restaurant23.cadences.app
Fecha: 26 de Diciembre 2025
Versión: 1.0
Estado: Borrador para revisión
📋 Índice
- Resumen del Proyecto
- Arquitectura General
- Modelo de Datos en Cadences
- Storefront Frontend
- Panel Admin (/admin)
- Sistema de Reservas
- Flujo de Auto-Configuración
- Integraciones
- Estimación y Roadmap
1. Resumen del Proyecto
1.1 Objetivo
Crear un storefront template para restaurantes que:
- Muestre información del restaurante (carta, menús, galería, etc.)
- Permita reservas online con sistema de disponibilidad
- Use Cadences.app como backoffice completo
- Tenga panel
/admincon Google Auth verificado contra Cadences - Sea multiidioma (ES/EN) como travel-template
- Auto-configure las tablas necesarias en Cadences al primer acceso
1.2 Filosofía
┌─────────────────────────────────────────────────────────────────┐
│ CADENCES.APP = BACKOFFICE │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Customer con tier "restaurant-template" habilitado │ │
│ │ ↓ │ │
│ │ Proyectos DATA_TABLE auto-creados: │ │
│ │ • 📋 Salas/Comedores │ │
│ │ • 📋 Horarios │ │
│ │ • 📋 Menús │ │
│ │ • 📋 Carta │ │
│ │ • 📋 Reservas │ │
│ │ • 📋 Clientes │ │
│ └──────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ STOREFRONT (restaurant.cadences.app) │
│ ┌───────────────┐ ┌───────────────┐ ┌───────────────┐ │
│ │ Página Web │ │ /admin │ │ /reservar │ │
│ │ Pública │ │ (Dashboard) │ │ (Booking) │ │
│ └───────────────┘ └───────────────┘ └───────────────┘ │
└─────────────────────────────────────────────────────────────────┘
2. Arquitectura General
2.1 Stack Tecnológico
| Componente | Tecnología | Notas |
|---|---|---|
| Frontend | Astro 4.x + React | SSG/SSR híbrido |
| Estilos | Tailwind CSS | Consistente con travel-template |
| i18n | ES + EN | Mismo patrón que travel-template |
| Traducciones | JSON por idioma | src/i18n/es.json, src/i18n/en.json |
| Backend | Cadences API | Via cadences-client.ts |
| Auth | Google OAuth | Verificación contra Cadences |
| Hosting | Cloudflare Pages | Con Workers para API |
| Base de datos | Cadences (DATA_TABLE projects) | Todo vive en Cadences |
2.2 Diagrama de Arquitectura
┌─────────────────────────────────────────────────────────────────────────┐
│ CLIENTE (Browser) │
└───────────────────────────────┬─────────────────────────────────────────┘
│
┌───────────────┼───────────────┐
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Páginas │ │ /admin │ │ /api/* │
│ Públicas │ │ React SPA │ │ Workers │
│ (Astro SSG) │ │ (Protected) │ │ │
└──────┬───────┘ └──────┬───────┘ └──────┬───────┘
│ │ │
└────────────────┼────────────────┘
│
▼
┌───────────────────────────────┐
│ CADENCES API │
│ ┌─────────────────────────┐ │
│ │ /api/projects │ │
│ │ /api/tasks │ │
│ │ /api/customers │ │
│ │ /api/storefronts/* │ │ ← NUEVO
│ └─────────────────────────┘ │
└───────────────────────────────┘
3. Modelo de Datos en Cadences
3.1 Proyectos DATA_TABLE Necesarios
Cada restaurante (customer con tier "restaurant-template") tendrá estos proyectos:
Nota: El proyecto
restaurant_bloges opcional y solo se crea si el cliente activa la función de blog.
📦 Proyecto: restaurant_config
Configuración general del restaurante
| Columna | Tipo | Descripción |
|---|---|---|
key |
string | Clave de configuración |
value_es |
json | Valor en español |
value_en |
json | Valor en inglés |
updated_at |
datetime | Última actualización |
Datos ejemplo:
[
{ "key": "name", "value_es": "Restaurant23", "value_en": "Restaurant23" },
{ "key": "tagline", "value_es": "Cocina mediterránea con alma", "value_en": "Mediterranean cuisine with soul" },
{ "key": "address", "value_es": { "street": "...", "city": "Barcelona", ... }, "value_en": { ... } },
{ "key": "phone", "value_es": "+34 93 123 45 67" },
{ "key": "email", "value_es": "hola@restaurant23.cadenceslab.com" },
{ "key": "social", "value_es": { "instagram": "@restaurant23", "facebook": "..." } }
]
📦 Proyecto: restaurant_rooms
Salas y comedores del restaurante
| Columna | Tipo | Requerido | Descripción |
|---|---|---|---|
id |
string | ✅ | ID único |
name_es |
string | ✅ | Nombre en español (ej: "Comedor Principal") |
name_en |
string | ✅ | Nombre en inglés (ej: "Main Dining Room") |
slug |
string | ✅ | URL friendly (ej: "comedor-principal") |
description_es |
string | Descripción en español | |
description_en |
string | Descripción en inglés | |
capacity_m2 |
number | Metros cuadrados | |
tables |
number | ✅ | Número de mesas |
chairs |
number | ✅ | Número de sillas totales |
has_bar |
boolean | ¿Tiene barra? | |
bar_meters |
number | Metros de barra | |
is_terrace |
boolean | ¿Es terraza? | |
terrace_access |
boolean | ¿Acceso a terraza? | |
is_private |
boolean | ¿Sala privada/reservable? | |
min_reservation |
number | Mínimo personas para reservar | |
photos |
json | Array de URLs de fotos | |
features |
json | Array de características ["wifi", "ac", "music"] | |
active |
boolean | ✅ | ¿Activo? |
order |
number | Orden de visualización |
📦 Proyecto: restaurant_schedules
Horarios por temporada/servicio
| Columna | Tipo | Requerido | Descripción |
|---|---|---|---|
id |
string | ✅ | ID único |
name_es |
string | ✅ | Nombre ES (ej: "Temporada Alta 2025") |
name_en |
string | ✅ | Nombre EN (ej: "High Season 2025") |
date_from |
date | ✅ | Fecha inicio vigencia |
date_to |
date | ✅ | Fecha fin vigencia |
room_id |
string | Sala específica (null = todas) | |
service_type |
string | ✅ | "lunch" / "dinner" / "brunch" / "drinks" / "events" |
day_of_week |
json | ✅ | [0,1,2,3,4,5,6] (0=domingo) |
time_from |
string | ✅ | Hora inicio (HH:MM) |
time_to |
string | ✅ | Hora fin (HH:MM) |
slot_duration |
number | Duración slot en minutos (default: 30) | |
max_reservations |
number | Máximo reservas simultáneas | |
requires_deposit |
boolean | ¿Requiere depósito? | |
deposit_amount |
number | Cantidad depósito | |
notes |
string | Notas internas | |
active |
boolean | ✅ | ¿Activo? |
Ejemplo de horarios:
[
{
"name": "Comidas Invierno 2025",
"date_from": "2025-01-01",
"date_to": "2025-03-31",
"service_type": "lunch",
"day_of_week": [2,3,4,5,6,0], // Mar-Dom
"time_from": "13:00",
"time_to": "16:00"
},
{
"name": "Cenas Fin de Semana",
"date_from": "2025-01-01",
"date_to": "2025-12-31",
"service_type": "dinner",
"day_of_week": [5,6], // Vie-Sáb
"time_from": "20:00",
"time_to": "23:30"
},
{
"name": "Copas Sábados",
"date_from": "2025-01-01",
"date_to": "2025-12-31",
"service_type": "drinks",
"day_of_week": [6], // Sábado
"time_from": "23:30",
"time_to": "03:00"
}
]
📦 Proyecto: restaurant_menus
Menús (menú del día, menú degustación, etc.)
| Columna | Tipo | Requerido | Descripción |
|---|---|---|---|
id |
string | ✅ | ID único |
name_es |
string | ✅ | Nombre ES (ej: "Menú Degustación Maridaje") |
name_en |
string | ✅ | Nombre EN (ej: "Tasting Menu with Pairing") |
slug |
string | ✅ | URL friendly |
description_es |
string | Descripción en español | |
description_en |
string | Descripción en inglés | |
price |
number | ✅ | Precio |
currency |
string | EUR por defecto | |
includes_drink |
boolean | ¿Incluye bebida? | |
includes_coffee |
boolean | ¿Incluye café? | |
includes_dessert |
boolean | ¿Incluye postre? | |
courses |
json | ✅ | Array de platos/opciones por curso |
date_from |
date | Fecha inicio vigencia | |
date_to |
date | Fecha fin vigencia | |
service_types |
json | ["lunch", "dinner"] | |
day_of_week |
json | Días disponible | |
photo |
string | URL foto | |
allergens_info |
string | Info alérgenos | |
advance_booking |
boolean | ¿Requiere reserva previa? | |
min_people |
number | Mínimo personas | |
max_people |
number | Máximo personas | |
active |
boolean | ✅ | ¿Activo? |
order |
number | Orden visualización |
Ejemplo de courses (estructura JSON multi-idioma):
{
"courses": [
{
"name_es": "Entrantes",
"name_en": "Starters",
"type": "choice",
"items": [
{
"name_es": "Ensalada de la huerta",
"name_en": "Garden Salad",
"description_es": "Verduras frescas de temporada",
"description_en": "Fresh seasonal vegetables",
"allergens": ["gluten"]
},
{
"name_es": "Crema de calabaza",
"name_en": "Pumpkin Cream Soup",
"description_es": "Con un toque de jengibre",
"description_en": "With a touch of ginger",
"allergens": ["lactose"]
}
]
},
{
"name_es": "Principal",
"name_en": "Main Course",
"type": "choice",
"items": [
{
"name_es": "Carrillera al vino tinto",
"name_en": "Beef Cheeks in Red Wine",
"supplement": 0
},
{
"name_es": "Lubina a la plancha",
"name_en": "Grilled Sea Bass",
"supplement": 5
}
]
},
{
"name_es": "Postre",
"name_en": "Dessert",
"type": "choice",
"items": [
{ "name_es": "Crema catalana", "name_en": "Catalan Cream" },
{ "name_es": "Helado artesano", "name_en": "Artisan Ice Cream" }
]
}
]
}
📦 Proyecto: restaurant_carta
Carta del restaurante (platos individuales)
| Columna | Tipo | Requerido | Descripción |
|---|---|---|---|
id |
string | ✅ | ID único |
category_es |
string | ✅ | Categoría ES (Entrantes, Carnes, Pescados...) |
category_en |
string | ✅ | Categoría EN (Starters, Meats, Fish...) |
subcategory_es |
string | Subcategoría ES opcional | |
subcategory_en |
string | Subcategoría EN opcional | |
name_es |
string | ✅ | Nombre del plato en español |
name_en |
string | ✅ | Nombre del plato en inglés |
description_es |
string | Descripción en español | |
description_en |
string | Descripción en inglés | |
price |
number | ✅ | Precio |
price_half |
number | Precio media ración (si aplica) | |
currency |
string | EUR por defecto | |
photo |
string | URL foto | |
allergens |
json | ["gluten", "lactose", "nuts", ...] | |
is_vegetarian |
boolean | ||
is_vegan |
boolean | ||
is_gluten_free |
boolean | ||
is_spicy |
boolean | ||
is_featured |
boolean | ¿Destacado/Recomendado? | |
is_seasonal |
boolean | ¿De temporada? | |
date_from |
date | Disponible desde | |
date_to |
date | Disponible hasta | |
active |
boolean | ✅ | ¿Activo? |
order |
number | Orden en la categoría |
📦 Proyecto: restaurant_customers
Base de clientes
| Columna | Tipo | Requerido | Descripción |
|---|---|---|---|
id |
string | ✅ | ID único |
name |
string | ✅ | Nombre completo |
email |
|||
phone |
string | ✅ | Teléfono (único) |
notes |
string | Notas internas (preferencias, alergias...) | |
vip |
boolean | Cliente VIP | |
blacklisted |
boolean | No aceptar reservas | |
blacklist_reason |
string | Motivo blacklist | |
total_visits |
number | Contador de visitas | |
total_spent |
number | Total gastado (si hay TPV integrado) | |
last_visit |
date | Última visita | |
tags |
json | ["cumpleaños_marzo", "celiaco", ...] | |
created_at |
datetime | ✅ | Fecha registro |
📦 Proyecto: restaurant_reservations
Reservas
| Columna | Tipo | Requerido | Descripción |
|---|---|---|---|
id |
string | ✅ | ID único / código reserva |
customer_id |
string | Referencia a cliente (si existe) | |
customer_name |
string | ✅ | Nombre (copia o nuevo) |
customer_phone |
string | ✅ | Teléfono |
customer_email |
|||
date |
date | ✅ | Fecha de la reserva |
time |
string | ✅ | Hora (HH:MM) |
party_size |
number | ✅ | Número de comensales |
room_id |
string | Sala preferida/asignada | |
table_number |
string | Mesa asignada | |
service_type |
string | ✅ | "lunch" / "dinner" / etc. |
menu_id |
string | Menú seleccionado (si aplica) | |
menu_selections |
json | Selecciones de cada comensal | |
special_requests |
string | Peticiones especiales | |
allergens_notes |
string | Alergias/intolerancias | |
occasion |
string | "birthday", "anniversary", "business"... | |
status |
string | ✅ | "pending", "confirmed", "seated", "completed", "cancelled", "no_show" |
deposit_paid |
boolean | ¿Depósito pagado? | |
deposit_amount |
number | Cantidad pagada | |
source |
string | "web", "phone", "walk-in", "google", "tripadvisor" | |
internal_notes |
string | Notas internas (no visible para cliente) | |
confirmation_sent |
boolean | ¿Email/SMS confirmación enviado? | |
reminder_sent |
boolean | ¿Recordatorio enviado? | |
locale |
string | Idioma preferido del cliente ("es" / "en") | |
created_at |
datetime | ✅ | Fecha creación |
updated_at |
datetime | Última actualización | |
created_by |
string | Usuario que creó (admin/web) |
Estados de reserva:
pending → confirmed → seated → completed
↘ cancelled
↘ no_show
3.2 Proyecto Opcional: Blog
📦 Proyecto: restaurant_blog (Opcional)
Artículos del blog del restaurante
| Columna | Tipo | Requerido | Descripción |
|---|---|---|---|
id |
string | ✅ | ID único |
slug |
string | ✅ | URL friendly (ej: "nuestra-nueva-terraza") |
title_es |
string | ✅ | Título en español |
title_en |
string | ✅ | Título en inglés |
excerpt_es |
string | Resumen/extracto ES (para listados) | |
excerpt_en |
string | Resumen/extracto EN | |
content_es |
string | ✅ | Contenido completo ES (Markdown o HTML) |
content_en |
string | ✅ | Contenido completo EN |
featured_image |
string | URL imagen principal | |
gallery |
json | Array de URLs de imágenes | |
category |
string | Categoría: "noticias", "recetas", "eventos", "equipo" | |
tags |
json | Array de tags ["temporada", "vino", "postres"] | |
author_name |
string | Nombre del autor | |
author_avatar |
string | URL foto del autor | |
published_at |
datetime | Fecha de publicación (null = borrador) | |
is_featured |
boolean | ¿Destacado en home? | |
seo_title_es |
string | Meta title ES (si difiere del título) | |
seo_title_en |
string | Meta title EN | |
seo_description_es |
string | Meta description ES | |
seo_description_en |
string | Meta description EN | |
views |
number | Contador de visitas | |
created_at |
datetime | ✅ | Fecha creación |
updated_at |
datetime | Última actualización |
Categorías predefinidas (traducibles):
{
"categories": {
"news": { "es": "Noticias", "en": "News" },
"recipes": { "es": "Recetas", "en": "Recipes" },
"events": { "es": "Eventos", "en": "Events" },
"team": { "es": "Equipo", "en": "Our Team" },
"wines": { "es": "Vinos", "en": "Wines" },
"seasonal": { "es": "Temporada", "en": "Seasonal" }
}
}
Ejemplo de artículo:
{
"slug": "nueva-carta-primavera-2025",
"title_es": "Descubre nuestra nueva carta de primavera",
"title_en": "Discover our new spring menu",
"excerpt_es": "Con la llegada del buen tiempo, renovamos nuestra carta con productos frescos de temporada...",
"excerpt_en": "With the arrival of good weather, we renew our menu with fresh seasonal products...",
"content_es": "## La primavera ha llegado a nuestra cocina\n\nCon la llegada del buen tiempo...",
"content_en": "## Spring has arrived in our kitchen\n\nWith the arrival of good weather...",
"featured_image": "https://...",
"category": "news",
"tags": ["primavera", "carta", "temporada"],
"author_name": "Chef María García",
"published_at": "2025-03-15T10:00:00Z",
"is_featured": true
}
3.3 Relaciones entre Proyectos
┌──────────────────┐ ┌──────────────────┐
│ restaurant_ │ │ restaurant_ │
│ customers │◄────│ reservations │
└──────────────────┘ └────────┬─────────┘
│
┌─────────────┼─────────────┐
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ restaurant_ │ │ restaurant_ │ │ restaurant_ │
│ rooms │ │ schedules │ │ menus │
└──────────────┘ └──────────────┘ └──────────────┘
▲
│
┌────────┴────────┐
│ restaurant_ │
│ carta │
└─────────────────┘
4. Storefront Frontend
4.1 Estructura de Páginas
src/pages/
├── index.astro # Home (ES - default)
├── carta.astro # Carta completa
├── menus.astro # Menús disponibles
├── menus/[slug].astro # Detalle de un menú
├── galeria.astro # Galería de fotos
├── nosotros.astro # Sobre nosotros
├── contacto.astro # Contacto y ubicación
├── reservar.astro # Sistema de reservas
├── reservar/confirmacion.astro # Confirmación reserva
├── blog/ # Blog (opcional)
│ ├── index.astro # Lista de artículos
│ └── [slug].astro # Artículo individual
├── admin/ # Panel de administración (multi-idioma)
│ ├── index.astro # Dashboard
│ ├── reservas.astro # Gestión reservas
│ ├── salas.astro # Config salas (edita _es y _en)
│ ├── horarios.astro # Config horarios
│ ├── carta.astro # Editar carta (edita _es y _en)
│ └── menus.astro # Editar menús (edita _es y _en)
└── en/ # Versión inglés (i18n routing)
├── index.astro # Home
├── menu.astro # "carta" → "menu"
├── set-menus.astro # "menus" → "set-menus"
├── set-menus/[slug].astro # Detalle menú
├── gallery.astro # "galeria" → "gallery"
├── about.astro # "nosotros" → "about"
├── contact.astro # "contacto" → "contact"
├── book.astro # "reservar" → "book"
├── book/confirmation.astro # Confirmación
├── blog/ # Blog (opcional)
│ ├── index.astro # Article list
│ └── [slug].astro # Single article
└── admin/ # Admin en inglés
└── ... # (mismas páginas, UI en inglés)
4.2 Estructura de Internacionalización (i18n)
src/i18n/
├── index.ts # Helper para obtener traducciones
├── es.json # Traducciones español
├── en.json # Traducciones inglés
└── types.ts # Tipos TypeScript para traducciones
src/i18n/es.json:
{
"nav": {
"home": "Inicio",
"menu": "Carta",
"setMenus": "Menús",
"gallery": "Galería",
"about": "Nosotros",
"contact": "Contacto",
"book": "Reservar",
"blog": "Blog"
},
"home": {
"hero": {
"title": "Bienvenidos a {restaurantName}",
"subtitle": "Cocina mediterránea con alma",
"cta": "Reservar mesa"
},
"featured": {
"title": "Nuestros platos destacados",
"viewMenu": "Ver carta completa"
},
"todaysMenu": {
"title": "Menú del día",
"price": "{price}€ por persona",
"includes": "Incluye bebida, pan y postre"
}
},
"menu": {
"title": "Nuestra Carta",
"categories": {
"starters": "Entrantes",
"meats": "Carnes",
"fish": "Pescados",
"desserts": "Postres",
"drinks": "Bebidas"
},
"halfPortion": "Media ración",
"recommended": "Recomendado",
"seasonal": "De temporada"
},
"booking": {
"title": "Reservar mesa",
"steps": {
"dateTime": "Fecha y hora",
"details": "Detalles",
"contact": "Contacto",
"confirm": "Confirmar"
},
"form": {
"date": "Fecha",
"time": "Hora",
"guests": "Comensales",
"service": "Servicio",
"lunch": "Comida",
"dinner": "Cena",
"name": "Nombre completo",
"phone": "Teléfono",
"email": "Email",
"specialRequests": "Peticiones especiales",
"allergies": "Alergias o intolerancias",
"occasion": "Ocasión especial",
"occasions": {
"none": "Ninguna",
"birthday": "Cumpleaños",
"anniversary": "Aniversario",
"business": "Comida de negocios"
}
},
"confirmation": {
"title": "¡Reserva confirmada!",
"code": "Código de reserva",
"summary": "Resumen",
"emailSent": "Te hemos enviado un email de confirmación.",
"reminder": "Recibirás un recordatorio 24h antes.",
"addToCalendar": "Añadir al calendario",
"modify": "Modificar reserva"
},
"errors": {
"noAvailability": "No hay disponibilidad para esta fecha",
"minGuests": "Mínimo {min} comensales",
"maxGuests": "Máximo {max} comensales",
"required": "Este campo es obligatorio"
}
},
"allergens": {
"gluten": "Gluten",
"lactose": "Lácteos",
"eggs": "Huevo",
"fish": "Pescado",
"shellfish": "Crustáceos",
"nuts": "Frutos secos",
"peanuts": "Cacahuetes",
"soy": "Soja",
"celery": "Apio",
"mustard": "Mostaza",
"sesame": "Sésamo",
"sulfites": "Sulfitos",
"mollusks": "Moluscos",
"lupin": "Altramuz"
},
"footer": {
"hours": "Horario",
"location": "Ubicación",
"contact": "Contacto",
"followUs": "Síguenos",
"rights": "Todos los derechos reservados"
},
"common": {
"close": "Cerrar",
"next": "Siguiente",
"back": "Atrás",
"confirm": "Confirmar",
"cancel": "Cancelar",
"loading": "Cargando...",
"error": "Error",
"success": "Éxito"
}
}
src/i18n/en.json:
{
"nav": {
"home": "Home",
"menu": "Menu",
"setMenus": "Set Menus",
"gallery": "Gallery",
"about": "About",
"contact": "Contact",
"book": "Book a Table",
"blog": "Blog"
},
"home": {
"hero": {
"title": "Welcome to {restaurantName}",
"subtitle": "Mediterranean cuisine with soul",
"cta": "Book a table"
},
"featured": {
"title": "Our signature dishes",
"viewMenu": "View full menu"
},
"todaysMenu": {
"title": "Today's Menu",
"price": "€{price} per person",
"includes": "Includes drink, bread and dessert"
}
},
"menu": {
"title": "Our Menu",
"categories": {
"starters": "Starters",
"meats": "Meat Dishes",
"fish": "Fish & Seafood",
"desserts": "Desserts",
"drinks": "Beverages"
},
"halfPortion": "Half portion",
"recommended": "Chef's choice",
"seasonal": "Seasonal"
},
"booking": {
"title": "Book a Table",
"steps": {
"dateTime": "Date & Time",
"details": "Details",
"contact": "Contact",
"confirm": "Confirm"
},
"form": {
"date": "Date",
"time": "Time",
"guests": "Guests",
"service": "Service",
"lunch": "Lunch",
"dinner": "Dinner",
"name": "Full name",
"phone": "Phone",
"email": "Email",
"specialRequests": "Special requests",
"allergies": "Allergies or dietary requirements",
"occasion": "Special occasion",
"occasions": {
"none": "None",
"birthday": "Birthday",
"anniversary": "Anniversary",
"business": "Business meal"
}
},
"confirmation": {
"title": "Booking Confirmed!",
"code": "Booking reference",
"summary": "Summary",
"emailSent": "We've sent you a confirmation email.",
"reminder": "You'll receive a reminder 24h before.",
"addToCalendar": "Add to calendar",
"modify": "Modify booking"
},
"errors": {
"noAvailability": "No availability for this date",
"minGuests": "Minimum {min} guests",
"maxGuests": "Maximum {max} guests",
"required": "This field is required"
}
},
"allergens": {
"gluten": "Gluten",
"lactose": "Dairy",
"eggs": "Eggs",
"fish": "Fish",
"shellfish": "Shellfish",
"nuts": "Tree nuts",
"peanuts": "Peanuts",
"soy": "Soy",
"celery": "Celery",
"mustard": "Mustard",
"sesame": "Sesame",
"sulfites": "Sulfites",
"mollusks": "Mollusks",
"lupin": "Lupin"
},
"footer": {
"hours": "Opening Hours",
"location": "Location",
"contact": "Contact",
"followUs": "Follow us",
"rights": "All rights reserved"
},
"common": {
"close": "Close",
"next": "Next",
"back": "Back",
"confirm": "Confirm",
"cancel": "Cancel",
"loading": "Loading...",
"error": "Error",
"success": "Success"
}
}
src/i18n/index.ts:
import es from './es.json';
import en from './en.json';
const translations = { es, en };
export type Locale = 'es' | 'en';
export const defaultLocale: Locale = 'es';
export const locales: Locale[] = ['es', 'en'];
// Obtener traducciones
export function t(locale: Locale, key: string, params?: Record<string, string | number>) {
const keys = key.split('.');
let value: any = translations[locale];
for (const k of keys) {
value = value?.[k];
if (!value) return key; // Fallback al key si no existe
}
// Interpolación de parámetros: {name} → valor
if (params && typeof value === 'string') {
return value.replace(/\{(\w+)\}/g, (_, k) => String(params[k] ?? `{${k}}`));
}
return value;
}
// Helper para obtener campo multiidioma desde datos de Cadences
export function getLocalizedField<T>(
data: Record<string, T>,
fieldBase: string,
locale: Locale
): T | undefined {
return data[`${fieldBase}_${locale}`] ?? data[`${fieldBase}_${defaultLocale}`];
}
// Ejemplo de uso:
// getLocalizedField(dish, 'name', 'en') → dish.name_en ?? dish.name_es
// getLocalizedField(room, 'description', locale) → room.description_es o room.description_en
4.3 Componentes Principales
src/components/
├── common/
│ ├── Header.tsx
│ ├── Footer.tsx
│ ├── LanguageSwitcher.tsx
│ └── NavigationMenu.tsx
├── home/
│ ├── Hero.tsx
│ ├── FeaturedDishes.tsx
│ ├── TodaysMenu.tsx
│ ├── Testimonials.tsx
│ └── InstagramFeed.tsx
├── carta/
│ ├── MenuCategory.tsx
│ ├── DishCard.tsx
│ ├── AllergenIcons.tsx
│ └── PriceDisplay.tsx
├── menus/
│ ├── MenuCard.tsx
│ ├── CourseSelector.tsx
│ └── MenuDetail.tsx
├── reservar/
│ ├── ReservationForm.tsx # Formulario principal
│ ├── DatePicker.tsx # Selector de fecha
│ ├── TimeSlotPicker.tsx # Selector de hora
│ ├── PartySizeSelector.tsx # Nº comensales
│ ├── RoomPreference.tsx # Preferencia sala
│ ├── MenuPreOrder.tsx # Pre-pedido menú
│ └── ConfirmationView.tsx # Resumen y confirmar
├── blog/ # Componentes del blog (opcional)
│ ├── BlogPostCard.tsx # Tarjeta de artículo
│ ├── BlogPostList.tsx # Lista/grid de artículos
│ ├── BlogPostContent.tsx # Contenido del artículo
│ ├── BlogSidebar.tsx # Sidebar con categorías/tags
│ ├── BlogSearch.tsx # Búsqueda de artículos
│ ├── RelatedPosts.tsx # Artículos relacionados
│ └── ShareButtons.tsx # Compartir en redes
├── admin/
│ ├── AdminLayout.tsx
│ ├── Sidebar.tsx
│ ├── ReservationsCalendar.tsx
│ ├── ReservationsList.tsx
│ ├── DayAgenda.tsx
│ ├── DataTableEditor.tsx # Editor genérico
│ ├── FormBuilder.tsx # Constructor de forms
│ ├── BlogPostEditor.tsx # Editor de artículos con Markdown/WYSIWYG
│ └── GoogleAuthButton.tsx
└── ui/
├── Button.tsx
├── Input.tsx
├── Select.tsx
├── Modal.tsx
└── Toast.tsx
5. Panel Admin (/admin)
5.1 Autenticación
// Flujo de autenticación
1. Usuario navega a /admin
2. Si no hay sesión → Muestra GoogleAuthButton
3. Google OAuth → Obtiene email
4. Llamada a Cadences API:
GET /api/customers?email={email}
5. Verificar:
- ✅ Email existe como customer
- ✅ customer.tierSpecials.includes("restaurant-template")
- ✅ customer tiene storefrontId que match
6. Si OK → JWT con { customerId, storefrontId, role }
7. Si NO → "No tienes acceso a este restaurante"
5.3 Edición Multi-idioma en Admin
En el panel de administración, los formularios de edición muestran ambos idiomas simultáneamente:
┌─────────────────────────────────────────────────────────────┐
│ 📝 Editar Plato [×] │
├─────────────────────────────────────────────────────────────┤
│ │
│ 🇪🇸 Español 🇬🇧 English │
│ ───────────────────────── ───────────────────────── │
│ Nombre * Name * │
│ [Carrillera al vino tinto] [Beef Cheeks in Red Wine] │
│ │
│ Descripción Description │
│ [Cocinada lentamente [Slow-cooked for 8 hours │
│ durante 8 horas con...] with...] │
│ │
│ ──────────────────────────────────────────────────────────│
│ Campos comunes (sin traducción) │
│ ──────────────────────────────────────────────────────────│
│ │
│ Precio Media ración Categoría │
│ [18.50] € [12.00] € [Carnes ▼] │
│ │
│ Alérgenos │
│ ☑️ Gluten ☐ Lácteos ☑️ Sulfitos ☐ Huevo ... │
│ │
│ Opciones │
│ ☑️ Activo ☐ Destacado ☐ Temporada ☐ Vegetariano │
│ │
│ [Cancelar] [💾 Guardar] │
└─────────────────────────────────────────────────────────────┘
Componente de campo traducible:
// src/components/admin/TranslatableField.tsx
interface TranslatableFieldProps {
fieldName: string;
label: string;
valueEs: string;
valueEn: string;
onChange: (locale: 'es' | 'en', value: string) => void;
type?: 'input' | 'textarea';
required?: boolean;
}
export function TranslatableField({
fieldName, label, valueEs, valueEn, onChange, type = 'input', required
}: TranslatableFieldProps) {
return (
<div className="grid grid-cols-2 gap-4">
<div>
<label className="flex items-center gap-2 text-sm font-medium mb-1">
<span>🇪🇸</span> {label} {required && '*'}
</label>
{type === 'input' ? (
<input
value={valueEs}
onChange={(e) => onChange('es', e.target.value)}
className="w-full px-3 py-2 border rounded-lg"
required={required}
/>
) : (
<textarea
value={valueEs}
onChange={(e) => onChange('es', e.target.value)}
className="w-full px-3 py-2 border rounded-lg"
rows={3}
/>
)}
</div>
<div>
<label className="flex items-center gap-2 text-sm font-medium mb-1">
<span>🇬🇧</span> {label} {required && '*'}
</label>
{type === 'input' ? (
<input
value={valueEn}
onChange={(e) => onChange('en', e.target.value)}
className="w-full px-3 py-2 border rounded-lg"
required={required}
/>
) : (
<textarea
value={valueEn}
onChange={(e) => onChange('en', e.target.value)}
className="w-full px-3 py-2 border rounded-lg"
rows={3}
/>
)}
</div>
</div>
);
}
5.4 Dashboard Principal
┌────────────────────────────────────────────────────────────────┐
│ 🍽️ Restaurant23 - Admin [Salir] │
├──────────────┬─────────────────────────────────────────────────┤
│ │ │
│ 📊 Panel │ ┌─────────────────────────────────────────────┐│
│ 📅 Reservas │ │ RESERVAS DE HOY ││
│ 🏠 Salas │ │ ┌───────────────────────────────────────┐ ││
│ ⏰ Horarios │ │ │ COMEDOR PRINCIPAL TERRAZA │ ││
│ 📋 Carta │ │ │ ┌─────────────────┐ ┌───────────────┐ │ ││
│ 🍽️ Menús │ │ │ │ 13:00 García 4p │ │ 14:00 Ruiz 6p │ │ ││
│ 👥 Clientes │ │ │ │ 13:30 López 2p │ │ │ │ ││
│ � Blog │ │ │ │ 14:00 Martín 8p │ │ 15:00 Vega 4p │ │ ││
│ 📈 Stats │ │ │ │ 14:30 Sánchez 2 │ │ │ │ ││
│ │ │ │ └─────────────────┘ └───────────────┘ │ ││
│ │ │ │ │ ││
│ │ │ │ 📊 Ocupación: 78% 👥 38 pax │ ││
│ │ │ └───────────────────────────────────────┘ ││
│ │ │ ││
│ │ │ ┌─────────────────────────────────────────┐││
│ │ │ │ PRÓXIMAS 48H │││
│ │ │ │ • Mañana 20:00 - Cumpleaños (12p) │││
│ │ │ │ • Mañana 21:00 - Empresa ABC (20p) │││
│ │ │ └─────────────────────────────────────────┘││
│ │ └─────────────────────────────────────────────┘│
└──────────────┴─────────────────────────────────────────────────┘
5.5 Vista de Reservas
Calendario mensual
- Vista mes con indicadores de ocupación por día
- Click en día → Vista de día con agenda por sala
Agenda por día
- Columnas por sala
- Filas por hora
- Drag & drop para mover reservas
- Click para editar/ver detalles
- Código de colores por estado
Lista de reservas
- Filtros: fecha, sala, estado, búsqueda
- Acciones rápidas: confirmar, cancelar, marcar seated
- Export a Excel
5.6 Gestión del Blog (Opcional)
Si el blog está activado, aparece la sección en el sidebar del admin:
┌─────────────────────────────────────────────────────────────┐
│ 📝 Blog [+ Nuevo]│
├─────────────────────────────────────────────────────────────┤
│ 🔍 Buscar... Estado: [Todos ▼] │
│ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ 📄 Nueva carta de primavera 🟢 Publicado │ │
│ │ 📅 15 Mar 2025 · 👁️ 234 visitas · 🏷️ noticias │ │
│ │ [Editar] [Vista previa] [Despublicar] │ │
│ ├────────────────────────────────────────────────────────┤ │
│ │ 📄 Receta: Crema catalana tradicional 🟢 Publicado │ │
│ │ 📅 10 Mar 2025 · 👁️ 567 visitas · 🏷️ recetas │ │
│ │ [Editar] [Vista previa] [Despublicar] │ │
│ ├────────────────────────────────────────────────────────┤ │
│ │ 📄 Evento: Cata de vinos abril 🟡 Borrador │ │
│ │ 📅 -- · 👁️ -- · 🏷️ eventos │ │
│ │ [Editar] [Vista previa] [Publicar] │ │
│ └────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
Editor de artículo:
┌─────────────────────────────────────────────────────────────┐
│ 📝 Editar Artículo [Guardar ▼] │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────┐│
│ │ [🇪🇸 Español] [🇬🇧 English] ││
│ └─────────────────────────────────────────────────────────┘│
│ │
│ Título * │
│ [Descubre nuestra nueva carta de primavera_____________] │
│ │
│ Extracto (para listados y SEO) │
│ [Con la llegada del buen tiempo, renovamos nuestra...] │
│ │
│ Contenido │
│ ┌─────────────────────────────────────────────────────────┐│
│ │ [B] [I] [H2] [H3] [Link] [Img] [Lista] [Cita] ││
│ ├─────────────────────────────────────────────────────────┤│
│ │ ## La primavera ha llegado a nuestra cocina ││
│ │ ││
│ │ Con la llegada del buen tiempo, nuestro equipo de ││
│ │ cocina ha renovado completamente la carta... ││
│ │ ││
│ │ ### Nuevos platos destacados ││
│ │ ││
│ │ - **Ensalada de espárragos** con vinagreta cítrica ││
│ │ - **Cordero lechal** con hierbas provenzales ││
│ └─────────────────────────────────────────────────────────┘│
│ │
│ ─────────────────── Configuración ─────────────────────── │
│ │
│ Imagen destacada │
│ [📷 Subir imagen] o [🔗 URL] [Vista previa] │
│ │
│ Categoría Fecha publicación │
│ [Noticias ▼] [15/03/2025] [10:00] ☐ Programar │
│ │
│ Tags │
│ [primavera] [carta] [temporada] [+ añadir] │
│ │
│ ☐ Destacar en página principal │
│ │
│ ─────────────────────── SEO ───────────────────────────── │
│ │
│ Meta título (opcional, si difiere del título) │
│ [________________________________________________] │
│ │
│ Meta descripción │
│ [________________________________________________] │
│ │
│ [Eliminar] [Vista previa] [Publicar] │
└─────────────────────────────────────────────────────────────┘
Componente del editor (React):
// src/components/admin/BlogPostEditor.tsx
interface BlogPostEditorProps {
post?: BlogPost;
onSave: (post: BlogPost) => Promise<void>;
onDelete?: () => Promise<void>;
}
export function BlogPostEditor({ post, onSave, onDelete }: BlogPostEditorProps) {
const [activeLocale, setActiveLocale] = useState<'es' | 'en'>('es');
const [formData, setFormData] = useState<BlogPost>(post || defaultPost);
const [isPreview, setIsPreview] = useState(false);
return (
<div className="max-w-4xl mx-auto">
{/* Tabs de idioma */}
<div className="flex gap-2 mb-4">
<button
onClick={() => setActiveLocale('es')}
className={`px-4 py-2 rounded-lg ${activeLocale === 'es' ? 'bg-primary-500 text-white' : 'bg-gray-100'}`}
>
🇪🇸 Español
</button>
<button
onClick={() => setActiveLocale('en')}
className={`px-4 py-2 rounded-lg ${activeLocale === 'en' ? 'bg-primary-500 text-white' : 'bg-gray-100'}`}
>
🇬🇧 English
</button>
</div>
{/* Campos traducibles - muestran según activeLocale */}
<div className="space-y-4">
<Input
label={activeLocale === 'es' ? 'Título' : 'Title'}
value={formData[`title_${activeLocale}`]}
onChange={(v) => setFormData({...formData, [`title_${activeLocale}`]: v})}
required
/>
<Textarea
label={activeLocale === 'es' ? 'Extracto' : 'Excerpt'}
value={formData[`excerpt_${activeLocale}`]}
onChange={(v) => setFormData({...formData, [`excerpt_${activeLocale}`]: v})}
rows={2}
/>
{/* Editor Markdown/WYSIWYG */}
<MarkdownEditor
label={activeLocale === 'es' ? 'Contenido' : 'Content'}
value={formData[`content_${activeLocale}`]}
onChange={(v) => setFormData({...formData, [`content_${activeLocale}`]: v})}
/>
</div>
{/* Campos comunes (no traducibles) */}
<div className="mt-6 pt-6 border-t">
<ImageUpload
label="Imagen destacada"
value={formData.featured_image}
onChange={(url) => setFormData({...formData, featured_image: url})}
/>
<div className="grid grid-cols-2 gap-4 mt-4">
<Select
label="Categoría"
value={formData.category}
options={blogCategories}
onChange={(v) => setFormData({...formData, category: v})}
/>
<DateTimePicker
label="Fecha publicación"
value={formData.published_at}
onChange={(v) => setFormData({...formData, published_at: v})}
/>
</div>
<TagInput
label="Tags"
value={formData.tags}
onChange={(tags) => setFormData({...formData, tags})}
/>
</div>
</div>
);
}
6. Sistema de Reservas
6.1 Flujo de Reserva (Cliente)
┌─────────────────────────────────────────────────────────────┐
│ PASO 1: Fecha y Comensales │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 📅 Selecciona fecha 👥 Comensales │ │
│ │ [Calendario] [- 2 +] │ │
│ │ │ │
│ │ 🍽️ Servicio │ │
│ │ ○ Comida (13:00-16:00) ○ Cena (20:00-23:30) │ │
│ └─────────────────────────────────────────────────────┘ │
│ [Siguiente →] │
└─────────────────────────────────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────────┐
│ PASO 2: Hora y Preferencias │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ ⏰ Horarios disponibles │ │
│ │ [13:00] [13:30] [14:00] [14:30] [15:00] │ │
│ │ │ │
│ │ 🏠 Preferencia de zona (opcional) │ │
│ │ ○ Sin preferencia ○ Interior ○ Terraza │ │
│ │ │ │
│ │ 🎉 Ocasión especial │ │
│ │ [Ninguna ▼] (Cumpleaños, Aniversario, Negocios) │ │
│ └─────────────────────────────────────────────────────┘ │
│ [← Atrás] [Siguiente →] │
└─────────────────────────────────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────────┐
│ PASO 3: Datos de Contacto │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 👤 Nombre * 📱 Teléfono * │ │
│ │ [_______________] [_______________] │ │
│ │ │ │
│ │ 📧 Email (para confirmación) │ │
│ │ [_______________________________________________] │ │
│ │ │ │
│ │ 📝 Peticiones especiales / Alergias │ │
│ │ [_______________________________________________] │ │
│ │ [_______________________________________________] │ │
│ └─────────────────────────────────────────────────────┘ │
│ [← Atrás] [Confirmar Reserva] │
└─────────────────────────────────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────────┐
│ ✅ ¡RESERVA CONFIRMADA! │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ Código: #RES-2025-1234 │ │
│ │ │ │
│ │ 📅 Viernes 27 de Diciembre, 2025 │ │
│ │ ⏰ 14:00 │ │
│ │ 👥 4 personas │ │
│ │ 🏠 Interior │ │
│ │ │ │
│ │ Te hemos enviado un email de confirmación. │ │
│ │ Recibirás un recordatorio 24h antes. │ │
│ │ │ │
│ │ [Añadir al calendario] [Modificar reserva] │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
6.2 Lógica de Disponibilidad
// Pseudocódigo para calcular disponibilidad
async function getAvailableSlots(date: Date, partySize: number) {
// 1. Obtener horarios activos para esa fecha
const schedules = await getActiveSchedules(date);
// 2. Por cada horario, calcular slots
const slots = [];
for (const schedule of schedules) {
const timeSlots = generateTimeSlots(
schedule.time_from,
schedule.time_to,
schedule.slot_duration
);
// 3. Por cada slot, verificar disponibilidad
for (const slot of timeSlots) {
const existing = await getReservationsForSlot(date, slot, schedule.room_id);
const capacity = await getRoomCapacity(schedule.room_id);
const occupied = existing.reduce((sum, r) => sum + r.party_size, 0);
if (capacity - occupied >= partySize) {
slots.push({
time: slot,
service: schedule.service_type,
room_id: schedule.room_id,
available_seats: capacity - occupied
});
}
}
}
return slots;
}
7. Flujo de Auto-Configuración
7.1 Primera Visita a /admin
// Cuando un customer accede por primera vez
async function onFirstAdminAccess(customer: Customer) {
// 1. Verificar que tiene el tier correcto
if (!customer.tierSpecials.includes('restaurant-template')) {
throw new Error('No tienes acceso a este template');
}
// 2. Buscar proyectos existentes del restaurante
const existingProjects = await getCustomerProjects(customer.id, {
prefix: 'restaurant_'
});
// 3. Determinar qué proyectos faltan
const required = [
'restaurant_config',
'restaurant_rooms',
'restaurant_schedules',
'restaurant_menus',
'restaurant_carta',
'restaurant_customers',
'restaurant_reservations'
];
const missing = required.filter(
name => !existingProjects.find(p => p.name === name)
);
if (missing.length > 0) {
// 4. Mostrar modal de configuración inicial
return showSetupModal({
missing,
options: {
createWithExampleData: true, // Datos de ejemplo
createEmptyStructure: false // Solo estructura vacía
}
});
}
// 5. Todo OK, mostrar dashboard
return redirectToDashboard();
}
7.2 Modal de Configuración Inicial
┌─────────────────────────────────────────────────────────────┐
│ 🍽️ Configuración Inicial │
│ ───────────────────────────────────────────────────────── │
│ │
│ Vamos a crear las tablas necesarias para tu restaurante. │
│ │
│ 📋 Se crearán los siguientes proyectos en Cadences: │
│ │
│ ☑️ Configuración general │
│ ☑️ Salas y comedores │
│ ☑️ Horarios │
│ ☑️ Menús │
│ ☑️ Carta │
│ ☑️ Clientes │
│ ☑️ Reservas │
│ │
│ ───────────────────────────────────────────────────────── │
│ │
│ 📝 Módulos opcionales: │
│ │
│ ☐ Blog - Publica noticias, recetas y eventos │
│ (Puedes activarlo más tarde desde Configuración) │
│ │
│ ───────────────────────────────────────────────────────── │
│ │
│ ¿Quieres añadir datos de ejemplo? │
│ │
│ ○ Sí, crear con datos de ejemplo │
│ (Útil para explorar el sistema) │
│ │
│ ○ No, solo la estructura vacía │
│ (Empezar desde cero) │
│ │
│ │
│ [Cancelar] [✨ Crear Tablas] │
└─────────────────────────────────────────────────────────────┘
7.3 Datos de Ejemplo
Si el usuario elige "datos de ejemplo", se crean:
restaurant_rooms:
- "Comedor Principal" (12 mesas, 48 sillas)
- "Terraza" (6 mesas, 24 sillas, is_terrace: true)
- "Sala Privada" (2 mesas, 12 sillas, is_private: true)
restaurant_schedules:
- Comidas L-D 13:00-16:00
- Cenas V-S 20:00-23:30
restaurant_carta:
- 3-4 platos por categoría (Entrantes, Carnes, Pescados, Postres)
restaurant_menus:
- "Menú del Día" (15€)
- "Menú Degustación" (45€)
8. Integraciones
8.1 Integración con Cadences Workflows
El restaurant template debe integrarse profundamente con el sistema de Workflows de Cadences para automatizaciones con IA:
8.1.1 Arquitectura de Integración
┌─────────────────────────────────────────────────────────────────────────┐
│ CADENCES PLATFORM │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ WORKFLOW ENGINE │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │ Trigger │→ │ AI Agent │→ │ Condition│→ │ Action │ │ │
│ │ │ (evento) │ │ (análisis)│ │ (decide) │ │ (ejecuta)│ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ ↑ ↓ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ COMMUNICATION HUB │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │ WhatsApp │ │ Llamadas │ │ Email │ │ SMS │ │ │
│ │ │ Business │ │ (Twilio) │ │ (SMTP) │ │ (Twilio) │ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ ↑ ↓ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ RESTAURANT DATA │ │
│ │ [Reservas] [Horarios] [Clientes] [Salas] [Menús] [Config] │ │
│ └─────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘
8.1.2 Workflows Predefinidos para Restaurante
1. 📞 Workflow: Atención de Llamadas
name: "Atender Llamada Entrante"
trigger:
type: "webhook"
source: "twilio_voice"
event: "incoming_call"
nodes:
- id: "identify_caller"
type: "data_lookup"
config:
table: "restaurant_customers"
match_field: "phone"
input: "{{trigger.from}}"
- id: "ai_receptionist"
type: "ai_agent"
config:
profile: "restaurant_receptionist"
context:
- customer: "{{identify_caller.result}}"
- today_schedule: "{{get_today_schedule()}}"
- available_slots: "{{get_available_slots(today, 7_days)}}"
system_prompt: |
Eres el recepcionista virtual de Restaurant23.
El cliente {{customer.name || 'nuevo'}} está llamando.
Horarios de hoy: {{today_schedule}}
Puedes:
1. Hacer una reserva
2. Consultar disponibilidad
3. Dar información del restaurante
4. Transferir a un humano si es necesario
Responde de forma natural, cálida y profesional.
voice_config:
provider: "elevenlabs"
voice_id: "spanish_female_warm"
- id: "process_intent"
type: "condition"
config:
conditions:
- if: "{{ai_receptionist.intent == 'make_reservation'}}"
goto: "create_reservation"
- if: "{{ai_receptionist.intent == 'cancel_reservation'}}"
goto: "cancel_reservation"
- if: "{{ai_receptionist.intent == 'transfer_human'}}"
goto: "transfer_call"
- else:
goto: "end_call"
- id: "create_reservation"
type: "action"
config:
action: "create_row"
table: "restaurant_reservations"
data:
customer_phone: "{{trigger.from}}"
customer_name: "{{ai_receptionist.extracted.name}}"
date: "{{ai_receptionist.extracted.date}}"
time: "{{ai_receptionist.extracted.time}}"
party_size: "{{ai_receptionist.extracted.guests}}"
status: "confirmed"
source: "phone"
2. 💬 Workflow: WhatsApp Bot
name: "WhatsApp Reservation Bot"
trigger:
type: "webhook"
source: "whatsapp_business"
event: "message_received"
nodes:
- id: "get_context"
type: "parallel"
nodes:
- lookup_customer:
table: "restaurant_customers"
match: "phone"
- get_conversation:
store: "whatsapp_sessions"
key: "{{trigger.from}}"
- id: "ai_chat"
type: "ai_agent"
config:
profile: "restaurant_whatsapp"
memory: "{{get_conversation.messages}}"
context:
customer: "{{lookup_customer.result}}"
schedule: "{{get_week_schedule()}}"
menu_summary: "{{get_active_menus()}}"
tools:
- check_availability:
description: "Consulta disponibilidad para una fecha"
params: [date, party_size]
- make_reservation:
description: "Crea una reserva"
params: [date, time, party_size, customer_name, customer_phone]
- get_menu_info:
description: "Obtiene información de la carta o menús"
params: [category?]
- send_location:
description: "Envía la ubicación del restaurante"
- id: "execute_tool"
type: "tool_executor"
config:
tool: "{{ai_chat.tool_call}}"
- id: "send_response"
type: "action"
config:
action: "whatsapp_send"
to: "{{trigger.from}}"
message: "{{ai_chat.response}}"
- id: "save_conversation"
type: "action"
config:
action: "update_session"
store: "whatsapp_sessions"
key: "{{trigger.from}}"
data:
messages: "{{append(get_conversation.messages, [user_msg, assistant_msg])}}"
last_activity: "{{now()}}"
3. 📅 Workflow: Recordatorios Automáticos
name: "Enviar Recordatorios de Reserva"
trigger:
type: "schedule"
cron: "0 10 * * *" # Cada día a las 10:00
nodes:
- id: "get_tomorrow_reservations"
type: "data_query"
config:
table: "restaurant_reservations"
filter:
date: "{{tomorrow()}}"
status: "confirmed"
reminder_sent: false
- id: "loop_reservations"
type: "loop"
config:
items: "{{get_tomorrow_reservations.rows}}"
- id: "ai_personalize"
type: "ai_agent"
config:
profile: "restaurant_marketing"
prompt: |
Genera un mensaje de recordatorio personalizado para:
Cliente: {{item.customer_name}}
Reserva: {{item.date}} a las {{item.time}} para {{item.party_size}} personas
Historial: {{get_customer_history(item.customer_id)}}
El mensaje debe ser cálido, breve y en {{item.locale || 'es'}}.
- id: "send_reminder"
type: "parallel"
nodes:
- whatsapp:
if: "{{item.customer_phone}}"
action: "whatsapp_send"
template: "reservation_reminder"
params:
name: "{{item.customer_name}}"
date: "{{format_date(item.date, item.locale)}}"
time: "{{item.time}}"
guests: "{{item.party_size}}"
- email:
if: "{{item.customer_email}}"
action: "email_send"
template: "reservation_reminder_{{item.locale}}"
to: "{{item.customer_email}}"
data: "{{ai_personalize.message}}"
- id: "mark_sent"
type: "action"
config:
action: "update_row"
table: "restaurant_reservations"
id: "{{item.id}}"
data:
reminder_sent: true
reminder_sent_at: "{{now()}}"
4. 🚨 Workflow: Gestión de No-Shows
name: "Detectar y Gestionar No-Shows"
trigger:
type: "schedule"
cron: "0 23 * * *" # Cada noche a las 23:00
nodes:
- id: "find_no_shows"
type: "data_query"
config:
table: "restaurant_reservations"
filter:
date: "{{today()}}"
status: "confirmed" # Nunca marcado como seated
- id: "loop_no_shows"
type: "loop"
config:
items: "{{find_no_shows.rows}}"
- id: "update_status"
type: "action"
config:
action: "update_row"
table: "restaurant_reservations"
id: "{{item.id}}"
data:
status: "no_show"
- id: "update_customer"
type: "action"
config:
action: "update_row"
table: "restaurant_customers"
id: "{{item.customer_id}}"
data:
no_show_count: "{{customer.no_show_count + 1}}"
- id: "ai_decision"
type: "ai_agent"
config:
profile: "restaurant_manager"
prompt: |
El cliente {{item.customer_name}} no se presentó a su reserva.
Historial de no-shows: {{customer.no_show_count}}
Es VIP: {{customer.vip}}
Total visitas: {{customer.total_visits}}
¿Qué acción recomiendas?
1. blacklist - Añadir a lista negra
2. warning - Enviar mensaje de advertencia
3. ignore - No hacer nada (cliente valioso/primera vez)
- id: "execute_decision"
type: "condition"
config:
conditions:
- if: "{{ai_decision.action == 'blacklist'}}"
goto: "add_to_blacklist"
- if: "{{ai_decision.action == 'warning'}}"
goto: "send_warning"
5. 📊 Workflow: Análisis Diario con IA
name: "Resumen Diario del Restaurante"
trigger:
type: "schedule"
cron: "0 8 * * *" # Cada mañana a las 8:00
nodes:
- id: "gather_data"
type: "parallel"
nodes:
- yesterday_reservations:
query: "SELECT * FROM reservations WHERE date = yesterday()"
- yesterday_no_shows:
query: "SELECT COUNT(*) FROM reservations WHERE date = yesterday() AND status = 'no_show'"
- today_reservations:
query: "SELECT * FROM reservations WHERE date = today()"
- week_trend:
query: "SELECT date, COUNT(*) FROM reservations WHERE date > today() - 7 GROUP BY date"
- id: "ai_analysis"
type: "ai_agent"
config:
profile: "restaurant_analyst"
prompt: |
Genera un resumen ejecutivo del día anterior y previsión para hoy:
AYER:
- Total reservas: {{yesterday_reservations.count}}
- Comensales: {{sum(yesterday_reservations, 'party_size')}}
- No-shows: {{yesterday_no_shows.count}}
- Ocupación por sala: {{calculate_occupancy(yesterday_reservations)}}
HOY:
- Reservas confirmadas: {{today_reservations.count}}
- Comensales esperados: {{sum(today_reservations, 'party_size')}}
- Distribución: {{group_by_service(today_reservations)}}
TENDENCIA SEMANAL: {{week_trend}}
Incluye:
1. Resumen ejecutivo (2-3 líneas)
2. Alertas importantes
3. Recomendaciones para hoy
- id: "send_summary"
type: "action"
config:
action: "notification"
channels: ["email", "whatsapp"]
recipients: "{{get_managers()}}"
subject: "📊 Resumen Restaurant23 - {{format_date(today())}}"
content: "{{ai_analysis.summary}}"
8.1.3 Perfiles de IA para Restaurante
En Cadences se crearán estos AI Profiles predefinidos:
const RESTAURANT_AI_PROFILES = [
{
id: 'restaurant_receptionist',
name: 'Recepcionista Virtual',
category: 'restaurant',
systemPrompt: `Eres el/la recepcionista virtual de un restaurante.
Tu rol es atender llamadas y mensajes de forma profesional, cálida y eficiente.
PUEDES:
- Consultar disponibilidad de mesas
- Hacer, modificar o cancelar reservas
- Informar sobre horarios, ubicación y servicios
- Responder preguntas sobre menús y carta
- Tomar nota de peticiones especiales
NO PUEDES:
- Hacer descuentos o promociones no autorizadas
- Confirmar reservas para grupos de más de 20 personas sin supervisión
- Dar información sobre precios exactos sin consultar
COMPORTAMIENTO:
- Sé amable y profesional
- Si no sabes algo, ofrece transferir a un humano
- Confirma siempre los datos antes de crear una reserva
- Usa el nombre del cliente cuando lo conozcas`,
icon: '📞'
},
{
id: 'restaurant_whatsapp',
name: 'Asistente WhatsApp',
category: 'restaurant',
systemPrompt: `Eres el asistente de WhatsApp del restaurante.
Respondes mensajes de clientes de forma conversacional y eficiente.
FORMATO:
- Mensajes breves y claros (WhatsApp)
- Usa emojis con moderación
- Si la conversación se alarga, ofrece llamar
FLUJO TÍPICO:
1. Saludo → 2. Detectar intención → 3. Recopilar datos → 4. Confirmar
Para reservas necesitas:
- Fecha y hora
- Número de comensales
- Nombre
- Teléfono (ya lo tienes del chat)
- Peticiones especiales (opcional)`,
icon: '💬'
},
{
id: 'restaurant_marketing',
name: 'Marketing & Comunicación',
category: 'restaurant',
systemPrompt: `Generas comunicaciones de marketing para el restaurante.
Tu tono es cálido, cercano y apetitoso.
TIPOS DE CONTENIDO:
- Recordatorios de reserva
- Promociones y ofertas
- Anuncios de nuevos platos
- Felicitaciones de cumpleaños/aniversarios
- Newsletters
REGLAS:
- Personaliza con el nombre del cliente
- Adapta al idioma del cliente (ES/EN)
- Incluye call-to-action claro
- No seas demasiado comercial`,
icon: '📣'
},
{
id: 'restaurant_manager',
name: 'Gestor de Operaciones',
category: 'restaurant',
systemPrompt: `Eres el asistente del gerente del restaurante.
Ayudas a tomar decisiones operativas basadas en datos.
ÁREAS:
- Gestión de no-shows y blacklist
- Optimización de turnos
- Análisis de ocupación
- Detección de patrones
CRITERIOS:
- Clientes VIP tienen más margen de error
- Primer no-show: advertencia
- 2+ no-shows: considerar restricciones
- Analiza contexto antes de decidir`,
icon: '👔'
},
{
id: 'restaurant_analyst',
name: 'Analista de Datos',
category: 'restaurant',
systemPrompt: `Generas informes y análisis para el restaurante.
Tu output es claro, conciso y accionable.
FORMATO DE INFORMES:
1. Resumen ejecutivo (1-2 líneas)
2. Métricas clave
3. Alertas/Problemas
4. Recomendaciones
MÉTRICAS IMPORTANTES:
- Ocupación por servicio/sala
- Ticket medio (si disponible)
- Tasa de no-show
- Tendencias semanales
- Comparación con semana anterior`,
icon: '📊'
}
];
8.1.4 Eventos y Webhooks
El storefront debe emitir eventos que el Workflow Engine pueda capturar:
// Eventos que dispara el storefront
const RESTAURANT_EVENTS = {
// Reservas
'reservation.created': {
payload: { reservation_id, customer, date, time, party_size, source }
},
'reservation.updated': {
payload: { reservation_id, changes, updated_by }
},
'reservation.cancelled': {
payload: { reservation_id, reason, cancelled_by }
},
'reservation.seated': {
payload: { reservation_id, actual_time, table_number }
},
'reservation.completed': {
payload: { reservation_id, duration, notes }
},
// Clientes
'customer.created': {
payload: { customer_id, name, phone, email, source }
},
'customer.updated': {
payload: { customer_id, changes }
},
// Comunicaciones entrantes
'communication.incoming_call': {
payload: { from, to, call_sid, timestamp }
},
'communication.whatsapp_message': {
payload: { from, message, media?, timestamp }
},
'communication.email_received': {
payload: { from, subject, body, attachments? }
},
// Formularios web
'form.contact_submitted': {
payload: { name, email, phone, message }
},
'form.reservation_request': {
payload: { date, time, guests, customer_info, special_requests }
},
// Blog
'blog.post_published': {
payload: { post_id, title, category }
}
};
8.1.5 Configuración en Admin
En /admin/automatizaciones se gestionan los workflows:
┌─────────────────────────────────────────────────────────────┐
│ 🤖 Automatizaciones [+ Crear nueva] │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ 📞 Atención de llamadas 🟢 Activo │ │
│ │ Atende llamadas entrantes con IA │ │
│ │ Última ejecución: hace 2 min · 23 hoy │ │
│ │ [Editar] [Ver logs] [Pausar] │ │
│ ├────────────────────────────────────────────────────────┤ │
│ │ 💬 WhatsApp Bot 🟢 Activo │ │
│ │ Responde mensajes de WhatsApp │ │
│ │ Última ejecución: hace 5 min · 156 hoy │ │
│ │ [Editar] [Ver logs] [Pausar] │ │
│ ├────────────────────────────────────────────────────────┤ │
│ │ 📅 Recordatorios 24h 🟢 Activo │ │
│ │ Envía recordatorios de reserva │ │
│ │ Próxima ejecución: mañana 10:00 │ │
│ │ [Editar] [Ver logs] [Pausar] │ │
│ ├────────────────────────────────────────────────────────┤ │
│ │ 📊 Resumen diario 🟡 Pausado │ │
│ │ Genera informe diario para managers │ │
│ │ [Editar] [Ver logs] [Activar] │ │
│ └────────────────────────────────────────────────────────┘ │
│ │
│ ─────────────────── Canales conectados ────────────────── │
│ │
│ 📞 Twilio Voice [Configurar] ✅ Conectado │
│ 💬 WhatsApp Business [Configurar] ✅ Conectado │
│ 📧 Email (SMTP) [Configurar] ✅ Conectado │
│ 📱 SMS [Configurar] ⚪ No configurado │
│ │
└─────────────────────────────────────────────────────────────┘
8.2 Cadences API (Endpoints Necesarios)
// Endpoints que Cadences debe exponer para storefronts
// Autenticación
POST /api/storefronts/auth/google
→ Verifica Google token, devuelve JWT si tiene acceso
// Proyectos DATA_TABLE
GET /api/storefronts/{sfId}/tables
GET /api/storefronts/{sfId}/tables/{tableId}/rows
POST /api/storefronts/{sfId}/tables/{tableId}/rows
PUT /api/storefronts/{sfId}/tables/{tableId}/rows/{rowId}
DELETE /api/storefronts/{sfId}/tables/{tableId}/rows/{rowId}
// Setup inicial
POST /api/storefronts/{sfId}/setup
→ Crea proyectos DATA_TABLE con schema predefinido
// Reservas (atajos optimizados)
GET /api/storefronts/{sfId}/availability?date=X&party_size=N
POST /api/storefronts/{sfId}/reservations
PUT /api/storefronts/{sfId}/reservations/{id}/status
// Workflows
GET /api/storefronts/{sfId}/workflows
POST /api/storefronts/{sfId}/workflows
PUT /api/storefronts/{sfId}/workflows/{id}
POST /api/storefronts/{sfId}/workflows/{id}/execute
GET /api/storefronts/{sfId}/workflows/{id}/logs
// Eventos (para triggers de workflows)
POST /api/storefronts/{sfId}/events
→ Emite un evento que puede disparar workflows
// Comunicaciones
POST /api/storefronts/{sfId}/communications/whatsapp/webhook
POST /api/storefronts/{sfId}/communications/twilio/voice/webhook
POST /api/storefronts/{sfId}/communications/twilio/sms/webhook
8.3 Notificaciones (Multi-idioma)
// Cuando se crea/modifica una reserva
async function onReservationChange(reservation, action) {
// Obtener idioma de la reserva (o default 'es')
const locale = reservation.locale || 'es';
// Email al cliente en su idioma
if (action === 'created') {
await sendEmail(`reservation-confirmation-${locale}`, reservation.customer_email, {
reservation,
restaurant: config,
t: (key) => translate(locale, key) // Helper de traducción
});
}
// SMS opcional
if (config.sms_enabled) {
await sendSMS(reservation.customer_phone,
`Reserva confirmada en Restaurant23 para ${reservation.date} a las ${reservation.time}`
);
}
// Notificación en Cadences (tarea o notificación)
await createCadencesNotification({
type: 'reservation',
action,
data: reservation
});
}
8.4 Integraciones de Comunicación
Twilio (Llamadas + SMS)
const twilioConfig = {
accountSid: 'AC...',
authToken: '...',
phoneNumber: '+34...',
voiceWebhook: '/api/communications/twilio/voice',
smsWebhook: '/api/communications/twilio/sms',
// Configuración de voz
voice: {
language: 'es-ES',
transcription: true,
recordCalls: false, // GDPR
}
};
WhatsApp Business API
const whatsappConfig = {
phoneNumberId: '...',
businessAccountId: '...',
accessToken: '...',
webhookVerifyToken: '...',
webhookUrl: '/api/communications/whatsapp/webhook',
// Templates aprobados
templates: {
reservation_confirmation: 'reserva_confirmada_v1',
reservation_reminder: 'recordatorio_reserva_v1',
reservation_cancelled: 'reserva_cancelada_v1',
}
};
ElevenLabs (Voz IA)
const voiceConfig = {
apiKey: '...',
voiceId: 'spanish_female_warm', // Voz natural en español
model: 'eleven_multilingual_v2',
settings: {
stability: 0.5,
similarity_boost: 0.75,
}
};
8.5 Integraciones Futuras
- Google Business: Sincronizar reservas
- TripAdvisor: Widget de reservas
- WhatsApp Business API: Confirmaciones y recordatorios
- TPV/POS: Integración con sistema de cobro
- Contabilidad: Export de datos para facturación
9. Estimación y Roadmap
9.1 Fases de Desarrollo
Fase 0: Setup Base (8-10h)
- Crear estructura del proyecto (copiar de travel-template)
- Configurar i18n ES/EN
- Crear storefront.config.ts con tipo 'restaurant'
- Diseñar layouts y componentes base
Fase 1: Páginas Públicas (15-20h)
- Home con hero, destacados, horarios
- Carta completa con categorías
- Menús con detalle
- Galería
- Contacto con mapa
- Sobre nosotros
Fase 2: Sistema de Reservas (20-25h)
- Formulario multi-step
- Lógica de disponibilidad
- Integración con Cadences
- Confirmación y emails
- Página de gestión de reserva (modificar/cancelar)
Fase 3: Admin Panel (25-30h)
- Google Auth con verificación Cadences
- Dashboard con vista de hoy
- Calendario de reservas
- CRUD de salas
- CRUD de horarios
- CRUD de carta
- CRUD de menús
- Lista de clientes
Fase 3.5: Blog (Opcional) (12-15h)
- Modelo de datos y proyecto DATA_TABLE
- Páginas públicas: listado y detalle
- Editor de artículos con Markdown
- Gestión de imágenes
- SEO y meta tags
- Categorías y tags
Fase 4: Auto-Setup y Polish (10-15h)
- Flujo de configuración inicial
- Datos de ejemplo
- Validaciones y manejo de errores
- Responsive y accesibilidad
- Testing
Fase 5: Workflows e Integraciones IA (25-35h)
- Integración con Workflow Engine de Cadences
- Webhooks para Twilio (llamadas + SMS)
- Webhooks para WhatsApp Business
- Perfiles de IA predefinidos
- Workflow: Atención de llamadas
- Workflow: WhatsApp Bot
- Workflow: Recordatorios automáticos
- Workflow: Gestión de no-shows
- Workflow: Resumen diario
- Panel de automatizaciones en Admin
Fase 6: Funcionalidades Premium (40-60h)
- Guest Experience Management (perfiles avanzados)
- Sistema de prepago/depósitos (Stripe)
- Lista de espera inteligente con IA
- Mapa de mesas visual en tiempo real
- Pre-order del menú (Omakase digital)
- Sommelier Virtual con IA
- Programa de fidelización y niveles VIP
- Experiencias especiales (Chef's Table, catas)
- Dynamic Pricing (opcional)
- Gestión de reviews con IA
- KDS Integration (opcional)
9.2 Total Estimado
| Fase | Horas | Semanas (part-time) |
|---|---|---|
| Fase 0: Setup | 8-10h | 1 semana |
| Fase 1: Páginas públicas | 15-20h | 2 semanas |
| Fase 2: Reservas | 20-25h | 2-3 semanas |
| Fase 3: Admin Panel | 25-30h | 3 semanas |
| Fase 3.5: Blog (opcional) | 12-15h | 1-2 semanas |
| Fase 4: Auto-setup | 10-15h | 1-2 semanas |
| Fase 5: Workflows + IA | 25-35h | 3-4 semanas |
| Fase 6: Premium Features | 40-60h | 5-7 semanas |
Configuraciones de lanzamiento:
| Versión | Fases | Horas | Semanas |
|---|---|---|---|
| MVP Básico | 0-4 | 78-100h | 9-11 sem |
| + Blog | 0-4 + 3.5 | 90-115h | 10-13 sem |
| + Workflows | 0-5 | 115-150h | 13-17 sem |
| Full Premium | 0-6 | 155-210h | 18-24 sem |
💡 Recomendación: Lanzar MVP (Fases 0-4), iterar con feedback real, y añadir Premium Features según demanda de clientes.
10. Funcionalidades Premium (Inspiradas en Restaurantes Top)
Funcionalidades inspiradas en restaurantes de alta gama de Nueva York (Eleven Madison Park, Alinea), Tokio (Sukiyabashi Jiro, Narisawa), Taiwán (Din Tai Fung, RAW), Copenhague (Noma) y otros referentes mundiales.
10.1 Guest Experience Management (GXM)
Los mejores restaurantes del mundo no solo gestionan reservas, gestionan relaciones. Cada visita alimenta un perfil que hace la siguiente experiencia más especial.
Perfil de Cliente Avanzado
┌─────────────────────────────────────────────────────────────────┐
│ 👤 María García Fernández VIP ⭐⭐⭐ │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 📊 ESTADÍSTICAS │
│ ├─ Visitas totales: 23 │
│ ├─ Última visita: 15 Dic 2025 │
│ ├─ Gasto medio: 85€ │
│ ├─ Gasto total: 1,955€ │
│ └─ Cliente desde: Mar 2024 │
│ │
│ 🍷 PREFERENCIAS │
│ ├─ Mesa favorita: #7 (ventana) │
│ ├─ Vino preferido: Ribera del Duero, crianza │
│ ├─ Platos favoritos: Carrillera, Pulpo a la brasa │
│ └─ Suele pedir: Menú degustación │
│ │
│ ⚠️ ALERGIAS / DIETA │
│ ├─ Intolerancia: Lactosa │
│ ├─ No le gusta: Cilantro, picante fuerte │
│ └─ Dieta: Ninguna restricción │
│ │
│ 🎂 FECHAS ESPECIALES │
│ ├─ Cumpleaños: 15 Marzo │
│ ├─ Aniversario: 22 Junio (viene con pareja) │
│ └─ Próximo: Cumpleaños en 79 días │
│ │
│ 📝 NOTAS DEL EQUIPO │
│ ├─ "Siempre llega 10min antes" │
│ ├─ "Le gusta que le recomienden vinos nuevos" │
│ └─ "Su pareja es celíaco - tener opciones GF" │
│ │
│ 📜 HISTORIAL DE VISITAS │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ 15/12/25 │ Cena 2p │ Mesa 7 │ Degustación │ 156€ │ ⭐⭐⭐⭐⭐ │ │
│ │ 28/11/25 │ Comida 4p │ Mesa 12 │ Carta │ 245€ │ ⭐⭐⭐⭐ │ │
│ │ 15/11/25 │ Cena 2p │ Mesa 7 │ Degustación │ 148€ │ ⭐⭐⭐⭐⭐ │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
Modelo de datos extendido para restaurant_customers:
// Campos adicionales premium
interface PremiumCustomerProfile {
// Básico (ya definido)
id: string;
name: string;
phone: string;
email: string;
// Premium: Preferencias
preferences: {
favorite_table?: string;
favorite_dishes: string[];
favorite_wines: string[];
seating_preference: 'window' | 'quiet' | 'bar' | 'terrace' | 'any';
usually_orders: 'carta' | 'menu' | 'tasting' | 'varies';
avg_party_size: number;
typical_spend: number;
};
// Premium: Dietary
dietary: {
allergies: string[];
intolerances: string[];
dislikes: string[];
diet: 'none' | 'vegetarian' | 'vegan' | 'pescatarian' | 'halal' | 'kosher' | 'keto' | 'other';
diet_notes?: string;
};
// Premium: Fechas especiales
special_dates: Array<{
type: 'birthday' | 'anniversary' | 'other';
date: string; // MM-DD
notes?: string; // "Viene con María", "Sorpresa"
}>;
// Premium: Estadísticas
stats: {
total_visits: number;
total_spent: number;
avg_spend: number;
first_visit: string;
last_visit: string;
no_show_count: number;
cancellation_count: number;
};
// Premium: VIP & Loyalty
vip_level: 0 | 1 | 2 | 3; // 0=normal, 1-3=VIP levels
loyalty_points: number;
referral_code?: string;
referred_by?: string;
// Premium: Notas del equipo
team_notes: Array<{
note: string;
added_by: string;
added_at: string;
pinned: boolean;
}>;
// Premium: Comunicación
communication_preferences: {
email_marketing: boolean;
sms_reminders: boolean;
whatsapp_enabled: boolean;
language: 'es' | 'en';
};
}
10.2 Sistema de Prepago y Depósitos (Estilo Tock/Resy)
Los restaurantes top como Alinea, Eleven Madison Park o Noma usan sistemas de prepago para:
- Eliminar no-shows (reducción del 95%)
- Garantizar ingresos
- Crear exclusividad
┌─────────────────────────────────────────────────────────────────┐
│ 💳 CONFIGURACIÓN DE PAGOS │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Modo de pago para reservas: │
│ │
│ ○ Sin pago previo │
│ Reserva tradicional, sin cargo │
│ │
│ ○ Depósito por persona │
│ Se cobra un depósito que se descuenta de la cuenta final │
│ Cantidad: [25] € por persona │
│ Política cancelación: [48h] antes = reembolso total │
│ │
│ ● Prepago completo (Menú Degustación) │
│ El cliente paga el menú completo al reservar │
│ Precio: [95] € por persona │
│ Política: No reembolsable, pero transferible │
│ │
│ ○ Solo guardar tarjeta (penalización no-show) │
│ Se cobra solo si no se presenta │
│ Penalización: [50] € por persona │
│ │
│ ───────────────────────────────────────────────────────────── │
│ │
│ Aplicar a: │
│ ☑️ Menú Degustación (siempre prepago) │
│ ☑️ Grupos de 6+ personas (depósito 25€/pax) │
│ ☑️ Viernes y Sábados cena (depósito 15€/pax) │
│ ☐ Todas las reservas │
│ │
│ Integración de pago: │
│ [Stripe ▼] API Key: [sk_live_•••••••••••] ✅ Conectado │
│ │
└─────────────────────────────────────────────────────────────────┘
Flujo de reserva con prepago:
Cliente selecciona Menú Degustación (95€/pax)
↓
Selecciona fecha/hora
↓
Introduce datos + tarjeta
↓
┌─────────────────────────────────────┐
│ Stripe: Cobro 190€ (2 personas) │
│ Metadata: reservation_id, menu_id │
└─────────────────────────────────────┘
↓
Reserva confirmada + Email con:
- Ticket/Voucher descargable
- QR para check-in
- Política de cancelación
↓
Día de la reserva:
- Escanean QR o dan nombre
- Sistema marca "seated"
- Cocina recibe orden automáticamente
10.3 Lista de Espera Inteligente (Estilo Din Tai Fung)
Din Tai Fung en Taiwán revolucionó la gestión de colas con predicción de tiempos exactos.
┌─────────────────────────────────────────────────────────────────┐
│ ⏳ LISTA DE ESPERA EN TIEMPO REAL │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Estado actual: 🟡 Alta demanda │
│ Tiempo estimado de espera: 25-35 min │
│ │
│ ┌────┬─────────────────┬────┬──────────┬──────────────────────┐│
│ │ # │ Nombre │ Pax│ Espera │ Estado ││
│ ├────┼─────────────────┼────┼──────────┼──────────────────────┤│
│ │ 1 │ García │ 4 │ 2 min │ 🔔 Llamando... ││
│ │ 2 │ López │ 2 │ 8 min │ ⏳ Esperando ││
│ │ 3 │ Martínez │ 6 │ 15 min │ ⏳ Esperando ││
│ │ 4 │ Chen │ 2 │ 18 min │ ⏳ Esperando ││
│ │ 5 │ Fernández │ 3 │ 22 min │ ⏳ Esperando ││
│ │ 6 │ Rodríguez │ 4 │ 28 min │ 📱 Notificado ││
│ └────┴─────────────────┴────┴──────────┴──────────────────────┘│
│ │
│ [+ Añadir a lista] [📊 Ver predicciones] [⚙️ Configurar] │
│ │
└─────────────────────────────────────────────────────────────────┘
IA para predicción de tiempos:
# Workflow: Predicción de tiempo de espera
name: "Calcular Tiempo de Espera"
trigger:
type: "event"
event: "waitlist.customer_added"
nodes:
- id: "gather_data"
type: "parallel"
nodes:
- current_tables:
query: "Mesas ocupadas y tiempo desde que se sentaron"
- historical_avg:
query: "Tiempo medio de comida por party_size y día/hora"
- kitchen_status:
query: "Carga actual de cocina"
- id: "ai_predict"
type: "ai_agent"
config:
profile: "restaurant_operations"
prompt: |
Predice el tiempo de espera para un grupo de {{party_size}} personas.
Mesas ocupadas: {{current_tables}}
- Mesa 3 (4p): 45 min, pidiendo postres
- Mesa 7 (2p): 20 min, plato principal
- Mesa 12 (6p): 10 min, entrantes
Promedios históricos ({{day_of_week}} {{hour}}):
- 2 personas: 52 min
- 4 personas: 68 min
- 6+ personas: 85 min
Cocina: {{kitchen_load}}% capacidad
Calcula:
1. Próxima mesa disponible para {{party_size}}
2. Tiempo estimado (rango min-max)
3. Confianza de la predicción
- id: "notify_customer"
type: "action"
config:
action: "sms_send"
message: |
🍽️ Restaurant23: Estás en la posición #{{position}}.
Tiempo estimado: {{ai_predict.time_range}}.
Te avisamos por SMS cuando tu mesa esté lista.
Responde CANCEL para cancelar.
Vista pública para clientes (QR en la puerta):
┌─────────────────────────────────────────────────────────────────┐
│ Restaurant23 │
│ ───────────────────────────────────────────────────────────── │
│ │
│ 🍽️ Lista de Espera │
│ │
│ Tiempo estimado ahora: ~25 min │
│ │
│ ┌─────────────────────────────────────────────────────────────┐│
│ │ ││
│ │ ¿Cuántos sois? ││
│ │ ││
│ │ [1] [2] [3] [4] [5] [6+] ││
│ │ ││
│ │ Tu teléfono (para avisarte): ││
│ │ [+34 _______________] ││
│ │ ││
│ │ ☐ Tenemos niños pequeños (trona) ││
│ │ ☐ Necesitamos acceso silla de ruedas ││
│ │ ││
│ │ [🔔 Apuntarme a la lista] ││
│ │ ││
│ └─────────────────────────────────────────────────────────────┘│
│ │
│ Ya estás en la lista? [Consultar mi posición] │
│ │
└─────────────────────────────────────────────────────────────────┘
10.4 Table Management Visual (Mapa de Mesas en Tiempo Real)
Como Noma o Per Se, control visual de cada mesa:
┌─────────────────────────────────────────────────────────────────┐
│ 🗺️ MAPA DE MESAS - Comedor Principal 14:35 | Comida │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────┐ ┌─────┐ ┌─────┐ │
│ │ 1 │ │ 2 │ │ 3 │ 🚪 Entrada │
│ │ 🟢 │ │ 🔴 │ │ 🟡 │ │
│ │ 2p │ │ 4p │ │ 4p │ │
│ └─────┘ └─────┘ └─────┘ │
│ 45m 12m │
│ │
│ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ │
│ │ 4 │ │ 5 │ │ 6 │ │ 7 │ │ 8 │ │
│ │ 🟢 │ │ 🔴 │ │ 🔴 │ │ 🔴 │ │ 🟢 │ │
│ │ 2p │ │ 2p │ │ 6p │ │ 2p │ │ 4p │ │
│ └─────┘ └─────┘ └─────┘ └─────┘ └─────┘ │
│ 68m 35m ⭐ VIP │
│ 22m │
│ │
│ ════════════════════ BARRA ════════════════════ │
│ 🟢 🟢 🔴 🔴 🟢 🟢 🟢 🔴 │
│ B1 B2 B3 B4 B5 B6 B7 B8 │
│ │
│ ───────────────────────────────────────────────────────────── │
│ 🟢 Libre 🔴 Ocupada (tiempo) 🟡 Reservada (próxima) │
│ ⭐ VIP ⚠️ Postre/Cuenta 🔔 Esperando comanda │
│ │
│ Ocupación: 68% │ Próxima libre: Mesa 3 (~10min) │ Espera: 4 │
│ │
└─────────────────────────────────────────────────────────────────┘
Click en una mesa muestra:
┌─────────────────────────────────────────────┐
│ Mesa 7 - 2 personas │
├─────────────────────────────────────────────┤
│ │
│ 👤 María García ⭐ VIP │
│ 📱 +34 612 345 678 │
│ │
│ ⏰ Sentados: 14:13 (22 min) │
│ 📋 Estado: Plato principal │
│ │
│ 🍽️ Pedido: │
│ - Menú Degustación x2 (prepago) │
│ - Botella Ribera del Duero │
│ │
│ ⚠️ Nota: Ella intolerante lactosa │
│ │
│ [📝 Añadir nota] [💳 Cobrar] [✓ Liberar] │
│ │
└─────────────────────────────────────────────┘
10.5 Pre-Order del Menú (Omakase Digital)
En Tokio, los mejores restaurantes piden que elijas tu menú días antes para preparar ingredientes frescos.
┌─────────────────────────────────────────────────────────────────┐
│ 🍽️ CONFIGURAR TU EXPERIENCIA │
│ Reserva: 27 Dic 2025, 21:00 - 2 personas │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Has reservado: Menú Degustación (95€/persona) │
│ │
│ Por favor, selecciona tus preferencias para que nuestro │
│ chef prepare tu experiencia perfecta: │
│ │
│ ┌─────────────────────────────────────────────────────────────┐│
│ │ COMENSAL 1: María ││
│ │ ───────────────────────────────────────────────────────── ││
│ │ ││
│ │ Maridaje de vinos: ││
│ │ ● Maridaje completo (+45€) ││
│ │ ○ Solo copa de bienvenida ││
│ │ ○ Sin alcohol (maridaje de zumos +25€) ││
│ │ ││
│ │ Restricciones alimentarias: ││
│ │ ☑️ Intolerante a la lactosa (recordamos de tu perfil) ││
│ │ ☐ Vegetariano ││
│ │ ☐ Otros: [________________________] ││
│ │ ││
│ │ ¿Hay algo que no te guste? ││
│ │ [Cilantro, vísceras_________________________] ││
│ │ ││
│ └─────────────────────────────────────────────────────────────┘│
│ │
│ ┌─────────────────────────────────────────────────────────────┐│
│ │ COMENSAL 2: Carlos ││
│ │ ───────────────────────────────────────────────────────── ││
│ │ (mismos campos...) ││
│ └─────────────────────────────────────────────────────────────┘│
│ │
│ ┌─────────────────────────────────────────────────────────────┐│
│ │ 🎉 ¿Es una ocasión especial? ││
│ │ ││
│ │ ○ No ││
│ │ ● Sí: [Cumpleaños de María ▼] ││
│ │ ││
│ │ ☑️ Queremos tarta de cumpleaños (+25€) ││
│ │ ☐ Solo un detalle sorpresa (incluido) ││
│ └─────────────────────────────────────────────────────────────┘│
│ │
│ Total adicional: 70€ │
│ [Guardar preferencias] │
│ │
│ ⚠️ Recuerda completar esto al menos 48h antes de tu reserva │
│ │
└─────────────────────────────────────────────────────────────────┘
Workflow de pre-order:
name: "Recordatorio Pre-Order"
trigger:
type: "schedule"
cron: "0 10 * * *"
nodes:
- id: "find_pending"
type: "data_query"
config:
table: "restaurant_reservations"
filter:
date: "{{today() + 3 days}}"
menu_id: { $exists: true } # Tiene menú que requiere pre-order
menu_selections: { $eq: null } # No ha completado
- id: "send_reminders"
type: "loop"
items: "{{find_pending.rows}}"
- id: "personalized_message"
type: "ai_agent"
config:
profile: "restaurant_marketing"
prompt: |
Genera un recordatorio amable para {{customer.name}}.
Tiene reserva el {{format_date(reservation.date)}} para {{party_size}} personas.
Menú: {{menu.name}}
Debe completar sus preferencias culinarias.
Incluye el link: {{preorder_link}}
- id: "send_whatsapp"
type: "action"
action: "whatsapp_send"
to: "{{item.customer_phone}}"
message: "{{personalized_message.text}}"
10.6 Wine Pairing AI & Sommelier Virtual
┌─────────────────────────────────────────────────────────────────┐
│ 🍷 SOMMELIER VIRTUAL │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Basándome en tu selección de menú y preferencias, │
│ te recomiendo: │
│ │
│ ┌─────────────────────────────────────────────────────────────┐│
│ │ 🥇 RECOMENDACIÓN PRINCIPAL ││
│ │ ││
│ │ Pago de Carraovejas Crianza 2019 ││
│ │ Ribera del Duero · 14% · 92 pts Parker ││
│ │ ││
│ │ "Perfecto para tu carrillera al vino tinto. ││
│ │ Notas de frutos negros y especias que ││
│ │ complementarán la intensidad del plato." ││
│ │ ││
│ │ Botella: 42€ │ Copa: 9€ ││
│ │ ││
│ │ [Añadir a mi reserva] ││
│ └─────────────────────────────────────────────────────────────┘│
│ │
│ ┌─────────────────────────────────────────────────────────────┐│
│ │ 🥈 ALTERNATIVA PREMIUM ││
│ │ ││
│ │ Vega Sicilia Valbuena 5º Año 2018 ││
│ │ Ribera del Duero · 14.5% · 96 pts Parker ││
│ │ ││
│ │ "Para una ocasión muy especial. Elegancia pura." ││
│ │ ││
│ │ Botella: 145€ ││
│ └─────────────────────────────────────────────────────────────┘│
│ │
│ ┌─────────────────────────────────────────────────────────────┐│
│ │ 🍇 OPCIÓN ECOLÓGICA ││
│ │ ││
│ │ Envínate Lousas 2020 ││
│ │ Ribeira Sacra · Orgánico · 13% ││
│ │ ││
│ │ Botella: 28€ │ Copa: 7€ ││
│ └─────────────────────────────────────────────────────────────┘│
│ │
│ [💬 Chatear con el sommelier] [Ver toda la carta de vinos] │
│ │
└─────────────────────────────────────────────────────────────────┘
10.7 Sistema de Fidelización y Niveles VIP
Inspirado en programas como American Express Centurion o los sistemas de puntos de grupos como Nobu:
┌─────────────────────────────────────────────────────────────────┐
│ ⭐ PROGRAMA DE FIDELIZACIÓN │
├─────────────────────────────────────────────────────────────────┤
│ │
│ NIVELES: │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ 🥉 BRONCE (0-499€) │ │
│ │ • Recordatorios de fechas especiales │ │
│ │ • Newsletter con novedades │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ 🥈 PLATA (500-1,499€) │ │
│ │ • Todo lo anterior + │ │
│ │ • Copa de cava de bienvenida │ │
│ │ • Acceso prioritario a reservas │ │
│ │ • 5% descuento en carta de vinos │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ 🥇 ORO (1,500-4,999€) │ │
│ │ • Todo lo anterior + │ │
│ │ • Mesa preferente garantizada │ │
│ │ • Petit four sorpresa del chef │ │
│ │ • Invitación a eventos exclusivos │ │
│ │ • 10% descuento en menús degustación │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ 💎 DIAMANTE (5,000€+) │ │
│ │ • Todo lo anterior + │ │
│ │ • Línea directa con el chef │ │
│ │ • Cena privada anual con maridaje (2 pax) │ │
│ │ • Menú personalizado cuando quieras │ │
│ │ • Parking gratuito │ │
│ │ • Acceso Chef's Table sin lista de espera │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ PUNTOS: │
│ • 1€ gastado = 1 punto │
│ • 100 puntos = 5€ de descuento │
│ • Puntos extra en cumpleaños (x2) y aniversario (x2) │
│ │
└─────────────────────────────────────────────────────────────────┘
10.8 Chef's Table / Experiencias Especiales
┌─────────────────────────────────────────────────────────────────┐
│ 🎩 EXPERIENCIAS EXCLUSIVAS │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────┐│
│ │ 👨🍳 CHEF'S TABLE ││
│ │ ││
│ │ [Imagen del Chef's Table] ││
│ │ ││
│ │ Vive la cocina desde dentro. 4 comensales máximo. ││
│ │ El chef Carlos prepara un menú único mientras ││
│ │ explica cada plato y técnica. ││
│ │ ││
│ │ 📅 Solo viernes y sábados, 21:00 ││
│ │ 👥 2-4 personas ││
│ │ 💰 195€/persona (todo incluido) ││
│ │ ⏱️ ~3 horas ││
│ │ ││
│ │ Próximas fechas disponibles: ││
│ │ [11 Ene] [18 Ene] [25 Ene] [Ver más] ││
│ │ ││
│ │ [Reservar Chef's Table] ││
│ └─────────────────────────────────────────────────────────────┘│
│ │
│ ┌─────────────────────────────────────────────────────────────┐│
│ │ 🍷 CATA DE VINOS CON ENÓLOGO ││
│ │ ││
│ │ Cada primer jueves de mes. 6 vinos + tapas maridaje. ││
│ │ Con nuestro sommelier invitado. ││
│ │ ││
│ │ 👥 8-12 personas ││
│ │ 💰 65€/persona ││
│ │ 📅 Próxima: 2 Enero - "Vinos de autor del Priorat" ││
│ │ ││
│ │ [Reservar plaza] ││
│ └─────────────────────────────────────────────────────────────┘│
│ │
│ ┌─────────────────────────────────────────────────────────────┐│
│ │ 🎓 CLASES DE COCINA ││
│ │ ││
│ │ Aprende los secretos de nuestra cocina mediterránea. ││
│ │ Incluye clase + almuerzo con lo preparado + recetas. ││
│ │ ││
│ │ 👥 6-10 personas ││
│ │ 💰 85€/persona ││
│ │ 📅 Sábados 11:00 ││
│ │ ││
│ │ [Ver calendario de clases] ││
│ └─────────────────────────────────────────────────────────────┘│
│ │
└─────────────────────────────────────────────────────────────────┘
10.9 Dynamic Pricing (Precios por Demanda)
Como los hoteles y aerolíneas, algunos restaurantes top ajustan precios:
// Configuración de pricing dinámico
interface DynamicPricingConfig {
enabled: boolean;
// Modificadores por día/hora
modifiers: {
// Descuento en días de baja demanda
monday_lunch: -15, // -15%
tuesday_lunch: -15,
wednesday_lunch: -10,
// Precio normal
thursday_dinner: 0,
// Premium en alta demanda
friday_dinner: +10, // +10%
saturday_dinner: +15,
// Super premium
valentines: +25,
new_years_eve: +50,
};
// Basado en ocupación
occupancy_pricing: {
below_30: -10, // Casi vacío: descuento
30_to_70: 0, // Normal
above_70: +5, // Alta demanda: premium
above_90: +15, // Casi lleno: premium alto
};
// Reserva anticipada
early_booking: {
30_days_ahead: -10, // Reserva con 30+ días: descuento
14_days_ahead: -5,
7_days_ahead: 0,
same_day: +10, // Última hora: premium
};
}
Vista para el cliente:
┌─────────────────────────────────────────────────────────────────┐
│ 📅 Selecciona fecha │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Diciembre 2025 │
│ Lu Ma Mi Ju Vi Sá Do │
│ │
│ 22 23 24 25 26 27 28 │
│ 🟢 🟢 ⚫ ⚫ 🟢 🟡 🟡 │
│ -15% -15% -- -- -- +10% +10% │
│ │
│ 29 30 31 1 2 3 4 │
│ 🟢 🟢 🔴 🟢 🟢 🟢 🟢 │
│ -15% -15% +50% -- -10% -10% -- │
│ │
│ ───────────────────────────────────────────────────────────── │
│ 🟢 Disponible 🟡 Últimas mesas 🔴 Evento especial ⚫ Cerrado│
│ Precios mostrados sobre el Menú Degustación (95€ base) │
│ │
└─────────────────────────────────────────────────────────────────┘
10.10 Integración con Reviews (Google, TripAdvisor)
Gestión de reputación con IA:
name: "Responder Reviews Automáticamente"
trigger:
type: "webhook"
source: "google_business"
event: "new_review"
nodes:
- id: "analyze_review"
type: "ai_agent"
config:
profile: "restaurant_pr"
prompt: |
Analiza esta reseña:
Rating: {{review.rating}}/5
Texto: {{review.text}}
Determina:
1. Sentimiento (positivo/neutro/negativo)
2. Temas mencionados (comida, servicio, ambiente, precio)
3. ¿Requiere respuesta urgente?
4. Borrador de respuesta apropiada
- id: "route_response"
type: "condition"
conditions:
- if: "{{analyze_review.sentiment == 'negative' && review.rating <= 2}}"
goto: "alert_manager"
- if: "{{analyze_review.sentiment == 'positive'}}"
goto: "auto_respond"
- else:
goto: "queue_for_review"
- id: "auto_respond"
type: "action"
config:
action: "google_review_reply"
review_id: "{{review.id}}"
response: "{{analyze_review.draft_response}}"
# Solo si está configurado en auto-reply
require_approval: "{{config.require_review_approval}}"
10.11 Kitchen Display System (KDS) Integration
Conexión directa con la cocina:
┌─────────────────────────────────────────────────────────────────┐
│ 👨🍳 COCINA - COMANDAS EN TIEMPO REAL │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ │
│ │ MESA 7 - 14:23 │ │ MESA 3 - 14:31 │ │ MESA 12 - 14:35 │ │
│ │ ⏱️ 12 min │ │ ⏱️ 4 min │ │ 🆕 NUEVA │ │
│ │ ═══════════════ │ │ ═══════════════ │ │ ═══════════════ │ │
│ │ │ │ │ │ │ │
│ │ ✓ Jamón ibérico │ │ □ Croquetas x6 │ │ □ Ensalada x2 │ │
│ │ ✓ Pan tomate x2 │ │ □ Pulpo brasa │ │ □ Gazpacho │ │
│ │ ▶ Carrillera x2 │ │ │ │ │ │
│ │ ▶ Lubina │ │ │ │ ─────────────── │ │
│ │ │ │ ⚠️ SIN GLUTEN │ │ 6 personas │ │
│ │ ─────────────── │ │ │ │ VIP ⭐ │ │
│ │ ⚠️ Sin lactosa │ │ │ │ │ │
│ │ │ │ │ │ │ │
│ │ [✓ Listo] │ │ [✓ Listo] │ │ [▶ Empezar] │ │
│ └──────────────────┘ └──────────────────┘ └──────────────────┘ │
│ │
│ 📊 ESTADÍSTICAS │
│ Tiempo medio: 18 min │ Platos en cola: 12 │ Completados: 34 │
│ │
└─────────────────────────────────────────────────────────────────┘
10.12 Resumen de Funcionalidades Premium
| Funcionalidad | Inspiración | Valor Añadido |
|---|---|---|
| Guest Experience Management | Eleven Madison Park | Cada cliente se siente único |
| Prepago/Depósitos | Tock, Resy | Elimina no-shows, garantiza ingresos |
| Lista de espera inteligente | Din Tai Fung | Predicción precisa, mejor UX |
| Mapa de mesas visual | Per Se | Control total en tiempo real |
| Pre-order del menú | Sukiyabashi Jiro | Ingredientes perfectos |
| Sommelier Virtual | AI | Aumenta venta de vinos |
| Programa VIP | Amex Centurion | Fidelización premium |
| Experiencias especiales | Noma | Revenue adicional, exclusividad |
| Dynamic Pricing | Hoteles | Optimiza ocupación e ingresos |
| Gestión de reviews | - | Reputación automatizada |
| KDS Integration | - | Cocina más eficiente |
11. Próximos Pasos
- Revisar y aprobar este documento de estudio
- Definir prioridades: ¿Empezar por el storefront público o por el admin?
- Crear proyecto:
storefronts/restaurant-template/ - Definir endpoints que Cadences necesita exponer
- Diseño visual: ¿Colores, estilo, referencias?
📎 Anexos
A.1 Iconos de Alérgenos
🌾 Gluten 🥜 Frutos secos 🥛 Lácteos
🥚 Huevo 🐟 Pescado 🦐 Crustáceos
🫘 Soja 🥬 Apio 🌿 Mostaza
🫛 Sésamo 🍷 Sulfitos 🐚 Moluscos
🥜 Cacahuetes 🌻 Altramuz
A.2 Estados de Reserva
| Estado | Color | Descripción |
|---|---|---|
pending |
🟡 Amarillo | Esperando confirmación |
confirmed |
🟢 Verde | Confirmada |
seated |
🔵 Azul | Cliente sentado |
completed |
⚫ Gris | Finalizada |
cancelled |
🔴 Rojo | Cancelada |
no_show |
⚫ Negro | No se presentó |
Documento preparado para revisión - Cadences Development Team