1123 lines
52 KiB
PHP
1123 lines
52 KiB
PHP
<?php
|
||
require_once('include/headscript.php');
|
||
require_once('include/csrf.php');
|
||
require_once(__DIR__ . '/class/ShopService.php');
|
||
SchoolContext::requireSchoolAdmin();
|
||
|
||
/**
|
||
* SCUOLA - Ordini: pagina unica (sostituisce "Ordini in attesa" e "Riepilogo ordini").
|
||
* - schede: Da pagare · Da propagare · Attivi · Scaduti · Annullati · Tutti
|
||
* - ricerca istantanea, colonne ordinabili
|
||
* - azioni per riga: pagamento ricevuto, annulla, propaga (con scadenza), elimina
|
||
* - modifica al clic: scadenza, max recuperi, recuperi fatti
|
||
* - nuovo ordine manuale in una finestra
|
||
* Riusa gli script esistenti: calculate_expiry, propagatesingleorder, inserisci_ordine,
|
||
* update_expiry, update_reschedule, cancelsingleorder, deleteorder, searchemail.
|
||
*/
|
||
$pdo = DBHandlerSelect::getInstance()->getConnection();
|
||
|
||
function e($value): string
|
||
{
|
||
return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
|
||
}
|
||
|
||
function back(string $msg, string $type = 'msg'): void
|
||
{
|
||
$f = preg_replace('/[^a-z]/', '', (string) ($_GET['f'] ?? ''));
|
||
header('Location: school_orders.php?' . http_build_query(array_filter(['f' => $f, $type => $msg])));
|
||
exit();
|
||
}
|
||
|
||
/* ------------------------------------------------- AZIONI (pagamento, annullo) */
|
||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||
yb_csrf_check();
|
||
$id = (int) ($_POST['id'] ?? 0);
|
||
if (!SchoolScope::belongs('orderbook', $id)) {
|
||
back('Ordine non trovato.', 'err');
|
||
}
|
||
try {
|
||
$action = (string) ($_POST['action'] ?? '');
|
||
if ($action === 'paid') {
|
||
ShopService::markPaid((int) $idschool, $id)
|
||
? back('Pagamento registrato per l\'ordine n. ' . $id . '.')
|
||
: back('L\'ordine n. ' . $id . ' non è più in attesa di pagamento.', 'err');
|
||
}
|
||
if ($action === 'cancel') {
|
||
ShopService::cancelPending((int) $idschool, $id)
|
||
? back('Ordine n. ' . $id . ' annullato.')
|
||
: back('Si possono annullare solo gli ordini in attesa di pagamento.', 'err');
|
||
}
|
||
} catch (PDOException $ex) {
|
||
error_log('[school_orders] ' . $ex->getMessage());
|
||
back('Errore database, operazione non eseguita.', 'err');
|
||
}
|
||
back('Azione non valida.', 'err');
|
||
}
|
||
|
||
/* ------------------------------------------------------------------ DATI */
|
||
$st = $pdo->prepare(
|
||
"SELECT ob.idorderbook, ob.order_id, ob.order_date_created, ob.first_name, ob.last_name, ob.order_billing_email,
|
||
ob.product_name, ob.cod, ob.nticket, ob.quantityclass, ob.expireon, ob.status, ob.maxreschedule, ob.reprogrammed,
|
||
ob.is_openflexy, ob.booking_mode, ob.source, ob.payment_status, ob.amount, ob.discount_amount, ob.order_total,
|
||
p.kind, s.servicename, s.day, s.time,
|
||
(SELECT COUNT(*) FROM bookingclass b WHERE b.idorder = ob.idorderbook AND b.idschool = ob.idschool
|
||
AND b.status <> 'cancelled' AND b.is_gift = 'N') AS used,
|
||
(SELECT COUNT(*) FROM bookingclass b2 WHERE b2.idorder = ob.idorderbook AND b2.idschool = ob.idschool) AS anybooking
|
||
FROM orderbook ob
|
||
LEFT JOIN products p ON p.idproduct = ob.idproduct
|
||
LEFT JOIN service s ON s.idservice = ob.idservice AND s.idschool = ob.idschool
|
||
WHERE ob.idschool = ?
|
||
ORDER BY ob.idorderbook DESC
|
||
LIMIT 3000"
|
||
);
|
||
$st->execute([(int) $idschool]);
|
||
$raw = $st->fetchAll(PDO::FETCH_ASSOC);
|
||
|
||
$ids = array_map(fn($o) => (int) $o['idorderbook'], $raw);
|
||
$classesBy = [];
|
||
$lessonBy = [];
|
||
$voucherBy = [];
|
||
if ($ids) {
|
||
$ph = implode(',', array_fill(0, count($ids), '?'));
|
||
$st = $pdo->prepare("SELECT os.idorderbook, s.servicename, s.day, s.time FROM orderbook_services os JOIN service s ON s.idservice = os.idservice WHERE os.idschool = ? AND os.idorderbook IN ($ph) ORDER BY os.slot");
|
||
$st->execute(array_merge([(int) $idschool], $ids));
|
||
foreach ($st->fetchAll(PDO::FETCH_ASSOC) as $r) {
|
||
$classesBy[(int) $r['idorderbook']][] = trim($r['servicename'] . ' ' . $r['day'] . ' ' . $r['time']);
|
||
}
|
||
$st = $pdo->prepare("SELECT b.idorder, b.bookingstart, b.status FROM bookingclass b JOIN orderbook ob ON ob.idorderbook = b.idorder WHERE b.idschool = ? AND ob.booking_mode = 'single' AND b.idorder IN ($ph)");
|
||
$st->execute(array_merge([(int) $idschool], $ids));
|
||
foreach ($st->fetchAll(PDO::FETCH_ASSOC) as $r) {
|
||
$lessonBy[(int) $r['idorder']] = $r;
|
||
}
|
||
$st = $pdo->prepare("SELECT ob.idorderbook, g.code, g.recipient_name FROM gift_vouchers g JOIN orderbook ob ON ob.cod = g.code COLLATE utf8mb4_general_ci AND ob.idschool = g.idschool WHERE g.idschool = ? AND ob.idorderbook IN ($ph)");
|
||
$st->execute(array_merge([(int) $idschool], $ids));
|
||
foreach ($st->fetchAll(PDO::FETCH_ASSOC) as $r) {
|
||
$voucherBy[(int) $r['idorderbook']] = $r;
|
||
}
|
||
}
|
||
|
||
$today = date('Y-m-d');
|
||
$dueDays = max(1, (int) SchoolSettings::get('bank_due_days', 5));
|
||
$sources = ['bank' => 'Bonifico', 'stripe' => 'Carta', 'manual' => 'Segreteria', 'voucher' => 'Buono regalo', 'woocommerce' => 'Sito'];
|
||
|
||
$rows = [];
|
||
foreach ($raw as $o) {
|
||
$oid = (int) $o['idorderbook'];
|
||
$canc = in_array($o['payment_status'], ['cancelled', 'refunded'], true) || $o['status'] === 'cancelled';
|
||
$pay = $o['payment_status'] === 'pending';
|
||
if ($canc) {
|
||
$state = 'cancelled';
|
||
} elseif ($pay) {
|
||
$state = 'topay';
|
||
} elseif ($o['status'] === 'pending') {
|
||
$state = 'prop';
|
||
} elseif ($o['expireon'] && $o['expireon'] < $today) {
|
||
$state = 'expired';
|
||
} else {
|
||
$state = 'active';
|
||
}
|
||
$details = $classesBy[$oid] ?? [];
|
||
if (!$details && $o['servicename'] && $o['booking_mode'] !== 'single') {
|
||
$details[] = trim($o['servicename'] . ' ' . $o['day'] . ' ' . $o['time']);
|
||
}
|
||
if (isset($lessonBy[$oid])) {
|
||
$details[] = ShopService::dateLabel(strtotime($lessonBy[$oid]['bookingstart'])) . ' (' . $lessonBy[$oid]['status'] . ')';
|
||
}
|
||
if (isset($voucherBy[$oid])) {
|
||
$details[] = 'Buono per ' . $voucherBy[$oid]['recipient_name'] . ' · ' . $voucherBy[$oid]['code'];
|
||
}
|
||
$due = $pay ? date('Y-m-d', strtotime(($o['order_date_created'] ?: $today) . ' +' . $dueDays . ' days')) : null;
|
||
$amount = $o['amount'] !== null ? (float) $o['amount'] : (is_numeric(str_replace(',', '.', (string) $o['order_total'])) ? (float) str_replace(',', '.', (string) $o['order_total']) : null);
|
||
|
||
$rows[] = [
|
||
'id' => $oid,
|
||
'wc' => $o['order_id'],
|
||
'date' => $o['order_date_created'],
|
||
'name' => trim($o['first_name'] . ' ' . $o['last_name']),
|
||
'email' => (string) $o['order_billing_email'],
|
||
'product' => (string) ($o['product_name'] ?: ($o['servicename'] ?: $o['cod'])),
|
||
'details' => $details,
|
||
'used' => (int) $o['used'],
|
||
'tot' => (int) $o['nticket'] > 0 ? (int) $o['nticket'] : (int) $o['quantityclass'],
|
||
'expire' => $o['expireon'],
|
||
'maxres' => $o['maxreschedule'] !== null ? (int) $o['maxreschedule'] : null,
|
||
'reprog' => (int) $o['reprogrammed'],
|
||
'amount' => $amount,
|
||
'discount' => $o['discount_amount'] !== null ? (float) $o['discount_amount'] : null,
|
||
'source' => $o['source'] ? ($sources[$o['source']] ?? $o['source']) : 'Storico',
|
||
'state' => $state,
|
||
'due' => $due,
|
||
'flex' => $o['is_openflexy'] === 'Y',
|
||
'single' => $o['booking_mode'] === 'single',
|
||
'kind' => (string) $o['kind'],
|
||
'bookings' => (int) $o['anybooking'],
|
||
];
|
||
}
|
||
|
||
// Classi per il nuovo ordine
|
||
$st = $pdo->prepare("SELECT idservice, servicename, day, time FROM service WHERE idschool = ? ORDER BY servicename, day, time");
|
||
$st->execute([(int) $idschool]);
|
||
$services = $st->fetchAll(PDO::FETCH_ASSOC);
|
||
|
||
$f = preg_replace('/[^a-z]/', '', (string) ($_GET['f'] ?? ''));
|
||
$jsonFlags = JSON_UNESCAPED_UNICODE | JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP;
|
||
?>
|
||
<!doctype html>
|
||
<html lang="it">
|
||
|
||
<head>
|
||
<meta charset="utf-8" />
|
||
<title>YogiBook - Ordini</title>
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<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 href="assets/css/yb-billing.css" rel="stylesheet" type="text/css" />
|
||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||
<style>
|
||
.ord-bar {
|
||
display: flex;
|
||
gap: 8px;
|
||
flex-wrap: wrap;
|
||
align-items: center;
|
||
margin-bottom: 14px;
|
||
}
|
||
|
||
.ord-tab {
|
||
border: 1px solid #e2e4e9;
|
||
background: #fff;
|
||
border-radius: 999px;
|
||
padding: 7px 14px;
|
||
font-weight: 600;
|
||
font-size: 14px;
|
||
color: #374151;
|
||
display: inline-flex;
|
||
gap: 6px;
|
||
align-items: center;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.ord-tab:hover {
|
||
border-color: #9fcfd2;
|
||
color: #1E4A4F;
|
||
}
|
||
|
||
.ord-tab.on {
|
||
background: #1E4A4F;
|
||
border-color: #1E4A4F;
|
||
color: #fff;
|
||
}
|
||
|
||
.ord-tab .n {
|
||
background: rgba(0, 0, 0, .08);
|
||
border-radius: 999px;
|
||
padding: 0 8px;
|
||
font-size: 12px;
|
||
}
|
||
|
||
.ord-tab.on .n {
|
||
background: rgba(255, 255, 255, .2);
|
||
}
|
||
|
||
.ord-tab.todo:not(.on) .n {
|
||
background: #fff4e0;
|
||
color: #92560b;
|
||
}
|
||
|
||
.ord-search {
|
||
margin-left: auto;
|
||
min-width: 280px;
|
||
position: relative;
|
||
}
|
||
|
||
.ord-search i {
|
||
position: absolute;
|
||
left: 12px;
|
||
top: 50%;
|
||
transform: translateY(-50%);
|
||
color: #9ca3af;
|
||
}
|
||
|
||
.ord-search input {
|
||
padding-left: 34px;
|
||
}
|
||
|
||
#ord-count {
|
||
font-size: 13px;
|
||
color: #6b7280;
|
||
min-height: 20px;
|
||
margin-bottom: 6px;
|
||
}
|
||
|
||
.yb-table th.sort {
|
||
cursor: pointer;
|
||
user-select: none;
|
||
}
|
||
|
||
.yb-table th.sort:hover {
|
||
color: #1E4A4F;
|
||
}
|
||
|
||
.yb-table th.sort::after {
|
||
content: "\2195";
|
||
margin-left: 4px;
|
||
opacity: .35;
|
||
font-size: 11px;
|
||
}
|
||
|
||
.yb-table th.sort.asc::after {
|
||
content: "\25B2";
|
||
opacity: 1;
|
||
}
|
||
|
||
.yb-table th.sort.desc::after {
|
||
content: "\25BC";
|
||
opacity: 1;
|
||
}
|
||
|
||
.edit {
|
||
cursor: pointer;
|
||
border-radius: 6px;
|
||
padding: 2px 6px;
|
||
margin: -2px -6px;
|
||
display: inline-block;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.edit:hover {
|
||
background: #F4FAFA;
|
||
box-shadow: inset 0 0 0 1px #cfe5e6;
|
||
}
|
||
|
||
.edit input {
|
||
width: 140px;
|
||
padding: 4px 8px;
|
||
border: 1px solid #2F7F84;
|
||
border-radius: 6px;
|
||
}
|
||
|
||
.edit input[type=number] {
|
||
width: 70px;
|
||
}
|
||
|
||
.overdue {
|
||
color: #b03a2e;
|
||
font-weight: 700;
|
||
}
|
||
|
||
.bar {
|
||
height: 6px;
|
||
border-radius: 6px;
|
||
background: #eef3f3;
|
||
overflow: hidden;
|
||
margin-top: 4px;
|
||
width: 90px;
|
||
}
|
||
|
||
.bar span {
|
||
display: block;
|
||
height: 100%;
|
||
background: #6FBABE;
|
||
}
|
||
|
||
.res {
|
||
display: inline-block;
|
||
font-size: 12px;
|
||
font-weight: 700;
|
||
padding: 2px 8px;
|
||
border-radius: 10px;
|
||
background: #e7f7ee;
|
||
color: #1a7f52;
|
||
}
|
||
|
||
.res.mid {
|
||
background: #fff4e0;
|
||
color: #92560b;
|
||
}
|
||
|
||
.res.max {
|
||
background: #fdecea;
|
||
color: #b03a2e;
|
||
}
|
||
|
||
.res.none {
|
||
background: #eef0f3;
|
||
color: #4b5563;
|
||
}
|
||
|
||
.actions {
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.user-results {
|
||
border: 1px solid #e2e4e9;
|
||
border-radius: 8px;
|
||
margin-top: 6px;
|
||
max-height: 220px;
|
||
overflow-y: auto;
|
||
background: #fff;
|
||
}
|
||
|
||
.user-results button {
|
||
display: block;
|
||
width: 100%;
|
||
text-align: left;
|
||
border: none;
|
||
background: #fff;
|
||
padding: 9px 12px;
|
||
border-bottom: 1px solid #f0f2f5;
|
||
font-size: 13px;
|
||
}
|
||
|
||
.user-results button:hover {
|
||
background: #F4FAFA;
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.ord-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">Ordini</h4>
|
||
</div>
|
||
</div>
|
||
<div class="d-flex">
|
||
<?php include('include/schoolswitcher.php'); ?><?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">
|
||
|
||
<?php if (!empty($_GET['msg'])) : ?><div class="alert alert-success"><?php echo e($_GET['msg']); ?></div><?php endif; ?>
|
||
<?php if (!empty($_GET['err'])) : ?><div class="alert alert-danger"><?php echo e($_GET['err']); ?></div><?php endif; ?>
|
||
|
||
<div class="card admin-card">
|
||
<div class="card-body">
|
||
<div class="list-head">
|
||
<div>
|
||
<div class="section-title"><span class="disc"></span>Ordini</div>
|
||
<p class="section-lead mb-0">Tutti gli ordini della scuola. Clicca su scadenza o recuperi per modificarli.</p>
|
||
</div>
|
||
<button type="button" class="btn btn-yb" data-bs-toggle="modal" data-bs-target="#newOrder"><i class="bx bx-plus"></i> Nuovo ordine</button>
|
||
</div>
|
||
|
||
<div class="ord-bar" id="ord-tabs" role="tablist"></div>
|
||
<div id="ord-count"></div>
|
||
|
||
<div class="table-responsive">
|
||
<table class="table yb-table align-middle" id="ord-table">
|
||
<thead>
|
||
<tr>
|
||
<th class="sort" data-key="id" data-type="num">N.</th>
|
||
<th class="sort" data-key="name" data-type="text">Allieva</th>
|
||
<th class="sort" data-key="product" data-type="text">Prodotto</th>
|
||
<th class="sort" data-key="left" data-type="num">Lezioni</th>
|
||
<th class="sort" data-key="expire" data-type="text">Scadenza</th>
|
||
<th class="sort" data-key="reprog" data-type="num">Recuperi</th>
|
||
<th class="sort" data-key="amount" data-type="num">Importo</th>
|
||
<th class="sort" data-key="stateOrder" data-type="num">Stato</th>
|
||
<th class="text-end">Azioni</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody id="ord-body"></tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Form nascosto per pagamento / annullo (POST + CSRF) -->
|
||
<form id="act-form" method="post" action="school_orders.php" hidden>
|
||
<?php echo yb_csrf_field(); ?>
|
||
<input type="hidden" name="action" id="act-name">
|
||
<input type="hidden" name="id" id="act-id">
|
||
</form>
|
||
|
||
<!-- Nuovo ordine -->
|
||
<div class="modal fade" id="newOrder" tabindex="-1" aria-labelledby="newOrderTitle" aria-hidden="true">
|
||
<div class="modal-dialog modal-lg modal-dialog-scrollable">
|
||
<form class="modal-content" id="new-order-form">
|
||
<div class="modal-header">
|
||
<h5 class="modal-title" id="newOrderTitle">Nuovo ordine</h5>
|
||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Chiudi"></button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<p class="hint mt-0 mb-3">Cerca l'allieva per nome o cognome, oppure scrivi i dati a mano. L'ordine finisce in "Da propagare".</p>
|
||
<div class="row g-3">
|
||
<div class="col-md-6">
|
||
<label for="no-first">Nome *</label>
|
||
<input type="text" id="no-first" class="form-control" name="formrow-firstname-input" autocomplete="off" required>
|
||
</div>
|
||
<div class="col-md-6">
|
||
<label for="no-last">Cognome *</label>
|
||
<input type="text" id="no-last" class="form-control" name="surname-input" autocomplete="off" required>
|
||
</div>
|
||
<div class="col-12" id="no-results-wrap" hidden>
|
||
<div class="user-results" id="no-results"></div>
|
||
</div>
|
||
<div class="col-md-6">
|
||
<label for="no-email">Email *</label>
|
||
<input type="email" id="no-email" class="form-control" name="formrow-email-input" required>
|
||
</div>
|
||
<div class="col-md-6">
|
||
<label for="no-date">Data ordine</label>
|
||
<input type="date" id="no-date" class="form-control" name="example-date-input" value="<?php echo e($today); ?>">
|
||
</div>
|
||
<div class="col-md-6">
|
||
<label for="no-service">Classe *</label>
|
||
<select id="no-service" class="form-select" name="formrow-inputState" required>
|
||
<option value="">Scegli…</option>
|
||
<?php foreach ($services as $svc) : ?>
|
||
<option value="<?php echo (int) $svc['idservice']; ?>"><?php echo e(trim($svc['servicename'] . ' - ' . $svc['day'] . ' - ' . $svc['time'], ' -')); ?></option>
|
||
<?php endforeach; ?>
|
||
</select>
|
||
</div>
|
||
<div class="col-md-3">
|
||
<label for="no-ticket">N. lezioni *</label>
|
||
<input type="number" min="1" id="no-ticket" class="form-control" name="ticket" required>
|
||
</div>
|
||
<div class="col-md-3">
|
||
<label for="no-first-lesson">Prima lezione</label>
|
||
<input type="date" id="no-first-lesson" class="form-control" name="first-lesson-date">
|
||
</div>
|
||
</div>
|
||
<input type="hidden" name="cod" id="no-cod">
|
||
<input type="hidden" name="userid" id="no-userid">
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button type="button" class="btn btn-light" data-bs-dismiss="modal">Chiudi</button>
|
||
<button type="submit" class="btn btn-yb" id="no-submit"><i class="bx bx-save"></i> Inserisci ordine</button>
|
||
</div>
|
||
</form>
|
||
</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() {
|
||
'use strict';
|
||
var ROWS = <?php echo json_encode($rows, $jsonFlags); ?>;
|
||
var TODAY = <?php echo json_encode($today); ?>;
|
||
var TABS = [
|
||
['topay', 'Da pagare', true],
|
||
['prop', 'Da propagare', true],
|
||
['active', 'Attivi'],
|
||
['expired', 'Scaduti'],
|
||
['cancelled', 'Annullati'],
|
||
['all', 'Tutti']
|
||
];
|
||
var STATE_ORDER = {
|
||
topay: 1,
|
||
prop: 2,
|
||
active: 3,
|
||
expired: 4,
|
||
cancelled: 5
|
||
};
|
||
var counts = {};
|
||
TABS.forEach(function(t) {
|
||
counts[t[0]] = 0;
|
||
});
|
||
ROWS.forEach(function(r) {
|
||
counts[r.state]++;
|
||
counts.all++;
|
||
r.stateOrder = STATE_ORDER[r.state];
|
||
r.left = r.tot ? r.tot - r.used : -1;
|
||
});
|
||
|
||
var params = new URLSearchParams(location.search);
|
||
var S = {
|
||
tab: TABS.some(function(t) {
|
||
return t[0] === params.get('f');
|
||
}) ? params.get('f') : (counts.topay ? 'topay' : (counts.prop ? 'prop' : 'active')),
|
||
q: params.get('q') || '',
|
||
sortKey: 'id',
|
||
sortDir: -1
|
||
};
|
||
|
||
/* ---------------- utilità ---------------- */
|
||
function esc(s) {
|
||
return String(s === null || s === undefined ? '' : s).replace(/[&<>"']/g, function(c) {
|
||
return {
|
||
'&': '&',
|
||
'<': '<',
|
||
'>': '>',
|
||
'"': '"',
|
||
"'": '''
|
||
} [c];
|
||
});
|
||
}
|
||
|
||
function norm(s) {
|
||
return String(s || '').toLowerCase().normalize('NFD').replace(/[̀-ͯ]/g, '');
|
||
}
|
||
|
||
function euro(n) {
|
||
return n === null ? '—' : Number(n).toLocaleString('it-IT', {
|
||
minimumFractionDigits: 2,
|
||
maximumFractionDigits: 2
|
||
}) + ' €';
|
||
}
|
||
|
||
function dIt(iso) {
|
||
if (!iso) return '—';
|
||
var p = String(iso).substr(0, 10).split('-');
|
||
return p[2] + '/' + p[1] + '/' + p[0];
|
||
}
|
||
|
||
function byId(id) {
|
||
for (var i = 0; i < ROWS.length; i++)
|
||
if (ROWS[i].id === id) return ROWS[i];
|
||
return null;
|
||
}
|
||
|
||
function resBadge(max, done) {
|
||
if (max === null) return '<span class="res none">' + done + '</span>';
|
||
return '<span class="res ' + (done >= max ? 'max' : (done > 0 ? 'mid' : '')) + '">' + done + ' / ' + max + '</span>';
|
||
}
|
||
ROWS.forEach(function(r) {
|
||
r.hay = norm([r.id, r.wc, r.name, r.email, r.product, r.details.join(' ')].join(' '));
|
||
});
|
||
|
||
/* ---------------- render ---------------- */
|
||
function renderTabs() {
|
||
var h = TABS.map(function(t) {
|
||
return '<button type="button" role="tab" aria-selected="' + (S.tab === t[0]) + '" class="ord-tab ' + (S.tab === t[0] ? 'on ' : '') + (t[2] ? 'todo' : '') + '" data-tab="' + t[0] + '">' +
|
||
esc(t[1]) + ' <span class="n">' + counts[t[0]] + '</span></button>';
|
||
}).join('');
|
||
h += '<div class="ord-search"><i class="bx bx-search"></i><input type="search" id="ord-q" class="form-control" placeholder="Cerca nome, email, prodotto, n. ordine" value="' + esc(S.q) + '" aria-label="Cerca"></div>';
|
||
document.getElementById('ord-tabs').innerHTML = h;
|
||
}
|
||
|
||
function stateCell(r) {
|
||
if (r.state === 'cancelled') return '<span class="pill muted">Annullato</span>';
|
||
if (r.state === 'topay') return '<span class="pill warn">Da pagare</span><div class="sub ' + (r.due < TODAY ? 'overdue' : '') + '">' + esc(r.source) + ' · entro il ' + dIt(r.due) + '</div>';
|
||
if (r.state === 'prop') return '<span class="pill info">Da propagare</span><div class="sub">' + esc(r.source) + '</div>';
|
||
if (r.state === 'expired') return '<span class="pill muted">Scaduto</span><div class="sub">' + esc(r.source) + '</div>';
|
||
return '<span class="pill ok">Attivo</span><div class="sub">' + esc(r.source) + (r.flex ? ' · ingressi liberi' : '') + '</div>';
|
||
}
|
||
|
||
function actionsCell(r) {
|
||
var h = '';
|
||
if (r.state === 'topay') {
|
||
h += '<button type="button" class="btn-act ok" data-act="paid" data-id="' + r.id + '"><i class="bx bx-check"></i> Pagamento ricevuto</button> ';
|
||
h += '<button type="button" class="btn-act danger" data-act="cancel" data-id="' + r.id + '" aria-label="Annulla ordine"><i class="bx bx-x"></i></button>';
|
||
} else if (r.state === 'prop') {
|
||
h += '<button type="button" class="btn-act ok" data-act="propagate" data-id="' + r.id + '"><i class="bx bx-calendar-check"></i> Propaga</button> ';
|
||
if (!r.bookings) h += '<button type="button" class="btn-act danger" data-act="discard" data-id="' + r.id + '" aria-label="Elimina ordine"><i class="bx bx-trash"></i></button>';
|
||
} else if (r.state !== 'cancelled') {
|
||
h += '<button type="button" class="btn-act danger" data-act="delete" data-id="' + r.id + '" aria-label="Elimina ordine e prenotazioni"><i class="bx bx-trash"></i></button>';
|
||
}
|
||
return h;
|
||
}
|
||
|
||
function editable(r) {
|
||
return r.state === 'active' || r.state === 'expired';
|
||
}
|
||
|
||
function renderRows() {
|
||
var terms = norm(S.q).split(/\s+/).filter(Boolean);
|
||
var list = ROWS.filter(function(r) {
|
||
if (S.tab !== 'all' && r.state !== S.tab) return false;
|
||
return terms.every(function(t) {
|
||
return r.hay.indexOf(t) !== -1;
|
||
});
|
||
});
|
||
var key = S.sortKey,
|
||
dir = S.sortDir;
|
||
list.sort(function(a, b) {
|
||
var va = a[key],
|
||
vb = b[key];
|
||
if (va === null || va === undefined || va === '') return 1;
|
||
if (vb === null || vb === undefined || vb === '') return -1;
|
||
if (typeof va === 'string') {
|
||
va = norm(va);
|
||
vb = norm(vb);
|
||
}
|
||
return va < vb ? -dir : (va > vb ? dir : 0);
|
||
});
|
||
|
||
document.getElementById('ord-count').textContent = terms.length ? list.length + (list.length === 1 ? ' ordine trovato' : ' ordini trovati') : '';
|
||
document.querySelectorAll('#ord-table th.sort').forEach(function(th) {
|
||
th.classList.toggle('asc', th.dataset.key === key && dir === 1);
|
||
th.classList.toggle('desc', th.dataset.key === key && dir === -1);
|
||
});
|
||
|
||
if (!list.length) {
|
||
document.getElementById('ord-body').innerHTML = '<tr><td colspan="9"><div class="empty-state"><i class="bx bx-receipt"></i>Nessun ordine in questo elenco.</div></td></tr>';
|
||
return;
|
||
}
|
||
document.getElementById('ord-body').innerHTML = list.map(function(r) {
|
||
var ed = editable(r);
|
||
var lessons = r.tot ? r.used + ' / ' + r.tot + '<div class="bar"><span style="width:' + Math.min(100, Math.round(r.used / r.tot * 100)) + '%;"></span></div>' : (r.flex ? 'Liberi' : '—');
|
||
var expire = ed ?
|
||
'<span class="edit" data-edit="expire" data-id="' + r.id + '" title="Clicca per modificare">' + dIt(r.expire) + '</span>' :
|
||
(r.single ? '' : dIt(r.expire));
|
||
var resched = r.single ? '—' : (ed ?
|
||
'<span class="edit" data-edit="maxreschedule" data-id="' + r.id + '" title="Max recuperi: clicca per modificare">max ' + (r.maxres === null ? '–' : r.maxres) + '</span><br>' +
|
||
'<span class="edit" data-edit="reprogrammed" data-id="' + r.id + '" title="Recuperi fatti: clicca per modificare">' + resBadge(r.maxres, r.reprog) + '</span>' :
|
||
resBadge(r.maxres, r.reprog));
|
||
return '<tr>' +
|
||
'<td><strong>' + r.id + '</strong><div class="sub">' + dIt(r.date) + '</div></td>' +
|
||
'<td>' + esc(r.name || '—') + '<div class="sub">' + esc(r.email) + '</div></td>' +
|
||
'<td>' + esc(r.product || '—') + r.details.map(function(d) {
|
||
return '<div class="sub">' + esc(d) + '</div>';
|
||
}).join('') + '</td>' +
|
||
'<td>' + lessons + '</td>' +
|
||
'<td>' + expire + '</td>' +
|
||
'<td>' + resched + '</td>' +
|
||
'<td>' + (r.amount !== null ? '<strong>' + euro(r.amount) + '</strong>' : '—') + (r.discount ? '<div class="sub">sconto ' + euro(r.discount) + '</div>' : '') + '</td>' +
|
||
'<td>' + stateCell(r) + '</td>' +
|
||
'<td class="text-end actions">' + actionsCell(r) + '</td></tr>';
|
||
}).join('');
|
||
}
|
||
|
||
function syncUrl() {
|
||
var u = new URL(location.href);
|
||
u.searchParams.set('f', S.tab);
|
||
if (S.q) u.searchParams.set('q', S.q);
|
||
else u.searchParams.delete('q');
|
||
u.searchParams.delete('msg');
|
||
u.searchParams.delete('err');
|
||
history.replaceState(null, '', u.toString());
|
||
document.getElementById('act-form').action = 'school_orders.php?f=' + encodeURIComponent(S.tab);
|
||
}
|
||
|
||
/* ---------------- eventi ---------------- */
|
||
document.getElementById('ord-tabs').addEventListener('click', function(ev) {
|
||
var b = ev.target.closest('[data-tab]');
|
||
if (!b) return;
|
||
S.tab = b.dataset.tab;
|
||
renderTabs();
|
||
bindSearch();
|
||
renderRows();
|
||
syncUrl();
|
||
});
|
||
|
||
function onSearch(ev) {
|
||
S.q = ev.target.value.trim();
|
||
renderRows();
|
||
syncUrl();
|
||
}
|
||
|
||
document.querySelector('#ord-table thead').addEventListener('click', function(ev) {
|
||
var th = ev.target.closest('th.sort');
|
||
if (!th) return;
|
||
S.sortDir = S.sortKey === th.dataset.key ? -S.sortDir : (th.dataset.type === 'text' ? 1 : -1);
|
||
S.sortKey = th.dataset.key;
|
||
renderRows();
|
||
});
|
||
|
||
// Risultato di uno script esistente che risponde con un redirect: leggo i parametri dell'indirizzo finale
|
||
function finalParams(res) {
|
||
try {
|
||
return new URL(res.url).searchParams;
|
||
} catch (e) {
|
||
return new URLSearchParams();
|
||
}
|
||
}
|
||
|
||
function reloadWith(msg, tab) {
|
||
location.href = 'school_orders.php?f=' + encodeURIComponent(tab || S.tab) + '&msg=' + encodeURIComponent(msg);
|
||
}
|
||
|
||
function fail(msg) {
|
||
Swal.fire('Errore', msg || 'Operazione non riuscita. Riprova.', 'error');
|
||
}
|
||
|
||
document.getElementById('ord-body').addEventListener('click', function(ev) {
|
||
var b = ev.target.closest('[data-act]');
|
||
if (b) {
|
||
actions[b.dataset.act](byId(+b.dataset.id));
|
||
return;
|
||
}
|
||
var ed = ev.target.closest('.edit');
|
||
if (ed && !ed.querySelector('input')) startEdit(ed, byId(+ed.dataset.id), ed.dataset.edit);
|
||
});
|
||
|
||
var actions = {
|
||
paid: function(r) {
|
||
Swal.fire({
|
||
title: 'Bonifico ricevuto?',
|
||
text: 'Ordine n. ' + r.id + ' · ' + r.name + ' · ' + euro(r.amount),
|
||
icon: 'question',
|
||
showCancelButton: true,
|
||
reverseButtons: true,
|
||
confirmButtonText: 'Sì, pagato',
|
||
cancelButtonText: 'Chiudi',
|
||
confirmButtonColor: '#2F7F84'
|
||
})
|
||
.then(function(x) {
|
||
if (x.isConfirmed) post('paid', r.id);
|
||
});
|
||
},
|
||
cancel: function(r) {
|
||
Swal.fire({
|
||
title: 'Annullare l\'ordine n. ' + r.id + '?',
|
||
text: r.single ? 'Il posto nella lezione viene liberato.' : '',
|
||
icon: 'warning',
|
||
showCancelButton: true,
|
||
reverseButtons: true,
|
||
confirmButtonText: 'Annulla ordine',
|
||
cancelButtonText: 'Chiudi',
|
||
confirmButtonColor: '#c0392b'
|
||
})
|
||
.then(function(x) {
|
||
if (x.isConfirmed) post('cancel', r.id);
|
||
});
|
||
},
|
||
propagate: function(r) {
|
||
fetch('calculate_expiry.php?idorder=' + r.id, {
|
||
credentials: 'same-origin'
|
||
})
|
||
.then(function(res) {
|
||
return res.json();
|
||
})
|
||
.then(function(d) {
|
||
if (d.error) {
|
||
fail(d.error);
|
||
return;
|
||
}
|
||
return Swal.fire({
|
||
title: 'Propaga ordine n. ' + r.id,
|
||
html: '<p style="margin:0 0 8px;">' + esc(r.name) + ' · ' + esc(r.product) + '</p><label for="sw-exp" style="font-weight:600;">Scadenza</label>' +
|
||
'<input type="date" id="sw-exp" class="swal2-input" style="margin-top:6px;" value="' + esc(d.expiry_date || '') + '">' +
|
||
'<p style="font-size:13px;color:#6b7280;margin-top:8px;">Calcolata in base alle lezioni in calendario: puoi modificarla.</p>',
|
||
showCancelButton: true,
|
||
reverseButtons: true,
|
||
confirmButtonText: 'Propaga',
|
||
cancelButtonText: 'Chiudi',
|
||
confirmButtonColor: '#2F7F84',
|
||
preConfirm: function() {
|
||
var v = document.getElementById('sw-exp').value;
|
||
if (!v) {
|
||
Swal.showValidationMessage('Indica la scadenza');
|
||
}
|
||
return v;
|
||
}
|
||
}).then(function(x) {
|
||
if (!x.isConfirmed) return;
|
||
Swal.fire({
|
||
title: 'Propagazione in corso…',
|
||
allowOutsideClick: false,
|
||
didOpen: function() {
|
||
Swal.showLoading();
|
||
}
|
||
});
|
||
fetch('propagatesingleorder.php?idorder=' + r.id + '&expiry_date=' + encodeURIComponent(x.value), {
|
||
credentials: 'same-origin'
|
||
})
|
||
.then(function(res) {
|
||
var p = finalParams(res),
|
||
m = p.get('msg');
|
||
if (m === 'propagated') {
|
||
var txt = p.get('flex') ? 'Pacchetto a ingressi liberi: l\'allieva prenota da sé.' : 'Lezioni prenotate: ' + (p.get('n') || 0) + (p.get('full') ? ' · ' + p.get('full') + ' saltate perché piene' : '') + '.';
|
||
reloadWith('Ordine n. ' + r.id + ' propagato. ' + txt, 'prop');
|
||
} else if (m) {
|
||
fail({
|
||
notfound: 'Ordine non trovato o già propagato.',
|
||
noemail: 'L\'ordine non ha un\'email valida.',
|
||
baddate: 'Data di scadenza non valida.',
|
||
error: 'Errore durante la propagazione: nessuna modifica salvata.'
|
||
} [m]);
|
||
} else {
|
||
reloadWith('Operazione eseguita.', 'prop');
|
||
}
|
||
}).catch(function() {
|
||
fail();
|
||
});
|
||
});
|
||
}).catch(function() {
|
||
fail('Impossibile calcolare la scadenza.');
|
||
});
|
||
},
|
||
discard: function(r) {
|
||
Swal.fire({
|
||
title: 'Eliminare l\'ordine n. ' + r.id + '?',
|
||
text: r.name + ' · non ha prenotazioni collegate. L\'operazione non è reversibile.',
|
||
icon: 'warning',
|
||
showCancelButton: true,
|
||
reverseButtons: true,
|
||
confirmButtonText: 'Elimina',
|
||
cancelButtonText: 'Chiudi',
|
||
confirmButtonColor: '#c0392b'
|
||
})
|
||
.then(function(x) {
|
||
if (!x.isConfirmed) return;
|
||
fetch('cancelsingleorder.php?idorder=' + r.id, {
|
||
credentials: 'same-origin'
|
||
}).then(function(res) {
|
||
var m = finalParams(res).get('msg');
|
||
if (m === 'deleted') reloadWith('Ordine n. ' + r.id + ' eliminato.');
|
||
else fail(m === 'hasbookings' ? 'L\'ordine ha già prenotazioni collegate.' : 'Eliminazione non riuscita.');
|
||
}).catch(function() {
|
||
fail();
|
||
});
|
||
});
|
||
},
|
||
'delete': function(r) {
|
||
Swal.fire({
|
||
title: 'Eliminare l\'ordine n. ' + r.id + '?',
|
||
html: 'Vengono cancellati l\'ordine e <strong>tutte le prenotazioni collegate</strong> di ' + esc(r.name) + '. L\'azione è irreversibile.',
|
||
icon: 'warning',
|
||
showCancelButton: true,
|
||
reverseButtons: true,
|
||
confirmButtonText: 'Sì, elimina tutto',
|
||
cancelButtonText: 'Chiudi',
|
||
confirmButtonColor: '#c0392b'
|
||
})
|
||
.then(function(x) {
|
||
if (!x.isConfirmed) return;
|
||
fetch('deleteorder.php?id=' + r.id, {
|
||
credentials: 'same-origin'
|
||
}).then(function(res) {
|
||
var m = finalParams(res).get('message');
|
||
if (m === 'success_delete') reloadWith('Ordine n. ' + r.id + ' e prenotazioni eliminati.');
|
||
else fail('Errore durante la cancellazione.');
|
||
}).catch(function() {
|
||
fail();
|
||
});
|
||
});
|
||
}
|
||
};
|
||
|
||
function post(action, id) {
|
||
document.getElementById('act-name').value = action;
|
||
document.getElementById('act-id').value = id;
|
||
document.getElementById('act-form').submit();
|
||
}
|
||
|
||
/* ---------------- modifica al clic ---------------- */
|
||
function startEdit(el, r, field) {
|
||
var isDate = field === 'expire';
|
||
var cur = isDate ? (r.expire || '') : String(field === 'maxreschedule' ? (r.maxres === null ? '' : r.maxres) : r.reprog);
|
||
var html = el.innerHTML;
|
||
el.innerHTML = '<input type="' + (isDate ? 'date' : 'number') + '" ' + (isDate ? '' : 'min="0" max="999" step="1" ') + 'value="' + esc(cur) + '" aria-label="Nuovo valore">';
|
||
var input = el.querySelector('input');
|
||
input.focus();
|
||
var done = false;
|
||
|
||
function restore() {
|
||
el.innerHTML = html;
|
||
}
|
||
|
||
function finish(save) {
|
||
if (done) return;
|
||
done = true;
|
||
var v = input.value.trim();
|
||
if (!save || v === '' || v === cur) {
|
||
restore();
|
||
return;
|
||
}
|
||
if (!isDate && !/^\d{1,3}$/.test(v)) {
|
||
restore();
|
||
Swal.fire('Valore non valido', 'Inserisci un numero intero da 0 a 999.', 'warning');
|
||
return;
|
||
}
|
||
var label = isDate ? 'La scadenza passa al ' + dIt(v) : (field === 'maxreschedule' ? 'Max recuperi: ' : 'Recuperi fatti: ') + v;
|
||
Swal.fire({
|
||
title: 'Confermi la modifica?',
|
||
text: 'Ordine n. ' + r.id + ' · ' + label,
|
||
icon: 'question',
|
||
showCancelButton: true,
|
||
reverseButtons: true,
|
||
confirmButtonText: 'Sì, modifica',
|
||
cancelButtonText: 'Annulla',
|
||
confirmButtonColor: '#2F7F84'
|
||
})
|
||
.then(function(x) {
|
||
if (!x.isConfirmed) {
|
||
restore();
|
||
return;
|
||
}
|
||
var fd = new FormData();
|
||
fd.append('order_id', r.id);
|
||
if (isDate) {
|
||
fd.append('new_expiry', v);
|
||
fd.append('update_expiry', '1');
|
||
} else {
|
||
fd.append('field', field);
|
||
fd.append('value', v);
|
||
}
|
||
fetch(isDate ? 'update_expiry.php' : 'update_reschedule.php', {
|
||
method: 'POST',
|
||
body: fd,
|
||
credentials: 'same-origin'
|
||
})
|
||
.then(function(res) {
|
||
return res.json();
|
||
})
|
||
.then(function(d) {
|
||
if (d.status !== 'success') {
|
||
restore();
|
||
fail(d.message);
|
||
return;
|
||
}
|
||
if (isDate) {
|
||
r.expire = v;
|
||
var ns = v < TODAY ? 'expired' : 'active';
|
||
if (ns !== r.state) {
|
||
counts[r.state]--;
|
||
counts[ns]++;
|
||
r.state = ns;
|
||
r.stateOrder = STATE_ORDER[ns];
|
||
renderTabs();
|
||
bindSearch();
|
||
}
|
||
} else {
|
||
r.maxres = d.maxreschedule === null || d.maxreschedule === '' ? null : +d.maxreschedule;
|
||
r.reprog = +d.reprogrammed;
|
||
}
|
||
renderRows();
|
||
Swal.fire({
|
||
icon: 'success',
|
||
title: 'Aggiornato',
|
||
timer: 1100,
|
||
showConfirmButton: false
|
||
});
|
||
}).catch(function() {
|
||
restore();
|
||
fail();
|
||
});
|
||
});
|
||
}
|
||
input.addEventListener('keydown', function(ev) {
|
||
if (ev.key === 'Enter') {
|
||
ev.preventDefault();
|
||
finish(true);
|
||
} else if (ev.key === 'Escape') {
|
||
finish(false);
|
||
}
|
||
});
|
||
input.addEventListener('blur', function() {
|
||
finish(true);
|
||
});
|
||
}
|
||
|
||
/* ---------------- nuovo ordine ---------------- */
|
||
(function() {
|
||
var form = document.getElementById('new-order-form');
|
||
var first = document.getElementById('no-first'),
|
||
last = document.getElementById('no-last');
|
||
var wrap = document.getElementById('no-results-wrap'),
|
||
box = document.getElementById('no-results');
|
||
var timer;
|
||
|
||
function search() {
|
||
var fn = first.value.trim(),
|
||
ln = last.value.trim();
|
||
document.getElementById('no-userid').value = '';
|
||
if (fn.length < 2 && ln.length < 2) {
|
||
wrap.hidden = true;
|
||
return;
|
||
}
|
||
clearTimeout(timer);
|
||
timer = setTimeout(function() {
|
||
var fd = new FormData();
|
||
fd.append('firstName', fn);
|
||
fd.append('lastName', ln);
|
||
fd.append('searchTerm', ln);
|
||
fetch('searchemail.php', {
|
||
method: 'POST',
|
||
body: fd,
|
||
credentials: 'same-origin'
|
||
}).then(function(r) {
|
||
return r.json();
|
||
}).then(function(d) {
|
||
var list = (d && d.results) || [];
|
||
box.innerHTML = list.length ? list.map(function(u) {
|
||
return '<button type="button" data-uid="' + esc(u.userid || u.id) + '" data-first="' + esc(u.first_name) + '" data-last="' + esc(u.last_name) + '" data-email="' + esc(u.email) + '"><strong>' + esc(u.first_name + ' ' + u.last_name) + '</strong><br><span class="sub">' + esc(u.email) + '</span></button>';
|
||
}).join('') : '<div class="sub" style="padding:9px 12px;">Nessuna allieva trovata: compila i dati a mano.</div>';
|
||
wrap.hidden = false;
|
||
}).catch(function() {
|
||
wrap.hidden = true;
|
||
});
|
||
}, 300);
|
||
}
|
||
first.addEventListener('input', search);
|
||
last.addEventListener('input', search);
|
||
box.addEventListener('click', function(ev) {
|
||
var b = ev.target.closest('button[data-uid]');
|
||
if (!b) return;
|
||
first.value = b.dataset.first;
|
||
last.value = b.dataset.last;
|
||
document.getElementById('no-email').value = b.dataset.email;
|
||
document.getElementById('no-userid').value = b.dataset.uid;
|
||
wrap.hidden = true;
|
||
});
|
||
var sel = document.getElementById('no-service');
|
||
sel.addEventListener('change', function() {
|
||
document.getElementById('no-cod').value = sel.value ? sel.options[sel.selectedIndex].text : '';
|
||
});
|
||
|
||
form.addEventListener('submit', function(ev) {
|
||
ev.preventDefault();
|
||
var btn = document.getElementById('no-submit');
|
||
btn.disabled = true;
|
||
fetch('inserisci_ordine.php', {
|
||
method: 'POST',
|
||
body: new FormData(form),
|
||
credentials: 'same-origin'
|
||
}).then(function(res) {
|
||
var m = finalParams(res).get('msg');
|
||
if (m === 'inserted') {
|
||
reloadWith('Ordine inserito: ora puoi propagarlo.', 'prop');
|
||
return;
|
||
}
|
||
btn.disabled = false;
|
||
fail(m === 'invaliddata' ? 'Dati mancanti o non validi: nome, cognome, classe e numero di lezioni sono obbligatori.' : (m === 'noemail' ? 'Email non valida.' : 'Inserimento non riuscito.'));
|
||
}).catch(function() {
|
||
btn.disabled = false;
|
||
fail();
|
||
});
|
||
});
|
||
})();
|
||
|
||
function bindSearch() {
|
||
document.getElementById('ord-q').addEventListener('input', onSearch);
|
||
}
|
||
renderTabs();
|
||
bindSearch();
|
||
renderRows();
|
||
syncUrl();
|
||
})();
|
||
</script>
|
||
</body>
|
||
|
||
</html>
|