# Sito web InsanityLab — Design **Data:** 2026-07-01 **Dominio di destinazione:** insanitylab.tielogic.xyz (VPS Hostinger, porting in fase successiva) **Stato:** approvato dall'operatore durante la sessione di brainstorming ## Obiettivo Realizzare il sito vetrina di InsanityLab (centro di allenamento e benessere, Bologna) a partire dal design Figma fornito dal designer (`FigmaDoc/*.pdf`, `Web Insanity.fig`), che ristilizza il template WordPress "Prowess" (analizzato in `Dati/cassetta-me-analisi.md`) con una palette beige/marrone al posto del rosso originale. Il sito include un blog gestibile dal cliente tramite pannello protetto da login e un form contatti che invia email. La gestione degli acquisti online (pagina prezzi Performance Class) è rimandata a una fase successiva. ## Stack | Livello | Scelta | |---|---| | Framework | Astro 5, output `server` con adapter Node | | Pagine vetrina | Prerenderizzate (`prerender = true`) | | Blog e admin | Server-rendered | | Stile | CSS custom (palette estratta dai PDF Figma), nessun framework CSS | | Font | Montserrat (titoli) + Open Sans (corpo), self-hosted | | JS client | Vanilla, minimo indispensabile (slider, tab, accordion, header sticky) | | Database | SQLite via `better-sqlite3`, file `data/insanitylab.db` | | Editor admin | WYSIWYG TipTap, con upload immagini nel corpo articolo | | Email | nodemailer → SMTP (credenziali via `.env`) | | Test | vitest (logica) + smoke test su build | | Versionamento | git dal primo commit, remote su GitLab | | Deploy (fase 2) | Dockerfile multi-stage + compose, gestito poi via Komodo sul VPS | Motivazione: sito prevalentemente statico con una sola area dinamica (blog). Un'unica applicazione Node leggera evita la manutenzione di un CMS completo, mantiene ottime prestazioni e SEO, e resta estendibile (pagamenti in fase futura). ## Route ### Pubbliche | Route | Contenuto | Fonte design | |---|---|---| | `/` | Homepage: hero slider, 3 colonne Performance/Balance/Longevity, feature alternata, banner citazione, Training (4 card), Programmi (6 icone), Agenda con tab, Team trainer, Metodo (4 step), loghi partner (Ticket Welfare Edenred, Welbee, AON, Howden), footer con mini-form | Homepage.pdf | | `/about` | Mission, direzione, approccio, about, timeline "Storylab" (2021-2024 aperta; +2025, +2026, +oggi ad accordion), convenzioni aziendali, team completo (9 persone), metodo, IN-SANITY STUDIOS, vision | Insanitylab.pdf | | `/training` | 4 servizi (One to One, Small Groups, Performance Class, Fit Remote) con blocchi alternati, agenda, CTA "prenota la lezione di prova" | Training.pdf | | `/programs` | Griglia 6 programmi con testi reali | Programmi.pdf | | `/programs/[slug]` | Pagina dettaglio per ogni programma: `personal-training`, `performance-class`, `coaching`, `pilates-flow`, `rehab`, `nutrition` | Performance class.pdf | | `/blog` | Blog & Edugo: intro, filtri per categoria, griglia articoli, paginazione | Blog & Edugo.pdf | | `/blog/[slug]` | Articolo singolo con sidebar (autore, categorie, ultimi articoli, archivio) | Articolo.pdf | | `/contact` | Form contatti (Nome, Cognome, Telefono, E-mail, Messaggio), mappa Google, recapiti | Contatti.pdf | Dettaglio programmi: layout unico modellato su Performance class.pdf (FAQ ad accordion, descrizione, sidebar categorie + altri programmi). Il blocco pricing compare solo dove esistono prezzi — al lancio solo `performance-class` (3 piani semestrali: 130/234/300 € al mese, con varianti trimestrale e mensile ad accordion). I bottoni "acquista" puntano per ora a `/contact`. I testi degli altri cinque programmi partono dalle descrizioni di Programmi.pdf; il cliente li integrerà. ### Admin (richiedono sessione) | Route | Funzione | |---|---| | `/admin/login` | Login user/password | | `/admin` | Elenco articoli (pubblicati e bozze) | | `/admin/new` | Nuovo articolo | | `/admin/edit/[id]` | Modifica articolo | ### API | Endpoint | Funzione | |---|---| | `POST /api/contact` | Invio email form contatti (honeypot antispam + rate-limit) | | `POST/PUT/DELETE /api/admin/posts*` | CRUD articoli (sessione richiesta) | | `POST /api/admin/upload` | Upload immagini (copertina e corpo articolo, salvate in `public/uploads/`) | ## Design system - **Palette** (valori esatti da campionare dai PDF in fase di implementazione): accento beige/tan ~`#b8a98a` (bottoni, etichette), marrone scuro ~`#3a2e28` (sezioni dark: agenda, banner citazione, footer, card scure), fondi chiari `#f5f3f0`/bianco. - **Tipografia**: Montserrat uppercase con letterspacing largo per titoli e bottoni; Open Sans per il corpo. File woff2 self-hosted (niente Google Fonts CDN, conformità GDPR). - **Componenti chiave**: header sticky (trasparente sopra l'hero, bianco con ombra allo scroll), hero slider (scroll-snap CSS + JS vanilla), card programma testo/foto alternate, agenda settimanale con tab per disciplina, accordion FAQ e pricing, form con stati di validazione. - **Responsive**: mobile-first; il Figma è solo desktop (1366 px), il comportamento mobile si deriva dalle convenzioni del template (menu hamburger sotto i 992 px, griglie che collassano a colonna). - **Immagini**: estratte dall'archivio `Web Insanity.fig` (è uno ZIP con cartella `images/`; i file hanno nomi hash e vanno identificati visivamente e rinominati per sezione). Fallback: asset in `Dati/` e estrazione da PDF con `pdfimages`. Ottimizzazione con `astro:assets` (webp, lazy loading, srcset). - **SEO**: title/description per pagina, Open Graph, sitemap, robots.txt, favicon dal logo. - **Mappa contatti**: iframe Google Maps con click-to-load (consenso prima del caricamento, GDPR). ## Backend ### Database SQLite in `data/insanitylab.db` (fuori da `src/`, montabile come volume Docker in fase 2). Schema creato da script SQL idempotente all'avvio. ```sql posts (id, title, slug UNIQUE, category, excerpt, body_html, cover, published_at, draft, created_at, updated_at) users (id, username UNIQUE, password_hash) sessions (token PK, user_id, expires_at) ``` Categorie blog fisse (da Figma): Articoli tecnici & scientifici, Contenuti educativi, LinkedIn / Insight, Eventi & Presidi. Il corpo articolo è HTML prodotto da TipTap, sanitizzato lato server prima del salvataggio e del rendering. ### Autenticazione - Password con hash bcrypt; utenti creati via script CLI (`npm run create-user`). - Sessione: token random in cookie httpOnly, SameSite=Lax, scadenza 7 giorni, verificata da middleware su tutte le route `/admin` e `/api/admin`. - Rate-limit sul login (protezione brute force). ### Email `POST /api/contact` valida i campi, applica honeypot e rate-limit, invia via nodemailer con SMTP configurato in `.env` (host, porta, credenziali, destinatario — default `info@insanitylab.it`). Risposta JSON per feedback inline nel form. ### Dati statici Agenda settimanale, prezzi, team, programmi, loghi partner: file TypeScript/JSON in `src/data/`, modificati dallo sviluppatore e versionati in git. Nessuna gestione da admin per questi contenuti al lancio. ## Deploy - **Fase attuale (locale)**: `npm run dev`; build verificata con `npm run build && npm run preview`. - **Versionamento**: repository git con remote su GitLab (namespace da confermare in fase di implementazione). - **Fase 2 (VPS)**: Dockerfile multi-stage (build → `node:22-slim`), `compose.yaml` con volumi per `data/` e `public/uploads/`, porta interna 4321; aggancio a Komodo e reverse proxy per `insanitylab.tielogic.xyz` quando si porterà in produzione. Fuori scope di questa spec. ## Gestione errori - Form contatti: validazione client + server, messaggi di errore inline, fallback se SMTP non risponde (errore esplicito all'utente, log lato server). - Admin: redirect a `/admin/login` se sessione assente/scaduta; conferma prima dell'eliminazione articoli. - 404 personalizzata coerente col design. - Upload: limite dimensione (5 MB) e whitelist tipi immagine. ## Test e verifica - vitest su: helper slug, validazione form, auth (hash/sessioni), query posts. - Smoke test: build completa senza errori; fetch delle route principali con risposta 200 e contenuti attesi. - Verifica visiva locale contro i PDF Figma pagina per pagina. ## Fuori scope (fasi future) - Pagamenti online piani Performance Class (Stripe o simile). - Gestione da admin di agenda, prezzi, team. - Porting su VPS, DNS, TLS (fase 2 dedicata). - Ricerca nel blog e ricerca globale (icona lente del template: omessa al lancio). - E-commerce/carrello (icona carrello nell'header Figma: omessa al lancio, era ereditata dal template).