Files
yogibook_aury_new/public/ordersummary.php
T
2026-09-24 11:20:05 +02:00

542 lines
24 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php require_once('include/headscript.php'); ?>
<?php
/**
* Riepilogo ordini (amministratore)
* - filtro Attivi / Scaduti (server)
* - ricerca istantanea per nome, cognome, email, n. ordine, classe (client)
* - modifica al click di: scadenza, max riprogrammazioni, riprogrammazioni effettuate
*/
$pdo = DBHandlerSelect::getInstance()->getConnection();
if (!function_exists('e')) {
function e($value): string
{
return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
}
}
function reschedBadge($max, $done): string
{
$done = (int) $done;
if ($max === null || $max === '') {
return '<span class="badge bg-light text-dark">' . $done . '</span>';
}
$max = (int) $max;
$cls = $done >= $max ? 'bg-danger' : ($done > 0 ? 'bg-warning text-dark' : 'bg-success');
return '<span class="badge ' . $cls . '">' . $done . ' / ' . $max . '</span>';
}
$message = $_GET['message'] ?? 'n';
$filter = $_GET['filter'] ?? 'all';
if (!in_array($filter, ['all', 'active', 'expired'], true)) {
$filter = 'all';
}
$q = trim((string) ($_GET['q'] ?? ''));
$today = date('Y-m-d');
$where = '';
$params = [];
if ($filter === 'active') {
$where = 'WHERE o.expireon > :today';
$params[':today'] = $today;
} elseif ($filter === 'expired') {
$where = 'WHERE o.expireon <= :today';
$params[':today'] = $today;
}
$sql = "SELECT o.idorderbook, o.order_id, o.first_name, o.last_name, o.order_billing_email,
s.servicename, o.expireon, o.status, o.nticket, o.maxreschedule, o.reprogrammed
FROM orderbook o
LEFT JOIN service s ON o.idservice = s.idservice
$where
ORDER BY o.idorderbook DESC";
$stmt = $pdo->prepare($sql);
$stmt->execute($params);
$orders = $stmt->fetchAll();
$qParam = $q !== '' ? '&q=' . urlencode($q) : '';
?>
<!doctype html>
<html lang="it">
<head>
<meta charset="utf-8" />
<title>YogiBook - Riepilogo Ordini</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://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@10"></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>
<style>
.orders-toolbar {
display: flex;
flex-wrap: wrap;
gap: 8px;
align-items: center;
margin-bottom: 12px;
}
.orders-search {
margin-left: auto;
position: relative;
min-width: 300px;
}
.orders-search i {
position: absolute;
left: 12px;
top: 50%;
transform: translateY(-50%);
color: #9ca3af;
}
.orders-search input {
padding-left: 34px;
}
#searchCount {
font-size: 13px;
color: #6b7280;
min-height: 20px;
margin-bottom: 6px;
}
.editable {
cursor: pointer;
white-space: nowrap;
}
.editable:hover {
background-color: #eef6f1 !important;
box-shadow: inset 0 0 0 1px #cfe8db;
}
.num-input {
width: 80px;
}
.expiryDateInput {
width: 130px;
}
.edit-hint {
font-size: 12px;
color: #9ca3af;
margin-bottom: 10px;
}
@media (max-width: 768px) {
.orders-search {
margin-left: 0;
min-width: 100%;
}
}
</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">Riepilogo Ordini</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-xl-12">
<div class="card">
<div class="card-body">
<h5>Riepilogo Ordini (Amministratore)</h5>
<p>Tutti gli ordini registrati</p>
<div class="orders-toolbar">
<a href="?filter=all<?php echo e($qParam); ?>" data-filter="all" class="filter-link btn btn-secondary <?php echo $filter === 'all' ? 'active' : ''; ?>">Tutti</a>
<a href="?filter=active<?php echo e($qParam); ?>" data-filter="active" class="filter-link btn btn-success <?php echo $filter === 'active' ? 'active' : ''; ?>">Attivi</a>
<a href="?filter=expired<?php echo e($qParam); ?>" data-filter="expired" class="filter-link btn btn-danger <?php echo $filter === 'expired' ? 'active' : ''; ?>">Scaduti</a>
<div class="orders-search">
<i class="fas fa-search"></i>
<input type="search" id="orderSearch" class="form-control" autocomplete="off"
placeholder="Cerca nome, cognome, email, n. ordine…" value="<?php echo e($q); ?>">
</div>
</div>
<div id="searchCount"></div>
<div class="edit-hint"><i class="fas fa-pen me-1"></i> Clicca su scadenza, max riprogr. o riprogrammate per modificarle.</div>
<div class="table-responsive">
<table class="table table-striped mb-0" id="ordersTable">
<thead>
<tr>
<th>Stato</th>
<th>N. Ordine</th>
<th>Nome</th>
<th>Cognome</th>
<th>Classe</th>
<th>Ticket</th>
<th>Scadenza</th>
<th>Max riprogr.</th>
<th>Riprogrammate</th>
<th>Status</th>
<th>Azioni</th>
</tr>
</thead>
<tbody>
<?php foreach ($orders as $order) :
$id = (int) $order['idorderbook'];
$isActive = ($order['expireon'] !== null && $order['expireon'] > $today);
$max = $order['maxreschedule'];
$done = (int) $order['reprogrammed'];
$search = implode(' ', [
$order['first_name'],
$order['last_name'],
$order['order_billing_email'],
$id,
$order['order_id'],
$order['servicename'],
]);
?>
<tr class="order-row" data-search="<?php echo e($search); ?>">
<td class="state-cell">
<span class="badge <?php echo $isActive ? 'bg-success' : 'bg-danger'; ?>"><?php echo $isActive ? 'Attivo' : 'Scaduto'; ?></span>
</td>
<td><?php echo $id; ?></td>
<td><?php echo e($order['first_name']); ?></td>
<td><?php echo e($order['last_name']); ?></td>
<td><?php echo e($order['servicename']); ?></td>
<td><?php echo e($order['nticket']); ?></td>
<td class="expiry-date editable" data-order-id="<?php echo $id; ?>" title="Clicca per modificare"><?php echo e($order['expireon']); ?></td>
<td class="num-edit editable" data-order-id="<?php echo $id; ?>" data-field="maxreschedule"
data-value="<?php echo $max === null ? '' : (int) $max; ?>" title="Clicca per modificare">
<?php echo $max === null ? '<span class="text-muted">–</span>' : (int) $max; ?>
</td>
<td class="num-edit editable" data-order-id="<?php echo $id; ?>" data-field="reprogrammed"
data-value="<?php echo $done; ?>" title="Clicca per modificare">
<?php echo reschedBadge($max, $done); ?>
</td>
<td><?php echo e($order['status']); ?></td>
<td>
<button type="button" class="btn btn-sm btn-danger" onclick="confirmDeleteOrder(<?php echo $id; ?>)">
<i class="fas fa-trash"></i>
</button>
</td>
</tr>
<?php endforeach; ?>
<tr id="noResults" style="display:none;">
<td colspan="11" class="text-center text-muted py-4">Nessun ordine trovato.</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
<?php include('include/footer.php'); ?>
</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>
function confirmDeleteOrder(id) {
Swal.fire({
title: "Sei sicuro?",
text: "Verranno cancellati l'ordine e TUTTE le prenotazioni collegate a questa persona. L'azione è irreversibile!",
icon: "warning",
showCancelButton: true,
confirmButtonColor: "#d33",
cancelButtonColor: "#3085d6",
confirmButtonText: "Sì, cancella tutto!",
cancelButtonText: "Annulla"
}).then((result) => {
if (result.isConfirmed) {
window.location.href = 'deleteorder.php?id=' + id;
}
});
}
$(function() {
var today = '<?php echo $today; ?>';
/* ===================== RICERCA ===================== */
function norm(s) {
return (s || '').toString().toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g, '');
}
$('#ordersTable tr.order-row').each(function() {
$(this).data('hay', norm($(this).attr('data-search')));
});
function applySearch() {
var raw = $('#orderSearch').val().trim();
var terms = norm(raw).split(/\s+/).filter(Boolean);
var visible = 0;
$('#ordersTable tr.order-row').each(function() {
var hay = $(this).data('hay');
var ok = terms.every(function(t) {
return hay.indexOf(t) !== -1;
});
$(this).toggle(ok);
if (ok) visible++;
});
$('#noResults').toggle(visible === 0);
$('#searchCount').text(terms.length ? visible + (visible === 1 ? ' ordine trovato' : ' ordini trovati') : '');
// i pulsanti Tutti/Attivi/Scaduti mantengono la ricerca
$('.filter-link').each(function() {
$(this).attr('href', '?filter=' + $(this).data('filter') + (raw ? '&q=' + encodeURIComponent(raw) : ''));
});
var url = new URL(window.location.href);
if (raw) url.searchParams.set('q', raw);
else url.searchParams.delete('q');
url.searchParams.delete('message');
history.replaceState(null, '', url.toString());
}
$('#orderSearch').on('input', applySearch);
applySearch();
/* ===================== SCADENZA ===================== */
$(document).on('click', 'td.expiry-date', function() {
var cell = $(this);
if (cell.find('input').length) return;
var orderId = cell.data('order-id');
var current = cell.text().trim();
cell.data('original', current).data('selected', false);
var input = $('<input type="text" class="form-control form-control-sm expiryDateInput" readonly>').val(current);
cell.empty().append(input);
input.datepicker({
dateFormat: 'yy-mm-dd',
minDate: 0,
onSelect: function(dateText) {
cell.data('selected', true);
confirmExpiry(cell, orderId, dateText);
},
onClose: function() {
setTimeout(function() {
if (!cell.data('selected')) cell.text(cell.data('original'));
}, 0);
}
});
input.datepicker('show');
});
function confirmExpiry(cell, orderId, newExpiry) {
var original = cell.data('original');
if (newExpiry === original) {
cell.text(original);
return;
}
Swal.fire({
title: "Sei sicuro?",
html: "La scadenza passerà a <strong>" + newExpiry + "</strong>.",
icon: "warning",
showCancelButton: true,
confirmButtonColor: "#3085d6",
cancelButtonColor: "#d33",
confirmButtonText: "Sì, modifica!",
cancelButtonText: "Annulla"
}).then(function(result) {
if (!result.isConfirmed) {
cell.text(original);
return;
}
$.ajax({
url: 'update_expiry.php',
type: 'POST',
dataType: 'json',
data: {
order_id: orderId,
new_expiry: newExpiry,
update_expiry: 1
}
}).done(function(res) {
if (res.status === 'success') {
cell.text(newExpiry);
var active = newExpiry > today;
cell.closest('tr').find('.state-cell').html(
'<span class="badge ' + (active ? 'bg-success' : 'bg-danger') + '">' + (active ? 'Attivo' : 'Scaduto') + '</span>'
);
Swal.fire('Successo!', res.message, 'success');
} else {
cell.text(original);
Swal.fire('Errore!', res.message, 'error');
}
}).fail(function(xhr, status, error) {
console.error('Errore AJAX:', xhr.responseText);
cell.text(original);
Swal.fire('Errore!', 'Errore durante la richiesta al server: ' + error, 'error');
});
});
}
/* ============ MAX RIPROGRAMMAZIONI / RIPROGRAMMATE ============ */
function badgeHtml(max, done) {
done = parseInt(done, 10) || 0;
if (max === null || max === '' || typeof max === 'undefined') {
return '<span class="badge bg-light text-dark">' + done + '</span>';
}
max = parseInt(max, 10);
var cls = done >= max ? 'bg-danger' : (done > 0 ? 'bg-warning text-dark' : 'bg-success');
return '<span class="badge ' + cls + '">' + done + ' / ' + max + '</span>';
}
function renderResched(row, max, done) {
var maxCell = row.find('td[data-field="maxreschedule"]');
var doneCell = row.find('td[data-field="reprogrammed"]');
var hasMax = !(max === null || max === '' || typeof max === 'undefined');
maxCell.attr('data-value', hasMax ? max : '').html(hasMax ? max : '<span class="text-muted">–</span>');
doneCell.attr('data-value', done).html(badgeHtml(max, done));
}
$(document).on('click', 'td.num-edit', function() {
var cell = $(this);
if (cell.find('input').length) return;
var field = cell.data('field');
var orderId = cell.data('order-id');
var current = String(cell.attr('data-value'));
var input = $('<input type="number" min="0" max="999" step="1" class="form-control form-control-sm num-input">').val(current);
cell.data('html', cell.html()).empty().append(input);
input.trigger('focus').trigger('select');
var finished = false;
function finish(save) {
if (finished) return;
finished = true;
var val = String(input.val()).trim();
if (!save || val === '' || val === current) {
cell.html(cell.data('html'));
return;
}
if (!/^\d{1,3}$/.test(val)) {
cell.html(cell.data('html'));
Swal.fire('Valore non valido', 'Inserisci un numero intero da 0 a 999.', 'warning');
return;
}
confirmNumber(cell, orderId, field, parseInt(val, 10));
}
input.on('keydown', function(ev) {
if (ev.key === 'Enter') {
ev.preventDefault();
finish(true);
} else if (ev.key === 'Escape') {
finish(false);
}
});
input.on('blur', function() {
finish(true);
});
});
function confirmNumber(cell, orderId, field, value) {
var label = field === 'maxreschedule' ? 'Max riprogrammazioni' : 'Riprogrammate';
Swal.fire({
title: 'Confermi la modifica?',
html: label + ': <strong>' + value + '</strong>',
icon: 'question',
showCancelButton: true,
confirmButtonColor: '#3085d6',
cancelButtonColor: '#d33',
confirmButtonText: 'Sì, modifica!',
cancelButtonText: 'Annulla'
}).then(function(result) {
if (!result.isConfirmed) {
cell.html(cell.data('html'));
return;
}
$.ajax({
url: 'update_reschedule.php',
type: 'POST',
dataType: 'json',
data: {
order_id: orderId,
field: field,
value: value
}
}).done(function(res) {
if (res.status === 'success') {
renderResched(cell.closest('tr'), res.maxreschedule, res.reprogrammed);
Swal.fire({
icon: 'success',
title: 'Aggiornato',
timer: 1200,
showConfirmButton: false
});
} else {
cell.html(cell.data('html'));
Swal.fire('Errore!', res.message || 'Aggiornamento non riuscito.', 'error');
}
}).fail(function(xhr) {
console.error('Errore AJAX:', xhr.responseText);
cell.html(cell.data('html'));
Swal.fire('Errore!', 'Errore di comunicazione con il server.', 'error');
});
});
}
});
</script>
<?php if ($message === 'success_delete') { ?>
<script>
Swal.fire('Fatto!', 'Ordine e prenotazioni cancellati.', 'success');
</script>
<?php } elseif ($message === 'error_delete') { ?>
<script>
Swal.fire('Errore!', 'Errore durante la cancellazione.', 'error');
</script>
<?php } ?>
</body>
</html>