Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
8.4 KiB
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 |
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 cartellaimages/; i file hanno nomi hash e vanno identificati visivamente e rinominati per sezione). Fallback: asset inDati/e estrazione da PDF conpdfimages. Ottimizzazione conastro: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.
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
/admine/api/admin. - Rate-limit sul login (protezione brute force).
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 connpm 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.yamlcon volumi perdata/epublic/uploads/, porta interna 4321; aggancio a Komodo e reverse proxy perinsanitylab.tielogic.xyzquando 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/loginse 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).