registri dei piani in git (AI-OS#309)

Refs gitea:Adriano/AI-OS#310

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-10-06 10:36:26 +02:00
parent 9126af28ce
commit b913a2bf90
94 changed files with 40294 additions and 0 deletions
@@ -0,0 +1,279 @@
# Review package: f4c350a..3f01721
## Commits
3f01721 feat: tag contenuti home hero, info, feature, quote
## Files changed
src/components/home/FeatureBlocks.astro | 21 +++++++++---------
src/components/home/HeroSlider.astro | 12 +++++-----
src/components/home/InfoColumns.astro | 14 +++++++-----
src/components/home/QuoteBanner.astro | 13 +++++------
src/data/content-seed.ts | 39 +++++++++++++++++++++++++++++++++
src/pages/index.astro | 8 ++++---
6 files changed, 75 insertions(+), 32 deletions(-)
## Diff
diff --git a/src/components/home/FeatureBlocks.astro b/src/components/home/FeatureBlocks.astro
index f35f301..9a7ae01 100644
--- a/src/components/home/FeatureBlocks.astro
+++ b/src/components/home/FeatureBlocks.astro
@@ -1,33 +1,32 @@
---
-import { Image } from 'astro:assets';
import featPerformance from '../../assets/img/feature-performance.jpg';
import featBalance from '../../assets/img/feature-balance.jpg';
import featLongevity from '../../assets/img/feature-longevity.jpg';
+import T from '../content/T.astro';
+import TImg from '../content/TImg.astro';
+import { t } from '../../lib/content';
const blocks = [
- { title: 'PERFORMANCE', image: featPerformance, reverse: false,
- text: 'Allenamenti progettati su misura per adattarsi ai tuoi obiettivi e al tuo livello. Programmi mirati che sviluppano forza, resistenza e funzionalità in modo equilibrato. Un metodo efficace per migliorare le performance e ottenere risultati concreti e duraturi.' },
- { title: 'BALANCE', image: featBalance, reverse: true,
- text: 'L’equilibrio tra corpo e mente è la base di ogni performance duratura. Attraverso movimento, recupero e gestione dello stress, costruiamo stabilità e controllo. Un approccio integrato che sostiene benessere, energia e continuità nel tempo.' },
- { title: 'LONGEVITY', image: featLongevity, reverse: false,
- text: 'Benessere completo tra movimento, nutrizione e stile di vita. Un percorso personalizzato che unisce prevenzione, recupero ed equilibrio. Investi nella tua salute e qualità della vita nel tempo.' },
+ { key: 'performance', image: featPerformance, reverse: false },
+ { key: 'balance', image: featBalance, reverse: true },
+ { key: 'longevity', image: featLongevity, reverse: false },
];
---
<section class="section">
<div class="container">
{blocks.map((b) => (
<div class:list={['feat', { 'feat--reverse': b.reverse }]}>
- <Image src={b.image} alt={b.title} class="feat__img" widths={[500, 900]} sizes="(max-width: 767px) 100vw, 45vw" data-reveal={b.reverse ? 'right' : 'left'} />
+ <TImg tag={`home.feature.${b.key}.image`} src={b.image} alt={t(`home.feature.${b.key}.title`)} class="feat__img" widths={[500, 900]} sizes="(max-width: 767px) 100vw, 45vw" data-reveal={b.reverse ? 'right' : 'left'} />
<div class="feat__text" data-reveal={b.reverse ? 'left' : 'right'}>
- <h2>{b.title}</h2>
- <p>{b.text}</p>
- <a class="btn" href="/programs">Scopri</a>
+ <T tag={`home.feature.${b.key}.title`} as="h2" />
+ <T tag={`home.feature.${b.key}.body`} as="p" />
+ <a class="btn" href="/programs">{t(`home.feature.${b.key}.cta`)}</a>
</div>
</div>
))}
</div>
</section>
<style>
.feat { display: grid; grid-template-columns: 1fr 1fr; gap: 60px; align-items: center; padding-block: 40px; }
.feat--reverse .feat__img { order: 2; }
.feat__text h2 { text-transform: uppercase; letter-spacing: .1em; }
diff --git a/src/components/home/HeroSlider.astro b/src/components/home/HeroSlider.astro
index b5239c1..7ce3273 100644
--- a/src/components/home/HeroSlider.astro
+++ b/src/components/home/HeroSlider.astro
@@ -1,26 +1,28 @@
---
-import { Image } from 'astro:assets';
import hero1 from '../../assets/img/hero-1.png';
+import T from '../content/T.astro';
+import TImg from '../content/TImg.astro';
+import { t } from '../../lib/content';
const slides = [
- { title: ['PERFORMANCE.', 'BALANCE.', 'LONGEVITY.'], image: hero1, alt: 'Atleti InsanityLab' },
+ { titleTags: ['home.hero.title-line-1', 'home.hero.title-line-2', 'home.hero.title-line-3'], image: hero1, alt: 'Atleti InsanityLab' },
];
---
<section class="hero" aria-label="presentazione">
{slides.map((s, i) => (
<div class:list={['hero__slide', { 'is-active': i === 0 }]}>
<div class="container hero__in">
<div class="hero__text">
- <h1>{s.title.map((line) => <span class="hero__line">{line}</span>)}</h1>
- <a class="btn" href="/programs">Scopri</a>
+ <h1>{s.titleTags.map((tag) => <T tag={tag} as="span" class="hero__line" />)}</h1>
+ <a class="btn" href="/programs">{t('home.hero.cta')}</a>
</div>
- <Image src={s.image} alt={s.alt} class="hero__img" widths={[600, 1000, 1400]} sizes="(max-width: 991px) 100vw, 60vw" loading="eager" />
+ <TImg tag="home.hero.image" src={s.image} alt={s.alt} class="hero__img" widths={[600, 1000, 1400]} sizes="(max-width: 991px) 100vw, 60vw" loading="eager" />
</div>
</div>
))}
{slides.length > 1 && (
<>
<button class="hero__arrow hero__arrow--prev" aria-label="precedente">‹</button>
<button class="hero__arrow hero__arrow--next" aria-label="successiva">›</button>
</>
)}
</section>
diff --git a/src/components/home/InfoColumns.astro b/src/components/home/InfoColumns.astro
index 3adba4d..a91f7af 100644
--- a/src/components/home/InfoColumns.astro
+++ b/src/components/home/InfoColumns.astro
@@ -1,27 +1,29 @@
---
import { getImage } from 'astro:assets';
import infoPerformance from '../../assets/img/info-performance.jpg';
import infoBalance from '../../assets/img/info-balance.jpg';
import infoLongevity from '../../assets/img/info-longevity.jpg';
+import T from '../content/T.astro';
+import { contentImageUrl } from '../../lib/content';
// getImage garantisce l'emissione dell'asset anche quando il file è byte-identico
// ad altri import (Astro dedupa e non emette l'originale usato solo come background).
const cols = await Promise.all([
- { title: 'PERFORMANCE.', image: infoPerformance },
- { title: 'BALANCE.', image: infoBalance },
- { title: 'LONGEVITY.', image: infoLongevity },
-].map(async (c) => ({ ...c, bg: (await getImage({ src: c.image, width: 1200 })).src })));
+ { titleTag: 'home.info.col1.title', imageTag: 'home.info.col1.image', image: infoPerformance },
+ { titleTag: 'home.info.col2.title', imageTag: 'home.info.col2.image', image: infoBalance },
+ { titleTag: 'home.info.col3.title', imageTag: 'home.info.col3.image', image: infoLongevity },
+].map(async (c) => ({ ...c, bg: contentImageUrl(c.imageTag, (await getImage({ src: c.image, width: 1200 })).src) })));
---
<section class="info" aria-label="pilastri">
{cols.map((c, i) => (
- <a class="info__col" href="/programs" style={`background-image:url(${c.bg});--reveal-delay:${i * 0.15}s`} data-reveal="fade">
- <span class="info__title">{c.title}</span>
+ <a class="info__col" href="/programs" style={`background-image:url(${c.bg});--reveal-delay:${i * 0.15}s`} data-reveal="fade" data-tag={c.imageTag}>
+ <T tag={c.titleTag} as="span" class="info__title" />
</a>
))}
</section>
<style>
.info { display: grid; grid-template-columns: repeat(3, 1fr); }
/* Proporzioni Figma: colonna 413x308 su frame 1209 — l'altezza scala con la larghezza */
.info__col { position: relative; aspect-ratio: 413 / 308; min-height: 320px; background-size: cover; background-position: center; display: flex; align-items: center; justify-content: center; text-decoration: none; }
.info__col::before { content: ''; position: absolute; inset: 0; background: rgba(30,24,20,.35); transition: background .2s; }
.info__col:hover::before { background: rgba(30,24,20,.15); }
diff --git a/src/components/home/QuoteBanner.astro b/src/components/home/QuoteBanner.astro
index 8ffaeeb..26247e7 100644
--- a/src/components/home/QuoteBanner.astro
+++ b/src/components/home/QuoteBanner.astro
@@ -1,29 +1,28 @@
---
import { getImage } from 'astro:assets';
import quoteBgSrc from '../../assets/img/quote-bg.jpg';
-const quoteBg = await getImage({ src: quoteBgSrc, width: 1920 });
-const quote = 'PERFORMANCE È SPINGERE OLTRE I PROPRI LIMITI, BALANCE È SAPERLI ASCOLTARE, LONGEVITY È COSTRUIRE NEL TEMPO CIÒ CHE DAVVERO CONTA.';
-const quoteHtml = quote.replace(/(PERFORMANCE|BALANCE|LONGEVITY)/g, '<strong>$1</strong>');
+import { t, contentImageUrl } from '../../lib/content';
+const quoteBg = contentImageUrl('home.quote.image', (await getImage({ src: quoteBgSrc, width: 1920 })).src);
---
<section class="quote" data-parallax>
- <div class="quote__bg" style={`background-image:url(${quoteBg.src})`}></div>
+ <div class="quote__bg" style={`background-image:url(${quoteBg})`} data-tag="home.quote.image"></div>
<div class="quote__in">
<p class="quote__mark">“</p>
<blockquote>
<div class="quote__marquee">
<p class="quote__track">
- <span class="quote__seg" set:html={quoteHtml} />
- <span class="quote__seg" aria-hidden="true" set:html={quoteHtml} />
+ <span class="quote__seg" data-tag="home.quote.text" set:html={t('home.quote.text')} />
+ <span class="quote__seg" aria-hidden="true" data-tag="home.quote.text" set:html={t('home.quote.text')} />
</p>
</div>
- <cite>Insanitylab</cite>
+ <cite data-tag="home.quote.cite">{t('home.quote.cite')}</cite>
</blockquote>
</div>
</section>
<style>
.quote { position: relative; overflow: hidden; text-align: center; }
/* Sfondo su div dedicato, esteso oltre i bordi per il margine di corsa del parallax */
.quote__bg { position: absolute; inset: -120px 0; background-size: cover; background-position: center; transform: translateY(var(--parallax-y, 0)); will-change: transform; }
.quote__bg::after { content: ''; position: absolute; inset: 0; background: rgba(46,32,24,.82); }
.quote__in { position: relative; padding-block: 110px; }
diff --git a/src/data/content-seed.ts b/src/data/content-seed.ts
index 93deb38..dfecec8 100644
--- a/src/data/content-seed.ts
+++ b/src/data/content-seed.ts
@@ -51,14 +51,53 @@ export const contentSeed: SeedEntry[] = [
// --- Contact form ---
T('form.field.first-name', 'Nome', []),
T('form.field.last-name', 'Cognome', []),
T('form.field.phone', 'Telefono', []),
T('form.field.email', 'E-mail', []),
T('form.field.message', 'Messaggio', []),
T('form.submit', 'Invia messaggio'),
T('form.msg.success', 'Messaggio inviato, ti ricontatteremo al più presto.', []),
T('form.msg.error-generic', 'Errore di invio, riprova più tardi.', []),
T('form.msg.error-network', 'Errore di rete, riprova più tardi.', []),
+
+ // --- Home: meta, training heading ---
+ T('home.meta.title', 'Performance. Balance. Longevity.', []),
+ T('home.training.title', 'Training'),
+ T('home.training.subtitle', 'Porta i tuoi allenamenti al livello successivo: trova il percorso più adatto alle tue esigenze.'),
+
+ // --- Home: hero ---
+ T('home.hero.title-line-1', 'PERFORMANCE.'),
+ T('home.hero.title-line-2', 'BALANCE.'),
+ T('home.hero.title-line-3', 'LONGEVITY.'),
+ T('home.hero.cta', 'Scopri'),
+ I('home.hero.image'),
+
+ // --- Home: info columns ---
+ T('home.info.col1.title', 'PERFORMANCE.'),
+ T('home.info.col2.title', 'BALANCE.'),
+ T('home.info.col3.title', 'LONGEVITY.'),
+ I('home.info.col1.image'),
+ I('home.info.col2.image'),
+ I('home.info.col3.image'),
+
+ // --- Home: feature blocks ---
+ T('home.feature.performance.title', 'PERFORMANCE'),
+ T('home.feature.performance.body', 'Allenamenti progettati su misura per adattarsi ai tuoi obiettivi e al tuo livello. Programmi mirati che sviluppano forza, resistenza e funzionalità in modo equilibrato. Un metodo efficace per migliorare le performance e ottenere risultati concreti e duraturi.'),
+ T('home.feature.performance.cta', 'Scopri'),
+ T('home.feature.balance.title', 'BALANCE'),
+ T('home.feature.balance.body', 'L’equilibrio tra corpo e mente è la base di ogni performance duratura. Attraverso movimento, recupero e gestione dello stress, costruiamo stabilità e controllo. Un approccio integrato che sostiene benessere, energia e continuità nel tempo.'),
+ T('home.feature.balance.cta', 'Scopri'),
+ T('home.feature.longevity.title', 'LONGEVITY'),
+ T('home.feature.longevity.body', 'Benessere completo tra movimento, nutrizione e stile di vita. Un percorso personalizzato che unisce prevenzione, recupero ed equilibrio. Investi nella tua salute e qualità della vita nel tempo.'),
+ T('home.feature.longevity.cta', 'Scopri'),
+ I('home.feature.performance.image'),
+ I('home.feature.balance.image'),
+ I('home.feature.longevity.image'),
+
+ // --- Home: quote banner ---
+ H('home.quote.text', '<strong>PERFORMANCE</strong> È SPINGERE OLTRE I PROPRI LIMITI, <strong>BALANCE</strong> È SAPERLI ASCOLTARE, <strong>LONGEVITY</strong> È COSTRUIRE NEL TEMPO CIÒ CHE DAVVERO CONTA.'),
+ T('home.quote.cite', 'Insanitylab', []),
+ I('home.quote.image'),
// I task di estrazione aggiungono qui le entry di header/footer/pagine/data file.
];
export const seedByTag = new Map(contentSeed.map((e) => [e.tag, e]));
diff --git a/src/pages/index.astro b/src/pages/index.astro
index 665a928..a99d529 100644
--- a/src/pages/index.astro
+++ b/src/pages/index.astro
@@ -3,30 +3,32 @@ import Base from '../layouts/Base.astro';
import HeroSlider from '../components/home/HeroSlider.astro';
import InfoColumns from '../components/home/InfoColumns.astro';
import FeatureBlocks from '../components/home/FeatureBlocks.astro';
import QuoteBanner from '../components/home/QuoteBanner.astro';
import TrainingCards from '../components/home/TrainingCards.astro';
import ProgramsGrid from '../components/home/ProgramsGrid.astro';
import Agenda from '../components/Agenda.astro';
import TeamSection from '../components/home/TeamSection.astro';
import MethodSteps from '../components/home/MethodSteps.astro';
import PartnersStrip from '../components/home/PartnersStrip.astro';
+import T from '../components/content/T.astro';
+import { t } from '../lib/content';
export const prerender = true;
---
-<Base title="Performance. Balance. Longevity.">
+<Base title={t('home.meta.title')}>
<HeroSlider />
<InfoColumns />
<FeatureBlocks />
<QuoteBanner />
<section class="section" style="padding-bottom:0">
<div class="section-heading">
- <h2>Training</h2>
- <p>Porta i tuoi allenamenti al livello successivo: trova il percorso più adatto alle tue esigenze.</p>
+ <T tag="home.training.title" as="h2" />
+ <T tag="home.training.subtitle" as="p" />
</div>
</section>
<TrainingCards />
<ProgramsGrid />
<Agenda />
<TeamSection />
<MethodSteps />
<PartnersStrip />
</Base>