getConnection();
// Forziamo l'id utente a intero: viene dalla sessione, ma è comunque
// buona norma non fidarsi mai e trattarlo come integer.
$iduserlogin = (int) $iduserlogin;
/* -------------------------------------------------------------------------
* 1) Calcolo del mese da visualizzare
* ---------------------------------------------------------------------- */
if (isset($_GET['prev_month'])) {
$currentMonthStart = preg_replace('/[^0-9\-]/', '', $_GET['prev_month']) . '-01';
} elseif (isset($_GET['next_month'])) {
$currentMonthStart = preg_replace('/[^0-9\-]/', '', $_GET['next_month']) . '-01';
} else {
$currentMonthStart = date("Y-m-01");
}
// Validazione: se la stringa non è una data valida, torna al mese corrente
$ts = strtotime($currentMonthStart);
if ($ts === false) {
$currentMonthStart = date("Y-m-01");
}
$currentDate = date("Y-m-d");
// Se oggi è dopo l'inizio del mese scelto, parti da oggi
if ($currentDate > $currentMonthStart) {
$currentMonthStart = $currentDate;
}
$currentMonthEnd = date("Y-m-t", strtotime($currentMonthStart));
/* -------------------------------------------------------------------------
* 2) Lezioni prenotate nel range del mese
* ---------------------------------------------------------------------- */
$sqlBooked = "SELECT bookingclass.*, service.*, serviceschedule.*, orderbook.expireon
FROM bookingclass
LEFT JOIN service ON bookingclass.idservice = service.idservice
LEFT JOIN serviceschedule ON bookingclass.idserviceschedule = serviceschedule.idserviceschedule
LEFT JOIN orderbook ON bookingclass.idorder = orderbook.idorderbook
WHERE bookingclass.iduser = :iduser
AND bookingclass.status = 'booked'
AND serviceschedule.dateschedule BETWEEN :monthStart AND DATE_ADD(:monthEnd, INTERVAL 1 DAY)
ORDER BY serviceschedule.dateschedule";
$stmtBooked = $pdo->prepare($sqlBooked);
$stmtBooked->execute([
':iduser' => $iduserlogin,
':monthStart' => $currentMonthStart,
':monthEnd' => $currentMonthEnd,
]);
$bookedRows = $stmtBooked->fetchAll();
// Lezioni in attesa di conferma (pending) dell'utente, nel mese visualizzato
$sqlPending = "SELECT bookingclass.*, service.servicename, service.colorclass, service.idteacher, serviceschedule.dateschedule
FROM bookingclass
LEFT JOIN service ON bookingclass.idservice = service.idservice
LEFT JOIN serviceschedule ON bookingclass.idserviceschedule = serviceschedule.idserviceschedule
WHERE bookingclass.iduser = :iduser
AND bookingclass.status = 'pending'
AND serviceschedule.dateschedule BETWEEN :monthStart AND DATE_ADD(:monthEnd, INTERVAL 1 DAY)
ORDER BY serviceschedule.dateschedule";
$stmtPending = $pdo->prepare($sqlPending);
$stmtPending->execute([
':iduser' => $iduserlogin,
':monthStart' => $currentMonthStart,
':monthEnd' => $currentMonthEnd,
]);
$pendingRows = $stmtPending->fetchAll();
// Lezioni in lista d'attesa (waiting) dell'utente, nel mese visualizzato
$sqlWaiting = "SELECT bookingclass.*, service.servicename, service.colorclass, service.maxcapacity, service.idteacher, serviceschedule.dateschedule
FROM bookingclass
LEFT JOIN service ON bookingclass.idservice = service.idservice
LEFT JOIN serviceschedule ON bookingclass.idserviceschedule = serviceschedule.idserviceschedule
WHERE bookingclass.iduser = :iduser
AND bookingclass.status = 'waiting'
AND serviceschedule.dateschedule BETWEEN :monthStart AND DATE_ADD(:monthEnd, INTERVAL 1 DAY)
ORDER BY serviceschedule.dateschedule";
$stmtWaiting = $pdo->prepare($sqlWaiting);
$stmtWaiting->execute([
':iduser' => $iduserlogin,
':monthStart' => $currentMonthStart,
':monthEnd' => $currentMonthEnd,
]);
$waitingRows = $stmtWaiting->fetchAll();
// Posizione in coda per ciascuna lezione waiting (1-based, ordine per created_at)
$waitingPositions = [];
foreach ($waitingRows as $wrow) {
$sqlPos = "SELECT COUNT(*) AS pos
FROM bookingclass
WHERE idserviceschedule = :sched
AND status = 'waiting'
AND created_at <= :mycreated";
$stmtPos = $pdo->prepare($sqlPos);
$stmtPos->execute([
':sched' => (int) $wrow['idserviceschedule'],
':mycreated' => $wrow['created_at'],
]);
$rowPos = $stmtPos->fetch();
$waitingPositions[(int) $wrow['idbookingclass']] = (int) ($rowPos['pos'] ?? 0);
}
/* -------------------------------------------------------------------------
* 3) Somma dei ticket acquistati dall'utente
* ---------------------------------------------------------------------- */
$sqlTickets = "SELECT SUM(nticket) AS total_tickets
FROM orderbook
WHERE iduser = :iduser";
$stmtTickets = $pdo->prepare($sqlTickets);
$stmtTickets->execute([':iduser' => $iduserlogin]);
$rowTickets = $stmtTickets->fetch();
$totalTickets = (int) ($rowTickets['total_tickets'] ?? 0);
/* -------------------------------------------------------------------------
* 4) Conteggio stati lezioni (praticate / future / perse / da confermare)
* ---------------------------------------------------------------------- */
$currentDateTime = date("Y-m-d H:i:s");
$sqlCounts = "SELECT
COUNT(*) AS total,
SUM(CASE WHEN serviceschedule.dateschedule <= :now1
AND bookingclass.status = 'booked'
AND bookingclass.lostlesson = 'N' THEN 1 ELSE 0 END) AS passed,
SUM(CASE WHEN serviceschedule.dateschedule > :now2
AND bookingclass.status = 'booked'
AND bookingclass.lostlesson = 'N' THEN 1 ELSE 0 END) AS future,
SUM(CASE WHEN bookingclass.lostlesson = 'Y'
AND bookingclass.status != 'cancelled' THEN 1 ELSE 0 END) AS lost,
SUM(CASE WHEN bookingclass.status = 'pending' THEN 1 ELSE 0 END) AS pending
FROM bookingclass
LEFT JOIN serviceschedule ON bookingclass.idserviceschedule = serviceschedule.idserviceschedule
WHERE bookingclass.iduser = :iduser
AND bookingclass.status != 'cancelled'";
$stmtCounts = $pdo->prepare($sqlCounts);
$stmtCounts->execute([
':now1' => $currentDateTime,
':now2' => $currentDateTime,
':iduser' => $iduserlogin,
]);
$rowCounts = $stmtCounts->fetch();
$totalRecords = (int) ($rowCounts['total'] ?? 0);
$passedRecords = (int) ($rowCounts['passed'] ?? 0);
$futureRecords = (int) ($rowCounts['future'] ?? 0);
$lost = (int) ($rowCounts['lost'] ?? 0);
$pending = (int) ($rowCounts['pending'] ?? 0);
$toprogram = $totalTickets - $passedRecords - $futureRecords - $pending - $lost;
/* -------------------------------------------------------------------------
* 4b) Insegnanti nelle card (solo se attivo nelle Opzioni)
* ---------------------------------------------------------------------- */
$optShow = $pdo->query("SELECT show_teacher FROM `option` LIMIT 1")->fetch();
$showTeacher = ($optShow['show_teacher'] ?? 'N') === 'Y';
$teachersById = [];
if ($showTeacher) {
foreach ($pdo->query("SELECT idemployee, nameemployee, surnameemployee, photo FROM employee")->fetchAll() as $t) {
$teachersById[(int) $t['idemployee']] = $t;
}
}
/* -------------------------------------------------------------------------
* 5) Pre-caricamento limiti di riprogrammazione per gli ordini coinvolti
* (una sola query invece di una per ogni card, dentro il loop)
* ---------------------------------------------------------------------- */
$reprogramInfo = [];
$orderIds = array_filter(array_unique(array_map(
fn($r) => (int) ($r['idorder'] ?? 0),
$bookedRows
)));
if (!empty($orderIds)) {
// Costruiamo i placeholder ?,?,? in numero pari agli id
$placeholders = implode(',', array_fill(0, count($orderIds), '?'));
$sqlReprog = "SELECT idorderbook, maxreschedule, reprogrammed
FROM orderbook
WHERE idorderbook IN ($placeholders)";
$stmtReprog = $pdo->prepare($sqlReprog);
$stmtReprog->execute(array_values($orderIds));
foreach ($stmtReprog->fetchAll() as $r) {
$reprogramInfo[(int) $r['idorderbook']] = [
'max' => (int) ($r['maxreschedule'] ?? 0),
'done' => (int) ($r['reprogrammed'] ?? 0),
];
}
}
/* -------------------------------------------------------------------------
* Helper output sicuro (evita XSS quando stampiamo dati DB nell'HTML)
* ---------------------------------------------------------------------- */
function e($value): string
{
return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
}
function teacherLine(array $row, bool $show, array $teachersById): string
{
if (!$show) return '';
$t = $teachersById[(int) ($row['idteacher'] ?? 0)] ?? null;
if (!$t) return '';
$name = trim($t['nameemployee'] . ' ' . $t['surnameemployee']);
if (!empty($t['photo'])) {
$avatar = ' ';
} else {
$ini = mb_strtoupper(mb_substr($t['nameemployee'], 0, 1) . mb_substr($t['surnameemployee'], 0, 1));
$avatar = '' . e($ini) . ' ';
}
return '
' . $avatar . 'Insegnante: ' . e($name) . '
';
}
$italianMonths = [
"January" => "Gennaio",
"February" => "Febbraio",
"March" => "Marzo",
"April" => "Aprile",
"May" => "Maggio",
"June" => "Giugno",
"July" => "Luglio",
"August" => "Agosto",
"September" => "Settembre",
"October" => "Ottobre",
"November" => "Novembre",
"December" => "Dicembre"
];
?>
YogiBook - Prenotazioni YogaSoul
Benvenuta/o
Di seguito puoi vedere lo stato delle tue prenotazioni
Car* Yogi, ti ricordiamo che il pacchetto 4 lezioni ha validità entro 5 settimane e il pacchetto da 12 lezioni entro 15 settimane dalla prima lezione 🙏
Richiesta di riprogrammazione inviata con successo!
La classe era piena: sei stata/o inserita/o in lista d'attesa . Se si libera un posto verrai avvisata/o.
Sei uscita/o dalla lista d'attesa.
La classe è al completo e la lista d'attesa non è attiva. Prova con un altro orario.
'Non è più possibile cancellare questa lezione: il tempo per la cancellazione è scaduto.',
'limit' => 'Hai raggiunto il numero massimo di riprogrammazioni per questo pacchetto.',
'status' => 'Questa prenotazione non può essere cancellata da qui.',
'notfound' => 'Prenotazione non trovata.',
];
$cancelMsg = $cancelErrors[$_GET['err'] ?? ''] ?? 'Si è verificato un errore: la lezione non è stata cancellata. Riprova o contattaci.';
?>
Lezione cancellata.
Lezione cancellata con successo! Grazie per aver liberato il posto!
In attesa di conferma
format("d");
$pMonth = $italianMonths[$pDateObj->format("F")];
$pWhen = $pDateObj->format("d-m-Y H:i");
$pColor = $prow['colorclass'] ?? '#1ebf73';
$pName = $prow['servicename'] ?? '';
?>
In attesa
Quando:
Luogo: via Valassina 62/B Seregno - Sala Contesto Yoga
In lista d'attesa
format("d");
$wMonth = $italianMonths[$wDateObj->format("F")];
$wWhen = $wDateObj->format("d-m-Y H:i");
$wColor = $wrow['colorclass'] ?? '#1ebf73';
$wName = $wrow['servicename'] ?? '';
$wPos = $waitingPositions[(int) $wrow['idbookingclass']] ?? 0;
?>
In coda
Quando:
Luogo: via Valassina 62/B Seregno - Sala Contesto Yoga
0) : ?>
La tua posizione in coda:
Se si libera un posto verrai avvisata/o. Non serve fare nulla.
Esci dalla lista d'attesa
Prenotate
Prenotazioni non presenti per questo mese
format("d");
$monthInItalian = $italianMonths[$dateObj->format("F")];
$newDateFormat = $dateObj->format("d-m-Y H:i");
// Calcolo finestra di cancellazione
$currentTime = new DateTime();
$classTime = new DateTime($dateschedule);
$isSameDay = $classTime->format('Y-m-d') === $currentTime->format('Y-m-d');
$classHour = (int) $classTime->format('H');
$classMinute = (int) $classTime->format('i');
$isBefore1700 = ($classHour < 17) || ($classHour === 17 && $classMinute === 0);
if ($isSameDay) {
if ($isBefore1700) {
$deadline = new DateTime($classTime->format('Y-m-d 00:01:00'));
} else {
$deadline = new DateTime($classTime->format('Y-m-d 12:00:00'));
}
$canBeDeleted = $currentTime <= $deadline;
} else {
$canBeDeleted = true;
}
// Limite riprogrammazioni (dai dati pre-caricati, niente query nel loop)
$idorder = (int) ($row['idorder'] ?? 0);
$canReprogram = true;
if (isset($reprogramInfo[$idorder])) {
$canReprogram = $reprogramInfo[$idorder]['done'] < $reprogramInfo[$idorder]['max'];
}
$idbookingclass = (int) $row['idbookingclass'];
$idservice = (int) $row['idservice'];
$expirydate = date("d/m/Y", strtotime($row['expireon']));
$colorclass = $row['colorclass'] ?? '#1ebf73';
$servicename = $row['servicename'] ?? '';
?>
Quando:
Luogo: via Valassina 62/B Seregno - Sala Contesto Yoga
Cal
Riprogramma
Cancella
Non più cancellabile