--- import Base from '../layouts/Base.astro'; import T from '../components/content/T.astro'; import { t, resolveContent } from '../lib/content'; import { edizioneAttiva, tagEdizione } from '../data/promo-edizioni'; import { getSessionUser, SESSION_COOKIE } from '../lib/auth'; import { getDb } from '../lib/db'; // Landing promo/lancio integrata nel sito (header e footer del sito, non standalone). // I bottoni "Acquista" portano al gestionale bsport: l'utente crea l'account e sceglie lì // dentro il prodotto, poi paga via Stripe. Il link è uno solo per tutte le card, ed è voluto: // il catalogo lo presenta bsport dopo l'accesso. const BSPORT_URL = 'https://backoffice.bsport.io/login/signup?membership=3428'; export const prerender = false; // Quale promozione è in vetrina lo decide il calendario, non un deploy: l'edizione attiva è // quella la cui finestra contiene questo istante (vedi `data/promo-edizioni.ts`). Alla // scadenza dell'una subentra l'altra da sola, e quando non ce n'è nessuna la pagina lo dice // invece di lasciare online i prezzi di un'offerta finita. // // Anteprima: `?anteprima=2026-08-20` mostra l'edizione di quella data. Solo per chi è loggato // — un visitatore non deve poter vedere prezzi non ancora validi, o non più. const param = Astro.url.searchParams.get('anteprima'); const token = Astro.cookies.get(SESSION_COOKIE)?.value; const utente = token ? getSessionUser(getDb(), token) : null; // Una data secca (`2026-08-20`) sarebbe letta come UTC: la si porta a mezzogiorno italiano, // lontano dai confini di mezzanotte dove sta la differenza fra un'edizione e l'altra. const quandoParam = param && /^\d{4}-\d{2}-\d{2}$/.test(param) ? `${param}T12:00:00+02:00` : param; const dataLeggibile = Boolean(quandoParam && !Number.isNaN(Date.parse(quandoParam))); const anteprima = Boolean(utente && dataLeggibile); // Un'anteprima chiesta e non applicata va **detta**: altrimenti la pagina risponde con // l'edizione di oggi e due date diverse danno lo stesso risultato — che si legge come «la // funzione non funziona» invece che «non sei entrato». const anteprimaIgnorata = !param || anteprima ? null : (!utente ? 'login' : 'data'); const quando = anteprima ? new Date(quandoParam!) : new Date(); const ed = edizioneAttiva(quando); // La pagina cambia contenuto col passare del tempo: una copia in cache la congelerebbe alla // vecchia edizione, e lo scambio avverrebbe quando pare al proxy invece che a mezzanotte. Astro.response.headers.set('Cache-Control', 'no-store'); // Tag dell'edizione attiva: `tg('card.1.price')` → `promo.card.1.price` o `bundle.card.1.price`. const tg = (suffisso: string) => (ed ? tagEdizione(ed, suffisso) : suffisso); // Numero WhatsApp centralizzato: le risposte FAQ contengono il segnaposto {{whatsapp}} // che qui viene sostituito col valore del tag globale, così si aggiorna in un punto solo. const wa = t('global.whatsapp'); const waLink = `https://wa.me/39${wa.replace(/\D/g, '')}`; // Scadenza dell'edizione in corso (tag editabile dal pannello, ISO con offset). Viene solo // passata al client: è il JS a decidere se mostrare il countdown, perché il confronto va fatto // col tempo del visitatore. const countdownDeadline = ed ? t(tg('countdown.deadline')) : ''; --- ]*>/g, ' ').replace(/\s+/g, ' ').trim() : 'Le promozioni di apertura sono terminate: scrivici su WhatsApp per i percorsi attivi.'}>
{anteprima && (
Anteprima al {quando.toLocaleString('it-IT', { timeZone: 'Europe/Rome' })} — {ed ? ` edizione «${ed.id}»` : ' nessuna edizione attiva'}
)} {anteprimaIgnorata === 'login' && (
Anteprima non applicata: questa è la pagina di oggi. Entra da /admin/login e ricarica.
)} {anteprimaIgnorata === 'data' && (
Anteprima non applicata: «{param}» non è una data leggibile. Usa il formato 2026-08-25.
)} {ed ? (
{ed.launch && (
)}
{ed.cards.map((c) => (
{c.featured && } {c.question && }
{c.unit && }
    {Array.from({ length: c.features }, (_, i) => i + 1).map((m) => ( ))}
{c.desc && } {c.note && (
)}
))}
{ed.faq.map((n) => { const a = resolveContent(tg(`faq.${n}.a`)); return (
+

); })}
) : (
)}