added modify to orders

This commit is contained in:
2026-09-24 11:20:05 +02:00
parent 649e4f2f33
commit 2512e7a7ab
3 changed files with 511 additions and 346 deletions
+445 -346
View File
@@ -1,314 +1,143 @@
<?php require_once('include/headscript.php'); ?>
<?php
// Creazione della connessione
$conn = new mysqli($servername, $username, $password, $dbname);
/**
* 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();
// Verifica della connessione
if ($conn->connect_error) {
die("Connessione fallita: " . $conn->connect_error);
}
// Gestione del messaggio GET
if (isset($_GET['message'])) {
$message = $_GET['message'];
} else {
$message = 'n';
}
// ID dell'insegnante (per il form DayOff)
$idTeacher = 1;
// Gestione dell'inserimento del DayOff
if (isset($_POST['submit'])) {
$dayoffdate = $_POST['dayoff'];
$insertQuery = "INSERT INTO dayoff (idteacher, dayoffdate) VALUES (?, ?)";
$stmt = $conn->prepare($insertQuery);
$stmt->bind_param("is", $idTeacher, $dayoffdate);
if ($stmt->execute()) {
$message = 'success_dayoff';
} else {
$message = 'error_dayoff';
}
$stmt->close();
}
// Gestione del filtro per gli ordini
$filter = isset($_GET['filter']) ? $_GET['filter'] : 'all';
$today = date("Y-m-d");
$whereClause = "";
if ($filter == 'active') {
$whereClause = "WHERE expireon > '$today'";
} elseif ($filter == 'expired') {
$whereClause = "WHERE expireon <= '$today'";
}
// Query SQL per recuperare tutti gli ordini con join su service
$query = "SELECT o.idorderbook, o.first_name, o.last_name, s.servicename, o.expireon, o.status, o.nticket
FROM orderbook o
LEFT JOIN service s ON o.idservice = s.idservice
$whereClause
ORDER BY o.idorderbook DESC";
$result = $conn->query($query);
$orders = [];
if ($result->num_rows > 0) {
while ($row = $result->fetch_assoc()) {
$orders[] = $row;
if (!function_exists('e')) {
function e($value): string
{
return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
}
}
// Query SQL per i DayOff
$query_dayoff = "SELECT iddayoff, dayoffdate FROM dayoff WHERE idteacher = '1'";
$result_dayoff = $conn->query($query_dayoff);
$documents = [];
if ($result_dayoff->num_rows > 0) {
while ($row = $result_dayoff->fetch_assoc()) {
$documents[] = $row;
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="en">
<html lang="it">
<head>
<meta charset="utf-8" />
<title>YogiBook - Prenotazioni YogaSoul</title>
<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="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">
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@10"></script>
<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>
<script>
$(function() {
// Inizializzazione del Datepicker per i campi DayOff
$("#dayoff").datepicker({
dateFormat: "yy-mm-dd",
minDate: 0
});
// Inizializzazione del Datepicker per i campi di scadenza editabili
$(document).on('focus', '.expiryDateInput', function() {
$(this).datepicker({
dateFormat: "yy-mm-dd",
minDate: 0,
onSelect: function(dateText) {
let orderId = $(this).data('order-id');
console.log("Datepicker selezionato - idorderbook: " + orderId + ", newExpiry: " + dateText);
confirmUpdate(orderId, dateText);
}
});
});
// Rendere il campo scadenza editabile al click
$(document).on('click', '.expiry-date', function() {
let orderId = $(this).data('order-id');
let currentDate = $(this).text().trim();
console.log("Clic su expiry-date - idorderbook: " + orderId + ", currentDate: " + currentDate);
if (!orderId) {
Swal.fire('Errore!', 'ID ordine non valido.', 'error');
return;
}
$(this).html(`<input type="text" class="form-control expiryDateInput" data-order-id="${orderId}" value="${currentDate}" />`);
$(this).find('.expiryDateInput').focus();
});
// Funzione per confermare l'aggiornamento della scadenza
function confirmUpdate(orderId, newExpiry) {
let originalCell = $(`td.expiry-date[data-order-id="${orderId}"]`);
let originalDate = originalCell.text().trim();
console.log("Invio AJAX - idorderbook: " + orderId + ", new_expiry: " + newExpiry);
Swal.fire({
title: "Sei sicuro?",
text: "La data di scadenza verrà modificata!",
icon: "warning",
showCancelButton: true,
confirmButtonColor: "#3085d6",
cancelButtonColor: "#d33",
confirmButtonText: "Sì, modifica!",
cancelButtonText: "Annulla"
}).then((result) => {
if (result.isConfirmed) {
$.ajax({
url: 'update_expiry.php',
type: 'POST',
data: {
order_id: orderId,
new_expiry: newExpiry,
update_expiry: 1
},
dataType: 'json',
success: function(response) {
console.log("Risposta AJAX:", response);
if (response.status === 'success') {
Swal.fire('Successo!', response.message, 'success');
originalCell.text(newExpiry);
} else {
Swal.fire('Errore!', response.message, 'error');
originalCell.text(originalDate);
}
},
error: function(xhr, status, error) {
console.error('Errore AJAX:', {
status: status,
error: error,
responseText: xhr.responseText
});
Swal.fire('Errore!', 'Errore durante la richiesta al server: ' + error, 'error');
originalCell.text(originalDate);
}
});
} else {
originalCell.text(originalDate);
}
});
}
// Funzione per confermare la cancellazione di un ordine e delle sue prenotazioni
window.confirmDeleteOrder = function(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}`;
}
});
}
Swal.fire({
title: "Sei sicuro?",
text: "Questa prenotazione verrà cancellata definitivamente! Ricordati poi di riprogrammare la tua lezione!",
icon: "warning",
showCancelButton: true,
confirmButtonColor: "#d33",
cancelButtonColor: "#3085d6",
confirmButtonText: "Sì, cancella!",
cancelButtonText: "Annulla"
}).then((result) => {
if (result.isConfirmed) {
window.location.href = `deleteclass.php?id=${id}`;
}
});
}
});
</script>
<style>
.custom-card {
margin: 10px auto;
.orders-toolbar {
display: flex;
width: 90%;
max-width: 700px;
background-color: white;
box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.1);
border-radius: 8px;
overflow: hidden;
cursor: pointer;
transition: transform 0.2s;
}
.custom-card:hover {
transform: translateY(-5px);
}
.custom-date-box {
flex: 1;
background-color: red;
color: white;
display: flex;
flex-direction: column;
justify-content: center;
flex-wrap: wrap;
gap: 8px;
align-items: center;
padding: 0;
font-size: 60px;
font-weight: bold;
border-top-left-radius: 8px;
border-bottom-left-radius: 8px;
margin-bottom: 12px;
}
.custom-day {
line-height: 1;
.orders-search {
margin-left: auto;
position: relative;
min-width: 300px;
}
.custom-month {
font-size: 28px;
.orders-search i {
position: absolute;
left: 12px;
top: 50%;
transform: translateY(-50%);
color: #9ca3af;
}
.custom-event-details {
flex: 2;
display: flex;
flex-direction: column;
padding: 10px 20px;
background-color: lightblue;
.orders-search input {
padding-left: 34px;
}
.custom-heading {
margin-top: 0;
font-size: 24px;
#searchCount {
font-size: 13px;
color: #6b7280;
min-height: 20px;
margin-bottom: 6px;
}
.custom-paragraph {
margin-bottom: 5px;
}
.custom-actions {
display: none;
flex-direction: row;
justify-content: space-between;
margin-top: 10px;
}
.custom-card.expanded .custom-actions {
display: flex;
}
.custom-action-button {
background-color: #f0f0f0;
border: none;
padding: 8px 12px;
border-radius: 4px;
.editable {
cursor: pointer;
transition: background-color 0.2s;
white-space: nowrap;
}
.custom-action-button:hover {
background-color: #e0e0e0;
.editable:hover {
background-color: #eef6f1 !important;
box-shadow: inset 0 0 0 1px #cfe8db;
}
.expiry-date {
cursor: pointer;
.num-input {
width: 80px;
}
.expiry-date:hover {
background-color: #f0f0f0;
.expiryDateInput {
width: 130px;
}
.edit-hint {
font-size: 12px;
color: #9ca3af;
margin-bottom: 10px;
}
@media (max-width: 768px) {
.custom-card {
flex-direction: column;
}
.custom-date-box,
.custom-event-details {
width: 100%;
border-radius: 0;
}
.custom-event-time {
font-size: 24px;
.orders-search {
margin-left: 0;
min-width: 100%;
}
}
</style>
@@ -329,98 +158,118 @@ if ($result_dayoff->num_rows > 0) {
</div>
<div class="d-flex">
<?php include('include/languageselection.php'); ?>
<div class="dropdown d-inline-block">
<button type="button" class="btn header-item noti-icon" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<i class="bx bx-search icon-sm align-middle"></i>
</button>
<div class="dropdown-menu dropdown-menu-lg dropdown-menu-end p-0">
<form class="p-2">
<div class="search-box">
<div class="position-relative">
<input type="text" class="form-control rounded bg-light border-0" placeholder="Search...">
<i class="bx bx-search search-icon"></i>
</div>
</form>
</div>
</div>
<?php include('include/profiletopbar.php'); ?>
</div>
<?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>
</header>
<?php include('include/sidebar.php'); ?>
<header class="ishorizontal-topbar">
<div class="navbar-header">
<div class="d-flex"></div>
</div>
</div>
</header>
<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="mb-3">
<a href="?filter=all" class="btn btn-secondary <?php echo $filter == 'all' ? 'active' : ''; ?>">Tutti</a>
<a href="?filter=active" class="btn btn-success <?php echo $filter == 'active' ? 'active' : ''; ?>">Attivi</a>
<a href="?filter=expired" class="btn btn-danger <?php echo $filter == 'expired' ? 'active' : ''; ?>">Scaduti</a>
</div>
<div class="table-responsive">
<table class="table table-striped mb-0">
<thead>
<tr>
<th>Stato</th>
<th>Numero Ordine</th>
<th>Nome</th>
<th>Cognome</th>
<th>Nome Classe</th>
<th>Numero Ticket</th>
<th>Scadenza</th>
<th>Status</th>
<th>Azioni</th>
</tr>
</thead>
<tbody>
<?php foreach ($orders as $order) {
$isActive = ($order["expireon"] > $today);
$badgeClass = $isActive ? 'badge bg-success' : 'badge bg-danger';
$badgeText = $isActive ? 'Attivo' : 'Scaduto';
?>
<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>
<td><span class="<?php echo $badgeClass; ?>"><?php echo $badgeText; ?></span></td>
<td><?php echo $order["idorderbook"]; ?></td>
<td><?php echo $order["first_name"]; ?></td>
<td><?php echo $order["last_name"]; ?></td>
<td><?php echo $order["servicename"]; ?></td>
<td><?php echo $order["nticket"]; ?></td>
<td class="expiry-date" data-order-id="<?php echo $order['idorderbook']; ?>"><?php echo $order["expireon"]; ?></td>
<td><?php echo $order["status"]; ?></td>
<td>
<button type="button" class="btn btn-sm btn-danger"
onclick="confirmDeleteOrder(<?php echo $order['idorderbook']; ?>)">
<i class="fas fa-trash"></i>
</button>
</td>
<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>
<?php } ?>
</tbody>
</table>
</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>
<?php include('include/footer.php'); ?>
</div>
</div>
@@ -429,11 +278,261 @@ if ($result_dayoff->num_rows > 0) {
<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>
<?php if ($message == 'success_delete') { ?>
<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') { ?>
<?php } elseif ($message === 'error_delete') { ?>
<script>
Swal.fire('Errore!', 'Errore durante la cancellazione.', 'error');
</script>
+1
View File
@@ -1,3 +1,4 @@
[15-Oct-2025 15:13:11 UTC] Tentativo di aggiornamento AJAX - idorderbook: 1, new_expiry: 2025-10-16
[15-Oct-2025 15:13:19 UTC] Tentativo di aggiornamento AJAX - idorderbook: 1, new_expiry: 2025-10-15
[15-Oct-2025 15:13:58 UTC] Tentativo di aggiornamento AJAX - idorderbook: 1, new_expiry: 2025-10-30
[24-Sep-2026 09:19:47 UTC] Tentativo di aggiornamento AJAX - idorderbook: 7, new_expiry: 2026-11-02
+65
View File
@@ -0,0 +1,65 @@
<?php
/**
* Aggiorna maxreschedule o reprogrammed di un ordine (chiamato via AJAX dal riepilogo ordini).
* Risponde sempre in JSON: {status, message, maxreschedule, reprogrammed}
*/
ob_start();
require_once('include/headscript.php');
ob_end_clean(); // scarta eventuale output di headscript, così il JSON resta pulito
header('Content-Type: application/json; charset=utf-8');
function jsonOut(array $data, int $code = 200): void
{
http_response_code($code);
echo json_encode($data);
exit;
}
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
jsonOut(['status' => 'error', 'message' => 'Metodo non consentito.'], 405);
}
$allowedFields = ['maxreschedule', 'reprogrammed'];
$field = $_POST['field'] ?? '';
$orderId = (int) ($_POST['order_id'] ?? 0);
$value = (string) ($_POST['value'] ?? '');
if (!in_array($field, $allowedFields, true)) {
jsonOut(['status' => 'error', 'message' => 'Campo non valido.'], 400);
}
if ($orderId <= 0) {
jsonOut(['status' => 'error', 'message' => 'ID ordine non valido.'], 400);
}
if (!preg_match('/^\d{1,3}$/', $value)) {
jsonOut(['status' => 'error', 'message' => 'Inserisci un numero intero da 0 a 999.'], 400);
}
try {
$pdo = DBHandlerSelect::getInstance()->getConnection();
$check = $pdo->prepare("SELECT idorderbook FROM orderbook WHERE idorderbook = :id");
$check->execute([':id' => $orderId]);
if (!$check->fetch()) {
jsonOut(['status' => 'error', 'message' => 'Ordine non trovato.'], 404);
}
// $field è da whitelist, quindi sicuro da inserire nella query
$upd = $pdo->prepare("UPDATE orderbook SET `$field` = :v WHERE idorderbook = :id");
$upd->execute([':v' => (int) $value, ':id' => $orderId]);
$row = $pdo->prepare("SELECT maxreschedule, reprogrammed FROM orderbook WHERE idorderbook = :id");
$row->execute([':id' => $orderId]);
$r = $row->fetch();
jsonOut([
'status' => 'success',
'message' => 'Valore aggiornato.',
'maxreschedule' => $r['maxreschedule'] === null ? null : (int) $r['maxreschedule'],
'reprogrammed' => (int) $r['reprogrammed'],
]);
} catch (Throwable $e) {
error_log('update_reschedule: ' . $e->getMessage());
jsonOut(['status' => 'error', 'message' => 'Errore durante il salvataggio.'], 500);
}