Changelog
v29

Historial de canvis

Tot el que hem afegit a GESEM Planner
v29 · actual
v29 Actual
07 maig 2026
Parser iCal pro: RRULE + TZID + cache persistent · Sincronitzacions que mai més es perden
🔁 Events recurrents (RRULE + EXDATE)
  • El parser ara expandeix events recurrents segons l'estàndard RFC 5545. Si un formador té "Reunió cada dilluns" al seu calendari, l'app ara bloqueja tots els dilluns (abans només el primer).
  • Suport per a: FREQ=DAILY/WEEKLY/MONTHLY/YEARLY, INTERVAL, COUNT, UNTIL, BYDAY=MO,WE,FR, BYMONTHDAY=15
  • EXDATE respectat: si un usuari diu "tots els dilluns excepte el 18 de maig", l'app exclou correctament aquesta data
  • Salvaguardes: horitzó de 18 mesos vista, màxim 400 ocurrències per event (evita bombes computacionals)
  • Cobert amb 7 tests unitaris automàtics
🌍 Timezone correcte (UTC → Madrid)
  • Events amb sufix Z (UTC) ara es converteixen correctament a wall-clock de Europe/Madrid abans d'assignar la data
  • Cas concret cobert: un event Google a les 23:30Z de l'11 de maig es comptabilitza al dia 12 a Madrid (DST +2h), no al dia 11
  • Usa Intl.DateTimeFormat sense dependències externes
💾 Cache de calendari persistent · 3 capes de defensa
  • Client (localStorage): la pàgina restaura les dades de sincronització abans del primer render, evitant el flicker de "pendent de sincronitzar"
  • Servidor (SQLite): el cache d'iCal sobreviu reinicis del servei (systemctl restart, deploy, crash)
  • Stale-while-error: si una sincronització falla per xarxa, l'app retorna dades antigues amb flag stale en lloc de mostrar res
  • Els cards de formador ara mostren "fa N min" indicant quan es va sincronitzar per última vegada
🐛 Fix events all-day TRANSPARENT no detectats
  • Causa: Google Calendar marca per defecte tots els events all-day com a TRANSP:TRANSPARENT. El parser els ignorava per ser "no-blocking" segons la spec iCal
  • Conseqüència: un formador que posava "Vacances" o "Formació externa" en un dia sencer NO sortia com ocupat. Resultat: conflictes invisibles al planificar.
  • Fix: all-day events compten com ocupats independentment del flag TRANSP (mantenint el comportament estricte només per events amb hora, on TRANSP:TRANSPARENT és un cas legítim)
  • Impacte real: el calendari del Ferran Garola passava de detectar 2 events a detectar tots 5 ✓
v28 Minor
07 maig 2026
Edició completa d'agents · Preview pre-reserva · Auto-refresc /formadors · UX +
👤 Edició completa d'agents comercials
  • Doble clic a qualsevol chip d'agent obre un modal d'edició
  • Permet canviar nom + color i pujar foto pròpia (avatar circular 256×256, alta resolució retina)
  • Tota la rodona de l'avatar és clicable per pujar foto · overlay fosc amb càmera al passar el ratolí
  • En renomenar un agent, el sistema migra automàticament totes les reserves associades (propagació al servidor + memòria local)
  • Endpoint nou PUT /api/agents/:nom que detecta col·lisions i rebutja noms duplicats
  • Botó d'eliminar integrat al modal amb avís si hi ha reserves afectades
📧 Preview email abans de crear la reserva
  • "Reservar + email (Comercial)" ara obre primer un preview editable del correu
  • 4 accions disponibles: Cancel·lar · Copiar · Crear reserva sense enviar · Crear reserva i enviar
  • Si l'usuari cancel·la, la reserva NO es crea (abans es creava sempre)
  • L'editor permet retocar destinatari, assumpte i cos abans de l'enviament SMTP final
🎨 Reorganització del card de proposta
  • Email formador i WhatsApp ara queden a l'esquerra del card (acció ràpida)
  • Eliminat el botó "Reservar" simple (era confús amb "Reservar + email")
  • L'únic botó d'acció ara és "Reservar + email (Comercial)", alineat a la dreta
🔄 Auto-refresc de calendaris a /formadors
  • Cada vegada que entres a la pàgina de formadors, es força un re-fetch de tots els iCal connectats
  • Toast resum quan acaba la sincronització (a part del cron diari de les 03:00)
  • Garanteix que la pàgina sempre mostra dades fresques sense haver de clicar el botó de refresc manual
📐 Panell "Nova petició" més ample
  • Amplada màxima del panell esquerre 420px → 540px (+28%)
  • Més espai per als formularis sense haver de scrollejar lateralment ni truncar text llarg
📷 Imatges en alta resolució
  • Bug: les fotos es desaven a 42×42 (formadors) i 84×84 (agents) — quedaven pixelades en pantalles retina
  • Fix: processador d'imatge unificat processAvatarImage():
    • Output 256×256 (suficient per a retina fins 128px de visualització)
    • Cover crop centrat (manté aspect ratio · no deforma)
    • Downsample en 2 passos amb smoothing high-quality
    • JPEG q=0.92 si la font és JPG (3-5× més petit) · PNG si cal transparència
  • Límit de mida pujat de 2MB → 20MB (body parser servidor: 32MB per cobrir el padding base64)
🐛 Fix botó "Restaurar esborrany"
  • Causa: el banner tenia un onclick="..."function"..." amb cometes dobles dins de cometes dobles, que trencaven el parser HTML
  • Fix: extret el codi a funcions netes restorePeticioDraftWithToast() i discardPeticioDraftWithToast() sense embolics de cometes
  • Ara el botó "Restaurar" recupera correctament tot el draft (camps, dies bloquejats, dates excloses, agent, formador preferit)
v27 Minor
07 maig 2026
IA real a /canvis · Suggeridor de formador · Google Calendar 2-way OAuth
🧠 IA real a la pàgina /canvis
  • Substituïda la lògica heurística per crides reals al model Llama 3.3 70B via Groq (suggestChanges a lib/ai.js)
  • L'IA rep tot el context: reserva, motiu del canvi, dates ja ocupades per altres reserves GESEM, festius, formadors alternatius amb les seves especialitats i ratings
  • Genera 3 propostes amb score, raonament detallat i color codificat (best / opt2 / opt3)
  • Si l'IA falla per qualsevol motiu, fallback automàtic a la lògica heurística antiga (sense interrupció per l'usuari)
  • Endpoint nou: POST /api/ai/suggest-changes
🤖 Suggeridor automàtic del millor formador
  • Botó nou "Suggerir IA" al costat del selector de formador a la pàgina /peticio
  • L'IA analitza el client + curs + especialitat i puntua tots els formadors segons:
    • Continuïtat amb el client (històric de cursos previs) → +25 punts
    • Match d'especialitat exacta → +20 punts
    • Disponibilitat declarada → +15/+5/-10
    • Rating ≥4.5 → +10
    • Marge econòmic òptim → +10/+5/-5
  • Retorna el TOP 3 amb raonament curt + marge calculat per a cadascun
  • Endpoint nou: POST /api/ai/suggest-formador
🔗 Google Calendar 2-way sync (OAuth 2.0)
  • Cada formador pot ara connectar el seu Google Calendar directament des del seu perfil amb un sol clic
  • Quan es confirma una reserva, el sistema crea automàticament tots els events al calendari del formador (un per sessió, amb torn correcte i descripció completa)
  • Els events porten extendedProperties per poder-los identificar i esborrar després si cal
  • Refresh tokens persistits a SQLite: la connexió no caduca encara que l'access token sí
  • Es manté la lectura iCal antiga com a fallback: ningú perd funcionalitat
  • Endpoints nous: /api/google/status, /auth/start, /callback, /connections, /disconnect, /sync-reserva
  • Mòdul nou: lib/google.js · Configuració Cloud Console: cal afegir GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET i GOOGLE_REDIRECT_URI al .env del servidor
v26 Minor
07 maig 2026
Cron de calendaris · Avatars d'agents · Fix manteniment
Cron diari de calendaris
  • Cron al servidor 🌙 que cada nit a les 03:00 refetcha tots els iCal dels formadors i actualitza la cache (refreshAllCalendarsCache a server.js)
  • Refresc on-demand al carregar Petició: en obrir /peticio, es dispara un refresc en segon pla de tots els calendaris perquè la proposta es generi amb dades fresques
  • Endpoint manual POST /api/admin/refresh-calendars per disparar el refresc des de fora (per scripts, monitoring...)
Avatars dels agents comercials
  • Si un agent té el mateix nom que un formador amb foto, ara el botó d'agent mostra la foto del formador en lloc de les inicials. Útil per a Jordi Llopart i altres que apareguin a tots dos llocs.
Fix mode manteniment
  • Cause: el navegador cachejava la pàgina /peticio, així que en activar el manteniment seguia mostrant l'app antiga.
  • Fix: totes les rutes HTML serveixen ara amb Cache-Control: no-store, no-cache + headers Pragma i Expires. La pàgina de manteniment també.
  • Resultat: al activar/desactivar el toggle a /admin, la transició és instantània a la pestanya del navegador (només cal recarregar).
v25 Minor
06 maig 2026
Bulk delete · Mode manteniment · Email redissenyat
Selecció múltiple a Reserves
  • Checkboxes a la taula de reserves cancel·lades / arxivades (VF)
  • Capçalera amb "Seleccionar totes" i barra superior amb el comptador
  • Botó 🗑 Eliminar seleccionades amb confirmació prèvia
  • Eliminació en bloc — molt més ràpid per netejar històric
Mode manteniment
  • Pàgina /admin nova amb panell de configuració d'administrador (no apareix a la sidebar normal)
  • Toggle de manteniment: quan està actiu, tots els usuaris veuen /maintenance amb un missatge personalitzable
  • /admin i /api/admin/* queden sempre accessibles per poder desactivar el mode des de dins
  • API REST per llegir/escriure l'estat (GET/POST /api/admin/maintenance)
  • L'estat es persisteix a SQLite (mateix store que la resta)
  • Pàgina de manteniment amb auto-refresc cada 60s i logo SVG inline
  • Resum de l'estat de SMTP i IA a /admin
Email del formador redissenyat
  • Logo SVG inline (no més caixa verda buida): el logo de stacked cards ara es renderitza correctament a Gmail, Outlook i la majoria de clients
  • Bloc CALENDARI duplicat eliminat: la funció stripCalendarBlock() detecta i treu el calendari del cos d'usuari per no aparèixer dues vegades (text + taula)
  • 3 cards de resum amb estadístiques (Sessions / Hores totals / Torn) en colors distintius (verd / blau / lila)
  • Taula de sessions amb files alternades (zebra striping) per millor lectura
  • Estructura semàntica amb <table role="presentation"> per màxima compatibilitat amb clients d'email antics
  • Preheader (text breu que es veu a la safata d'entrada al costat del subject)
v24 Major
06 maig 2026
Email del formador amb Acceptar/Declinar + auto-add al calendari (.ics)
Feature gran (#4)
  • Botó "✨ Enviar amb confirmació" al modal d'email del formador. Envia un email HTML amb:
    • Disseny polit (logo GESEM, taula de sessions amb dies de la setmana)
    • Botó verd ✓ Acceptar les dates
    • Botó vermell ✕ Declinar / proposar canvis
    • Fitxer .ics adjunt amb totes les sessions (auto-add al calendari amb un sol clic)
  • Tokens HMAC signats (lib/tokens.js): cada link de l'email és únic i caduca a 60 dies. Impossible que un atacant endevini reserves alienes.
  • Pàgina pública de resposta a /r/:token amb 3 vistes:
    • View · mostra detalls + botons Accept/Decline si encara no ha respost
    • Accept · "Gràcies!" + descarregar .ics
    • Decline · form per al motiu
  • Auto-actualització de la reserva: si formador accepta, l'estat passa a confirmada. Si declina, queda registrat amb el motiu.
  • Notificació automàtica al comercial: quan formador accepta o declina, el comercial rep email amb la novetat.
  • Generador .ics (lib/ics.js): RFC 5545, suporta múltiples sessions com a VEVENTs separats.
UX millorada
  • Sidebar plegable redissenyada: amaga TOT el text correctament, mostra tooltips a la dreta en hover (descobertes de la funcionalitat amb només icones), animació d'aparició suau, traducció automàtica de les tooltips quan canvies idioma.
Backend nou
  • POST /api/email/send-formador-confirm · envia HTML + .ics + genera token
  • GET /r/:token · pàgina de resposta del formador
  • POST /r/:token/decline · processa declinació amb motiu
  • GET /r/:token/ics · descarrega el calendari un cop acceptat
  • Variables noves al .env: TOKEN_SECRET (signatura HMAC), BASE_URL (URL pública del servidor)
⚠️ Limitació actual
  • L'URL del servidor és http://192.168.3.208:3001 (LAN GESEM). Si un formador clica el botó des de fora de l'oficina, no podrà accedir-hi. Cal exposar via Cloudflare Tunnel, reverse proxy públic, o VPN. De moment funciona només dins de la xarxa GESEM.
v23 Minor
06 maig 2026
Wave 2 · Sidebar plegable, refresh calendaris, sol·licitar URL, save draft formador
Noves funcionalitats
  • Sidebar plegable ◀ · Botó a l'appbar per col·lapsar la sidebar a només icones (60px) — més espai per a la pàgina. Estat persistent a localStorage.
  • Refresc automàtic de calendaris 🔄 · En crear una nova reserva, el calendari del formador assignat s'invalida i es re-fetcha del seu iCal (en segon pla). La resta de formadors també s'actualitzen amb prioritat menor.
  • Botó "✉️ Demanar URL calendari" al modal de formador · envia un email amb instruccions detallades (Google Calendar, Outlook, iCloud) perquè el formador comparteixi la seva URL iCal.
  • Save draft del formulari formador · si omples mig formulari nou i tanques per accident, l'app et proposa restaurar-lo en obrir-lo de nou. Inclou foto, especialitats i tots els camps. Caduca als 7 dies.
Pendent (Wave 3)
  • Vista calendari mensual del formador a la pàgina Petició (#3)
  • Botons accept/decline a l'email del formador + auto-add al seu calendari (#4)
v22 Minor
06 maig 2026
Bugfixes + neteja UX (wave 1 de 13)
Bugs arreglats
  • "undefined dies ocupats" a les targetes de formador: calData[id] tenia dues representacions inconsistents. Unificat a {slots, fullDayDates}.
  • Disponibilitat irreal: ara, si el formador té calendari sincronitzat, el % es calcula de veritat (dies laborables ocupats vs lliures als propers 60 dies). Si no, fallback al càlcul antic.
  • Email al formador incloïa dates no disponibles: ara filtra les ocupades del cos principal i les afegeix com a "ALERTA · caldrà alternatives".
  • Email "De" incorrecte: 2 modals tenien gestio@gesem.es hardcoded. Ara mostren comunicacions@gesem.cat (coincideix amb el From SMTP real).
UX polish
  • Eliminada la pàgina Dashboard: / redirigeix ara a /peticio, i /dashboard redirigeix a /peticio per compatibilitat.
  • Tret botó "Copiar telèfon" de les targetes de formador (només queden Email i WhatsApp directe).
  • Eliminar formador: nou botó al modal d'edició (només visible quan s'edita un existent), amb confirmació mostrant si té reserves actives.
  • Eliminar reserva permanentment: nou botó 🗑 a la taula de Gestió per a reserves cancel·lades o arxivades (VF).
Pendent (Wave 2-3)
  • Sidebar plegable / desplegable
  • Botó "Sol·licitar URL calendari" al formador
  • Save draft del formulari de formador
  • Refresc automàtic dels calendaris al crear petició
  • Vista calendari mensual del formador a Petició
  • Botons accept/decline a l'email del formador + auto-add al seu calendari
v21 Major
06 maig 2026
IA real al parser d'emails (Groq + Llama 3.3)
IA real (no més heurístiques)
  • Mòdul lib/ai.js 🤖 que crida l'API de Groq (compatible OpenAI) amb Llama 3.3 70B
  • Substitueix les regex i keyword matching anteriors a la pàgina Entrades → Email comercial
  • Precisió esperada: ~95% en emails complexos (vs ~30% amb heurística)
  • Detecta dates relatives ("passada la Setmana Santa"), sinònims, modalitats híbrides, agents implícits
  • Retorna "notes" amb què és ambigu o què falta a l'email — visible com a banner groc al resultat
  • Fallback automàtic a heurística si l'API falla (sense crash)
Endpoints nous
  • GET /api/ai/status — comprova si l'IA està configurada (sense exposar la key)
  • POST /api/ai/test — verifica connexió a Groq sense gastar quota d'inferència
  • POST /api/ai/parse-email — analitza un email i retorna els camps estructurats + temps + model utilitzat
UX
  • Header del resultat indica model usat i temps de resposta (~500-1000ms via Groq)
  • Banner groc "💡 Notes IA" quan l'IA detecta ambigüitats
  • Confiança real (0-100) calculada per l'IA segons claredat del email
Cost
  • 0 € · Groq free tier inclou 14.400 req/dia (= 600/hora)
  • Velocitat: ~500 tokens/s (10× més ràpid que altres APIs)
  • Privadesa: emails es processen al cloud de Groq
v20 Major
06 maig 2026
Enviament real d'emails (SMTP) + deploy a producció
Sistema d'emails real
  • Mòdul SMTP 📤 amb nodemailer: substitueix el "copiar al portapapers" per enviament directe
  • Configuració via .env (mai al codi committat) carregada per systemd amb EnvironmentFile
  • Connexió segura SSL al port 465 a mail.gesem.cat
  • 3 endpoints nous:
    • GET /api/email/status — comprova si està configurat (sense exposar la contrasenya)
    • POST /api/email/test — verifica la connexió SMTP sense enviar
    • POST /api/email/send — envia un email amb {to, subject, text/html, replyTo, cc, bcc}
  • Botó 📤 Enviar afegit als 3 modals d'email (client, formador, pendents per agent) + funció sendEmailViaSMTP() al frontend
  • Estat visual durant l'enviament: el botó passa a "Enviant..." i la toast mostra "✓ Email enviat a [destinatari]"
Deploy a producció
  • App en producció a http://192.168.3.208:3001 (servidor Ubuntu intern de la xarxa GESEM)
  • Servei systemd amb auto-restart i hardening (ProtectSystem, NoNewPrivileges, ReadWritePaths)
  • Backup diari programat amb cron a les 03:00
  • Scripts de deploy reutilitzables: deploy/setup-server.sh, deploy/gesem-planner.service
  • Workflow d'actualització: build ZIP local → SCP → unzip al servidor → restart
  • Autenticació via SSH keys (Ed25519) — sense contrasenyes per a futures actualitzacions
Seguretat
  • .env al servidor amb permisos 600 (només l'usuari del procés)
  • .gitignore exclou .env i .env.local per defecte
  • L'endpoint /api/email/status mai retorna la contrasenya en text pla (la mostra com ***)
v19 Minor
04 maig 2026
Sistema d'idiomes (CA / ES) bilingüe
Internacionalització (i18n)
  • Sistema d'i18n complet 🌐 amb suport per a català i castellà (public/js/i18n.js, ~26 KB)
  • 180+ traduccions que cobreixen sidebar, appbar, formularis, filtres, tabs, KPIs, toasts i confirm dialogs
  • Detecció automàtica de l'idioma del navegador a la primera visita
  • Persistència a localStorage — la preferència segueix entre sessions
  • Sense recarregar: el canvi d'idioma s'aplica instantàniament a tota la pàgina (event langchange)
Selector d'idioma millorat
  • Popover al peu de la sidebar amb les 2 opcions visibles (🇨🇦 Català · 🇪🇸 Español)
  • L'idioma actiu es destaca amb fons emerald i check verd ✓
  • Animació suau d'aparició (slide + fade)
  • Es tanca al clicar fora o seleccionar opció
Cobertura per pàgina
  • Petició: 44 atributs (formulari sencer · sections · placeholders · botons distribució)
  • Canvis: 30 atributs (4 tipus de canvi · opcions · tabs)
  • Entrades: 25 atributs (3 tabs · botons IA · arxiu)
  • Formadors: 22 atributs (cerca · filtres · panells laterals)
  • Gestió: 21 atributs (chips d'estat · ordenacions)
  • Dashboard: 20 atributs (KPIs · panel titles · salutació)
  • Toasts a app.js: 12 missatges dinàmics traduïts amb fallback segur
v18 Major
04 maig 2026
Dashboard, navegació i pàgina 404
Noves pàgines
  • Dashboard 📊 com a pàgina d'inici (/dashboard): KPIs (reserves actives, pendents, facturat mes, hores, formadors), donut de distribució per estat, properes 7 sessions, top formadors per hores, top clients per facturat €, alertes automàtiques
  • Pàgina 404 personalitzada amb logo i enllaços a totes les seccions (substitueix Cannot GET)
Millores de navegació
  • / ara redirigeix a /dashboard (era /peticio)
  • Logo de la sidebar és clicable → torna al dashboard
  • Mode fosc i Changelog al peu de la sidebar (sota usuari)
  • Appbar simplificat: botó únic ⌨️ que obre les dreceres de teclat
  • Headers Cache-Control: no-store al 404 (impedeixen cachejar pàgines inexistents)
Fixes
  • Estructura HTML del peu de la sidebar: eliminats <span> i </div> orfes que trencaven el layout
v17 Minor
04 maig 2026
Polish UX i optimitzacions
Noves funcionalitats
  • Mode fosc 🌙 amb toggle persistent i detecció automàtica del SO
  • Pàgina 404 personalitzada amb enllaços ràpids a totes les seccions
  • Pàgina /changelog (aquesta) amb l'historial complet de versions
  • Save draft automàtic del formulari Petició a localStorage (banner per restaurar)
  • Estats buits informatius a Reserves, Canvis i Arxiu (amb CTA)
  • Diàleg de confirmació polit per accions destructives (cancel·lar reserva, eliminar...)
Millores
  • Endpoint /api/bootstrap: 4 fetches → 1 sol (latència reduïda)
  • Menú d'opcions a l'app bar (substitueix el toggle de tema de la sidebar)
v16 Minor
04 maig 2026
Performance i mobile
Noves funcionalitats
  • Mobile responsive: sidebar com a drawer lliscant amb backdrop
  • Botó hamburger a l'app bar (només mòbil) per obrir/tancar la sidebar
  • Keyboard shortcuts estil Linear: Ctrl+K palette, G+P/R/C/F/E navegació, N nova petició, ? ajuda
  • Compressió gzip al servidor: app.js de 144 KB → 37 KB a la xarxa
Neteja
  • Eliminats index.html (209 KB), index.html.bak i logos.html
  • Alliberats 432 KB del repositori
v15 Patch
04 maig 2026
Identitat de marca
Disseny
  • Logo nou: stacked cards en degradat verd (representa reserves apilades)
  • Favicon SVG escalable a totes les pàgines
  • apple-touch-icon i theme-color per a iOS i Chrome mòbil
v14 Major
04 maig 2026
Pàgines separades amb URLs reals
Arquitectura
  • SPA monolítica → 5 pàgines independents (/peticio, /gestio, /canvis, /formadors, /entrades)
  • CSS extret a public/css/styles.css
  • JS extret a public/js/app.js amb routing automàtic
  • Botó "Enrere" del navegador funciona, URLs es poden compartir/bookmark
  • Rutes Express netes, redirecció //peticio
v13 Major
04 maig 2026
Redisseny estil Notion / Stripe
Disseny
  • Sidebar lateral amb icones (substitueix top nav)
  • Tipografia Inter (Google Fonts)
  • Paleta off-white càlid + emerald primary
  • Cards amb shadow subtil + hover elevat
  • Botons, badges, pills i inputs refinats
  • Backdrop blur als modals
  • Scrollbars estilats fins
v12 Major
04 maig 2026
Migració a SQLite + sistema de backups
Backend
  • SQLite (better-sqlite3) en mode WAL substitueix els fitxers JSON
  • Atomicitat: impossible corrompre dades per escriptures concurrents
  • Script scripts/migrate.js per migrar JSON → DB
Backups
  • Script scripts/backup.js amb online backup (segur amb el servidor actiu)
  • Tasca programada Windows diària a les 03:00
  • Retenció: últims 14 backups (esborrat automàtic)
  • S'executa també amb bateria, es desperta si l'ordinador estava apagat
v11 Patch
abans de l'auditoria
Versió original (punt de partida)
Capacitats existents
  • 5 pàgines: Petició, Gestió, Canvis, Formadors, Entrades
  • Integració iCal real per saber disponibilitat de formadors
  • Scoring sofisticat (especialitat, càrrega, disponibilitat, marge)
  • Càlcul automàtic de dates (festius, exclusions, distribució)
  • Plantilles d'email per a clients i formadors (copia al portapapers)
  • Persistència en fitxers JSON