From dea86cb6dfb1533cd45b745a3d6974f180e4b33b Mon Sep 17 00:00:00 2001 From: solocla Date: Thu, 17 Sep 2026 16:53:51 +0200 Subject: [PATCH] create events api wp --- public/apiwp/cancel.php | 257 +++++++++ public/event_form.php | 10 +- public/event_locations.php | 4 +- public/event_participants.php | 60 ++- public/event_preview.php | 45 +- public/event_save.php | 6 + public/event_waitlist.php | 59 +++ wp plugin/yogasoul-eventi.zip | Bin 0 -> 9818 bytes .../assets/yogasoul-eventi.css | 497 ++++++++++++++++++ .../yogasoul-eventi/assets/yogasoul-eventi.js | 246 +++++++++ wp plugin/yogasoul-eventi/yogasoul-eventi.php | 275 ++++++++++ 11 files changed, 1433 insertions(+), 26 deletions(-) create mode 100644 public/apiwp/cancel.php create mode 100644 public/event_waitlist.php create mode 100644 wp plugin/yogasoul-eventi.zip create mode 100644 wp plugin/yogasoul-eventi/assets/yogasoul-eventi.css create mode 100644 wp plugin/yogasoul-eventi/assets/yogasoul-eventi.js create mode 100644 wp plugin/yogasoul-eventi/yogasoul-eventi.php diff --git a/public/apiwp/cancel.php b/public/apiwp/cancel.php new file mode 100644 index 0000000..21c084a --- /dev/null +++ b/public/apiwp/cancel.php @@ -0,0 +1,257 @@ + mostra pagina di conferma (NON annulla: + * evita che gli scanner email prefetchino il link e annullino da soli) + * POST (token + confirm=1) -> annulla e, se la prenotazione occupava posti, + * promuove automaticamente la prima in lista d'attesa. + */ +$pdo = pdo_conn(); +$token = trim($_POST['token'] ?? $_GET['token'] ?? ''); +$isPost = ($_SERVER['REQUEST_METHOD'] ?? 'GET') === 'POST'; + +$booking = null; +if ($token !== '') { + $q = $pdo->prepare("SELECT b.*, e.title AS ev_title, e.starts_at AS ev_starts, e.id AS eid + FROM event_bookings b JOIN events e ON e.id = b.event_id + WHERE b.token = ?"); + $q->execute([$token]); + $booking = $q->fetch(PDO::FETCH_ASSOC); +} + +$state = 'form'; // form | notfound | already | toolate | done +$promotedCount = 0; + +if (!$booking) { + $state = 'notfound'; +} elseif ($booking['status'] === 'cancelled') { + $state = 'already'; +} elseif (strtotime($booking['ev_starts']) <= time()) { + $state = 'toolate'; +} elseif ($isPost && !empty($_POST['confirm'])) { + try { + $pdo->beginTransaction(); + // Lock evento per serializzare con eventuali prenotazioni concorrenti + $pdo->prepare("SELECT id FROM events WHERE id = ? FOR UPDATE")->execute([(int) $booking['eid']]); + + // Rileggo lo stato aggiornato della prenotazione + $bl = $pdo->prepare("SELECT * FROM event_bookings WHERE id = ?"); + $bl->execute([(int) $booking['id']]); + $fresh = $bl->fetch(PDO::FETCH_ASSOC); + + if ($fresh && $fresh['status'] !== 'cancelled') { + $wasOccupying = in_array($fresh['status'], ['confirmed', 'pending'], true); + $pdo->prepare("UPDATE event_bookings SET status = 'cancelled', updated_at = NOW() WHERE id = ?") + ->execute([(int) $fresh['id']]); + + if ($wasOccupying) { + $promoted = promoteFromWaitlist($pdo, (int) $booking['eid']); + $promotedCount = count($promoted); + // TODO Step 4: email "sei stato promosso" ai $promoted + } + } + $pdo->commit(); + $state = 'done'; + } catch (Throwable $ex) { + if ($pdo->inTransaction()) { + $pdo->rollBack(); + } + error_log('[apiwp/cancel] ' . $ex->getMessage()); + $state = 'error'; + } +} + +function e($v): string +{ + return htmlspecialchars((string) $v, ENT_QUOTES, 'UTF-8'); +} +?> + + + + + + Disdetta prenotazione — YogaSoul + + + + + + + + +
+ +
✕
+

Link non valido

+

Questa prenotazione non esiste o il link è errato.

+ + +
✓
+

Già annullata

+

Questa prenotazione risulta già annullata.

+ + +
⏱
+

Non più annullabile

+

L'evento è già iniziato: non è più possibile annullare online. Contattaci per assistenza.

+ + +
!
+

Si è verificato un errore

+

Riprova tra poco. Se il problema persiste, contattaci.

+ + +
✓
+

Prenotazione annullata

+

La tua prenotazione per è stata annullata.

+ 0) : ?> +

Un posto è stato assegnato alla prima persona in lista d'attesa.

+ + + +
?
+

Annullare la prenotazione?

+
+
+
· post
+
+

Confermi di voler annullare la prenotazione a nome di ?

+
+ + + +
+

Se hai cambiato idea, chiudi pure questa pagina.

+ +
+ + + \ No newline at end of file diff --git a/public/event_form.php b/public/event_form.php index 9612e1c..f4975ff 100644 --- a/public/event_form.php +++ b/public/event_form.php @@ -67,6 +67,7 @@ $val = [ 'what_to_bring' => $ev['what_to_bring'] ?? '', 'suitable_for' => $ev['suitable_for'] ?? '', 'gallery' => $ev['gallery'] ?? '', + 'waitlist_enabled' => ($ev['waitlist_enabled'] ?? 'Y') === 'Y', ]; $isEdit = $id > 0; @@ -531,7 +532,7 @@ $pageTitle = $isEdit ? 'Modifica evento' : 'Nuovo evento'; -
+
+
+ +
+ > + +
+
diff --git a/public/event_locations.php b/public/event_locations.php index a2ac5a9..d7dbac9 100644 --- a/public/event_locations.php +++ b/public/event_locations.php @@ -274,7 +274,7 @@ $isEdit = $editId > 0 && $edit;
-
+
@@ -327,7 +327,7 @@ $isEdit = $editId > 0 && $edit;
-
+
Sedi salvate
diff --git a/public/event_participants.php b/public/event_participants.php index c4fb0dc..4004be0 100644 --- a/public/event_participants.php +++ b/public/event_participants.php @@ -1,5 +1,6 @@ prepare("UPDATE event_bookings SET status = 'cancelled', updated_at = NOW() WHERE id = ? AND event_id = ?"); - $upd->execute([$bookingId, $eventId]); + try { + $pdo->beginTransaction(); + $pdo->prepare("SELECT id FROM events WHERE id = ? FOR UPDATE")->execute([$eventId]); + + $bl = $pdo->prepare("SELECT status FROM event_bookings WHERE id = ? AND event_id = ?"); + $bl->execute([$bookingId, $eventId]); + $bStatus = $bl->fetchColumn(); + + if ($bStatus !== false && $bStatus !== 'cancelled') { + $wasOccupying = in_array($bStatus, ['confirmed', 'pending'], true); + $pdo->prepare("UPDATE event_bookings SET status = 'cancelled', updated_at = NOW() WHERE id = ? AND event_id = ?") + ->execute([$bookingId, $eventId]); + if ($wasOccupying) { + // libera posti -> promuove la prima in lista d'attesa + promoteFromWaitlist($pdo, $eventId); + } + } + $pdo->commit(); + } catch (Throwable $ex) { + if ($pdo->inTransaction()) { + $pdo->rollBack(); + } + error_log('[participants cancel] ' . $ex->getMessage()); + } header('Location: event_participants.php?id=' . $eventId . '&message=booking_cancelled'); exit(); } @@ -52,15 +75,18 @@ $bStmt = $pdo->prepare("SELECT * FROM event_bookings WHERE event_id = ? ORDER BY $bStmt->execute([$eventId]); $bookings = $bStmt->fetchAll(PDO::FETCH_ASSOC); -// Riepilogo (solo prenotazioni non annullate) -$bookedSeats = 0; -$activeCount = 0; -$revenue = 0.0; +// Riepilogo: i posti occupati sono solo confermate/pending; la lista d'attesa a parte +$bookedSeats = 0; +$waitingSeats = 0; +$activeCount = 0; +$revenue = 0.0; foreach ($bookings as $b) { - if ($b['status'] !== 'cancelled') { + if (in_array($b['status'], ['confirmed', 'pending'], true)) { $bookedSeats += (int) $b['seats']; $activeCount++; $revenue += (float) $b['amount']; + } elseif ($b['status'] === 'waiting') { + $waitingSeats += (int) $b['seats']; } } $maxSeats = (int) $event['max_seats']; @@ -70,11 +96,13 @@ function bookingBadge(string $status): array { switch ($status) { case 'confirmed': - return ['Confermata', '#e7f7ee', '#1a7f52']; + return ['Confermata', '#e7f7ee', '#1a7f52']; case 'cancelled': - return ['Annullata', '#fdecea', '#c0392b']; + return ['Annullata', '#fdecea', '#c0392b']; + case 'waiting': + return ["Lista d'attesa", '#eef2fb', '#3a5bb8']; default: - return ['In attesa', '#fff4e5', '#b26a00']; + return ['In attesa', '#fff4e5', '#b26a00']; } } ?> @@ -290,19 +318,25 @@ function bookingBadge(string $status): array
-
+
/
Posti prenotati
-
+
+
+
+
In lista d'attesa
+
+
+
Iscrizioni attive
-
+
Incasso stimato
diff --git a/public/event_preview.php b/public/event_preview.php index 1ff0a61..adddb45 100644 --- a/public/event_preview.php +++ b/public/event_preview.php @@ -35,14 +35,17 @@ if (!$ev) { exit(); } -$bs = $pdo->prepare("SELECT COALESCE(SUM(seats),0) FROM event_bookings WHERE event_id = ? AND status <> 'cancelled'"); +$bs = $pdo->prepare("SELECT COALESCE(SUM(seats),0) FROM event_bookings WHERE event_id = ? AND status IN ('confirmed','pending')"); $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)); +$waitlistOn = ($ev['waitlist_enabled'] ?? 'N') === 'Y'; +$waitMode = $soldOut && $waitlistOn; // pieno ma con lista d'attesa +$hardFull = $soldOut && !$waitlistOn; // pieno senza lista d'attesa +$maxQty = $soldOut ? ($waitMode ? $maxPer : 0) : max(1, min($maxPer, $remaining)); $cost = (float) $ev['cost']; $curr = $ev['currency'] ?: 'EUR'; @@ -573,6 +576,15 @@ function textBlock(?string $t): string color: #c0392b; } + .waitlist-banner { + background: color-mix(in srgb, var(--accent) 12%, white); + color: var(--accent-ink); + border-radius: 12px; + padding: 12px 14px; + font-size: 13.5px; + margin-bottom: 14px; + } + .form-error { color: #c0392b; font-size: 13px; @@ -817,11 +829,14 @@ function textBlock(?string $t): string
- +
Evento esaurito — non ci sono più posti disponibili.
+ +
Posti esauriti — puoi iscriverti alla lista d'attesa: ti avviseremo se si libera un posto.
+
-
Prenota questo evento
+
Persone
@@ -834,7 +849,7 @@ function textBlock(?string $t): string
Totale
- +
@@ -860,12 +875,15 @@ function textBlock(?string $t): string Desidero ricevere aggiornamenti e novità via email. - +
+