843 lines
38 KiB
PHP
843 lines
38 KiB
PHP
<?php
|
|
require_once('include/headscript.php');
|
|
|
|
/**
|
|
* Statistiche utenti: lezioni acquistate/praticate/programmate/perse/da programmare,
|
|
* con dettaglio lezioni per utente, riprogrammazione e invio email (singola/massa).
|
|
*/
|
|
$pdo = DBHandlerSelect::getInstance()->getConnection();
|
|
|
|
function e($value): string
|
|
{
|
|
return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
|
|
}
|
|
|
|
$serverCurrentDateTime = date('Y-m-d H:i:s');
|
|
|
|
$sql = "
|
|
SELECT
|
|
auth_users.id AS id_utente,
|
|
auth_users.first_name AS nome_utente,
|
|
auth_users.last_name AS cognome_utente,
|
|
auth_users.email AS email_utente,
|
|
COALESCE(orderbook_lessons.lezioni_acquistate, 0) AS lezioni_acquistate,
|
|
COUNT(CASE WHEN bookingclass.bookingstart <= CURDATE() AND bookingclass.lostlesson = 'N' THEN 1 END) AS lezioni_praticate,
|
|
COUNT(CASE WHEN bookingclass.bookingstart > CURDATE() THEN 1 END) AS lezioni_programmate,
|
|
COUNT(CASE WHEN bookingclass.lostlesson = 'Y' THEN 1 END) AS lezioni_perse,
|
|
COALESCE(orderbook_lessons.lezioni_acquistate, 0)
|
|
- COUNT(CASE WHEN bookingclass.bookingstart > CURDATE() THEN 1 END)
|
|
- COUNT(CASE WHEN bookingclass.bookingstart <= CURDATE() AND bookingclass.lostlesson = 'N' THEN 1 END)
|
|
- COUNT(CASE WHEN bookingclass.lostlesson = 'Y' THEN 1 END) AS lezioni_da_programmare
|
|
FROM auth_users
|
|
LEFT JOIN (
|
|
SELECT iduser, SUM(quantityclass) AS lezioni_acquistate
|
|
FROM orderbook
|
|
GROUP BY iduser
|
|
) AS orderbook_lessons ON auth_users.id = orderbook_lessons.iduser
|
|
LEFT JOIN bookingclass ON auth_users.id = bookingclass.iduser
|
|
GROUP BY auth_users.id, auth_users.last_name
|
|
ORDER BY auth_users.last_name";
|
|
|
|
$users = $pdo->query($sql)->fetchAll();
|
|
?>
|
|
<!doctype html>
|
|
<html lang="it">
|
|
|
|
<head>
|
|
<meta charset="utf-8" />
|
|
<title>YogiBook - Statistiche utenti</title>
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<meta content="YogiBook - Prenotazione facile YogaSoul" name="description" />
|
|
<meta content="Advanced Creative Solutions" name="author" />
|
|
<link rel="shortcut icon" href="assets/images/favicon.ico">
|
|
<link href="assets/css/bootstrap.min.css" id="bootstrap-style" rel="stylesheet" type="text/css" />
|
|
<link href="assets/css/icons.min.css" rel="stylesheet" type="text/css" />
|
|
<link href="assets/css/app.min.css" id="app-style" rel="stylesheet" type="text/css" />
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
|
|
<link rel="stylesheet" href="https://cdn.datatables.net/1.13.6/css/jquery.dataTables.min.css">
|
|
<link rel="stylesheet" href="https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
|
|
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
|
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
|
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
|
|
<script src="https://cdn.datatables.net/1.13.6/js/jquery.dataTables.min.js"></script>
|
|
|
|
<style>
|
|
.admin-card {
|
|
border: none;
|
|
border-radius: 14px;
|
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.06);
|
|
}
|
|
|
|
.admin-card .card-body {
|
|
padding: 26px 30px;
|
|
}
|
|
|
|
.section-title {
|
|
font-size: 15px;
|
|
font-weight: 700;
|
|
letter-spacing: 0.04em;
|
|
text-transform: uppercase;
|
|
color: #6b7280;
|
|
margin-bottom: 4px;
|
|
}
|
|
|
|
.section-lead {
|
|
color: #9ca3af;
|
|
font-size: 14px;
|
|
margin-bottom: 22px;
|
|
}
|
|
|
|
#userStatsTable thead th {
|
|
font-size: 12px;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.03em;
|
|
color: #6b7280;
|
|
}
|
|
|
|
#userStatsTable td {
|
|
vertical-align: middle;
|
|
}
|
|
|
|
.stat-num {
|
|
display: inline-block;
|
|
min-width: 28px;
|
|
text-align: center;
|
|
font-weight: 700;
|
|
border-radius: 6px;
|
|
padding: 2px 6px;
|
|
font-size: 13px;
|
|
}
|
|
|
|
.stat-acq {
|
|
background: #e2d3f5;
|
|
color: #4c2c92;
|
|
}
|
|
|
|
.stat-prat {
|
|
background: #c4e1fb;
|
|
color: #1a4f7a;
|
|
}
|
|
|
|
.stat-prog {
|
|
background: #cdfbc4;
|
|
color: #1a7f2e;
|
|
}
|
|
|
|
.stat-perse {
|
|
background: #fbc7c4;
|
|
color: #a12820;
|
|
}
|
|
|
|
.stat-daprog {
|
|
background: #fbe4c4;
|
|
color: #8a5a1a;
|
|
}
|
|
|
|
.btn-email-all {
|
|
background: #007bff;
|
|
border: none;
|
|
color: #fff;
|
|
border-radius: 8px;
|
|
padding: 9px 18px;
|
|
font-weight: 600;
|
|
margin-bottom: 16px;
|
|
}
|
|
|
|
.btn-email-all:hover {
|
|
filter: brightness(0.93);
|
|
}
|
|
|
|
.lost-lesson {
|
|
background-color: #ffcc80 !important;
|
|
}
|
|
|
|
.past-lesson {
|
|
background-color: #c6d9f0 !important;
|
|
}
|
|
|
|
.past-lesson-reprogrammed {
|
|
background-color: #a9bcd8 !important;
|
|
}
|
|
|
|
.future-lesson {
|
|
background-color: #b3ffb3 !important;
|
|
}
|
|
|
|
.future-lesson-reprogrammed {
|
|
background-color: #a3e6a3 !important;
|
|
}
|
|
|
|
.status-reprogrammed {
|
|
background-color: #fffacd !important;
|
|
}
|
|
|
|
.btn-warning {
|
|
background-color: #ff8c00 !important;
|
|
border-color: #ff8c00 !important;
|
|
color: #fff !important;
|
|
}
|
|
|
|
.btn-reprogram {
|
|
background-color: #28a745 !important;
|
|
border-color: #28a745 !important;
|
|
color: #fff !important;
|
|
}
|
|
|
|
.action-cell {
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.action-icon {
|
|
width: 30px;
|
|
height: 30px;
|
|
padding: 0;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
border-radius: 7px;
|
|
margin: 0 1px;
|
|
}
|
|
|
|
.action-icon i {
|
|
font-size: 13px;
|
|
}
|
|
|
|
.modal-xl-custom {
|
|
max-width: 1200px;
|
|
}
|
|
|
|
#emailBody {
|
|
width: 100%;
|
|
height: 200px;
|
|
resize: vertical;
|
|
}
|
|
|
|
.recipient-preview {
|
|
max-height: 200px;
|
|
overflow-y: auto;
|
|
text-align: left;
|
|
font-size: 13px;
|
|
border: 1px solid #eef0f3;
|
|
border-radius: 8px;
|
|
padding: 10px;
|
|
margin-top: 10px;
|
|
}
|
|
|
|
.recipient-preview div {
|
|
padding: 3px 0;
|
|
border-bottom: 1px solid #f5f6f8;
|
|
}
|
|
</style>
|
|
</head>
|
|
|
|
<body>
|
|
<div id="layout-wrapper">
|
|
<header id="page-topbar" class="isvertical-topbar">
|
|
<div class="navbar-header">
|
|
<div class="d-flex">
|
|
<?php include('include/logoarea.php'); ?>
|
|
<button type="button" class="btn btn-sm px-3 font-size-24 header-item waves-effect vertical-menu-btn">
|
|
<i class="bx bx-menu align-middle"></i>
|
|
</button>
|
|
<div class="page-title-box align-self-center d-none d-md-block">
|
|
<h4 class="page-title mb-0">Statistiche utenti</h4>
|
|
</div>
|
|
</div>
|
|
<div class="d-flex">
|
|
<?php include('include/languageselection.php'); ?>
|
|
<?php include('include/profiletopbar.php'); ?>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
<?php include('include/sidebar.php'); ?>
|
|
<header class="ishorizontal-topbar">
|
|
<div class="navbar-header">
|
|
<div class="d-flex"></div>
|
|
</div>
|
|
<div class="topnav">
|
|
<div class="container-fluid">
|
|
<nav class="navbar navbar-light navbar-expand-lg topnav-menu"></nav>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
<div class="main-content">
|
|
<div class="page-content">
|
|
<div class="container-fluid">
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<div class="card admin-card">
|
|
<div class="card-body">
|
|
<div class="section-title">Statistiche utenti</div>
|
|
<p class="section-lead">Ciao <?php echo e($firstname); ?>, clicca sul nome di un utente per il dettaglio delle lezioni.</p>
|
|
|
|
<button class="btn-email-all" id="sendEmailToAll"><i class="fas fa-envelope me-1"></i> Invia email a tutti</button>
|
|
|
|
<div class="table-responsive">
|
|
<table id="userStatsTable" class="table table-striped">
|
|
<thead>
|
|
<tr>
|
|
<th>ID</th>
|
|
<th>Nome</th>
|
|
<th>Cognome</th>
|
|
<th>Acquist.</th>
|
|
<th>Pratic.</th>
|
|
<th>Program.</th>
|
|
<th>Perse</th>
|
|
<th>Da progr.</th>
|
|
<th>Email</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php foreach ($users as $u) : ?>
|
|
<tr>
|
|
<td><?php echo (int) $u['id_utente']; ?></td>
|
|
<td>
|
|
<a href="#" class="user-link" data-id="<?php echo (int) $u['id_utente']; ?>"
|
|
data-nome="<?php echo e($u['nome_utente'] . ' ' . $u['cognome_utente']); ?>"
|
|
data-bs-toggle="modal" data-bs-target="#classModal">
|
|
<?php echo e($u['nome_utente']); ?>
|
|
</a>
|
|
</td>
|
|
<td><?php echo e($u['cognome_utente']); ?></td>
|
|
<td><span class="stat-num stat-acq"><?php echo (int) $u['lezioni_acquistate']; ?></span></td>
|
|
<td><span class="stat-num stat-prat"><?php echo (int) $u['lezioni_praticate']; ?></span></td>
|
|
<td><span class="stat-num stat-prog"><?php echo (int) $u['lezioni_programmate']; ?></span></td>
|
|
<td><span class="stat-num stat-perse"><?php echo (int) $u['lezioni_perse']; ?></span></td>
|
|
<td><span class="stat-num stat-daprog"><?php echo (int) $u['lezioni_da_programmare']; ?></span></td>
|
|
<td>
|
|
<button class="btn btn-sm btn-secondary send-email"
|
|
data-id="<?php echo (int) $u['id_utente']; ?>"
|
|
data-nome="<?php echo e($u['nome_utente']); ?>"
|
|
data-email="<?php echo e($u['email_utente']); ?>">
|
|
<i class="fas fa-envelope"></i>
|
|
</button>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<?php include('include/footer.php'); ?>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Modal dettaglio classi -->
|
|
<div class="modal fade" id="classModal" tabindex="-1" aria-hidden="true">
|
|
<div class="modal-dialog modal-xl modal-xl-custom">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">Dettagli lezioni per <span id="userName"></span></h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="table-responsive">
|
|
<table class="table table-bordered">
|
|
<thead>
|
|
<tr>
|
|
<th>Data</th>
|
|
<th>Lezione</th>
|
|
<th>Stato</th>
|
|
<th>Data prec.</th>
|
|
<th>Ordine</th>
|
|
<th>Data ordine</th>
|
|
<th>Azione</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="classDetailsBody"></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Chiudi</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Modal email -->
|
|
<div class="modal fade" id="emailModal" tabindex="-1" aria-hidden="true">
|
|
<div class="modal-dialog modal-lg">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title" id="emailModalLabel">Invia Email</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<form id="emailForm">
|
|
<div class="mb-3">
|
|
<label for="emailSubject" class="form-label">Oggetto</label>
|
|
<input type="text" class="form-control" id="emailSubject" required>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="emailBody" class="form-label">Messaggio</label>
|
|
<textarea class="form-control" id="emailBody" required></textarea>
|
|
</div>
|
|
<div id="emailRecipientCount" class="text-muted" style="font-size:13px;"></div>
|
|
<input type="hidden" id="emailRecipients" name="recipients">
|
|
</form>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Chiudi</button>
|
|
<button type="button" class="btn btn-primary" id="sendEmailBtn">Invia Email</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Modal riprogramma -->
|
|
<div class="modal fade" id="reprogramModal" tabindex="-1" aria-hidden="true">
|
|
<div class="modal-dialog modal-lg">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title" id="reprogramModalLabel">Riprogramma Lezione</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<form id="reprogramForm">
|
|
<div class="mb-3">
|
|
<label for="classSelect" class="form-label">Seleziona classe</label>
|
|
<select class="form-control" id="classSelect" required></select>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="reprogramDate" class="form-label">Seleziona data</label>
|
|
<input type="text" class="form-control" id="reprogramDate" readonly required>
|
|
</div>
|
|
<input type="hidden" id="reprogramIdBookingClass">
|
|
</form>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Chiudi</button>
|
|
<button type="button" class="btn btn-primary" id="reprogramBtn">Riprogramma</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script src="assets/libs/bootstrap/js/bootstrap.bundle.min.js"></script>
|
|
<script src="assets/libs/metismenujs/metismenujs.min.js"></script>
|
|
<script src="assets/libs/simplebar/simplebar.min.js"></script>
|
|
<script src="assets/libs/eva-icons/eva.min.js"></script>
|
|
<script src="assets/js/app.js"></script>
|
|
|
|
<script>
|
|
const serverNow = '<?php echo $serverCurrentDateTime; ?>';
|
|
|
|
// Helper Swal che rimuove aria-hidden (fix focus modali Bootstrap)
|
|
function alertBox(opts) {
|
|
return Swal.fire(Object.assign({
|
|
didOpen: () => document.body.removeAttribute('aria-hidden')
|
|
}, opts));
|
|
}
|
|
|
|
function formatDate(dateStr) {
|
|
if (!dateStr || dateStr === '-') return '-';
|
|
const months = ['Gen', 'Feb', 'Mar', 'Apr', 'Mag', 'Giu', 'Lug', 'Ago', 'Set', 'Ott', 'Nov', 'Dic'];
|
|
const parts = dateStr.split(' ');
|
|
const d = parts[0].split('-');
|
|
const time = parts[1] || '';
|
|
if (d.length < 3) return dateStr;
|
|
return `${d[2].padStart(2,'0')} ${months[parseInt(d[1],10)-1]} ${d[0]} ${time}`.trim();
|
|
}
|
|
|
|
$('#userStatsTable').DataTable({
|
|
paging: false,
|
|
searching: true,
|
|
ordering: true,
|
|
language: {
|
|
url: "//cdn.datatables.net/plug-ins/1.13.6/i18n/it-IT.json"
|
|
}
|
|
});
|
|
|
|
$(document).ready(function() {
|
|
function refreshTable(idUtente, nomeUtente) {
|
|
$.ajax({
|
|
url: 'fetch_class_details.php',
|
|
method: 'POST',
|
|
data: {
|
|
id_utente: idUtente
|
|
},
|
|
dataType: 'json',
|
|
success: function(data) {
|
|
var tbody = $('#classDetailsBody').empty();
|
|
if (!Array.isArray(data)) data = data.error ? [] : [data];
|
|
if (data.length === 0) {
|
|
tbody.append('<tr><td colspan="7" class="text-center">Nessuna classe trovata.</td></tr>');
|
|
return;
|
|
}
|
|
var now = new Date(serverNow);
|
|
$.each(data, function(i, row) {
|
|
var isPast = new Date(row.bookingstart) < now;
|
|
var isReprogrammed = row.prevbookingstart !== null;
|
|
var rowClass = '',
|
|
statusText = '';
|
|
if (row.lostlesson === 'Y') {
|
|
rowClass = 'lost-lesson';
|
|
statusText = 'Persa';
|
|
} else if (isReprogrammed) {
|
|
statusText = 'Riprogrammata';
|
|
rowClass = isPast ? 'past-lesson-reprogrammed' : 'future-lesson-reprogrammed';
|
|
} else if (isPast) {
|
|
statusText = 'Praticata';
|
|
rowClass = 'past-lesson';
|
|
} else {
|
|
statusText = 'Programmata';
|
|
rowClass = 'future-lesson';
|
|
}
|
|
|
|
var tr = $('<tr>').addClass(rowClass);
|
|
tr.append('<td>' + formatDate(row.bookingstart) + '</td>');
|
|
tr.append('<td>' + row.servicename + ' (' + row.day + ' ' + row.time + ')</td>');
|
|
var statusTd = $('<td>').text(statusText);
|
|
if (isReprogrammed) statusTd.addClass('status-reprogrammed');
|
|
tr.append(statusTd);
|
|
tr.append('<td>' + formatDate(row.prevbookingstart) + '</td>');
|
|
tr.append('<td>' + (row.idorder || '-') + '</td>');
|
|
tr.append('<td>' + (row.order_date_created || '-') + '</td>');
|
|
|
|
var actionTd = $('<td>').addClass('action-cell');
|
|
if (row.lostlesson !== 'Y') {
|
|
// Segna come persa
|
|
actionTd.append($('<button>').addClass('btn btn-sm btn-warning mark-lost action-icon')
|
|
.attr('title', 'Segna come persa')
|
|
.html('<i class="fas fa-wind"></i>')
|
|
.data('id', row.idbookingclass));
|
|
actionTd.append(' ');
|
|
// Riprogramma
|
|
actionTd.append($('<button>').addClass('btn btn-sm btn-reprogram reprogram-class action-icon')
|
|
.attr('title', 'Riprogramma')
|
|
.html('<i class="fas fa-calendar-day"></i>')
|
|
.data('id', row.idbookingclass).data('servicename', row.servicename).data('day', row.day).data('time', row.time));
|
|
actionTd.append(' ');
|
|
// Rimuovi
|
|
actionTd.append($('<button>').addClass('btn btn-sm btn-danger remove-class action-icon')
|
|
.attr('title', 'Rimuovi')
|
|
.html('<i class="fas fa-trash"></i>')
|
|
.data('id', row.idbookingclass));
|
|
} else {
|
|
actionTd.text('-');
|
|
}
|
|
tr.append(actionTd);
|
|
tbody.append(tr);
|
|
});
|
|
},
|
|
error: function() {
|
|
$('#classDetailsBody').html('<tr><td colspan="7" class="text-center">Errore nel caricamento.</td></tr>');
|
|
}
|
|
});
|
|
}
|
|
|
|
$('.user-link').on('click', function() {
|
|
var id = $(this).data('id'),
|
|
nome = $(this).data('nome');
|
|
$('#classModal').data('current-id', id);
|
|
$('#userName').text(nome);
|
|
refreshTable(id, nome);
|
|
});
|
|
|
|
// Rimuovi lezione
|
|
$(document).on('click', '.remove-class', function() {
|
|
var id = $(this).data('id');
|
|
var idU = $('#classModal').data('current-id'),
|
|
nomeU = $('#userName').text();
|
|
alertBox({
|
|
title: 'Sei sicuro?',
|
|
text: 'Questa lezione verrà rimossa definitivamente.',
|
|
icon: 'warning',
|
|
showCancelButton: true,
|
|
confirmButtonColor: '#d33',
|
|
confirmButtonText: 'Sì, rimuovi',
|
|
cancelButtonText: 'Annulla'
|
|
}).then((r) => {
|
|
if (r.isConfirmed) {
|
|
$.post('deleteclass.php', {
|
|
id: id
|
|
}, null, 'json')
|
|
.done(function(resp) {
|
|
if (resp.success) {
|
|
alertBox({
|
|
title: 'Fatto',
|
|
text: 'Lezione rimossa.',
|
|
icon: 'success'
|
|
});
|
|
refreshTable(idU, nomeU);
|
|
} else alertBox({
|
|
title: 'Errore',
|
|
text: resp.message || 'Errore durante la rimozione.',
|
|
icon: 'error'
|
|
});
|
|
})
|
|
.fail(() => alertBox({
|
|
title: 'Errore',
|
|
text: 'Errore durante la rimozione.',
|
|
icon: 'error'
|
|
}));
|
|
}
|
|
});
|
|
});
|
|
|
|
// Segna persa
|
|
$(document).on('click', '.mark-lost', function() {
|
|
var id = $(this).data('id');
|
|
var idU = $('#classModal').data('current-id'),
|
|
nomeU = $('#userName').text();
|
|
alertBox({
|
|
title: 'Sei sicuro?',
|
|
text: 'Questa lezione verrà segnata come persa.',
|
|
icon: 'warning',
|
|
showCancelButton: true,
|
|
confirmButtonColor: '#d33',
|
|
confirmButtonText: 'Sì',
|
|
cancelButtonText: 'Annulla'
|
|
}).then((r) => {
|
|
if (r.isConfirmed) {
|
|
$.post('mark_lost.php', {
|
|
id: id
|
|
}, null, 'json')
|
|
.done(function(resp) {
|
|
if (resp && resp.success) {
|
|
alertBox({
|
|
title: 'Fatto',
|
|
text: 'Lezione segnata come persa.',
|
|
icon: 'success'
|
|
});
|
|
refreshTable(idU, nomeU);
|
|
} else alertBox({
|
|
title: 'Errore',
|
|
text: (resp.message || 'Errore sconosciuto'),
|
|
icon: 'error'
|
|
});
|
|
})
|
|
.fail(() => alertBox({
|
|
title: 'Errore',
|
|
text: 'Errore durante l\'aggiornamento.',
|
|
icon: 'error'
|
|
}));
|
|
}
|
|
});
|
|
});
|
|
|
|
// Riprogramma: apri modale
|
|
$(document).on('click', '.reprogram-class', function() {
|
|
var id = $(this).data('id'),
|
|
servicename = $(this).data('servicename'),
|
|
day = $(this).data('day'),
|
|
time = $(this).data('time');
|
|
$('#reprogramIdBookingClass').val(id);
|
|
$('#reprogramModalLabel').text('Riprogramma: ' + servicename);
|
|
$.ajax({
|
|
url: 'fetch_available_classes.php',
|
|
method: 'GET',
|
|
dataType: 'json',
|
|
success: function(data) {
|
|
var sel = $('#classSelect').empty();
|
|
$.each(data, function(i, c) {
|
|
var opt = $('<option>').val(c.id).text(c.servicename + ' (' + c.day + ' ' + c.time + ')');
|
|
if (c.servicename === servicename && c.day === day && c.time === time) opt.prop('selected', true);
|
|
sel.append(opt);
|
|
});
|
|
$('#reprogramDate').datepicker({
|
|
dateFormat: 'yy-mm-dd',
|
|
changeMonth: true,
|
|
changeYear: true
|
|
});
|
|
loadAvailableDates(sel.val());
|
|
$('#reprogramModal').modal('show');
|
|
},
|
|
error: () => alertBox({
|
|
title: 'Errore',
|
|
text: 'Errore nel caricamento delle classi.',
|
|
icon: 'error'
|
|
})
|
|
});
|
|
});
|
|
|
|
$('#classSelect').on('change', function() {
|
|
loadAvailableDates($(this).val());
|
|
});
|
|
|
|
function loadAvailableDates(classId) {
|
|
$.ajax({
|
|
url: 'fetch_available_dates.php',
|
|
method: 'POST',
|
|
data: {
|
|
class_id: classId
|
|
},
|
|
dataType: 'json',
|
|
success: function(data) {
|
|
$('#reprogramDate').datepicker('option', 'beforeShowDay', function(date) {
|
|
var s = $.datepicker.formatDate('yy-mm-dd', date);
|
|
return [data.availableDates.includes(s), ''];
|
|
});
|
|
},
|
|
error: () => alertBox({
|
|
title: 'Errore',
|
|
text: 'Errore nel caricamento delle date.',
|
|
icon: 'error'
|
|
})
|
|
});
|
|
}
|
|
|
|
$('#reprogramBtn').on('click', function() {
|
|
var id = $('#reprogramIdBookingClass').val(),
|
|
classId = $('#classSelect').val(),
|
|
newDate = $('#reprogramDate').val();
|
|
var idU = $('#classModal').data('current-id'),
|
|
nomeU = $('#userName').text();
|
|
if (!newDate || !classId) {
|
|
alertBox({
|
|
title: 'Errore',
|
|
text: 'Seleziona classe e data.',
|
|
icon: 'error'
|
|
});
|
|
return;
|
|
}
|
|
$.post('reprogram_class.php', {
|
|
id: id,
|
|
class_id: classId,
|
|
new_date: newDate
|
|
}, null, 'json')
|
|
.done(function(resp) {
|
|
if (resp.success) {
|
|
alertBox({
|
|
title: 'Fatto',
|
|
text: 'Lezione riprogrammata.',
|
|
icon: 'success'
|
|
});
|
|
$('#reprogramModal').modal('hide');
|
|
refreshTable(idU, nomeU);
|
|
} else alertBox({
|
|
title: 'Errore',
|
|
text: resp.message || 'Errore durante la riprogrammazione.',
|
|
icon: 'error'
|
|
});
|
|
})
|
|
.fail(() => alertBox({
|
|
title: 'Errore',
|
|
text: 'Errore durante la riprogrammazione.',
|
|
icon: 'error'
|
|
}));
|
|
});
|
|
|
|
// Email singola
|
|
$('.send-email').on('click', function() {
|
|
var id = $(this).data('id'),
|
|
nome = $(this).data('nome'),
|
|
email = $(this).data('email');
|
|
$('#emailModalLabel').text('Invia email a ' + nome);
|
|
$('#emailBody').val('Ciao ' + nome.split(' ')[0] + ',\n\n');
|
|
$('#emailRecipientCount').text('Destinatario: ' + email);
|
|
$('#emailRecipients').val(JSON.stringify([{
|
|
id: id,
|
|
email: email,
|
|
nome: nome
|
|
}]));
|
|
$('#emailModal').modal('show');
|
|
});
|
|
|
|
// Email a TUTTI con routine di conferma robusta
|
|
$('#sendEmailToAll').on('click', function() {
|
|
var recipients = [];
|
|
$('.send-email').each(function() {
|
|
recipients.push({
|
|
id: $(this).data('id'),
|
|
email: $(this).data('email'),
|
|
nome: $(this).data('nome')
|
|
});
|
|
});
|
|
|
|
if (recipients.length === 0) {
|
|
alertBox({
|
|
title: 'Attenzione',
|
|
text: 'Nessun destinatario disponibile.',
|
|
icon: 'warning'
|
|
});
|
|
return;
|
|
}
|
|
|
|
// Anteprima destinatari + doppia conferma
|
|
var previewHtml = '<p>Stai per inviare un\'email a <strong>' + recipients.length + ' utenti</strong>.</p>';
|
|
previewHtml += '<div class="recipient-preview">';
|
|
recipients.forEach(function(r) {
|
|
previewHtml += '<div>' + r.nome + ' <' + r.email + '></div>';
|
|
});
|
|
previewHtml += '</div>';
|
|
previewHtml += '<p class="mt-3" style="font-size:13px;">Per confermare, scrivi <strong>INVIA</strong> qui sotto:</p>';
|
|
|
|
alertBox({
|
|
title: 'Conferma invio di massa',
|
|
html: previewHtml,
|
|
icon: 'warning',
|
|
input: 'text',
|
|
inputPlaceholder: 'Scrivi INVIA',
|
|
showCancelButton: true,
|
|
confirmButtonText: 'Procedi',
|
|
cancelButtonText: 'Annulla',
|
|
confirmButtonColor: '#007bff',
|
|
preConfirm: (val) => {
|
|
if (val !== 'INVIA') {
|
|
Swal.showValidationMessage('Devi scrivere INVIA per confermare');
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
}).then((result) => {
|
|
if (result.isConfirmed) {
|
|
$('#emailModalLabel').text('Invia email a ' + recipients.length + ' utenti');
|
|
$('#emailBody').val('Ciao,\n\n');
|
|
$('#emailRecipientCount').text(recipients.length + ' destinatari');
|
|
$('#emailRecipients').val(JSON.stringify(recipients));
|
|
$('#emailModal').modal('show');
|
|
}
|
|
});
|
|
});
|
|
|
|
// Invio effettivo email
|
|
$('#sendEmailBtn').on('click', function() {
|
|
var subject = $('#emailSubject').val(),
|
|
body = $('#emailBody').val();
|
|
var recipients = $('#emailRecipients').val();
|
|
if (!subject || !body) {
|
|
alertBox({
|
|
title: 'Errore',
|
|
text: 'Compila oggetto e messaggio.',
|
|
icon: 'error'
|
|
});
|
|
return;
|
|
}
|
|
|
|
$.post('/public/send_email.php', {
|
|
subject: subject,
|
|
body: body,
|
|
recipients: recipients
|
|
}, null, 'json')
|
|
.done(function(resp) {
|
|
if (resp.success) {
|
|
alertBox({
|
|
title: 'Fatto',
|
|
text: resp.message,
|
|
icon: 'success'
|
|
});
|
|
$('#emailModal').modal('hide');
|
|
} else alertBox({
|
|
title: 'Errore',
|
|
text: resp.message,
|
|
icon: 'error'
|
|
});
|
|
})
|
|
.fail(() => alertBox({
|
|
title: 'Errore',
|
|
text: 'Errore durante l\'invio.',
|
|
icon: 'error'
|
|
}));
|
|
});
|
|
});
|
|
</script>
|
|
</body>
|
|
|
|
</html>
|