events yogibook

This commit is contained in:
2026-09-17 10:22:30 +02:00
parent 434eb8bc48
commit 5df1f2d261
26 changed files with 4394 additions and 30 deletions
+995
View File
@@ -0,0 +1,995 @@
<?php
require_once('include/headscript.php');
/**
* Anteprima "scheda evento" pubblica (prototipo del widget WordPress).
* Versione COMPATTA + espandibile:
* - stato compatto: data, titolo, capacità/luogo/orario, prezzo, prenotazione
* - "Dettagli": espande la scheda completa (cover, descrizione, gallery, mappa,
* per chi è adatto / cosa portare / istruzioni)
* Prenotazione a passo unico: qty + "Prenota" rivela Nome/Cognome/Email + consensi
* nella stessa card. In anteprima "Conferma" non invia: mostra un riepilogo.
*/
$pdo = DBHandlerSelect::getInstance()->getConnection();
function e($value): string
{
return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
}
$id = isset($_GET['id']) ? (int) $_GET['id'] : 0;
if ($id <= 0) {
http_response_code(404);
echo 'Evento non trovato';
exit();
}
$st = $pdo->prepare("SELECT e.*, u.first_name AS cf, u.last_name AS cl
FROM events e LEFT JOIN auth_users u ON u.id = e.user_id
WHERE e.id = ?");
$st->execute([$id]);
$ev = $st->fetch(PDO::FETCH_ASSOC);
if (!$ev) {
http_response_code(404);
echo 'Evento non trovato';
exit();
}
$bs = $pdo->prepare("SELECT COALESCE(SUM(seats),0) FROM event_bookings WHERE event_id = ? AND status <> 'cancelled'");
$bs->execute([$id]);
$booked = (int) $bs->fetchColumn();
$maxSeats = (int) $ev['max_seats'];
$remaining = max(0, $maxSeats - $booked);
$soldOut = $remaining <= 0;
$maxPer = max(1, (int) $ev['max_per_booking']);
$maxQty = $soldOut ? 0 : max(1, min($maxPer, $remaining));
$cost = (float) $ev['cost'];
$curr = $ev['currency'] ?: 'EUR';
$symbols = ['EUR' => '€', 'USD' => '$', 'GBP' => '£', 'CHF' => 'CHF'];
$sym = $symbols[$curr] ?? $curr;
$priceLabel = $cost > 0 ? $sym . number_format($cost, 2, ',', '.') : 'Gratuito';
$color = (isset($ev['color']) && preg_match('/^#[0-9a-fA-F]{6}$/', $ev['color'])) ? $ev['color'] : '#4CAF50';
$gallery = [];
if (!empty($ev['gallery'])) {
$g = json_decode($ev['gallery'], true);
if (is_array($g)) {
$gallery = $g;
}
}
$mesiAbbr = [1 => 'GEN', 2 => 'FEB', 3 => 'MAR', 4 => 'APR', 5 => 'MAG', 6 => 'GIU', 7 => 'LUG', 8 => 'AGO', 9 => 'SET', 10 => 'OTT', 11 => 'NOV', 12 => 'DIC'];
$mesi = [1 => 'gennaio', 2 => 'febbraio', 3 => 'marzo', 4 => 'aprile', 5 => 'maggio', 6 => 'giugno', 7 => 'luglio', 8 => 'agosto', 9 => 'settembre', 10 => 'ottobre', 11 => 'novembre', 12 => 'dicembre'];
$startTs = strtotime($ev['starts_at']);
$dayNum = (int) date('j', $startTs);
$monAbbr = $mesiAbbr[(int) date('n', $startTs)];
$dateLong = $dayNum . ' ' . $mesi[(int) date('n', $startTs)] . ' ' . date('Y', $startTs);
$timeLabel = date('H:i', $startTs);
if (!empty($ev['ends_at'])) {
$timeLabel .= '–' . date('H:i', strtotime($ev['ends_at']));
}
$organizer = trim((string) $ev['organizer_name']) !== ''
? $ev['organizer_name']
: trim(($ev['cf'] ?? '') . ' ' . ($ev['cl'] ?? ''));
$mapQuery = trim((string) $ev['address']) !== ''
? $ev['address']
: (($ev['lat'] !== null && $ev['lng'] !== null) ? ($ev['lat'] . ',' . $ev['lng']) : '');
$hasDetails = trim((string) $ev['description']) !== ''
|| trim((string) $ev['suitable_for']) !== ''
|| trim((string) $ev['what_to_bring']) !== ''
|| trim((string) $ev['instructions']) !== ''
|| !empty($ev['cover_image'])
|| !empty($gallery)
|| $mapQuery !== '';
function textBlock(?string $t): string
{
return nl2br(e((string) $t));
}
?>
<!doctype html>
<html lang="it">
<head>
<meta charset="utf-8" />
<title><?php echo e($ev['title']); ?> — Anteprima</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,400;9..144,500;9..144,600&family=Inter:wght@400;500;600&display=swap" rel="stylesheet">
<style>
:root {
--accent: <?php echo $color; ?>;
--ink: #2b2622;
--muted: #8c837c;
--line: #ece7e2;
--paper: #faf8f6;
--card: #ffffff;
--accent-soft: color-mix(in srgb, var(--accent) 10%, white);
--accent-tint: color-mix(in srgb, var(--accent) 16%, white);
--accent-ink: color-mix(in srgb, var(--accent) 62%, black);
}
* {
box-sizing: border-box;
}
body {
margin: 0;
background: var(--paper);
color: var(--ink);
font-family: 'Inter', system-ui, -apple-system, sans-serif;
line-height: 1.55;
-webkit-font-smoothing: antialiased;
}
.preview-bar {
background: var(--ink);
color: #f4efe9;
font-size: 13px;
text-align: center;
padding: 8px 16px;
}
.wrap {
max-width: 720px;
margin: 0 auto;
padding: 26px 18px 60px;
}
.card {
background: var(--card);
border-radius: 18px;
overflow: hidden;
box-shadow: 0 14px 40px rgba(43, 38, 34, 0.10);
}
/* ---------- HEADER COMPATTO ---------- */
.chead {
display: flex;
gap: 16px;
padding: 18px 22px;
align-items: flex-start;
}
.date-chip {
flex-shrink: 0;
border: 1px solid var(--line);
border-radius: 12px;
padding: 7px 11px 5px;
text-align: center;
min-width: 54px;
}
.date-chip .m {
font-size: 10.5px;
font-weight: 600;
letter-spacing: .12em;
color: var(--accent-ink);
}
.date-chip .d {
font-family: 'Fraunces', serif;
font-size: 23px;
font-weight: 600;
color: var(--ink);
line-height: 1;
}
.chead .main {
flex: 1;
min-width: 0;
}
.title-row {
display: flex;
align-items: center;
gap: 10px;
flex-wrap: wrap;
}
.title-row h1 {
font-family: 'Fraunces', serif;
font-weight: 500;
font-size: 21px;
margin: 0;
color: var(--ink);
}
.toggle {
background: none;
border: none;
color: var(--accent-ink);
font-weight: 600;
font-size: 13px;
cursor: pointer;
display: inline-flex;
align-items: center;
gap: 4px;
padding: 2px 2px;
}
.toggle svg {
width: 15px;
height: 15px;
transition: transform .2s;
}
.toggle.open svg {
transform: rotate(180deg);
}
.cmeta {
display: flex;
flex-wrap: wrap;
gap: 6px 20px;
margin-top: 9px;
}
.cmeta .item {
display: flex;
align-items: center;
gap: 7px;
color: var(--muted);
font-size: 13.5px;
}
.cmeta .item svg {
width: 16px;
height: 16px;
color: var(--accent-ink);
flex-shrink: 0;
}
.cmeta .item strong {
color: var(--ink);
font-weight: 600;
}
.price-badge {
flex-shrink: 0;
align-self: flex-start;
background: var(--accent);
color: #fff;
font-weight: 600;
font-size: 14px;
padding: 7px 13px;
border-radius: 999px;
}
/* ---------- DETTAGLI ESPANDIBILI ---------- */
.details {
display: none;
border-top: 1px solid var(--line);
}
.details.open {
display: block;
}
.hero {
position: relative;
height: 260px;
background: linear-gradient(135deg, var(--accent-ink), var(--accent));
}
.hero img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}
.hero .emblem {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
color: rgba(255, 255, 255, .35);
}
.hero .emblem svg {
width: 72px;
height: 72px;
}
.body {
padding: 22px;
}
.section+.section {
margin-top: 24px;
}
.section h3 {
font-family: 'Fraunces', serif;
font-weight: 600;
font-size: 18px;
color: var(--ink);
margin: 0 0 10px;
display: flex;
align-items: center;
gap: 9px;
}
.section h3 svg {
width: 19px;
height: 19px;
color: var(--accent-ink);
}
.desc {
font-size: 15px;
color: #4a433d;
}
.desc :where(p) {
margin: 0 0 10px;
}
.desc :where(img) {
max-width: 100%;
border-radius: 10px;
}
.detail-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
gap: 12px;
}
.detail {
background: var(--accent-soft);
border: 1px solid color-mix(in srgb, var(--accent) 18%, white);
border-radius: 13px;
padding: 14px 15px;
}
.detail .h {
display: flex;
align-items: center;
gap: 8px;
font-weight: 600;
color: var(--accent-ink);
margin-bottom: 6px;
font-size: 13.5px;
}
.detail .h svg {
width: 16px;
height: 16px;
}
.detail .t {
font-size: 13.5px;
color: #4a433d;
}
.gallery {
display: flex;
gap: 10px;
flex-wrap: wrap;
}
.gallery img {
width: 100px;
height: 100px;
object-fit: cover;
border-radius: 11px;
cursor: pointer;
transition: transform .15s, box-shadow .15s;
}
.gallery img:hover {
transform: translateY(-2px);
box-shadow: 0 8px 18px rgba(0, 0, 0, .15);
}
.map {
border: 0;
width: 100%;
height: 210px;
border-radius: 13px;
display: block;
}
/* ---------- BOOKING ---------- */
.booking {
padding: 18px 22px;
border-top: 1px solid var(--line);
}
.booking-compact {
display: flex;
align-items: flex-end;
gap: 18px;
flex-wrap: wrap;
}
.bc-block .lbl {
font-size: 12px;
color: var(--muted);
text-transform: uppercase;
letter-spacing: .05em;
margin-bottom: 5px;
}
.bc-title {
font-family: 'Fraunces', serif;
font-weight: 600;
font-size: 18px;
color: var(--ink);
margin-right: auto;
align-self: center;
}
.stepper {
display: flex;
align-items: center;
border: 1px solid var(--line);
border-radius: 11px;
overflow: hidden;
background: #fff;
}
.stepper button {
border: none;
background: #fff;
width: 42px;
height: 44px;
font-size: 21px;
color: var(--accent-ink);
cursor: pointer;
}
.stepper button:disabled {
color: #cfc8c2;
cursor: not-allowed;
}
.stepper .n {
width: 50px;
text-align: center;
font-size: 16px;
font-weight: 600;
}
.total-box .val {
font-family: 'Fraunces', serif;
font-size: 24px;
font-weight: 600;
color: var(--accent-ink);
}
.cta {
border: none;
border-radius: 11px;
background: var(--accent);
color: #fff;
font-size: 15px;
font-weight: 600;
font-family: inherit;
cursor: pointer;
transition: filter .15s;
}
.cta:hover {
filter: brightness(.94);
}
.cta:disabled {
background: #cfc8c2;
cursor: not-allowed;
}
.cta.compact {
padding: 13px 24px;
}
.cta.full {
width: 100%;
padding: 14px;
margin-top: 6px;
}
.booking-form {
display: none;
margin-top: 18px;
padding-top: 18px;
border-top: 1px dashed var(--line);
}
.booking-form.open {
display: block;
}
.field-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
}
.field {
margin-bottom: 12px;
}
.field label {
display: block;
font-size: 13px;
font-weight: 600;
color: var(--ink);
margin-bottom: 5px;
}
.field label .req {
color: #c0392b;
}
.field input {
width: 100%;
border: 1px solid var(--line);
border-radius: 10px;
padding: 11px 13px;
font-size: 15px;
font-family: inherit;
background: #fff;
color: var(--ink);
}
.field input:focus {
outline: none;
border-color: var(--accent);
box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 22%, white);
}
.consent {
font-size: 13px;
color: #4a433d;
display: flex;
gap: 9px;
align-items: flex-start;
margin-bottom: 9px;
}
.consent input {
margin-top: 3px;
}
.consent a {
color: var(--accent-ink);
}
.soldout-note {
text-align: center;
font-weight: 600;
color: #c0392b;
}
.form-error {
color: #c0392b;
font-size: 13px;
margin-top: 8px;
min-height: 18px;
}
.booking-done {
text-align: center;
padding: 6px 4px;
}
.booking-done .ok {
width: 52px;
height: 52px;
border-radius: 50%;
background: var(--accent);
color: #fff;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto 10px;
}
.booking-done h3 {
margin: 0 0 6px;
font-family: 'Fraunces', serif;
}
.booking-done p {
color: #4a433d;
font-size: 14px;
margin: 2px 0;
}
.booking-done .hintline {
color: var(--muted);
font-size: 12.5px;
margin-top: 10px;
}
.lightbox {
position: fixed;
inset: 0;
background: rgba(24, 19, 16, .9);
display: none;
align-items: center;
justify-content: center;
z-index: 50;
padding: 24px;
}
.lightbox.open {
display: flex;
}
.lightbox img {
max-width: 92vw;
max-height: 86vh;
border-radius: 12px;
}
.lightbox .close {
position: absolute;
top: 18px;
right: 22px;
color: #fff;
font-size: 34px;
cursor: pointer;
background: none;
border: none;
line-height: 1;
}
@media (max-width: 560px) {
.hero {
height: 200px;
}
.title-row h1 {
font-size: 19px;
}
.field-row {
grid-template-columns: 1fr;
}
.booking-compact {
gap: 14px;
}
.bc-title {
width: 100%;
margin-bottom: 4px;
}
}
@media (prefers-reduced-motion: reduce) {
* {
transition: none !important;
}
}
</style>
</head>
<body>
<div class="preview-bar">Anteprima — la card parte compatta; “Dettagli” apre la scheda completa. Il pulsante non invia nulla.</div>
<div class="wrap">
<div class="card">
<!-- HEADER COMPATTO -->
<div class="chead">
<div class="date-chip">
<div class="m"><?php echo e($monAbbr); ?></div>
<div class="d"><?php echo $dayNum; ?></div>
</div>
<div class="main">
<div class="title-row">
<h1><?php echo e($ev['title']); ?></h1>
<?php if ($hasDetails) : ?>
<button type="button" class="toggle" id="details-toggle" aria-expanded="false">
<span id="toggle-label">Dettagli</span>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="m6 9 6 6 6-6" />
</svg>
</button>
<?php endif; ?>
</div>
<div class="cmeta">
<div class="item">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7">
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2" />
<circle cx="12" cy="7" r="4" />
</svg>
<span>Capacità <strong><?php echo $booked; ?>/<?php echo $maxSeats; ?></strong></span>
</div>
<?php if (trim((string) $ev['address']) !== '') : ?>
<div class="item">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7">
<path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z" />
<circle cx="12" cy="10" r="3" />
</svg>
<span><?php echo e($ev['address']); ?></span>
</div>
<?php endif; ?>
<div class="item">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7">
<circle cx="12" cy="12" r="9" />
<path d="M12 7v5l3 2" />
</svg>
<span><?php echo e($dateLong); ?> · <?php echo e($timeLabel); ?></span>
</div>
</div>
</div>
<div class="price-badge"><?php echo e($priceLabel); ?></div>
</div>
<!-- DETTAGLI ESPANDIBILI -->
<?php if ($hasDetails) : ?>
<div class="details" id="details">
<div class="hero">
<?php if (!empty($ev['cover_image'])) : ?>
<img src="<?php echo e($ev['cover_image']); ?>" alt="<?php echo e($ev['title']); ?>">
<?php else : ?>
<div class="emblem"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.4">
<rect x="3" y="3" width="18" height="18" rx="2" />
<circle cx="9" cy="9" r="2" />
<path d="m21 15-5-5L5 21" />
</svg></div>
<?php endif; ?>
</div>
<div class="body">
<?php if (trim((string) $ev['description']) !== '') : ?>
<div class="section">
<div class="desc"><?php echo $ev['description']; ?></div>
</div>
<?php endif; ?>
<?php if (trim((string) $ev['suitable_for']) !== '' || trim((string) $ev['what_to_bring']) !== '' || trim((string) $ev['instructions']) !== '') : ?>
<div class="section">
<div class="detail-grid">
<?php if (trim((string) $ev['suitable_for']) !== '') : ?>
<div class="detail">
<div class="h"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7">
<path d="M20.8 5.6a5 5 0 0 0-7-.1L12 7l-1.8-1.5a5 5 0 1 0-6.6 7.5L12 21l8.4-8a5 5 0 0 0 .4-7.4z" />
</svg> Per chi è adatto</div>
<div class="t"><?php echo textBlock($ev['suitable_for']); ?></div>
</div>
<?php endif; ?>
<?php if (trim((string) $ev['what_to_bring']) !== '') : ?>
<div class="detail">
<div class="h"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7">
<path d="M6 7h12l1 13H5L6 7z" />
<path d="M9 7a3 3 0 0 1 6 0" />
</svg> Cosa portare</div>
<div class="t"><?php echo textBlock($ev['what_to_bring']); ?></div>
</div>
<?php endif; ?>
<?php if (trim((string) $ev['instructions']) !== '') : ?>
<div class="detail">
<div class="h"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7">
<circle cx="12" cy="12" r="9" />
<path d="M12 11v5" />
<path d="M12 8h.01" />
</svg> Istruzioni</div>
<div class="t"><?php echo textBlock($ev['instructions']); ?></div>
</div>
<?php endif; ?>
</div>
</div>
<?php endif; ?>
<?php if (!empty($gallery)) : ?>
<div class="section">
<h3><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7">
<rect x="3" y="3" width="18" height="18" rx="2" />
<circle cx="9" cy="9" r="2" />
<path d="m21 15-5-5L5 21" />
</svg> Galleria</h3>
<div class="gallery">
<?php foreach ($gallery as $g) : ?>
<img src="<?php echo e($g); ?>" alt="" data-full="<?php echo e($g); ?>">
<?php endforeach; ?>
</div>
</div>
<?php endif; ?>
<?php if ($mapQuery !== '') : ?>
<div class="section">
<h3><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7">
<path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z" />
<circle cx="12" cy="10" r="3" />
</svg> Dove</h3>
<iframe class="map" loading="lazy" src="https://www.google.com/maps?q=<?php echo urlencode($mapQuery); ?>&output=embed"></iframe>
</div>
<?php endif; ?>
</div>
</div>
<?php endif; ?>
<!-- BOOKING (compatto, passo unico) -->
<div class="booking" id="booking">
<?php if ($soldOut) : ?>
<div class="soldout-note">Evento esaurito — non ci sono più posti disponibili.</div>
<?php else : ?>
<div class="booking-compact">
<div class="bc-title">Prenota questo evento</div>
<div class="bc-block">
<div class="lbl">Persone</div>
<div class="stepper">
<button type="button" id="q-minus">−</button>
<span class="n" id="q-num">1</span>
<button type="button" id="q-plus">+</button>
</div>
</div>
<div class="bc-block total-box">
<div class="lbl">Totale</div>
<div class="val" id="q-total"><?php echo e($priceLabel); ?></div>
</div>
<button type="button" class="cta compact" id="b-open">Prenota</button>
</div>
<div class="booking-form" id="booking-form">
<div class="field-row">
<div class="field">
<label>Nome <span class="req">*</span></label>
<input type="text" id="b-name" placeholder="Il tuo nome">
</div>
<div class="field">
<label>Cognome <span class="req">*</span></label>
<input type="text" id="b-surname" placeholder="Il tuo cognome">
</div>
</div>
<div class="field">
<label>Email <span class="req">*</span></label>
<input type="email" id="b-email" placeholder="latua@email.it">
</div>
<label class="consent">
<input type="checkbox" id="b-consent">
<span>Ho letto e accetto l'<a href="#" target="_blank">informativa privacy</a>. <span class="req">*</span></span>
</label>
<label class="consent">
<input type="checkbox" id="b-marketing">
<span>Desidero ricevere aggiornamenti e novità via email.</span>
</label>
<button type="button" class="cta full" id="b-submit">Conferma prenotazione</button>
<div class="form-error" id="b-error"></div>
</div>
<?php endif; ?>
</div>
</div>
</div>
<div class="lightbox" id="lightbox">
<button class="close" id="lb-close">&times;</button>
<img src="" alt="" id="lb-img">
</div>
<script>
var COST = <?php echo json_encode($cost); ?>;
var SYM = <?php echo json_encode($sym); ?>;
var MAXQTY = <?php echo (int) $maxQty; ?>;
function fmtMoney(v) {
if (v <= 0) return 'Gratuito';
return SYM + v.toLocaleString('it-IT', {
minimumFractionDigits: 2,
maximumFractionDigits: 2
});
}
// Toggle dettagli
var dt = document.getElementById('details-toggle');
if (dt) {
dt.addEventListener('click', function() {
var box = document.getElementById('details');
var open = box.classList.toggle('open');
dt.classList.toggle('open', open);
dt.setAttribute('aria-expanded', open ? 'true' : 'false');
document.getElementById('toggle-label').textContent = open ? 'Chiudi' : 'Dettagli';
});
}
(function() {
var qty = 1;
var numEl = document.getElementById('q-num');
if (!numEl) return;
var totEl = document.getElementById('q-total');
var minus = document.getElementById('q-minus');
var plus = document.getElementById('q-plus');
var submitBtn = document.getElementById('b-submit');
function refresh() {
numEl.textContent = qty;
totEl.textContent = fmtMoney(qty * COST);
minus.disabled = qty <= 1;
plus.disabled = qty >= MAXQTY;
if (submitBtn) submitBtn.textContent = 'Conferma prenotazione' + (COST > 0 ? ' · ' + fmtMoney(qty * COST) : '');
}
minus.addEventListener('click', function() {
if (qty > 1) {
qty--;
refresh();
}
});
plus.addEventListener('click', function() {
if (qty < MAXQTY) {
qty++;
refresh();
}
});
refresh();
// "Prenota" rivela i campi (stessa card, passo unico)
document.getElementById('b-open').addEventListener('click', function() {
document.getElementById('booking-form').classList.add('open');
this.style.display = 'none';
document.getElementById('b-name').focus();
});
submitBtn.addEventListener('click', function() {
var name = document.getElementById('b-name').value.trim();
var surname = document.getElementById('b-surname').value.trim();
var email = document.getElementById('b-email').value.trim();
var consent = document.getElementById('b-consent').checked;
var err = document.getElementById('b-error');
if (!name || !surname) {
err.textContent = 'Inserisci nome e cognome.';
return;
}
if (!/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(email)) {
err.textContent = 'Inserisci un\'email valida.';
return;
}
if (!consent) {
err.textContent = 'Devi accettare l\'informativa privacy.';
return;
}
err.textContent = '';
var marketing = document.getElementById('b-marketing').checked;
document.getElementById('booking').innerHTML =
'<div class="booking-done">' +
' <div class="ok"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" width="24" height="24"><path d="M20 6 9 17l-5-5"/></svg></div>' +
' <h3>Anteprima prenotazione</h3>' +
' <p><strong>' + name + ' ' + surname + '</strong> · ' + email + '</p>' +
' <p>' + qty + ' ' + (qty === 1 ? 'persona' : 'persone') + ' · Totale <strong>' + fmtMoney(qty * COST) + '</strong></p>' +
' <p>Marketing: ' + (marketing ? 'sì' : 'no') + '</p>' +
' <p class="hintline">Nel widget pubblico, qui parte la prenotazione reale (POST all\'endpoint).</p>' +
'</div>';
});
})();
// Lightbox galleria
var lb = document.getElementById('lightbox');
var lbImg = document.getElementById('lb-img');
document.querySelectorAll('.gallery img').forEach(function(img) {
img.addEventListener('click', function() {
lbImg.src = img.getAttribute('data-full');
lb.classList.add('open');
});
});
function closeLb() {
lb.classList.remove('open');
lbImg.src = '';
}
document.getElementById('lb-close').addEventListener('click', closeLb);
lb.addEventListener('click', function(e) {
if (e.target === lb) closeLb();
});
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape') closeLb();
});
</script>
</body>
</html>