added modify to orders
This commit is contained in:
+400
-301
@@ -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';
|
||||
if (!function_exists('e')) {
|
||||
function e($value): string
|
||||
{
|
||||
return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
$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'";
|
||||
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>';
|
||||
}
|
||||
|
||||
// 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
|
||||
$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
|
||||
$whereClause
|
||||
$where
|
||||
ORDER BY o.idorderbook DESC";
|
||||
$result = $conn->query($query);
|
||||
$orders = [];
|
||||
if ($result->num_rows > 0) {
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
$orders[] = $row;
|
||||
}
|
||||
}
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$stmt->execute($params);
|
||||
$orders = $stmt->fetchAll();
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
$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,21 +158,6 @@ 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>
|
||||
</div>
|
||||
<?php include('include/profiletopbar.php'); ?>
|
||||
</div>
|
||||
</div>
|
||||
@@ -369,49 +183,83 @@ if ($result_dayoff->num_rows > 0) {
|
||||
<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 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">
|
||||
<table class="table table-striped mb-0" id="ordersTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Stato</th>
|
||||
<th>Numero Ordine</th>
|
||||
<th>N. Ordine</th>
|
||||
<th>Nome</th>
|
||||
<th>Cognome</th>
|
||||
<th>Nome Classe</th>
|
||||
<th>Numero Ticket</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) {
|
||||
$isActive = ($order["expireon"] > $today);
|
||||
$badgeClass = $isActive ? 'badge bg-success' : 'badge bg-danger';
|
||||
$badgeText = $isActive ? 'Attivo' : 'Scaduto';
|
||||
<?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>
|
||||
<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>
|
||||
<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 $order['idorderbook']; ?>)">
|
||||
<button type="button" class="btn btn-sm btn-danger" onclick="confirmDeleteOrder(<?php echo $id; ?>)">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<?php } ?>
|
||||
<?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>
|
||||
@@ -423,17 +271,268 @@ if ($result_dayoff->num_rows > 0) {
|
||||
<?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>
|
||||
<?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,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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
Reference in New Issue
Block a user