feat: tag contenuti header, footer e form contatti

This commit is contained in:
2026-07-05 15:54:29 +02:00
parent b079ded266
commit f4c350ad64
4 changed files with 78 additions and 24 deletions
+19 -9
View File
@@ -1,15 +1,25 @@
---
import { t } from '../lib/content';
import T from './content/T.astro';
interface Props { variant?: 'full' | 'footer' }
const { variant = 'full' } = Astro.props;
---
<form class:list={['cform', `cform--${variant}`]} method="post" action="/api/contact" novalidate>
<input class="field" name="firstName" placeholder="Nome" required maxlength="100" />
{variant === 'full' && <input class="field" name="lastName" placeholder="Cognome" maxlength="100" />}
{variant === 'full' && <input class="field" name="phone" placeholder="Telefono" maxlength="40" />}
<input class="field" type="email" name="email" placeholder="E-mail" required maxlength="200" />
<textarea class="field" name="message" placeholder="Messaggio" required maxlength="5000"></textarea>
<form
class:list={['cform', `cform--${variant}`]}
method="post"
action="/api/contact"
novalidate
data-msg-success={t('form.msg.success')}
data-msg-error-generic={t('form.msg.error-generic')}
data-msg-error-network={t('form.msg.error-network')}
>
<input class="field" name="firstName" placeholder={t('form.field.first-name')} required maxlength="100" />
{variant === 'full' && <input class="field" name="lastName" placeholder={t('form.field.last-name')} maxlength="100" />}
{variant === 'full' && <input class="field" name="phone" placeholder={t('form.field.phone')} maxlength="40" />}
<input class="field" type="email" name="email" placeholder={t('form.field.email')} required maxlength="200" />
<textarea class="field" name="message" placeholder={t('form.field.message')} required maxlength="5000"></textarea>
<input class="hp" type="text" name="website" tabindex="-1" autocomplete="off" />
<button class="btn" type="submit">Invia messaggio</button>
<button class="btn" type="submit"><T tag="form.submit" as="span" /></button>
<p class="form-msg" hidden></p>
</form>
@@ -28,11 +38,11 @@ const { variant = 'full' } = Astro.props;
body: JSON.stringify(Object.fromEntries(new FormData(form))),
});
const data = await res.json();
msg.textContent = res.ok ? 'Messaggio inviato, ti ricontatteremo al più presto.' : (data.error ?? 'Errore di invio, riprova più tardi.');
msg.textContent = res.ok ? form.dataset.msgSuccess! : (data.error ?? form.dataset.msgErrorGeneric);
msg.className = `form-msg ${res.ok ? 'form-msg--ok' : 'form-msg--err'}`;
if (res.ok) form.reset();
} catch {
msg.textContent = 'Errore di rete, riprova più tardi.';
msg.textContent = form.dataset.msgErrorNetwork!;
msg.className = 'form-msg form-msg--err';
}
msg.hidden = false;
+15 -12
View File
@@ -1,34 +1,37 @@
---
import { site } from '../data/site';
import ContactForm from './ContactForm.astro';
import { t, contentImageUrl } from '../lib/content';
import T from './content/T.astro';
const year = new Date().getFullYear();
---
<footer class="ftr">
<div class="container ftr__grid">
<div>
<img src="/img/logo-light.png" alt="InsanityLab" width="140" loading="lazy" />
<p class="ftr__tag">PERFORMANCE.&nbsp; BALANCE.&nbsp; LONGEVITY.</p>
<p>Promuoviamo il movimento come cura di sé. Abitudini sostenibili per benessere fisico e mentale.</p>
<p>{site.phone}<br />{site.address}, {site.cityLine}</p>
{site.hours.map((h) => <p class="ftr__hours">{h}</p>)}
<img src={contentImageUrl('footer.logo.image', '/img/logo-light.png')} alt="InsanityLab" width="140" loading="lazy" />
<T tag="footer.tagline" as="p" class="ftr__tag" />
<T tag="footer.about" as="p" />
<p>{t('global.contact.phone')}<br />{t('global.contact.address')}, {t('global.contact.city-line')}</p>
<p class="ftr__hours">{t('global.hours.1')}</p>
<p class="ftr__hours">{t('global.hours.2')}</p>
</div>
<nav aria-label="training">
<h4>Training</h4>
{site.footerTraining.map((l) => <a href={l.href}>{l.label}</a>)}
<T tag="footer.col.training-title" as="h4" />
{site.footerTraining.map((l, i) => <a href={l.href}>{t(`footer.training.${i + 1}.label`)}</a>)}
</nav>
<nav aria-label="programmi">
<h4>Programmi</h4>
{site.footerPrograms.map((l) => <a href={l.href}>{l.label}</a>)}
<T tag="footer.col.programs-title" as="h4" />
{site.footerPrograms.map((l, i) => <a href={l.href}>{t(`footer.programs.${i + 1}.label`)}</a>)}
</nav>
<div>
<h4>Contact</h4>
<T tag="footer.col.contact-title" as="h4" />
<ContactForm variant="footer" />
</div>
</div>
<div class="ftr__bar">
<div class="container ftr__bar-in">
<p>© {year} <strong>Insanitylab</strong>, All Rights Reserved</p>
<p>Seguici {site.socials.map((s) => <a href={s.url} rel="noopener" target="_blank">{s.label}</a>)}</p>
<p>© {year} <T tag="footer.copyright" as="span" /></p>
<p>{t('footer.social-label')} {site.socials.map((s) => <a href={s.url} rel="noopener" target="_blank">{s.label}</a>)}</p>
</div>
</div>
</footer>
+4 -3
View File
@@ -1,13 +1,14 @@
---
import { site } from '../data/site';
import { t, contentImageUrl } from '../lib/content';
const path = Astro.url.pathname;
---
<header class="hdr" id="site-header">
<div class="container hdr__in">
<a href="/" class="hdr__logo"><img src="/img/logo-dark.png" alt="InsanityLab" width="150" /></a>
<a href="/" class="hdr__logo"><img src={contentImageUrl('header.logo.image', '/img/logo-dark.png')} alt={t('header.logo.alt')} width="150" /></a>
<nav class="hdr__nav" id="site-nav" aria-label="principale">
{site.navigation.map((item) => (
<a href={item.href} class:list={['hdr__link', { 'is-active': path === item.href || (item.href !== '/' && path.startsWith(item.href)) }]}>{item.label}</a>
{site.navigation.map((item, i) => (
<a href={item.href} class:list={['hdr__link', { 'is-active': path === item.href || (item.href !== '/' && path.startsWith(item.href)) }]}>{t(`global.nav.${i + 1}.label`)}</a>
))}
</nav>
<button class="hdr__burger" id="nav-toggle" aria-label="Apri menu" aria-expanded="false">
+40
View File
@@ -18,6 +18,46 @@ export const contentSeed: SeedEntry[] = [
T('global.contact.city-line', site.cityLine, []),
T('global.hours.1', site.hours[0], []),
T('global.hours.2', site.hours[1], []),
T('global.nav.1.label', site.navigation[0].label, []),
T('global.nav.2.label', site.navigation[1].label, []),
T('global.nav.3.label', site.navigation[2].label, []),
T('global.nav.4.label', site.navigation[3].label, []),
T('global.nav.5.label', site.navigation[4].label, []),
// --- Header ---
T('header.logo.alt', 'InsanityLab', []),
I('header.logo.image'),
// --- Footer ---
I('footer.logo.image'),
H('footer.tagline', 'PERFORMANCE.&nbsp; BALANCE.&nbsp; LONGEVITY.'),
T('footer.about', 'Promuoviamo il movimento come cura di sé. Abitudini sostenibili per benessere fisico e mentale.'),
T('footer.col.training-title', 'Training'),
T('footer.col.programs-title', 'Programmi'),
T('footer.col.contact-title', 'Contact'),
H('footer.copyright', '<strong>Insanitylab</strong>, All Rights Reserved'),
T('footer.social-label', 'Seguici', []),
T('footer.training.1.label', site.footerTraining[0].label, []),
T('footer.training.2.label', site.footerTraining[1].label, []),
T('footer.training.3.label', site.footerTraining[2].label, []),
T('footer.training.4.label', site.footerTraining[3].label, []),
T('footer.programs.1.label', site.footerPrograms[0].label, []),
T('footer.programs.2.label', site.footerPrograms[1].label, []),
T('footer.programs.3.label', site.footerPrograms[2].label, []),
T('footer.programs.4.label', site.footerPrograms[3].label, []),
T('footer.programs.5.label', site.footerPrograms[4].label, []),
T('footer.programs.6.label', site.footerPrograms[5].label, []),
// --- 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.', []),
// I task di estrazione aggiungono qui le entry di header/footer/pagine/data file.
];