Nombre provisional del proyecto. El naming definitivo será definido más adelante por el equipo de Marketing.
Plangune Euskadi es una web/app responsive, mobile-first y SPA orientada a familias jóvenes con bebés y niños pequeños que buscan planes, lugares, actividades y negocios familiares en Euskadi de forma sencilla, segura y sin complicaciones.
El objetivo principal es ayudar a las familias a responder preguntas prácticas antes de salir de casa:
- ¿Es adecuado para la edad de mi peque?
- ¿Puedo ir con carrito?
- ¿Hay baño o cambiador?
- ¿Es un plan a cubierto si llueve?
- ¿Es tranquilo y cómodo?
- ¿Qué opinan otras familias?
- ¿Hay ofertas o actividades familiares cerca?
Este proyecto se desarrolla dentro del Desafío de Tripulaciones 2026.
Proyecto en desarrollo del MVP. Producto y documentación inicial:
- Brief oficial del desafío.
- Investigación inicial de producto y competencia.
- Mocks UX/UI.
- Dosier preliminar de objetivos.
- Carpeta Drive organizada por verticales.
- Plan inicial de arquitectura Full Stack.
El backend Express expone la API REST pública bajo /api. El frontend consume siempre
Express; no llama directamente a servicios internos como la API Flask de Data.
Estado actual:
/api/events,/api/recommendationsy/api/favoritesusan datos reales vía PostgreSQL/Prisma.- Login real mínimo con roles (
family,business,admin) en/api/auth. - La sesión usa cookie
httpOnly; el frontend no guarda JWT enlocalStorage. /api/favoritesrequiere usuario autenticado con rolfamily.- Auth endurecida para despliegue: CORS cerrado por
CLIENT_URL, rate limit en login/registro,JWT_SECRETfuerte, JWTHS256explícito y seed bloqueado en producción. /api/recommendationsusa Data Flask como recomendador principal cuandoDATA_RECOMMENDER_ENABLED=true.- Si Data está deshabilitada, falla o agota timeout, Express usa el recomendador local Prisma/PostgreSQL como fallback.
- Existe un servicio local opcional
ai-service/para demo del asistente LLM con Ollama. Corre enhttp://localhost:5001y Express lo consume desdePOST /api/assistant/family-plan; el frontend no llama a Flask. - Si
LLM_ASSISTANT_ENABLED=falseo elai-servicefalla, Express mantiene el fallback local sin IA. - El campo actual para planes interiores/a cubierto es
events.es_interior. - Existe una migración incremental segura para renombrar
es_lluviaaes_interior. - Tests backend actuales: 16 suites · 158/158 verdes.
- PostgreSQL local usa
localhost:5434desde el host para evitar conflictos con otros proyectos en5432; dentro de Docker el backend sigue usandopostgres:5432. - Existe un importador CSV seguro (
backend/prisma/import-events-from-csv.js) para ampliar eventos con datos de Data bajo validación explícita. Ver docs/database.md.
Endpoints actuales:
GET /api/healthPOST /api/auth/register·POST /api/auth/login·GET /api/auth/me·POST /api/auth/logoutGET /api/activities·GET /api/activities/:id(soloapproved)GET /api/recommendations(hasta 3 planes con Family Score reglado y explicable)POST /api/assistant/family-plan(LLM local opcional con fallback sin IA)POST /api/reviews·POST /api/incidentsGET/POST/DELETE /api/favorites(requiere rolfamily)
Detalle de la feature auth/roles: docs/features/auth-roles-minimum.md · memoria de cierre: docs/memoria/auth-roles-minimum-cierre.md.
Variables Data (backend/.env.example):
DATA_RECOMMENDER_ENABLED=false
DATA_API_URL=http://localhost:5000
DATA_API_TIMEOUT_MS=2000Para activar Data en local:
- Windows/Linux:
DATA_API_URL=http://localhost:5000. - Mac: usar
DATA_API_URL=http://localhost:5050si AirPlay/Control Center ocupa el puerto5000.
Data vive en el repo externo Desafio-Data. Express sigue siendo la única fachada pública para
frontend; el frontend nunca llama directamente a Data.
Variables LLM local (backend/.env.example):
LLM_ASSISTANT_ENABLED=false
LLM_ASSISTANT_API_URL=http://localhost:5001
LLM_ASSISTANT_TIMEOUT_MS=8000
LLM_ASSISTANT_CONTRACT=get-questionCon LLM_ASSISTANT_CONTRACT=get-question, Express consume el chatbot Data por contrato GET /<pregunta> y mantiene fallback local si Data falla. Documentación completa:
docs/integration-ai-ollama-local.md.
Arranque y tests (monorepo npm workspaces):
npm install
npm run dev:backend # API en http://localhost:3000
npm run prisma:generate --workspace backend
npm run prisma:migrate --workspace backend
npm run db:seed --workspace backend
npm run test:backend # tests con Vitest + SupertestContrato detallado en docs/api.md · base de datos y seed en docs/database.md · seguridad en docs/security.md · calidad y cobertura en docs/quality/.
El frontend incluye GUNI, el asistente familiar conversacional, como playground visual aislado
en la ruta de desarrollo /dev/family-chat.
- Consume el backend real en
POST /api/assistant/family-planvíaVITE_API_URL. El frontend ya usa backend real para auth/favoritos; varias pantallas de negocio/admin siguen usando stores mock hasta sus features específicas. - La ruta solo se registra en desarrollo (
import.meta.env.DEV); en el build de producción se elimina y no es accesible. - Cumple el contrato Frontend ↔ Backend:
distingue
mode:"ai"(assistantMessageMarkdown) ymode:"fallback"(message+recommendations), degrada con un mensaje amable si la IA falla y no exponesource/modeen crudo al usuario. - Mobile-first, CSS propio namespaced (
.fcp), accesible. Tests: 6/6 verdes.
Detalle completo en docs/features/frontend-family-chat-playground-guni.md.
npm run dev:backend # API en http://localhost:3000
npm run dev --workspace frontend # http://localhost:5173/dev/family-chatFamilias jóvenes, locales o visitantes, con bebés o niños pequeños, que buscan planes cómodos, seguros y bien planificados en Euskadi.
Especialmente familias que valoran:
- evitar improvisaciones;
- saber si un sitio es cómodo con carrito;
- filtrar por edad, clima, precio, zona y duración;
- consultar reseñas útiles de otras familias;
- encontrar lugares y actividades familiares sin perder tiempo.
Planes familiares en Euskadi filtrados por edad, comodidad, ubicación y necesidades reales de las familias.
La aplicación no pretende ser otro portal genérico de ocio, sino una herramienta práctica para decidir rápido qué hacer con peques.
Usuario final de la aplicación.
Funcionalidades previstas:
- registrarse e iniciar sesión;
- crear perfil familiar básico;
- buscar planes;
- filtrar por edad, zona, precio, interior/exterior, carrito, cambiador y duración;
- consultar detalle de planes;
- guardar favoritos;
- dejar reseñas;
- reportar incidencias.
Comercios, entidades o profesionales que quieran publicar actividades, eventos, planes u ofertas familiares.
Funcionalidades previstas:
- registrarse como negocio;
- crear perfil de negocio;
- publicar actividades;
- crear ofertas o promociones;
- consultar estado de publicaciones;
- ver reseñas recibidas.
Rol interno encargado de moderar y controlar la calidad del contenido.
Funcionalidades previstas:
- aprobar o rechazar actividades;
- moderar reseñas e incidencias;
- gestionar usuarios y negocios;
- revisar métricas básicas;
- mantener la calidad de los datos.
El MVP se centra en construir una demo funcional, clara y defendible.
- Landing inicial.
- Login y registro.
- Selección de rol: familia o negocio.
- Rol admin interno.
- Perfil familiar básico.
- Buscador de planes.
- Filtros familiares.
- Resultados en listado.
- Detalle de plan.
- Reseñas e incidencias.
- Alta de actividad por negocio.
- Alta de oferta por negocio.
- Panel admin para aprobar/rechazar contenido.
- Sistema de recomendación mediante Family Score reglado y explicable.
- Favoritos.
- Vista mapa simple.
- Métricas básicas de negocio.
- Métricas básicas de admin.
- Moderación básica de reseñas.
- Sellos familiares propios.
- Recomendación por clima.
- Itinerarios familiares.
- Multiidioma.
- QR para negocios.
- Notificaciones.
- Pagos reales.
- Reservas reales.
- Chat en tiempo real.
- Red social completa.
- App móvil nativa.
- Marketplace complejo.
- IA generativa compleja.
- Machine Learning avanzado.
La aplicación podrá usar sellos propios para ayudar a las familias a decidir rápido:
- Carrito Friendly
- Plan a Cubierto
- Baño / Cambiador
- Ambiente Tranquilo
- Familia Verificada
Estos sellos deberán ser asignados o validados por el equipo admin, por datos de negocio o por reseñas verificadas.
El recomendador inicial será un sistema de scoring simple y explicable.
No se plantea un modelo de Machine Learning complejo en la primera versión.
Ejemplo de variables para calcular el Family Score:
- edad recomendada;
- distancia o zona;
- interior/exterior;
- plan a cubierto;
- accesibilidad con carrito;
- baño o cambiador;
- duración;
- precio;
- valoración media;
- incidencias recientes;
- popularidad;
- fiabilidad del dato.
Ejemplo de explicación al usuario:
Te recomendamos este plan porque es a cubierto, apto para carrito, adecuado para 0-3 años y tiene buenas valoraciones recientes.
- React
- Vite
- React Router
- CSS modular o sistema de estilos acordado por el equipo
- Diseño mobile-first
- Node.js
- Express
- Arquitectura MVC práctica
- API REST
- Middlewares de autenticación y roles
- PostgreSQL
- Prisma (ORM)
Decisión de equipo: de momento NO se usa MongoDB. La base de datos actual es PostgreSQL con Prisma.
- GitHub
- Git Flow simplificado
- Postman / Insomnia para pruebas de API
- Figma, Excalidraw o recursos UX/UI compartidos en Drive
plangune-euskadi/
├── frontend/
├── backend/
├── docs/
├── README.md
├── .gitignore
└── package.jsonbackend/
├── src/
│ ├── config/
│ ├── controllers/
│ ├── middlewares/
│ ├── models/
│ ├── routes/
│ ├── seed/
│ ├── services/
│ ├── utils/
│ └── app.js
├── server.js
├── .env.example
└── package.jsonfrontend/
├── src/
│ ├── assets/
│ ├── components/
│ ├── context/
│ ├── hooks/
│ ├── pages/
│ ├── routes/
│ ├── services/
│ ├── styles/
│ ├── App.jsx
│ └── main.jsx
├── .env.example
└── package.jsonRepresenta a cualquier usuario autenticado.
Campos mínimos:
- name
- passwordHash
- role: family | business | admin
- status
- createdAt
- updatedAt
Perfil familiar asociado a un usuario familia.
Campos mínimos:
- userId
- city
- childrenAgeRanges
- strollerNeeded
- preferredIndoor
- preferredBudget
- favoriteCategories
Nota: no se deben guardar nombres reales, fotos ni datos sensibles de menores.
Perfil de negocio o entidad.
Campos mínimos:
- ownerId
- name
- description
- category
- city
- address
- contactEmail
- status
Plan, lugar, evento o actividad familiar.
Campos mínimos:
- businessId
- title
- description
- category
- city
- address
- location
- ageMin
- ageMax
- indoorOutdoor
- isCovered
- strollerFriendly
- hasChangingTable
- hasBathroom
- calmEnvironment
- priceType
- durationMinutes
- images
- status
- familyScore
- source
Reseña de una familia sobre una actividad.
Campos mínimos:
- userId
- activityId
- rating
- comment
- tags
- status
Incidencia reportada por una familia.
Campos mínimos:
- userId
- activityId
- type
- description
- status
Actividad guardada por una familia.
Campos mínimos:
- userId
- activityId
Oferta o promoción creada por un negocio.
Campos mínimos:
- businessId
- activityId
- title
- description
- conditions
- validFrom
- validTo
- status
- sponsoredTier
Registro opcional para explicar recomendaciones.
Campos mínimos:
- userId
- activityId
- score
- reasons
- createdAt
POST /api/auth/register
POST /api/auth/login
GET /api/auth/me
POST /api/auth/logoutGET /api/family/profile
POST /api/family/profile
PUT /api/family/profileGET /api/activities
GET /api/activities/:id
POST /api/activities
PUT /api/activities/:id
DELETE /api/activities/:idGET /api/activities/:id/reviews
POST /api/activities/:id/reviews
PUT /api/reviews/:id
DELETE /api/reviews/:idPOST /api/activities/:id/incidents
GET /api/admin/incidents
PATCH /api/admin/incidents/:id/statusGET /api/favorites
POST /api/favorites/:activityId
DELETE /api/favorites/:activityIdGET /api/business/me
POST /api/business/profile
PUT /api/business/profile
GET /api/business/activities
GET /api/business/offersPOST /api/offers
GET /api/offers
GET /api/offers/:id
PUT /api/offers/:id
DELETE /api/offers/:idGET /api/admin/dashboard
GET /api/admin/activities/pending
PATCH /api/admin/activities/:id/approve
PATCH /api/admin/activities/:id/reject
GET /api/admin/reviews
PATCH /api/admin/reviews/:id/status
GET /api/admin/usersGET /api/recommendations
GET /api/recommendations/activity/:id/explanation/
/login
/register
/home
/search
/activities
/activities/:id
/family-profile
/favorites
/business
/business/activities/new
/business/offers/new
/business/offers
/admin
/admin/activities
/admin/reviews
/admin/incidentsLanding
→ Registro/Login
→ Perfil familiar
→ Buscar planes
→ Aplicar filtros
→ Ver resultados
→ Ver detalle
→ Guardar favorito o dejar reseña/incidenciaLogin como negocio
→ Panel negocio
→ Crear actividad
→ Crear oferta
→ Ver estado pendiente/aprobadoLogin admin
→ Panel admin
→ Revisar actividad pendiente
→ Aprobar o rechazar
→ Moderar reseñas/incidenciasPendiente de cerrar durante el bootstrap técnico.
Estructura esperada:
git clone <url-del-repositorio>
cd plangune-euskadiInstalar frontend:
cd frontend
npm install
npm run devInstalar backend:
cd backend
npm install
npm run devCrear archivo:
backend/.envBasado en:
backend/.env.exampleVariables previstas:
PORT=3000
NODE_ENV=development
DATABASE_URL=postgresql://desafio26:desafio26_dev_password@localhost:5434/desafio26_dev?schema=public
JWT_SECRET=change_me
JWT_EXPIRES_IN=7d
CLIENT_URL=http://localhost:5173Crear archivo:
frontend/.envBasado en:
frontend/.env.exampleVariables previstas:
VITE_API_URL=http://localhost:3000/api{
"dev": "nodemon server.js",
"start": "node server.js",
"seed": "node src/seed/index.js"
}{
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
}Ramas principales:
main → rama estable/final
dev → rama de integración
feat/* → nuevas funcionalidades
fix/* → correcciones
docs/* → documentación
test/* → pruebasReglas:
- No trabajar directamente sobre
main. - Crear ramas desde
dev. - Pull Requests siempre hacia
dev. - Commits pequeños y descriptivos.
- No subir secretos.
- No usar
git add .sin revisar antes. - Probar antes de abrir PR.
Ejemplos de ramas iniciales:
feat/project-bootstrap
feat/frontend-shell
feat/backend-api-base
feat/auth-roles
feat/family-search
feat/activity-detail
feat/business-admin-flow
docs/project-documentationEjemplos de commits:
chore: initialize project structure
feat: add backend healthcheck
feat: add frontend shell layout
docs: add initial README
feat: add auth routes
feat: add activity modelLa carpeta /docs deberá contener progresivamente:
docs/
├── architecture.md
├── api.md
├── data-model.md
├── security.md
├── family-score.md
├── git-workflow.md
├── presentation-script.md
└── decisions.mdDespliegue en VPS (IONOS) detrás de Nginx Proxy Manager, con solo 80/443 públicos. El backend Express es la única fachada /api; PostgreSQL y servicios internos no se exponen a Internet.
- Guía de despliegue: docs/deployment/vps-demo-deploy.md
- Checklist de seguridad pre-deploy: docs/security/predeploy-checklist.md
- Plantilla compose de producción: compose.prod.yaml
No ejecutar deploy sin completar el checklist. Los secretos reales (JWT, DB, API keys) viven fuera del repo (env del host / Docker), nunca versionados.
Medidas mínimas previstas:
- Autenticación con JWT.
- Hash de contraseñas.
- Roles y permisos.
- Validación de inputs.
- Protección de rutas admin.
- Moderación de reseñas e incidencias.
- No almacenar datos sensibles de menores.
- No subir
.envni secretos al repositorio. - Sanitización de contenido generado por usuarios.
- Rate limiting si da tiempo.
El MVP será válido si permite demostrar:
- Una familia puede registrarse, buscar planes y ver detalles.
- La búsqueda permite filtros familiares útiles.
- Una actividad muestra información práctica: edad, precio, duración, carrito, cambiador, interior/exterior y valoración.
- Un negocio puede crear una actividad u oferta.
- Un admin puede aprobar o rechazar contenido.
- Existe un Family Score explicable.
- La app es responsive y usable en móvil.
- La solución está documentada.
- Cada vertical puede explicar su aportación.
Proyecto multidisciplinar con participación de:
- Full Stack
- Data Science
- Ciberseguridad
- Marketing Digital
- UX/UI
- El nombre Plangune Euskadi es provisional.
- Los mocks UX/UI son referencia visual, no código final obligatorio.
- Los archivos
code.htmlde los mocks no deben condicionar la arquitectura final. - Prioridad absoluta: MVP funcional antes que funcionalidades avanzadas.
- KISS: primero que funcione, luego se mejora.
- La demo final manda sobre las ideas secundarias.