feat: tag contenuti home cards, team, metodo, partner, agenda

This commit is contained in:
2026-07-05 16:17:31 +02:00
parent 3f01721a99
commit 34fd3d2e18
9 changed files with 108 additions and 40 deletions
+6 -5
View File
@@ -1,20 +1,21 @@
--- ---
import { agenda, disciplines, days, slots } from '../data/agenda'; import { agenda, disciplines, days, slots } from '../data/agenda';
import T from './content/T.astro';
--- ---
<section class="section section--dark agenda"> <section class="section section--dark agenda">
<div class="container"> <div class="container">
<div class="section-heading" data-reveal><h2>Agenda</h2></div> <div class="section-heading" data-reveal><T tag="agenda.title" as="h2" /></div>
<div class="agenda__tabs" role="tablist" data-reveal style="--reveal-delay:.1s"> <div class="agenda__tabs" role="tablist" data-reveal style="--reveal-delay:.1s">
{disciplines.map((d, i) => ( {disciplines.map((d, i) => (
<button class:list={['agenda__tab', { 'is-active': i === 0 }]} data-filter={d} role="tab" aria-selected={i === 0 ? 'true' : 'false'}>{d}</button> <button class:list={['agenda__tab', { 'is-active': i === 0 }]} data-filter={d} role="tab" aria-selected={i === 0 ? 'true' : 'false'}><T tag={`agenda.discipline.${i + 1}`} as="span" /></button>
))} ))}
</div> </div>
<div class="agenda__grid" data-reveal style="--reveal-delay:.2s"> <div class="agenda__grid" data-reveal style="--reveal-delay:.2s">
<div class="agenda__corner"></div> <div class="agenda__corner"></div>
{days.map((d) => <div class="agenda__day">{d}</div>)} {days.map((_, i) => <T tag={`agenda.day.${i + 1}`} as="div" class="agenda__day" />)}
{slots.map((slot) => ( {slots.map((slot, si) => (
<> <>
<div class="agenda__slot">{slot}</div> <T tag={`agenda.slot.${si + 1}`} as="div" class="agenda__slot" />
{days.map((_, dayIdx) => { {days.map((_, dayIdx) => {
const entry = agenda.find((e) => e.day === dayIdx && e.slot === slot); const entry = agenda.find((e) => e.day === dayIdx && e.slot === slot);
return entry return entry
+1 -1
View File
@@ -4,7 +4,7 @@ import type { ImageMetadata } from 'astro';
import { getImageOverride } from '../../lib/content'; import { getImageOverride } from '../../lib/content';
interface Props { interface Props {
tag: string; src: ImageMetadata; alt: string; tag: string; src: ImageMetadata; alt: string;
class?: string; loading?: 'lazy' | 'eager'; widths?: number[]; sizes?: string; class?: string; loading?: 'lazy' | 'eager'; widths?: number[]; sizes?: string; height?: number;
} }
const { tag, src, alt, class: cls, ...rest } = Astro.props; const { tag, src, alt, class: cls, ...rest } = Astro.props;
const override = getImageOverride(tag); const override = getImageOverride(tag);
+9 -8
View File
@@ -1,26 +1,27 @@
--- ---
import { Image } from 'astro:assets';
import type { ImageMetadata } from 'astro'; import type { ImageMetadata } from 'astro';
import { methodIntro, methodExtraNote, methodSteps } from '../../data/method'; import { methodSteps } from '../../data/method';
import T from '../content/T.astro';
import TImg from '../content/TImg.astro';
const images = import.meta.glob<{ default: ImageMetadata }>('../../assets/img/method-*.{jpg,png}', { eager: true }); const images = import.meta.glob<{ default: ImageMetadata }>('../../assets/img/method-*.{jpg,png}', { eager: true });
const imgOf = (name: string) => Object.entries(images).find(([p]) => p.includes(name))?.[1].default; const imgOf = (name: string) => Object.entries(images).find(([p]) => p.includes(name))?.[1].default;
--- ---
<section class="section section--alt"> <section class="section section--alt">
<div class="container"> <div class="container">
<div class="section-heading" data-reveal> <div class="section-heading" data-reveal>
<h2>Il metodo<br />InsanityLab</h2> <T tag="home.method.title" as="h2" />
<p set:html={methodIntro.replace('Metodo Insanitylab', '<strong>Metodo Insanitylab</strong>')} /> <T tag="method.intro" as="p" />
</div> </div>
<p class="method__note">{methodExtraNote}</p> <T tag="method.extra-note" as="p" class="method__note" />
<div class="method__row"> <div class="method__row">
{methodSteps.map((s, i) => ( {methodSteps.map((s, i) => (
<div class:list={['method__step', { 'method__step--extra': s.extra }]} data-reveal style={`--reveal-delay:${(i * 0.15).toFixed(2)}s`}> <div class:list={['method__step', { 'method__step--extra': s.extra }]} data-reveal style={`--reveal-delay:${(i * 0.15).toFixed(2)}s`}>
<div class="method__circle"> <div class="method__circle">
{imgOf(s.image) && <Image src={imgOf(s.image)!} alt="" widths={[220]} sizes="220px" />} {imgOf(s.image) && <TImg tag={`method.step.${s.n}.image`} src={imgOf(s.image)!} alt="" widths={[220]} sizes="220px" />}
<span>{s.n}</span> <span>{s.n}</span>
</div> </div>
<h3>{s.title}</h3> <T tag={`method.step.${s.n}.title`} as="h3" />
<p>{s.text}</p> <T tag={`method.step.${s.n}.text`} as="p" />
</div> </div>
))} ))}
</div> </div>
+3 -2
View File
@@ -1,13 +1,14 @@
--- ---
import { Image } from 'astro:assets';
import type { ImageMetadata } from 'astro'; import type { ImageMetadata } from 'astro';
import { partners } from '../../data/partners'; import { partners } from '../../data/partners';
import TImg from '../content/TImg.astro';
import { t } from '../../lib/content';
const images = import.meta.glob<{ default: ImageMetadata }>('../../assets/img/partner-*.{jpg,png}', { eager: true }); const images = import.meta.glob<{ default: ImageMetadata }>('../../assets/img/partner-*.{jpg,png}', { eager: true });
const imgOf = (name: string) => Object.entries(images).find(([p]) => p.includes(name))?.[1].default; const imgOf = (name: string) => Object.entries(images).find(([p]) => p.includes(name))?.[1].default;
--- ---
<section class="pstrip" aria-label="partner"> <section class="pstrip" aria-label="partner">
<div class="container pstrip__in" data-reveal="fade"> <div class="container pstrip__in" data-reveal="fade">
{partners.map((p) => imgOf(p.image) && <Image src={imgOf(p.image)!} alt={p.name} height={44} />)} {partners.map((p, i) => imgOf(p.image) && <TImg tag={`partner.${i + 1}.image`} src={imgOf(p.image)!} alt={t(`partner.${i + 1}.name`)} height={44} />)}
</div> </div>
</section> </section>
+3 -2
View File
@@ -1,12 +1,13 @@
--- ---
import { programs } from '../../data/programs'; import { programs } from '../../data/programs';
import { programIcons as icons } from '../../data/program-icons'; import { programIcons as icons } from '../../data/program-icons';
import T from '../content/T.astro';
--- ---
<section class="section"> <section class="section">
<div class="container"> <div class="container">
<div class="section-heading" data-reveal> <div class="section-heading" data-reveal>
<h2>Programmi</h2> <T tag="home.programs.title" as="h2" />
<p>Questi sono i programmi delle classi di Insanity Lab selezionati per voi. Abbiamo scelto i migliori percorsi per offrirvi unesperienza di allenamento completa e mirata.</p> <T tag="home.programs.body" as="p" />
</div> </div>
<div class="pgrid"> <div class="pgrid">
{programs.map((p, i) => ( {programs.map((p, i) => (
+11 -6
View File
@@ -1,7 +1,9 @@
--- ---
import { Image } from 'astro:assets';
import type { ImageMetadata } from 'astro'; import type { ImageMetadata } from 'astro';
import { team, homeTeam } from '../../data/team'; import { team, homeTeam } from '../../data/team';
import T from '../content/T.astro';
import TImg from '../content/TImg.astro';
import { t } from '../../lib/content';
const images = import.meta.glob<{ default: ImageMetadata }>('../../assets/img/team-*.{jpg,png}', { eager: true }); const images = import.meta.glob<{ default: ImageMetadata }>('../../assets/img/team-*.{jpg,png}', { eager: true });
const members = homeTeam.map((short) => team.find((m) => m.shortName === short)!).filter((m) => m.image); const members = homeTeam.map((short) => team.find((m) => m.shortName === short)!).filter((m) => m.image);
const imgOf = (name: string) => Object.entries(images).find(([p]) => p.includes(`${name}.`))?.[1].default; const imgOf = (name: string) => Object.entries(images).find(([p]) => p.includes(`${name}.`))?.[1].default;
@@ -9,16 +11,19 @@ const imgOf = (name: string) => Object.entries(images).find(([p]) => p.includes(
<section class="section"> <section class="section">
<div class="container"> <div class="container">
<div class="tteam__head" data-reveal> <div class="tteam__head" data-reveal>
<h2>Team<br />Trainer</h2> <T tag="home.team.title" as="h2" />
<a class="btn" href="/about">Scopri team</a> <a class="btn" href="/about"><T tag="home.team.cta" as="span" /></a>
</div> </div>
<div class="tteam__grid"> <div class="tteam__grid">
{members.map((m, i) => ( {members.map((m, i) => {
const slug = m.shortName.toLowerCase();
return (
<figure class="tteam__card" data-reveal style={`--reveal-delay:${i * 0.12}s`}> <figure class="tteam__card" data-reveal style={`--reveal-delay:${i * 0.12}s`}>
<Image src={imgOf(m.image!)!} alt={m.name} widths={[300, 500]} sizes="(max-width: 767px) 50vw, 25vw" /> <TImg tag={`team.${slug}.image`} src={imgOf(m.image!)!} alt={t(`team.${slug}.name`)} widths={[300, 500]} sizes="(max-width: 767px) 50vw, 25vw" />
<figcaption>{m.shortName}</figcaption> <figcaption>{m.shortName}</figcaption>
</figure> </figure>
))} );
})}
</div> </div>
</div> </div>
</section> </section>
+9 -7
View File
@@ -1,7 +1,9 @@
--- ---
import { Image } from 'astro:assets';
import type { ImageMetadata } from 'astro'; import type { ImageMetadata } from 'astro';
import { trainings } from '../../data/trainings'; import { trainings } from '../../data/trainings';
import T from '../content/T.astro';
import TImg from '../content/TImg.astro';
import { t } from '../../lib/content';
const images = import.meta.glob<{ default: ImageMetadata }>('../../assets/img/training-*.{jpg,png}', { eager: true }); const images = import.meta.glob<{ default: ImageMetadata }>('../../assets/img/training-*.{jpg,png}', { eager: true });
const imgOf = (name: string) => Object.entries(images).find(([p]) => p.includes(name))?.[1].default; const imgOf = (name: string) => Object.entries(images).find(([p]) => p.includes(name))?.[1].default;
const tones = ['taupe', 'tan', 'dark', 'charcoal']; const tones = ['taupe', 'tan', 'dark', 'charcoal'];
@@ -9,18 +11,18 @@ const tones = ['taupe', 'tan', 'dark', 'charcoal'];
const isSwapRow = (i: number) => Math.floor(i / 2) % 2 === 1; const isSwapRow = (i: number) => Math.floor(i / 2) % 2 === 1;
--- ---
<section class="tcards"> <section class="tcards">
{trainings.map((t, i) => { {trainings.map((tr, i) => {
const img = imgOf(t.image); const img = imgOf(tr.image);
const card = ( const card = (
<div class:list={['tcards__cell', `tcards__cell--${tones[i]}`]} data-reveal="fade" style={`--reveal-delay:${(i % 2) * 0.15}s`}> <div class:list={['tcards__cell', `tcards__cell--${tones[i]}`]} data-reveal="fade" style={`--reveal-delay:${(i % 2) * 0.15}s`}>
<h3>{t.title}</h3> <T tag={`training.${tr.id}.title`} as="h3" />
<p>{t.caption}</p> <T tag={`training.${tr.id}.caption`} as="p" />
<a class="tcards__link" href={t.cta.href}>Scopri →</a> <a class="tcards__link" href={tr.cta.href}><T tag="home.training-cards.link-label" as="span" /> →</a>
</div> </div>
); );
const photo = img ? ( const photo = img ? (
<div class="tcards__photo" data-reveal="fade" style={`--reveal-delay:${((i % 2) * 0.15 + 0.1).toFixed(2)}s`}> <div class="tcards__photo" data-reveal="fade" style={`--reveal-delay:${((i % 2) * 0.15 + 0.1).toFixed(2)}s`}>
<Image src={img} alt={t.title} class="tcards__img" widths={[400, 700]} sizes="(max-width: 767px) 50vw, 25vw" /> <TImg tag={`training.${tr.id}.image`} src={img} alt={t(`training.${tr.id}.title`)} class="tcards__img" widths={[400, 700]} sizes="(max-width: 767px) 50vw, 25vw" />
</div> </div>
) : null; ) : null;
return isSwapRow(i) ? <>{photo}{card}</> : <>{card}{photo}</>; return isSwapRow(i) ? <>{photo}{card}</> : <>{card}{photo}</>;
+55
View File
@@ -1,5 +1,10 @@
import type { StyleGroup } from '../lib/style-presets'; import type { StyleGroup } from '../lib/style-presets';
import { site } from './site'; import { site } from './site';
import { trainings } from './trainings';
import { team } from './team';
import { methodIntro, methodExtraNote, methodSteps } from './method';
import { partners } from './partners';
import { disciplines, days, slots } from './agenda';
export type ContentType = 'text' | 'html' | 'image'; export type ContentType = 'text' | 'html' | 'image';
export interface SeedEntry { tag: string; type: ContentType; value: string; styleOptions?: StyleGroup[] } export interface SeedEntry { tag: string; type: ContentType; value: string; styleOptions?: StyleGroup[] }
@@ -97,6 +102,56 @@ export const contentSeed: SeedEntry[] = [
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.'), 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', []), T('home.quote.cite', 'Insanitylab', []),
I('home.quote.image'), I('home.quote.image'),
// --- Home: training cards (data-driven da trainings.ts) ---
T('home.training-cards.link-label', 'Scopri', []),
...trainings.flatMap((tr) => [
T(`training.${tr.id}.title`, tr.title),
T(`training.${tr.id}.caption`, tr.caption),
T(`training.${tr.id}.description`, tr.description),
T(`training.${tr.id}.cta-label`, tr.cta.label, []),
I(`training.${tr.id}.image`),
]),
// --- Home: programmi ---
T('home.programs.title', 'Programmi'),
T('home.programs.body', 'Questi sono i programmi delle classi di Insanity Lab selezionati per voi. Abbiamo scelto i migliori percorsi per offrirvi unesperienza di allenamento completa e mirata.'),
// --- Home: team (data-driven da team.ts) ---
H('home.team.title', 'Team<br />Trainer'),
T('home.team.cta', 'Scopri team', []),
...team.flatMap((m) => {
const slug = m.shortName.toLowerCase();
return [
T(`team.${slug}.name`, m.name, []),
T(`team.${slug}.role`, m.role, []),
T(`team.${slug}.bio`, m.bio),
I(`team.${slug}.image`),
I(`team.${slug}.image-about`),
];
}),
// --- Home: metodo (data-driven da method.ts) ---
H('home.method.title', 'Il metodo<br />InsanityLab'),
H('method.intro', methodIntro.replace('Metodo Insanitylab', '<strong>Metodo Insanitylab</strong>')),
T('method.extra-note', methodExtraNote, []),
...methodSteps.flatMap((s) => [
T(`method.step.${s.n}.title`, s.title),
T(`method.step.${s.n}.text`, s.text),
I(`method.step.${s.n}.image`),
]),
// --- Home: partner (data-driven da partners.ts) ---
...partners.flatMap((p, i) => [
T(`partner.${i + 1}.name`, p.name, []),
I(`partner.${i + 1}.image`),
]),
// --- Agenda (scope limitato: titolo, discipline, giorni, slot — celle escluse) ---
T('agenda.title', 'Agenda'),
...disciplines.map((d, i) => T(`agenda.discipline.${i + 1}`, d, [])),
...days.map((d, i) => T(`agenda.day.${i + 1}`, d, [])),
...slots.map((s, i) => T(`agenda.slot.${i + 1}`, s, [])),
// I task di estrazione aggiungono qui le entry di header/footer/pagine/data file. // I task di estrazione aggiungono qui le entry di header/footer/pagine/data file.
]; ];
+8 -6
View File
@@ -6,6 +6,8 @@ import Agenda from '../components/Agenda.astro';
import { Image } from 'astro:assets'; import { Image } from 'astro:assets';
import { trainings } from '../data/trainings'; import { trainings } from '../data/trainings';
import trialImg from '../assets/img/trial-class.jpg'; import trialImg from '../assets/img/trial-class.jpg';
import T from '../components/content/T.astro';
import { t } from '../lib/content';
const images = import.meta.glob<{ default: ImageMetadata }>('../assets/img/training-*.{jpg,png}', { eager: true }); const images = import.meta.glob<{ default: ImageMetadata }>('../assets/img/training-*.{jpg,png}', { eager: true });
const imgOf = (name: string) => Object.entries(images).find(([p]) => p.includes(name))?.[1]?.default; const imgOf = (name: string) => Object.entries(images).find(([p]) => p.includes(name))?.[1]?.default;
export const prerender = true; export const prerender = true;
@@ -23,13 +25,13 @@ export const prerender = true;
<section class="section"> <section class="section">
<div class="container"> <div class="container">
{trainings.map((t, i) => ( {trainings.map((tr, i) => (
<div class:list={['tblock', { 'tblock--reverse': i % 2 === 1 }]} id={t.id}> <div class:list={['tblock', { 'tblock--reverse': i % 2 === 1 }]} id={tr.id}>
{imgOf(t.image) && <Image src={imgOf(t.image)!} alt={t.title} widths={[500, 900]} sizes="(max-width:767px) 100vw, 45vw" data-reveal={i % 2 === 1 ? 'right' : 'left'} />} {imgOf(tr.image) && <Image src={imgOf(tr.image)!} alt={t(`training.${tr.id}.title`)} widths={[500, 900]} sizes="(max-width:767px) 100vw, 45vw" data-reveal={i % 2 === 1 ? 'right' : 'left'} />}
<div data-reveal={i % 2 === 1 ? 'left' : 'right'}> <div data-reveal={i % 2 === 1 ? 'left' : 'right'}>
<h2>{t.title.toLowerCase()}</h2> <h2 data-tag={`training.${tr.id}.title`}>{t(`training.${tr.id}.title`).toLowerCase()}</h2>
<p>{t.description}</p> <T tag={`training.${tr.id}.description`} as="p" />
<a class="btn" href={t.cta.href}>{t.cta.label}</a> <a class="btn" href={tr.cta.href}><T tag={`training.${tr.id}.cta-label`} as="span" /></a>
</div> </div>
</div> </div>
))} ))}