[{"data":1,"prerenderedAt":828},["ShallowReactive",2],{"sideproject-es-menuescolar-gestor-menus-escolars":3},{"id":4,"title":5,"body":6,"date":790,"description":791,"extension":792,"featured":793,"image":794,"locale":795,"meta":796,"navigation":815,"order":751,"path":816,"repo":817,"seo":818,"status":819,"stem":820,"technologies":821,"url":817,"__hash__":827},"sideprojects\u002Fes\u002Fsideprojects\u002Fmenuescolar-gestor-menus-escolars.md","menuescolar.es - Gestor de Menús Escolares (2025)",{"type":7,"value":8,"toc":749},"minimark",[9,13,17,22,34,41,48,52,57,102,106,132,136,140,143,158,162,167,181,186,200,205,219,223,226,243,247,251,254,258,261,272,276,279,290,294,297,308,312,315,335,338,342,346,366,370,393,397,411,415,419,445,449,472,476,505,509,513,564,568,606,610,613,651,655,664,668,700,704,715,718,730,736,738,744],[10,11,5],"h1",{"id":12},"menuescolares-gestor-de-menús-escolares-2025",[14,15,16],"p",{},"Una plataforma SaaS para transformar PDFs de menús escolares en calendarios digitales accesibles",[18,19,21],"h2",{"id":20},"el-proyecto","El Proyecto",[14,23,24,28,29,32],{},[25,26,27],"strong",{},"menuescolar.es"," es una aplicación web que resuelve un problema real: muchas escuelas publican los menús mensuales en formato PDF, lo que dificulta la consulta rápida por parte de las familias.",[30,31],"br",{},[30,33],{},[14,35,36,37,39],{},"La solución: una plataforma donde cada escuela puede tener su propia página pública con el menú del día siempre visible y actualizado.",[30,38],{},[30,40],{},[14,42,43,44,46],{},"El proceso es sencillo: la escuela sube un PDF del menú mensual, la Inteligencia Artificial (Google Gemini) extrae automáticamente las comidas de cada día, y en segundos queda publicado en formato web accesible desde cualquier dispositivo.",[30,45],{},[30,47],{},[18,49,51],{"id":50},"cómo-funciona","Cómo Funciona",[53,54,56],"h3",{"id":55},"para-las-escuelas-gestores","Para las Escuelas (Gestores)",[58,59,60,72,78,84,90,96],"ol",{},[61,62,63,66,67,71],"li",{},[25,64,65],{},"Registro"," → Creación de cuenta y organización con URL única (ejemplo: ",[68,69,70],"code",{},"menuescolar.es\u002Fescola-abellerol",")",[61,73,74,77],{},[25,75,76],{},"Configuración"," → Personalización con logo, descripción y preferencias de idioma",[61,79,80,83],{},[25,81,82],{},"Subida de PDF"," → Upload del calendario mensual en formato PDF",[61,85,86,89],{},[25,87,88],{},"Procesamiento IA"," → Google Gemini analiza el documento y extrae las comidas estructuradas",[61,91,92,95],{},[25,93,94],{},"Publicación"," → Los menús quedan disponibles automáticamente en la URL pública",[61,97,98,101],{},[25,99,100],{},"Compartir"," → Envío del enlace a familias y personal del centro",[53,103,105],{"id":104},"para-las-familias-visitantes","Para las Familias (Visitantes)",[58,107,108,114,120,126],{},[61,109,110,113],{},[25,111,112],{},"Acceso directo"," → Visita la URL de la escuela (sin necesidad de registro)",[61,115,116,119],{},[25,117,118],{},"Visualización"," → Consulta el menú de hoy o navega por el calendario mensual",[61,121,122,125],{},[25,123,124],{},"Multi-dispositivo"," → Funciona perfectamente en móvil, tablet y ordenador",[61,127,128,131],{},[25,129,130],{},"Multi-idioma"," → Disponible en catalán, castellano, inglés, vasco y gallego",[18,133,135],{"id":134},"arquitectura-técnica","Arquitectura Técnica",[53,137,139],{"id":138},"sistema-multi-tenant","Sistema Multi-Tenant",[14,141,142],{},"Cada escuela tiene sus datos completamente aislados:",[144,145,146,149,152,155],"ul",{},[61,147,148],{},"Una base de datos con relaciones estrictas organización → comidas",[61,150,151],{},"Un solo usuario-propietario por organización (modelo 1:1)",[61,153,154],{},"URLs únicas generadas automáticamente (slugs)",[61,156,157],{},"Logos y configuraciones personalizadas para cada centro",[53,159,161],{"id":160},"seguridad-y-rendimiento","Seguridad y Rendimiento",[14,163,164],{},[25,165,166],{},"Autenticación y Autorización:",[144,168,169,172,175,178],{},[61,170,171],{},"JWT tokens gestionados por PocketBase",[61,173,174],{},"Validación de propiedad en cada operación de modificación",[61,176,177],{},"API keys de IA mantenidas en servidor (nunca expuestas al cliente)",[61,179,180],{},"Headers de seguridad con Helmet (CSP, X-Frame-Options, etc.)",[14,182,183],{},[25,184,185],{},"Protección de Abuso:",[144,187,188,191,194,197],{},[61,189,190],{},"Rate limiting: 10 peticiones generales cada 15 minutos",[61,192,193],{},"Rate limiting PDFs: máximo 5 subidas por hora",[61,195,196],{},"CORS configurado con origins permitidos",[61,198,199],{},"Validación de formatos y tamaño de ficheros",[14,201,202],{},[25,203,204],{},"Optimizaciones:",[144,206,207,210,213,216],{},[61,208,209],{},"Build multi-stage con Docker (imagen ligera)",[61,211,212],{},"Cache de estáticos con hash de Vite (1 año)",[61,214,215],{},"Compilación TypeScript para detección de errores en tiempo de desarrollo",[61,217,218],{},"Lazy loading de componentes React",[53,220,222],{"id":221},"procesamiento-de-ia","Procesamiento de IA",[14,224,225],{},"El cerebro del sistema es Google Gemini:",[58,227,228,231,234,237,240],{},[61,229,230],{},"El PDF se convierte a Base64 para transporte seguro",[61,232,233],{},"Se envía a Gemini con un prompt específico para extraer menús",[61,235,236],{},"La IA retorna datos estructurados (fecha + comida)",[61,238,239],{},"Se validan y guardan en la base de datos",[61,241,242],{},"Quedan disponibles inmediatamente en la web pública",[18,244,246],{"id":245},"retos-superados","Retos Superados",[53,248,250],{"id":249},"precisión-de-la-extracción","🤖 Precisión de la Extracción",[14,252,253],{},"Los PDFs de menús escolares vienen en formatos muy variados (tablas, texto libre, imágenes). Gemini ha demostrado ser lo suficientemente flexible para adaptarse a diferentes layouts y extraer correctamente los datos.",[53,255,257],{"id":256},"aislamiento-multi-tenant","🏢 Aislamiento Multi-Tenant",[14,259,260],{},"Asegurar que cada escuela solo vea y modifique sus propios datos requirió:",[144,262,263,266,269],{},[61,264,265],{},"Relaciones foráneas estrictas en PocketBase",[61,267,268],{},"Middleware de validación de propiedad en cada endpoint",[61,270,271],{},"Tests exhaustivos de acceso no autorizado",[53,273,275],{"id":274},"sistema-de-correos","📧 Sistema de Correos",[14,277,278],{},"Integración con Resend para enviar:",[144,280,281,284,287],{},[61,282,283],{},"Emails de bienvenida",[61,285,286],{},"Verificación de cuentas",[61,288,289],{},"Notificaciones de nuevos menús (futuro)",[53,291,293],{"id":292},"internacionalización","🌍 Internacionalización",[14,295,296],{},"Soporte para 5 idiomas con archivo de traducciones de 94KB que cubre:",[144,298,299,302,305],{},[61,300,301],{},"Interfaz completa (botones, mensajes, errores)",[61,303,304],{},"Formatos de fecha adaptados a cada lengua",[61,306,307],{},"Configuración por usuario y por organización",[53,309,311],{"id":310},"despliegue-docker","🐳 Despliegue Docker",[14,313,314],{},"Dockerfile multi-stage que:",[58,316,317,323,329],{},[61,318,319,322],{},[25,320,321],{},"Stage 1",": Compila el frontend React con Vite",[61,324,325,328],{},[25,326,327],{},"Stage 2",": Copia el build y prepara el backend Node.js",[61,330,331,334],{},[25,332,333],{},"Resultado",": Una sola imagen que sirve frontend estático + API",[14,336,337],{},"Compatible con Dokploy para despliegues automáticos desde GitHub.",[18,339,341],{"id":340},"stack-tecnológico","Stack Tecnológico",[53,343,345],{"id":344},"frontend","Frontend",[144,347,348,351,354,357,360,363],{},[61,349,350],{},"React 18 con TypeScript",[61,352,353],{},"Vite (build tool ultrarrápido)",[61,355,356],{},"React Router 7 (enrutamiento client-side)",[61,358,359],{},"Tailwind CSS (styling utility-first)",[61,361,362],{},"PocketBase SDK (cliente de autenticación)",[61,364,365],{},"pdfjs-dist (previsualización de PDFs)",[53,367,369],{"id":368},"backend","Backend",[144,371,372,375,378,381,384,387,390],{},[61,373,374],{},"Node.js 22 & Express",[61,376,377],{},"PocketBase (base de datos SQLite + auth)",[61,379,380],{},"Google Gemini API (extracción IA)",[61,382,383],{},"Resend (servicio de emails transaccionales)",[61,385,386],{},"Multer (gestión de subidas de ficheros)",[61,388,389],{},"Helmet (cabeceras de seguridad)",[61,391,392],{},"Rate Limit (protección anti-abuso)",[53,394,396],{"id":395},"infraestructura","Infraestructura",[144,398,399,402,405,408],{},[61,400,401],{},"Docker (contenedorización)",[61,403,404],{},"Dokploy (despliegue automático)",[61,406,407],{},"GitHub (control de versiones)",[61,409,410],{},"PocketBase (hosting separado para auth y datos)",[18,412,414],{"id":413},"características-principales","Características Principales",[53,416,418],{"id":417},"para-gestores-de-escuela","Para Gestores de Escuela",[144,420,421,424,427,430,433,436,439,442],{},[61,422,423],{},"✅ Registro y creación de organización con URL única",[61,425,426],{},"✅ Subida de PDFs de menús mensuales",[61,428,429],{},"✅ Extracción automática de menús con IA",[61,431,432],{},"✅ Personalización con logo y descripción",[61,434,435],{},"✅ Configuración de idioma por defecto",[61,437,438],{},"✅ Validación de email obligatoria",[61,440,441],{},"✅ Gestión completa de menús (edición, eliminación)",[61,443,444],{},"✅ Dashboard privado con estadísticas",[53,446,448],{"id":447},"para-familias","Para Familias",[144,450,451,454,457,460,463,466,469],{},[61,452,453],{},"✅ Acceso público sin registro",[61,455,456],{},"✅ Vista del menú de hoy destacado",[61,458,459],{},"✅ Calendario mensual navegable",[61,461,462],{},"✅ Diseño responsive (móvil, tablet, desktop)",[61,464,465],{},"✅ Cambio de idioma (5 lenguas disponibles)",[61,467,468],{},"✅ Carga rápida y optimizada",[61,470,471],{},"✅ URLs amigables para compartir",[53,473,475],{"id":474},"funcionalidades-del-sistema","Funcionalidades del Sistema",[144,477,478,481,484,487,490,493,496,499,502],{},[61,479,480],{},"✅ Multi-tenant con aislamiento de datos",[61,482,483],{},"✅ Una organización por usuario",[61,485,486],{},"✅ Slugs únicos autogenerados",[61,488,489],{},"✅ Sistema de templates (modern\u002Fclassic\u002Fcolorful)",[61,491,492],{},"✅ Detección de menús duplicados (fecha + organización)",[61,494,495],{},"✅ Validación de formatos PDF",[61,497,498],{},"✅ Gestión de errores con mensajes claros",[61,500,501],{},"✅ Logs de servidor para debugging",[61,503,504],{},"✅ Health check endpoint para monitoring",[18,506,508],{"id":507},"roadmap-y-mejoras-futuras","Roadmap y Mejoras Futuras",[53,510,512],{"id":511},"funcionalidades-planificadas","Funcionalidades Planificadas",[144,514,515,522,528,534,540,546,552,558],{},[61,516,517,518,521],{},"🔄 ",[25,519,520],{},"Múltiples menús por organización",": Menú basal, celíaco, vegetariano, etc.",[61,523,517,524,527],{},[25,525,526],{},"Colaboradores",": Permitir múltiples usuarios por escuela",[61,529,517,530,533],{},[25,531,532],{},"Notificaciones email",": Alertas automáticas de nuevos menús",[61,535,517,536,539],{},[25,537,538],{},"Dominios personalizados",": menudescuela.escolapios.cat",[61,541,517,542,545],{},[25,543,544],{},"API pública",": Integración con otros sistemas escolares",[61,547,517,548,551],{},[25,549,550],{},"App móvil",": Versiones nativas para iOS y Android",[61,553,517,554,557],{},[25,555,556],{},"Exportación",": Descargar menús en diferentes formatos",[61,559,517,560,563],{},[25,561,562],{},"Analytics",": Estadísticas de uso y visualizaciones",[53,565,567],{"id":566},"mejoras-técnicas","Mejoras Técnicas",[144,569,570,576,582,588,594,600],{},[61,571,517,572,575],{},[25,573,574],{},"Tests automatizados",": Cobertura con Jest y React Testing Library",[61,577,517,578,581],{},[25,579,580],{},"CI\u002FCD pipeline",": GitHub Actions para tests y despliegue",[61,583,517,584,587],{},[25,585,586],{},"Monitoring",": Integración con Sentry para tracking de errores",[61,589,517,590,593],{},[25,591,592],{},"Performance",": Lighthouse score 90+ en todas las categorías",[61,595,517,596,599],{},[25,597,598],{},"SEO",": Meta tags dinámicos para cada organización",[61,601,517,602,605],{},[25,603,604],{},"PWA",": Capacidades offline con Service Workers",[18,607,609],{"id":608},"documentación-técnica","Documentación Técnica",[14,611,612],{},"El proyecto incluye documentación exhaustiva:",[144,614,615,621,627,633,639,645],{},[61,616,617,620],{},[25,618,619],{},"README.md"," - Guía de inicio rápido y configuración local",[61,622,623,626],{},[25,624,625],{},"DEPLOYMENT.md"," - Despliegue a producción con Dokploy",[61,628,629,632],{},[25,630,631],{},"MULTI_TENANT_SETUP.md"," - Configuración del esquema multi-tenant",[61,634,635,638],{},[25,636,637],{},"POCKETBASE_SETUP.md"," - Instalación e inicialización de PocketBase",[61,640,641,644],{},[25,642,643],{},"PDF_UPLOAD_IMPLEMENTATION.md"," - Detalles del procesamiento de PDFs",[61,646,647,650],{},[25,648,649],{},"TESTING_CHECKLIST.md"," - Escenarios de testing y casos límite",[18,652,654],{"id":653},"estado-del-proyecto","Estado del Proyecto",[14,656,657,660,661],{},[25,658,659],{},"Estado actual",": ✅ ",[25,662,663],{},"95% completo y listo para testing",[53,665,667],{"id":666},"implementado","Implementado",[144,669,670,673,676,679,682,685,688,691,694,697],{},[61,671,672],{},"✅ Arquitectura multi-tenant funcional",[61,674,675],{},"✅ Sistema de autenticación y autorización",[61,677,678],{},"✅ CRUD completo de organizaciones",[61,680,681],{},"✅ Subida y procesamiento de PDFs con IA",[61,683,684],{},"✅ Interfaz pública para organizaciones",[61,686,687],{},"✅ Internacionalización 5 idiomas",[61,689,690],{},"✅ Despliegue Docker automatizado",[61,692,693],{},"✅ Sistema de emails transaccionales",[61,695,696],{},"✅ Rate limiting y seguridad",[61,698,699],{},"✅ Documentación completa",[53,701,703],{"id":702},"en-pulido","En Pulido",[144,705,706,709,712],{},[61,707,708],{},"🔧 Branding de cabeceras",[61,710,711],{},"🔧 Refinamiento de rutas entre componentes",[61,713,714],{},"🔧 Optimización de rendimiento",[716,717],"hr",{},[14,719,720,723,724],{},[25,721,722],{},"Prueba la plataforma",": ",[725,726,727],"a",{"href":727,"rel":728},"https:\u002F\u002Fmenuescolar.es",[729],"nofollow",[14,731,732,735],{},[25,733,734],{},"GitHub",": Código privado (disponible bajo petición)",[716,737],{},[14,739,740],{},[741,742,743],"em",{},"Fecha de lanzamiento: Noviembre 2025",[14,745,746],{},[741,747,748],{},"Desarrollado por Albert Sarlé",{"title":750,"searchDepth":751,"depth":751,"links":752},"",2,[753,754,759,764,771,776,781,785,786],{"id":20,"depth":751,"text":21},{"id":50,"depth":751,"text":51,"children":755},[756,758],{"id":55,"depth":757,"text":56},3,{"id":104,"depth":757,"text":105},{"id":134,"depth":751,"text":135,"children":760},[761,762,763],{"id":138,"depth":757,"text":139},{"id":160,"depth":757,"text":161},{"id":221,"depth":757,"text":222},{"id":245,"depth":751,"text":246,"children":765},[766,767,768,769,770],{"id":249,"depth":757,"text":250},{"id":256,"depth":757,"text":257},{"id":274,"depth":757,"text":275},{"id":292,"depth":757,"text":293},{"id":310,"depth":757,"text":311},{"id":340,"depth":751,"text":341,"children":772},[773,774,775],{"id":344,"depth":757,"text":345},{"id":368,"depth":757,"text":369},{"id":395,"depth":757,"text":396},{"id":413,"depth":751,"text":414,"children":777},[778,779,780],{"id":417,"depth":757,"text":418},{"id":447,"depth":757,"text":448},{"id":474,"depth":757,"text":475},{"id":507,"depth":751,"text":508,"children":782},[783,784],{"id":511,"depth":757,"text":512},{"id":566,"depth":757,"text":567},{"id":608,"depth":751,"text":609},{"id":653,"depth":751,"text":654,"children":787},[788,789],{"id":666,"depth":757,"text":667},{"id":702,"depth":757,"text":703},"2025-11-20T12:00:00Z","Plataforma SaaS multi-tenant para gestionar calendarios de menús escolares con IA y mostrarlos en una webapp personalizada para cada escuela.","md",false,"https:\u002F\u002Fres.cloudinary.com\u002Fdnyvmvkqi\u002Fimage\u002Fupload\u002Fc_scale,f_auto,q_auto,w_900\u002Fv1763640341\u002Fmenus_escolars_fearrf.png","es",{"slug":797,"type":798,"categories":799,"tags":800,"achievements":809,"liveUrl":727},"menuescolar-gestor-menus-escolars","sideproject",[798],[801,802,803,804,805,806,807,808],"React & TypeScript","Node.js & Express","PocketBase","Google Gemini AI","Multi-tenant SaaS","Docker","Tailwind CSS","Vite",[810,811,812,813,814],"Extracción de menús con IA desde PDFs","Arquitectura multi-tenant con aislamiento de datos","Soporte 5 idiomas (CA, ES, EN, EU, GL)","Sistema de autenticación JWT seguro","Despliegue automatizado con Docker",true,"\u002Fes\u002Fsideprojects\u002Fmenuescolar-gestor-menus-escolars",null,{"title":5,"description":791},"published","es\u002Fsideprojects\u002Fmenuescolar-gestor-menus-escolars",[822,802,823,804,807,808,806,824,825,826],"React 18 & TypeScript","PocketBase (Auth & Database)","Resend (Email)","Multi-tenant Architecture","Internationalization (i18n)","wZRZo6xo_6CbdzGxUpiiw2Hz_F9BjbEjDx0w-3yPkJI",1779354921613]