From e6c493b756be6f6e9412aca711f44923a1e5dc85 Mon Sep 17 00:00:00 2001 From: AdrianoDev Date: Mon, 20 Jul 2026 21:06:28 +0200 Subject: [PATCH] Contatti obbligatori, hero senza foto, menu Promo2 solo admin MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Contatti: nome, cognome, telefono e mail resi obbligatori (validazione client + server); il form del footer ora include cognome e telefono. - Home hero: rimossa la foto (file conservato su disco), fascia ridotta da 100vh a padding contenuto, testo allargato. - Menu: la voce "Promo 2 (test)" è visibile solo agli admin loggati. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/components/ContactForm.astro | 4 ++-- src/components/Header.astro | 4 +++- src/components/home/HeroSlider.astro | 16 +++++----------- src/data/site.ts | 3 ++- src/lib/contact.ts | 2 ++ tests/contact.test.ts | 8 +++++--- 6 files changed, 19 insertions(+), 18 deletions(-) diff --git a/src/components/ContactForm.astro b/src/components/ContactForm.astro index d7865ed..cb7c3ea 100644 --- a/src/components/ContactForm.astro +++ b/src/components/ContactForm.astro @@ -14,8 +14,8 @@ const { variant = 'full' } = Astro.props; data-msg-error-network={t('form.msg.error-network')} > - {variant === 'full' && } - {variant === 'full' && } + + diff --git a/src/components/Header.astro b/src/components/Header.astro index bfcbcdc..1729495 100644 --- a/src/components/Header.astro +++ b/src/components/Header.astro @@ -16,7 +16,9 @@ const areaHref = user?.role === 'superuser' ? '/admin/content' : '/admin';
diff --git a/src/components/home/HeroSlider.astro b/src/components/home/HeroSlider.astro index 869d980..1d16bdf 100644 --- a/src/components/home/HeroSlider.astro +++ b/src/components/home/HeroSlider.astro @@ -1,10 +1,9 @@ --- -import hero1 from '../../assets/img/hero-1.png'; +// Foto hero rimossa ma conservata su disco per riuso futuro: assets/img/hero-1.png import T from '../content/T.astro'; -import TImg from '../content/TImg.astro'; import { t } from '../../lib/content'; const slides = [ - { titleTags: ['home.hero.title-line-1', 'home.hero.title-line-2', 'home.hero.title-line-3'], image: hero1, alt: 'Atleti InsanityLab' }, + { titleTags: ['home.hero.title-line-1', 'home.hero.title-line-2', 'home.hero.title-line-3'] }, ]; ---
@@ -15,7 +14,6 @@ const slides = [

{s.titleTags.map((tag) => )}

{t('home.hero.cta')}
- ))} @@ -34,32 +32,28 @@ const slides = [ .hero__slide.is-active { display: block; } /* Come nel Figma: figure a piena altezza ancorate in basso a destra, testo sovrapposto sulla sinistra. */ - .hero__in { position: relative; display: flex; align-items: center; min-height: calc(100vh - var(--header-h)); } + .hero__in { position: relative; display: flex; align-items: center; min-height: 0; padding-block: 90px; } /* Lo span .hero__line è reso dal componente e non riceve il data-astro-cid di questo componente: si ancora a .hero__text (che il cid ce l'ha) + :global. */ .hero__text h1 :global(.hero__line) { display: block; } - .hero__text { position: relative; z-index: 1; max-width: 46%; } + .hero__text { position: relative; z-index: 1; max-width: 640px; } .hero__text h1 { font-weight: 500; } - .hero__img { position: absolute; right: 0; bottom: 0; height: 100%; width: auto; max-width: 80%; object-fit: contain; object-position: right bottom; } .hero__arrow { position: absolute; top: 50%; transform: translateY(-50%); background: none; border: 0; font-size: 3rem; color: var(--c-heading); cursor: pointer; } .hero__arrow--prev { left: 12px; } .hero__arrow--next { right: 12px; } @media (max-width: 991px) { .hero__in { flex-direction: column; align-items: flex-start; padding-block: 40px; min-height: 0; gap: 24px; } .hero__text { max-width: none; } - .hero__img { position: static; max-width: 100%; height: auto; } } /* Ingresso al load stile Slider Revolution del template */ @keyframes hero-up { from { opacity: 0; transform: translateY(60px); } to { opacity: 1; transform: none; } } - @keyframes hero-side { from { opacity: 0; transform: translateX(60px); } to { opacity: 1; transform: none; } } :global(html.js) .hero__text h1 :global(.hero__line) { opacity: 0; animation: hero-up .8s ease forwards; } :global(html.js) .hero__text h1 :global(.hero__line:nth-child(1)) { animation-delay: .15s; } :global(html.js) .hero__text h1 :global(.hero__line:nth-child(2)) { animation-delay: .3s; } :global(html.js) .hero__text h1 :global(.hero__line:nth-child(3)) { animation-delay: .45s; } :global(html.js) .hero__text .btn { opacity: 0; animation: hero-up .8s ease .7s forwards; } - :global(html.js) .hero__img { opacity: 0; animation: hero-side 1s ease .2s forwards; } @media (prefers-reduced-motion: reduce) { - :global(html.js) .hero__text h1 :global(.hero__line), :global(html.js) .hero__text .btn, :global(html.js) .hero__img { opacity: 1; animation: none; } + :global(html.js) .hero__text h1 :global(.hero__line), :global(html.js) .hero__text .btn { opacity: 1; animation: none; } } diff --git a/src/data/site.ts b/src/data/site.ts index 3ecebb7..2ade69f 100644 --- a/src/data/site.ts +++ b/src/data/site.ts @@ -18,7 +18,8 @@ export const site = { { label: "Blog & Edugo", href: "/blog" }, { label: "Promo ed eventi", href: "/promo-eventi" }, { label: "Contatti", href: "/contact" }, - { label: "Promo 2 (test)", href: "/promo-eventi2" }, + // Pagina di test: visibile solo agli admin loggati, nascosta al pubblico. + { label: "Promo 2 (test)", href: "/promo-eventi2", adminOnly: true }, ], footerTraining: [ { label: "One to one", href: "/training#one-to-one" }, diff --git a/src/lib/contact.ts b/src/lib/contact.ts index 96f9a32..1140aba 100644 --- a/src/lib/contact.ts +++ b/src/lib/contact.ts @@ -24,6 +24,8 @@ export function validateContact(data: unknown): }; if (value.website) return { ok: false, error: 'Richiesta non valida.' }; if (!value.firstName) return { ok: false, error: 'Il nome è obbligatorio.' }; + if (!value.lastName) return { ok: false, error: 'Il cognome è obbligatorio.' }; + if (!value.phone) return { ok: false, error: 'Il telefono è obbligatorio.' }; if (!EMAIL_RE.test(value.email)) return { ok: false, error: 'Inserisci una e-mail valida.' }; if (!value.message) return { ok: false, error: 'Il messaggio è obbligatorio.' }; if (value.message.length > 5000) return { ok: false, error: 'Messaggio troppo lungo.' }; diff --git a/tests/contact.test.ts b/tests/contact.test.ts index 9fdbf67..d848382 100644 --- a/tests/contact.test.ts +++ b/tests/contact.test.ts @@ -2,13 +2,13 @@ import { describe, it, expect } from 'vitest'; import { validateContact } from '../src/lib/contact'; describe('validateContact', () => { - const good = { firstName: 'Mario', email: 'mario@example.com', message: 'Ciao, vorrei informazioni.' }; + const good = { firstName: 'Mario', lastName: 'Rossi', phone: '3331234567', email: 'mario@example.com', message: 'Ciao, vorrei informazioni.' }; - it('accetta payload valido e normalizza i campi opzionali', () => { + it('accetta payload valido', () => { const r = validateContact(good); expect(r.ok).toBe(true); if (r.ok) { - expect(r.value.lastName).toBe(''); + expect(r.value.lastName).toBe('Rossi'); expect(r.value.email).toBe('mario@example.com'); } }); @@ -19,6 +19,8 @@ describe('validateContact', () => { it('rifiuta campi obbligatori mancanti o vuoti', () => { expect(validateContact({ ...good, firstName: ' ' }).ok).toBe(false); + expect(validateContact({ ...good, lastName: ' ' }).ok).toBe(false); + expect(validateContact({ ...good, phone: '' }).ok).toBe(false); expect(validateContact({ ...good, message: '' }).ok).toBe(false); expect(validateContact(null).ok).toBe(false); });