reprogrammed functionality

This commit is contained in:
Claudio 2026-01-17 15:39:29 +01:00
parent f911b82716
commit e76fe4dfd6
7 changed files with 689 additions and 51 deletions

View File

@ -63,3 +63,6 @@ $_SESSION["emailuser"] = $emailuser;
$_SESSION["photouser"] = $avatar; $_SESSION["photouser"] = $avatar;
$photouser = $_SESSION["photouser"]; $photouser = $_SESSION["photouser"];
// include school settings
include('school_settings_loader.php');

View File

@ -24,12 +24,14 @@
<div class="menu-title">Certificati</div> <div class="menu-title">Certificati</div>
</a> </a>
</li> </li>
<li> <?php if (!empty($schoolSettings['portal_purchases_enabled'])): ?>
<a href="shop-school.php"> <li>
<div class="parent-icon"><i class="bx bx-store"></i></div> <a href="shop-school.php">
<div class="menu-title">Shop</div> <div class="parent-icon"><i class="bx bx-store"></i></div>
</a> <div class="menu-title">Shop</div>
</li> </a>
</li>
<?php endif; ?>
<li> <li>
<a href="checkout.php"> <a href="checkout.php">
<div class="parent-icon"><i class="bx bx-cart"></i></div> <div class="parent-icon"><i class="bx bx-cart"></i></div>

View File

@ -0,0 +1,70 @@
<?php
// include/school_settings_loader.php
// Evita inclusioni multiple
if (defined('SCHOOL_SETTINGS_LOADED')) return;
define('SCHOOL_SETTINGS_LOADED', true);
global $schoolSettings; // o usa $_SESSION['school_settings'] se preferisci
$school_id = (int)($_SESSION['school_id'] ?? 0);
if ($school_id <= 0) {
// Nessuna scuola selezionata → valori di default minimi
$schoolSettings = [
'portal_purchases_enabled' => 0,
'allowed_product_types' => 'subscription,carnet,drop_in',
'payment_methods' => 'manual',
'currency_code' => 'EUR',
'enable_notifications' => 1,
'allow_freeze_global' => 1,
'freeze_max_days_global' => 30,
'auto_propagate_on_purchase' => 1,
'allow_full_access_rebooking' => 1,
// ... aggiungi tutti gli altri campi con default sensati
];
} else {
$pdo = DBHandlerSelect::getInstance()->getConnection();
$stmt = $pdo->prepare("
SELECT *
FROM school_settings
WHERE school_id = ?
LIMIT 1
");
$stmt->execute([$school_id]);
$settings = $stmt->fetch(PDO::FETCH_ASSOC);
if ($settings) {
$schoolSettings = $settings;
} else {
// Scuola senza impostazioni → crea record con default
$stmt_insert = $pdo->prepare("
INSERT INTO school_settings (school_id) VALUES (?)
");
$stmt_insert->execute([$school_id]);
// Ricarica dopo insert
$stmt = $pdo->prepare("SELECT * FROM school_settings WHERE school_id = ? LIMIT 1");
$stmt->execute([$school_id]);
$schoolSettings = $stmt->fetch(PDO::FETCH_ASSOC) ?: [];
}
// Fallback per campi che potrebbero essere NULL
$schoolSettings = array_merge([
'portal_purchases_enabled' => 1,
'allowed_product_types' => 'subscription,carnet,drop_in',
'payment_methods' => 'manual',
'currency_code' => 'EUR',
'enable_notifications' => 1,
'allow_freeze_global' => 1,
'freeze_max_days_global' => 30,
'auto_propagate_on_purchase' => 1,
'allow_full_access_rebooking' => 1,
// ... tutti gli altri campi che vuoi default
], $schoolSettings);
}
// Trasforma stringhe separate da virgola in array (molto comodo)
$schoolSettings['payment_methods_array'] = array_filter(explode(',', $schoolSettings['payment_methods'] ?? ''));
$schoolSettings['allowed_product_types_array'] = array_filter(explode(',', $schoolSettings['allowed_product_types'] ?? ''));

View File

@ -219,6 +219,12 @@ $has_valid_cert = ($cert_result['valid_count'] > 0);
<a href="?month=<?= $nextMonth ?>" class="nav-arrow"></a> <a href="?month=<?= $nextMonth ?>" class="nav-arrow"></a>
</div> </div>
</div> </div>
<?php if (!empty($_GET['rebook']) && !empty($_GET['msg'])): ?>
<div class="alert alert-success alert-dismissible fade show mb-4">
<?= htmlspecialchars($_GET['msg']) ?>
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
<?php endif; ?>
<div class="container-fluid px-0"> <div class="container-fluid px-0">
<?php if (!$has_valid_cert): ?> <?php if (!$has_valid_cert): ?>
@ -357,6 +363,10 @@ $has_valid_cert = ($cert_result['valid_count'] > 0);
} }
}); });
}; };
const reschedule = id => {
location = 'reschedule.php?booking=' + id;
};
</script> </script>
</body> </body>

View File

@ -0,0 +1,500 @@
<?php
session_start();
//setlocale(LC_TIME, 'it_IT.UTF-8', 'it_IT.utf8', 'Italian_Italy.1252', 'it_IT');
include('include/headscript.php');
if (!isset($iduserlogin)) {
header('Location: login.php');
exit;
}
$school_id = $_SESSION['school_id'];
if (!$school_id) die("Nessuna scuola selezionata");
$booking_id = (int)($_GET['booking'] ?? 0);
if ($booking_id <= 0) die("Prenotazione non valida");
$dbHandler = DBHandlerSelect::getInstance();
$pdo = $dbHandler->getConnection();
$stmt = $pdo->prepare("SELECT name, address_street, address_city, address_province FROM schools WHERE id = ?");
$stmt->execute([$school_id]);
$school = $stmt->fetch();
// Recupera dettagli vecchia prenotazione
$stmt = $pdo->prepare("
SELECT
sb.id, sb.order_id,
c.name AS class_name, ct.level, ct.class_id, cs.session_date, cs.start_time, cs.end_time
FROM session_bookings sb
JOIN class_sessions cs ON sb.session_id = cs.id
JOIN class_types ct ON cs.class_type_id = ct.id
JOIN classes c ON ct.class_id = c.id
WHERE sb.id = ? AND sb.user_id = ? AND sb.status = 'booked'
");
$stmt->execute([$booking_id, $iduserlogin]);
$old_booking = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$old_booking) die("Prenotazione non trovata o non modificabile");
// Recupera impostazioni scuola (per auto-approved)
$stmt = $pdo->prepare("SELECT rebooking_auto_approved FROM school_settings WHERE school_id = ?");
$stmt->execute([$school_id]);
$settings = $stmt->fetch(PDO::FETCH_ASSOC);
$auto_approved = $settings['rebooking_auto_approved'] ?? 1;
// Lezioni future disponibili (tutte le scheduled future)
$stmt = $pdo->prepare("
SELECT
cs.id AS session_id,
cs.session_date,
cs.start_time,
cs.end_time,
cs.room_name,
cs.max_capacity,
c.name AS class_name,
ct.level,
(SELECT COUNT(*) FROM session_bookings WHERE session_id = cs.id AND status IN ('booked', 'pending')) AS booked_count,
(SELECT COUNT(*) FROM session_bookings WHERE session_id = cs.id AND user_id = ? AND status IN ('booked', 'pending')) AS user_booked
FROM class_sessions cs
JOIN class_types ct ON cs.class_type_id = ct.id
JOIN classes c ON ct.class_id = c.id
WHERE cs.school_id = ?
AND cs.status = 'scheduled'
AND cs.session_date >= CURDATE()
ORDER BY cs.session_date ASC, cs.start_time ASC
");
$stmt->execute([$iduserlogin, $school_id]);
$available_lessons = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Gestione POST: riprogrammazione
$success = $error = "";
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['new_session_id'])) {
$new_session_id = (int)$_POST['new_session_id'];
try {
$pdo->beginTransaction();
// Verifica disponibilità nuova sessione
$stmt_check = $pdo->prepare("
SELECT max_capacity,
(SELECT COUNT(*) FROM session_bookings WHERE session_id = ? AND status IN ('booked', 'pending')) AS booked_count
FROM class_sessions
WHERE id = ? AND status = 'scheduled' AND session_date >= CURDATE()
");
$stmt_check->execute([$new_session_id, $new_session_id]);
$new_session = $stmt_check->fetch(PDO::FETCH_ASSOC);
if (!$new_session || $new_session['booked_count'] >= $new_session['max_capacity']) {
throw new Exception("Lezione non disponibile o piena");
}
// Crea nuova prenotazione
$status_new = $auto_approved ? 'booked' : 'pending';
$stmt_new = $pdo->prepare("
INSERT INTO session_bookings
(session_id, user_id, order_id, status, booked_at, created_at)
VALUES (?, ?, ?, ?, NOW(), NOW())
");
$stmt_new->execute([$new_session_id, $iduserlogin, $old_booking['order_id'], $status_new]);
// Marca vecchia come rescheduled
$stmt_old = $pdo->prepare("
UPDATE session_bookings
SET status = 'rescheduled', updated_at = NOW()
WHERE id = ?
");
$stmt_old->execute([$booking_id]);
$pdo->commit();
// Redirect to my_lessons.php with a success flag/message
$msg = $auto_approved ? "Riprogrammazione completata. Lezione confermata." : "Riprogrammazione inviata. In attesa di approvazione.";
header("Location: my_lessons.php?rebook=1&msg=" . urlencode($msg));
exit;
} catch (Exception $e) {
$pdo->rollBack();
$error = "Errore durante la riprogrammazione: " . $e->getMessage();
}
}
function it_weekday($dateYmd)
{
$fmt = new IntlDateFormatter(
'it_IT',
IntlDateFormatter::FULL,
IntlDateFormatter::NONE,
'Europe/Rome',
IntlDateFormatter::GREGORIAN,
'EEEE'
);
return ucfirst($fmt->format(strtotime($dateYmd)));
}
function it_day_month($dateYmd)
{
$fmt = new IntlDateFormatter(
'it_IT',
IntlDateFormatter::NONE,
IntlDateFormatter::NONE,
'Europe/Rome',
IntlDateFormatter::GREGORIAN,
'd MMMM'
);
return ucfirst($fmt->format(strtotime($dateYmd)));
}
?>
<!doctype html>
<html lang="it">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Riprogramma Lezione - YoGiBook</title>
<?php include('cssinclude.php'); ?>
<?php include('siteinfo.php'); ?>
<style>
/* Riutilizzo esatto gli stili da my_lessons.php */
.page-content {
background: #f9fff9;
padding-bottom: 3rem;
}
.month-header {
background: white;
padding: 0.9rem 1rem;
text-align: center;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.07);
position: sticky;
top: 0;
z-index: 10;
margin-bottom: 1.2rem;
}
.lesson-card {
background: white;
border-radius: 18px;
margin: 1rem;
overflow: hidden;
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.09);
}
.lesson-card.available {
border-left: 6px solid #10b981;
/* Verde */
}
.lesson-card.user-booked {
border-left: 6px solid #f59e0b;
/* Arancione */
opacity: 0.85;
}
.lesson-card.full {
border-left: 6px solid #6b7280;
/* Grigio */
opacity: 0.7;
}
.date-header {
background: #d1fae5;
color: #065f46;
padding: 1.4rem 1rem;
text-align: center;
}
/* Mantieni il verde chiaro originale per date-header delle card disponibili */
.lesson-card.available .date-header {
background: #d1fae5;
/* Verde chiaro originale */
color: #065f46;
}
/* Date-header più scuro per card già prenotate (arancione) */
.lesson-card.user-booked .date-header {
background: #ff8c4f;
/* Arancione scuro */
color: white;
}
/* Date-header più scuro per card piena (grigio) */
.lesson-card.full .date-header {
background: linear-gradient(135deg, #4b5563, #6b7280);
/* Grigio scuro */
color: white;
}
/* Opzionale: rendi testo data più leggibile su sfondi scuri */
.lesson-card.user-booked .date-header .date-num,
.lesson-card.full .date-header .date-num {
text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.6);
}
.lesson-card.user-booked .date-header .date-day,
.lesson-card.full .date-header .date-day {
color: rgba(255, 255, 255, 0.95);
}
.date-num {
font-size: 2.1rem;
font-weight: 800;
line-height: 1;
margin: 0;
}
.date-day {
font-size: 1.18rem;
font-weight: 600;
margin: 5px 0 0;
}
.lesson-body {
padding: 1.4rem 1.3rem;
}
.lesson-title {
font-size: 1.38rem;
font-weight: 700;
color: #1a1a1a;
margin: 0 0 0.8rem;
}
.lesson-meta {
font-size: 0.98rem;
color: #444;
margin: 0.5rem 0;
display: flex;
align-items: center;
gap: 10px;
}
.lesson-meta i {
width: 20px;
color: #666;
font-size: 1.1rem;
}
.entries-badge {
display: inline-block;
background: #dbeafe;
color: #1e40af;
padding: 0.6rem 1rem;
border-radius: 12px;
font-weight: 600;
font-size: 0.92rem;
margin: 0.8rem 0;
}
.action-buttons {
margin-top: 1.2rem;
}
.btn-custom {
padding: 0.85rem;
border-radius: 14px;
font-weight: 600;
font-size: 0.95rem;
width: 100%;
}
.btn-primary-custom {
background: #10b981;
color: white;
border: none;
}
.lesson-card {
background: white;
border-radius: 18px;
margin: 1rem;
overflow: hidden;
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.09);
transition: all 0.2s;
}
.lesson-card.available {
border-left: 6px solid #10b981;
/* Verde solo bordo sinistro */
}
.lesson-card.user-booked {
background: #fff7ed !important;
/* Arancione chiaro sfondo tutta card */
border-left: 6px solid #f59e0b;
opacity: 0.92;
}
.lesson-card.full {
background: #f3f4f6 !important;
/* Grigio chiaro sfondo tutta card */
border-left: 6px solid #6b7280;
opacity: 0.75;
}
</style>
</head>
<body>
<div class="wrapper">
<?php include('include/navbar.php'); ?>
<?php include('include/topbar.php'); ?>
<div class="page-wrapper">
<div class="page-content">
<div class="month-header">
<h2>Riprogramma lezione</h2>
<p class="text-muted small mt-2 mb-1">
<?= htmlspecialchars($old_booking['class_name']) ?>
<?php if ($old_booking['level']): ?> - <?= ucfirst($old_booking['level']) ?><?php endif; ?>
</p>
<p class="fw-bold text-primary mb-0">
<?= htmlspecialchars(it_day_month($old_booking['session_date'])) ?> <?= date('Y', strtotime($old_booking['session_date'])) ?>
dalle <?= date('H:i', strtotime($old_booking['start_time'])) ?>
alle <?= date('H:i', strtotime($old_booking['end_time'])) ?>
</p>
</div>
<div class="text-end mb-3">
<a href="my_lessons.php" class="btn btn-outline-secondary">
<i class="bx bx-arrow-back me-1"></i> Annulla e torna indietro
</a>
</div>
<div class="container-fluid px-0">
<?php if ($success): ?>
<div class="alert alert-success alert-dismissible fade show mb-4">
<?= htmlspecialchars($success) ?>
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
<?php endif; ?>
<?php if ($error): ?>
<div class="alert alert-danger alert-dismissible fade show mb-4">
<?= htmlspecialchars($error) ?>
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
<?php endif; ?>
<div class="row">
<?php if (empty($available_lessons)): ?>
<div class="text-center py-5">
<i class="bx bx-calendar-x bx-lg text-muted"></i>
<h5 class="mt-3 text-secondary">Nessuna lezione disponibile per la riprogrammazione</h5>
</div>
<?php else: ?>
<?php foreach ($available_lessons as $lesson): ?>
<?php
$is_full = $lesson['booked_count'] >= $lesson['max_capacity'];
$is_user_booked = $lesson['user_booked'] > 0;
$card_class = $is_full ? 'full' : ($is_user_booked ? 'user-booked' : 'available');
?>
<div class="col-md-6 col-lg-4 mb-4">
<div class="lesson-card <?= $card_class ?>">
<div class="date-header">
<div class="date-num"><?= htmlspecialchars(it_day_month($lesson['session_date'])) ?></div>
<div class="date-day">
<?= htmlspecialchars(it_weekday($lesson['session_date'])) ?>
</div>
</div>
<div class="lesson-body">
<h3 class="lesson-title">
<?= htmlspecialchars($lesson['class_name']) ?>
<?php if ($lesson['level']): ?>
<small style="color:#666;">- <?= ucfirst($lesson['level']) ?></small>
<?php endif; ?>
</h3>
<div class="lesson-meta">
<i class="bx bx-time"></i>
<?= date('H:i', strtotime($lesson['start_time'])) ?> - <?= date('H:i', strtotime($lesson['end_time'])) ?>
</div>
<div class="lesson-meta">
<i class="bx bx-home"></i>
<?= htmlspecialchars($school['name']) ?>
<?php if ($lesson['room_name']): ?> - <?= htmlspecialchars($lesson['room_name']) ?><?php endif; ?>
</div>
<div class="lesson-meta">
<i class="bx bx-map"></i>
<?= htmlspecialchars(trim($school['address_street'] . ', ' . $school['address_city'] . ' ' . $school['address_province'])) ?>
</div>
<div class="entries-badge">
Posti liberi: <strong><?= max(0, $lesson['max_capacity'] - $lesson['booked_count']) ?></strong>
</div>
<?php if ($is_full): ?>
<div class="text-danger fw-bold text-center mt-4 fs-5">
Lezione piena
</div>
<?php elseif ($is_user_booked): ?>
<div class="text-warning fw-bold text-center mt-4 fs-5">
Già prenotata da te
</div>
<?php else: ?>
<div class="action-buttons">
<form method="POST" class="js-rebook-form"
data-date="<?= htmlspecialchars(date('d/m/Y', strtotime($lesson['session_date']))) ?>"
data-start="<?= htmlspecialchars(date('H:i', strtotime($lesson['start_time']))) ?>"
data-end="<?= htmlspecialchars(date('H:i', strtotime($lesson['end_time']))) ?>"
data-class="<?= htmlspecialchars($lesson['class_name']) ?>">
<input type="hidden" name="new_session_id" value="<?= (int)$lesson['session_id'] ?>">
<button type="submit" class="btn-custom btn-primary-custom">
Riprogramma qui
</button>
</form>
</div>
<?php endif; ?>
</div>
</div>
</div>
<?php endforeach; ?>
<?php endif; ?>
</div>
</div>
</div>
</div>
<?php include('include/footer.php'); ?>
</div>
<?php include('jsinclude.php'); ?>
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
document.querySelectorAll('.js-rebook-form').forEach(function(form) {
form.addEventListener('submit', function(e) {
e.preventDefault();
const d = form.dataset.date || '';
const s = form.dataset.start || '';
const en = form.dataset.end || '';
const cls = form.dataset.class || '';
Swal.fire({
title: 'Confermi la riprogrammazione?',
html: `
<div style="text-align:left">
<div><strong>Lezione:</strong> ${cls}</div>
<div><strong>Quando:</strong> ${d} ${s} - ${en}</div>
</div>
`,
icon: 'question',
showCancelButton: true,
confirmButtonText: 'Sì, riprogramma',
cancelButtonText: 'No, annulla',
reverseButtons: true
}).then((result) => {
if (result.isConfirmed) {
form.submit();
}
});
});
});
});
</script>
</body>
</html>

View File

@ -23,6 +23,10 @@ if ($settings && !array_key_exists('portal_purchases_enabled', $settings)) {
$settings['portal_purchases_enabled'] = 1; $settings['portal_purchases_enabled'] = 1;
} }
if ($settings && !array_key_exists('rebooking_auto_approved', $settings)) {
$settings['rebooking_auto_approved'] = 1;
}
$is_new = !$settings; $is_new = !$settings;
$success_message = $error = ""; $success_message = $error = "";
@ -55,6 +59,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$portal_purchases_enabled = !empty($_POST['portal_purchases_enabled']) ? 1 : 0; $portal_purchases_enabled = !empty($_POST['portal_purchases_enabled']) ? 1 : 0;
$rebooking_auto_approved = !empty($_POST['rebooking_auto_approved']) ? 1 : 0;
// Se acquisti portale disabilitati → forza anche propagate a 0 // Se acquisti portale disabilitati → forza anche propagate a 0
$auto_propagate_on_purchase = $portal_purchases_enabled $auto_propagate_on_purchase = $portal_purchases_enabled
@ -68,8 +73,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
school_id, header_color, sidebar_color, payment_methods, currency_code, enable_notifications, school_id, header_color, sidebar_color, payment_methods, currency_code, enable_notifications,
allow_freeze_global, freeze_max_days_global, auto_propagate_on_purchase, allow_freeze_global, freeze_max_days_global, auto_propagate_on_purchase,
allow_full_access_rebooking, allowed_product_types, allow_full_access_rebooking, allowed_product_types,
portal_purchases_enabled portal_purchases_enabled, rebooking_auto_approved
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
"); ");
$stmt->execute([ $stmt->execute([
$school_id, $school_id,
@ -83,7 +88,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$auto_propagate_on_purchase, $auto_propagate_on_purchase,
$allow_full_access_rebooking, $allow_full_access_rebooking,
$allowed_product_types, $allowed_product_types,
$portal_purchases_enabled $portal_purchases_enabled,
$rebooking_auto_approved
]); ]);
$success_message = "Impostazioni create con successo!"; $success_message = "Impostazioni create con successo!";
} else { } else {
@ -92,7 +98,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
header_color = ?, sidebar_color = ?, payment_methods = ?, currency_code = ?, enable_notifications = ?, header_color = ?, sidebar_color = ?, payment_methods = ?, currency_code = ?, enable_notifications = ?,
allow_freeze_global = ?, freeze_max_days_global = ?, auto_propagate_on_purchase = ?, allow_freeze_global = ?, freeze_max_days_global = ?, auto_propagate_on_purchase = ?,
allow_full_access_rebooking = ?, allowed_product_types = ?, allow_full_access_rebooking = ?, allowed_product_types = ?,
portal_purchases_enabled = ? portal_purchases_enabled = ?, rebooking_auto_approved = ?
WHERE school_id = ? WHERE school_id = ?
"); ");
$stmt->execute([ $stmt->execute([
@ -107,8 +113,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$allow_full_access_rebooking, $allow_full_access_rebooking,
$allowed_product_types, $allowed_product_types,
$portal_purchases_enabled, $portal_purchases_enabled,
$rebooking_auto_approved,
$school_id $school_id
]); ]);
$success_message = "Impostazioni aggiornate con successo!"; $success_message = "Impostazioni aggiornate con successo!";
} }
@ -292,6 +298,24 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
</div> </div>
</div> </div>
<hr class="my-5">
<!-- NUOVA SEZIONE: Approvazioni e Lezioni -->
<h5 class="text-primary mb-4">Approvazioni e Lezioni</h5>
<div class="mt-4">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" name="rebooking_auto_approved" id="rebooking_auto"
<?php echo ($settings['rebooking_auto_approved'] ?? 1) ? 'checked' : ''; ?>>
<label class="form-check-label fw-bold" for="rebooking_auto">
Riprogrammazione lezioni automatica (approvata subito)
</label>
</div>
<small class="form-text text-muted">
Se disattivato, ogni richiesta di riprogrammazione dovrà essere approvata manualmente dalla segreteria.
</small>
</div>
<div class="text-center mt-5"> <div class="text-center mt-5">
<button type="submit" class="btn btn-primary btn-lg px-5"> <button type="submit" class="btn btn-primary btn-lg px-5">
Salva Impostazioni Salva Impostazioni

View File

@ -157,18 +157,18 @@ $stmt = $pdo->prepare("
$stmt->execute([$iduserlogin, $school_id]); $stmt->execute([$iduserlogin, $school_id]);
$orders = $stmt->fetchAll(); $orders = $stmt->fetchAll();
// 1. Lezioni acquistate totali (somma total_entries da ordini completati) // Lezioni ACQUISTATE totali
$stmt_total_lessons = $pdo->prepare(" $stmt_acquistate = $pdo->prepare("
SELECT COALESCE(SUM(total_entries), 0) AS total_lessons SELECT COALESCE(SUM(total_entries), 0) AS acquistate
FROM orders FROM orders
WHERE user_id = ? AND school_id = ? AND status = 'completed' WHERE user_id = ? AND school_id = ? AND status = 'completed'
"); ");
$stmt_total_lessons->execute([$iduserlogin, $school_id]); $stmt_acquistate->execute([$iduserlogin, $school_id]);
$total_lessons = $stmt_total_lessons->fetchColumn() ?: 0; $acquistate = (int) $stmt_acquistate->fetchColumn();
// 2. Lezioni da praticare (booked + data futura) // Da PRATICARE (booked future)
$stmt_to_practice = $pdo->prepare(" $stmt_da_praticare = $pdo->prepare("
SELECT COUNT(sb.id) AS to_practice SELECT COUNT(sb.id) AS da_praticare
FROM session_bookings sb FROM session_bookings sb
JOIN class_sessions cs ON sb.session_id = cs.id JOIN class_sessions cs ON sb.session_id = cs.id
WHERE sb.user_id = ? WHERE sb.user_id = ?
@ -176,12 +176,27 @@ $stmt_to_practice = $pdo->prepare("
AND sb.status = 'booked' AND sb.status = 'booked'
AND cs.session_date >= CURDATE() AND cs.session_date >= CURDATE()
"); ");
$stmt_to_practice->execute([$iduserlogin, $school_id]); $stmt_da_praticare->execute([$iduserlogin, $school_id]);
$to_practice = $stmt_to_practice->fetchColumn() ?: 0; $da_praticare = (int) $stmt_da_praticare->fetchColumn();
// 3. Lezioni perse (missed + data passata) // PRATICATE (attended + booked passate)
$stmt_missed = $pdo->prepare(" $stmt_praticate = $pdo->prepare("
SELECT COUNT(sb.id) AS missed SELECT COUNT(sb.id) AS praticate
FROM session_bookings sb
JOIN class_sessions cs ON sb.session_id = cs.id
WHERE sb.user_id = ?
AND cs.school_id = ?
AND (
sb.status = 'attended'
OR (sb.status = 'booked' AND cs.session_date < CURDATE())
)
");
$stmt_praticate->execute([$iduserlogin, $school_id]);
$praticate = (int) $stmt_praticate->fetchColumn();
// PERSE (missed + data passata)
$stmt_perse = $pdo->prepare("
SELECT COUNT(sb.id) AS perse
FROM session_bookings sb FROM session_bookings sb
JOIN class_sessions cs ON sb.session_id = cs.id JOIN class_sessions cs ON sb.session_id = cs.id
WHERE sb.user_id = ? WHERE sb.user_id = ?
@ -189,8 +204,11 @@ $stmt_missed = $pdo->prepare("
AND sb.status = 'missed' AND sb.status = 'missed'
AND cs.session_date < CURDATE() AND cs.session_date < CURDATE()
"); ");
$stmt_missed->execute([$iduserlogin, $school_id]); $stmt_perse->execute([$iduserlogin, $school_id]);
$missed = $stmt_missed->fetchColumn() ?: 0; $perse = (int) $stmt_perse->fetchColumn();
// DA PROGRAMMARE (residuo = acquistate - perse - praticate - da_praticare)
$da_programmare = max(0, $acquistate - $perse - $praticate - $da_praticare);;
// === CONTROLLA CERTIFICATI VALIDI === // === CONTROLLA CERTIFICATI VALIDI ===
$stmt_cert = $pdo->prepare(" $stmt_cert = $pdo->prepare("
@ -325,43 +343,54 @@ $active_orders = count(array_filter($orders, fn($o) => $o['status'] === 'complet
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button> <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div> </div>
<?php endif; ?> <?php endif; ?>
<!-- STATISTICHE PASTELLO - NUOVA VERSIONE --> <!-- STATISTICHE - 5 BOX CHE OCCUPANO TUTTA LA RIGA SU DESKTOP -->
<div class="row g-4 mb-5"> <div class="row g-3 mb-5">
<!-- Box 1: Lezioni acquistate totali --> <!-- 1. Lezioni acquistate -->
<div class="col-md-3"> <div class="col-6 col-sm-4 col-md">
<div class="card stat-card text-center h-100" style="background: linear-gradient(135deg, #d1fae5, #a7f3d0);"> <div class="card stat-card text-center h-100 shadow-sm" style="background: linear-gradient(135deg, #d1fae5, #a7f3d0); border: none;">
<div class="card-body py-4"> <div class="card-body py-4 px-3">
<h3 class="fw-bold text-success mb-1"><?= number_format($total_lessons) ?></h3> <h4 class="fw-bold text-success mb-1"><?= number_format($acquistate) ?></h4>
<p class="text-dark mb-0">Lezioni acquistate totali</p> <p class="text-dark mb-0 small">Acquistate</p>
</div> </div>
</div> </div>
</div> </div>
<!-- Box 2: Lezioni da praticare --> <!-- 2. Praticate -->
<div class="col-md-3"> <div class="col-6 col-sm-4 col-md">
<div class="card stat-card text-center h-100" style="background: linear-gradient(135deg, #dbeafe, #bfdbfe);"> <div class="card stat-card text-center h-100 shadow-sm" style="background: linear-gradient(135deg, #e0f2fe, #bae6fd); border: none;">
<div class="card-body py-4"> <div class="card-body py-4 px-3">
<h3 class="fw-bold text-primary mb-1"><?= number_format($to_practice) ?></h3> <h4 class="fw-bold text-info mb-1"><?= number_format($praticate) ?></h4>
<p class="text-dark mb-0">Lezioni da praticare</p> <p class="text-dark mb-0 small">Praticate</p>
</div> </div>
</div> </div>
</div> </div>
<!-- Box 3: Lezioni perse --> <!-- 3. Da praticare -->
<div class="col-md-3"> <div class="col-6 col-sm-4 col-md">
<div class="card stat-card text-center h-100" style="background: linear-gradient(135deg, #fee2e2, #fecaca);"> <div class="card stat-card text-center h-100 shadow-sm" style="background: linear-gradient(135deg, #dbeafe, #bfdbfe); border: none;">
<div class="card-body py-4"> <div class="card-body py-4 px-3">
<h3 class="fw-bold text-danger mb-1"><?= number_format($missed) ?></h3> <h4 class="fw-bold text-primary mb-1"><?= number_format($da_praticare) ?></h4>
<p class="text-dark mb-0">Lezioni perse</p> <p class="text-dark mb-0 small">Da praticare</p>
</div> </div>
</div> </div>
</div> </div>
<!-- Box 4: Vuoto (pronto per futuro) --> <!-- 4. Perse -->
<div class="col-md-3"> <div class="col-6 col-sm-4 col-md">
<div class="card stat-card text-center h-100" style="background: linear-gradient(135deg, #fef3c7, #fde68a);"> <div class="card stat-card text-center h-100 shadow-sm" style="background: linear-gradient(135deg, #fee2e2, #fecaca); border: none;">
<div class="card-body py-4 d-flex align-items-center justify-content-center"> <div class="card-body py-4 px-3">
<p class="text-muted mb-0 fst-italic">Prossimamente...</p> <h4 class="fw-bold text-danger mb-1"><?= number_format($perse) ?></h4>
<p class="text-dark mb-0 small">Perse</p>
</div>
</div>
</div>
<!-- 5. Da programmare -->
<div class="col-6 col-sm-4 col-md">
<div class="card stat-card text-center h-100 shadow-sm" style="background: linear-gradient(135deg, #fef3c7, #fde68a); border: none;">
<div class="card-body py-4 px-3">
<h4 class="fw-bold text-warning mb-1"><?= number_format($da_programmare) ?></h4>
<p class="text-dark mb-0 small">Da programmare</p>
</div> </div>
</div> </div>
</div> </div>