push notification setting and update pages with PDO

This commit is contained in:
2026-07-31 14:34:26 +02:00
parent 8ad8d279e6
commit b553f7e9e9
10 changed files with 1971 additions and 2290 deletions
@@ -0,0 +1,18 @@
<?php
use Phinx\Migration\AbstractMigration;
final class AddPushNotificationToAuthUsers extends AbstractMigration
{
public function change(): void
{
$this->table('auth_users')
->addColumn('pushnotification', 'char', [
'limit' => 1,
'null' => false,
'default' => 'Y',
'comment' => 'Accetta notifiche push app mobile (Y/N)',
])
->update();
}
}
+272 -325
View File
@@ -1,223 +1,264 @@
<?php require_once('include/headscript.php'); ?>
<?php if (isset($_POST['classinsert'])) { $formclass='Y'; } else { $formclass='N'; }
if (isset($_GET['message'])) { $message=$_GET['message']; } else { $message='N'; }
?>
<?php
if ($formclass=='Y') {
$InsertQuery = new WA_MySQLi_Query($bkngstm);
$InsertQuery->Action = "insert";
$InsertQuery->Table = "service";
$InsertQuery->bindColumn("servicename", "s", "".((isset($_POST["servicename"]))?$_POST["servicename"]:"") ."", "WA_DEFAULT");
$InsertQuery->bindColumn("wpcatalognumber", "i", "".((isset($_POST["wpcatalognumber"]))?$_POST["wpcatalognumber"]:"") ."", "WA_DEFAULT");
$InsertQuery->bindColumn("day", "s", "".((isset($_POST["classdayday"]))?$_POST["classdayday"]:"") ."", "WA_DEFAULT");
$InsertQuery->bindColumn("time", "s", "".((isset($_POST["classtime"]))?$_POST["classtime"]:"") ."", "WA_DEFAULT");
$InsertQuery->bindColumn("category", "i", "".((isset($_POST["servicecategory"]))?$_POST["servicecategory"]:"") ."", "WA_DEFAULT");
$InsertQuery->bindColumn("maxcapacity", "i", "".((isset($_POST["maxcapacity"]))?$_POST["maxcapacity"]:"") ."", "WA_DEFAULT");
$InsertQuery->bindColumn("colorclass", "s", "".((isset($_POST["colorclass"]))?$_POST["colorclass"]:"") ."", "WA_DEFAULT");
$InsertQuery->bindColumn("classduration", "d", "".((isset($_POST["classduration"]))?$_POST["classduration"]:"") ."", "WA_DEFAULT");
$InsertQuery->saveInSession("");
$InsertQuery->execute();
$InsertGoTo = "";
if (function_exists("rel2abs")) $InsertGoTo = $InsertGoTo?rel2abs($InsertGoTo,dirname(__FILE__)):"";
$InsertQuery->redirect($InsertGoTo);
}
?>
<?php
$servicesclass = new WA_MySQLi_RS("servicesclass",$bkngstm,0);
$servicesclass->setQuery("SELECT * FROM service LEFT JOIN servicecategory on service.category=servicecategory.idservicecategory");
$servicesclass->execute();
?>
<?php
require_once('include/headscript.php');
$conn = new mysqli($servername, $username, $password, $dbname);
/**
* Connessione unica PDO (singleton).
*/
$pdo = DBHandlerSelect::getInstance()->getConnection();
// Controlla la connessione
if ($conn->connect_error) {
die("Connessione fallita: " . $conn->connect_error);
function e($value): string
{
return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
}
// Query per ottenere le categorie dal database
$query = "SELECT idservicecategory, namecategory FROM servicecategory";
$result = $conn->query($query);
$message = $_GET['message'] ?? 'N';
$insertFeedback = null;
/* -------------------------------------------------------------------------
* Inserimento nuova classe (POST) con prepared statement
* ---------------------------------------------------------------------- */
if (isset($_POST['classinsert']) && $_POST['classinsert'] === 'Y') {
$servicename = trim($_POST['servicename'] ?? '');
$wpcatalog = $_POST['wpcatalognumber'] ?? '';
$day = trim($_POST['classdayday'] ?? '');
$time = trim($_POST['classtime'] ?? '');
$category = $_POST['servicecategory'] ?? '';
$maxcapacity = $_POST['maxcapacity'] ?? '';
$colorclass = trim($_POST['colorclass'] ?? '');
$classduration = $_POST['classduration'] ?? '';
if ($servicename === '') {
$insertFeedback = ['type' => 'error', 'text' => 'La descrizione della classe è obbligatoria.'];
} else {
$sql = "INSERT INTO service
(servicename, wpcatalognumber, day, time, category, maxcapacity, colorclass, classduration)
VALUES
(:servicename, :wpcatalog, :day, :time, :category, :maxcapacity, :colorclass, :classduration)";
$stmt = $pdo->prepare($sql);
$ok = $stmt->execute([
':servicename' => $servicename,
':wpcatalog' => $wpcatalog !== '' ? (int) $wpcatalog : null,
':day' => $day,
':time' => $time,
':category' => $category !== '' ? (int) $category : null,
':maxcapacity' => $maxcapacity !== '' ? (int) $maxcapacity : null,
':colorclass' => $colorclass,
':classduration' => $classduration !== '' ? (float) $classduration : null,
]);
if ($ok) {
// Redirect per evitare re-invio del form al refresh (pattern PRG)
header('Location: ' . $_SERVER['PHP_SELF'] . '?message=inserted');
exit;
}
$insertFeedback = ['type' => 'error', 'text' => 'Errore durante l\'inserimento della classe. Riprova.'];
}
}
/* -------------------------------------------------------------------------
* Categorie per il dropdown
* ---------------------------------------------------------------------- */
$categories = $pdo->query("SELECT idservicecategory, namecategory FROM servicecategory ORDER BY namecategory")->fetchAll();
/* -------------------------------------------------------------------------
* Elenco classi esistenti
* ---------------------------------------------------------------------- */
$services = $pdo->query(
"SELECT service.*, servicecategory.namecategory
FROM service
LEFT JOIN servicecategory ON service.category = servicecategory.idservicecategory
ORDER BY service.servicename"
)->fetchAll();
?>
<!doctype html>
<html lang="en">
<html lang="it">
<head>
<meta charset="utf-8" />
<title>YogiBook - Prenotazioni YogaSoul</title>
<title>YogiBook - Inserimento e Propagazione Classi</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" />
<!-- App favicon -->
<link rel="shortcut icon" href="assets/images/favicon.ico">
<!-- Bootstrap Css -->
<link href="assets/css/bootstrap.min.css" id="bootstrap-style" rel="stylesheet" type="text/css" />
<!-- Icons Css -->
<link href="assets/css/icons.min.css" rel="stylesheet" type="text/css" />
<!-- App Css-->
<link href="assets/css/app.min.css" id="app-style" rel="stylesheet" type="text/css" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
<link rel="stylesheet" href="https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/spectrum/1.8.1/spectrum.min.css">
<link rel="stylesheet" type="text/css" href="https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<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 type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/spectrum/1.8.1/spectrum.min.js"></script>
<!-- Includi le librerie jQuery e jQuery UI prima di includere lo script per il calendario -->
<script>
$(document).ready(function() {
// Initialize datepickers
$(".datepicker").datepicker();
$(".calendar-form").on("submit", function() {
// Allow the form to be submitted
return true;
});
});
</script>
<script>
</script>
<style>
.calendar-input {
width: 30%;
.admin-card {
border: none;
border-radius: 14px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.06);
}
</style>
<style>
.admin-card .card-body {
padding: 26px 30px;
}
.section-title {
font-size: 15px;
font-weight: 700;
letter-spacing: 0.04em;
text-transform: uppercase;
color: #6b7280;
margin-bottom: 4px;
}
.section-lead {
color: #9ca3af;
font-size: 14px;
margin-bottom: 22px;
}
.form-label {
font-weight: 600;
font-size: 14px;
color: #374151;
}
.form-control,
.form-select {
border-radius: 8px;
border: 1px solid #e2e4e9;
padding: 10px 12px;
}
.form-control:focus,
.form-select:focus {
border-color: #1ebf73;
box-shadow: 0 0 0 3px rgba(30, 191, 115, 0.12);
}
.btn-save {
border-radius: 8px;
padding: 10px 26px;
font-weight: 600;
}
.class-table thead th {
font-size: 12px;
text-transform: uppercase;
letter-spacing: 0.03em;
color: #6b7280;
border-bottom: 2px solid #eef0f3;
}
.class-table td {
vertical-align: middle;
}
.color-dot {
display: inline-block;
width: 14px;
height: 14px;
border-radius: 50%;
border: 1px solid rgba(0, 0, 0, 0.1);
vertical-align: middle;
margin-right: 6px;
}
.expanded-row {
display: none;
}
.expanded {
.expanded-row.expanded {
display: table-row;
transition: display 0.3s ease;
}
.expanded-content {
background: #f8fbf9;
border-radius: 10px;
padding: 18px 20px;
margin: 8px 0;
}
.action-btns .btn {
margin: 2px;
}
</style>
</head>
<body>
<!-- <body data-layout="horizontal"> -->
<!-- Begin page -->
<div id="layout-wrapper">
<!-- Top Bar -->
<header id="page-topbar" class="isvertical-topbar">
<div class="navbar-header">
<div class="d-flex">
<!-- LOGO -->
<?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>
<!-- start page title -->
<div class="page-title-box align-self-center d-none d-md-block">
<h4 class="page-title mb-0">Inserimento e Propagazione Classi</h4>
</div>
<!-- end page title -->
</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>
</div>
</form>
</div>
</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 class="d-flex"></div>
</div>
</div>
<div class="topnav">
<div class="container-fluid">
<nav class="navbar navbar-light navbar-expand-lg topnav-menu">
</nav>
<nav class="navbar navbar-light navbar-expand-lg topnav-menu"></nav>
</div>
</div>
</header>
<!-- ============================================================== -->
<!-- Start right Content here -->
<!-- ============================================================== -->
<div class="main-content">
<div class="page-content">
<div class="container-fluid">
<?php if ($message === 'inserted') : ?>
<div class="alert alert-success" role="alert">Classe inserita con successo.</div>
<?php endif; ?>
<?php if ($message === 'success') : ?>
<div class="alert alert-success" role="alert">Propagazione avvenuta con successo.</div>
<?php endif; ?>
<?php if ($insertFeedback) : ?>
<div class="alert alert-<?php echo $insertFeedback['type'] === 'success' ? 'success' : 'danger'; ?>" role="alert">
<?php echo e($insertFeedback['text']); ?>
</div>
<?php endif; ?>
<!-- Form inserimento classe -->
<div class="row">
<div class="col-xl-12">
<div class="card">
<div class="col-12">
<div class="card admin-card mb-4">
<div class="card-body">
<h5>Benvenuta/o </h5>
<p>Di seguito puoi vedere lo stato delle tue prenotazioni</p>
<div class="section-title">Nuova classe</div>
<p class="section-lead">Inserisci i dettagli della classe da aggiungere al catalogo.</p>
<form method="post" name="insertclass" id="insertclass">
<input type="hidden" name="classinsert" value="Y">
<div class="mb-3">
<label for="formrow-firstname-input" class="form-label">Descrizione classe</label>
<input type="text" class="form-control" placeholder="Classe" id="servicename" name="servicename">
<input type="hidden" class="form-control" id="classinsert" name="classinsert" value="Y">
<label for="servicename" class="form-label">Descrizione classe</label>
<input type="text" class="form-control" placeholder="Es. Hatha Yoga" id="servicename" name="servicename" required>
</div>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label for="formrow-email-input" class="form-label">Inserisci il WP catalog number</label>
<label for="wpcatalognumber" class="form-label">WP catalog number</label>
<input type="text" class="form-control" placeholder="WP Number" id="wpcatalognumber" name="wpcatalognumber">
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label for="choices-single-default" class="form-label font-size-13 text-muted">Seleziona il giorno</label>
<label for="classdayday" class="form-label">Giorno</label>
<select class="form-select" id="classdayday" name="classdayday">
<option value="">Seleziona</option>
<option value="Monday">Lunedì</option>
@@ -235,287 +276,193 @@ $result = $conn->query($query);
<div class="row">
<div class="col-lg-4">
<div class="mb-3">
<label for="formrow-inputCity" class="form-label">Ora</label>
<input type="text" class="form-control" placeholder="Ora" id="classtime" name="classtime">
<label for="classtime" class="form-label">Ora</label>
<input type="text" class="form-control" placeholder="Es. 18:30" id="classtime" name="classtime">
</div>
</div>
<div class="col-lg-4">
<div class="mb-3">
<label for="formrow-inputZip" class="form-label">Durata</label>
<input type="text" class="form-control" placeholder="Durata" id="classduration" name="classduration">
<label for="classduration" class="form-label">Durata (ore)</label>
<input type="text" class="form-control" placeholder="Es. 1.5" id="classduration" name="classduration">
</div>
</div>
<div class="col-lg-4">
<div class="mb-3">
<label for="servicecategory">Seleziona una categoria:</label>
<label for="servicecategory" class="form-label">Categoria</label>
<select name="servicecategory" id="servicecategory" class="form-select">
<?php
// Popola il dropdown con le opzioni dalla query
while ($row = $result->fetch_assoc()) {
$idservicecategory = $row['idservicecategory'];
$namecategory = $row['namecategory'];
echo "<option value='$idservicecategory'>$namecategory</option>";
}
?>
<option value="">Seleziona</option>
<?php foreach ($categories as $cat) : ?>
<option value="<?php echo e($cat['idservicecategory']); ?>">
<?php echo e($cat['namecategory']); ?>
</option>
<?php endforeach; ?>
</select>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-4">
<div class="mb-3">
<label for="formrow-inputCity" class="form-label">Capacità Massima</label>
<input type="text" class="form-control" placeholder="Max Capacity" id="maxcapacity" name="maxcapacity">
<label for="maxcapacity" class="form-label">Capacità massima</label>
<input type="text" class="form-control" placeholder="Es. 12" id="maxcapacity" name="maxcapacity">
</div>
</div>
<div class="col-lg-4">
<div class="mb-3">
<label for="color-picker" class="form-label">Seleziona e modifica un colore</label>
<input type="color" class="form-control color-picker" id="color-picker" name="color-picker">
<input type="text" class="form-control color-code" id="color-code" name="color-code" maxlength="7" pattern="#[0-9A-Fa-f]{6}">
<label for="colorclass" class="form-label">Colore classe</label>
<input type="text" class="form-control" id="colorclass" name="colorclass" value="#1ebf73">
</div>
</div>
</div>
<script>
const colorPicker = document.querySelector('.color-picker');
const colorCodeInput = document.querySelector('.color-code');
colorPicker.addEventListener('input', function(event) {
colorCodeInput.value = event.target.value;
});
colorCodeInput.addEventListener('input', function(event) {
const colorCode = event.target.value;
if (colorCode.match(/^#[0-9A-Fa-f]{6}$/)) {
colorPicker.value = colorCode;
}
});
</script>
</div>
</div>
<div class="mb-3">
</div>
<div>
<button type="submit" class="btn btn-primary w-md">Inserisci</button>
</div>
<button type="submit" class="btn btn-primary btn-save">Inserisci classe</button>
</form>
</div>
</div>
</div>
</div>
</div>
<!-- container-fluid -->
</div>
<div class="container-fluid">
<!-- Elenco classi -->
<div class="row">
<div class="col-xl-12">
<div class="card">
<div class="col-12">
<div class="card admin-card">
<div class="card-body">
<?php if ($message=='success') { ?>
<div class="alert alert-success" role="alert">
Propagazione avvenuta con successo
</div>
<?php } ?>
<div class="section-title">Classi esistenti</div>
<p class="section-lead">Propaga una classe sul calendario, associala o modificane i dettagli.</p>
<div class="table-responsive">
<table class="table table-nowrap align-middle mb-0">
<thead class="table-light">
<table class="table class-table align-middle mb-0">
<thead>
<tr>
<th></th>
<th>Classe</th>
<th>Giorno</th>
<th>Orario</th>
<th>Durata</th>
<th>Categoria</th>
<th>Action</th>
<th class="text-end">Azioni</th>
</tr>
</thead>
<tbody>
<?php
$wa_startindex = 0;
while (!$servicesclass->atEnd()) {
$wa_startindex = $servicesclass->Index;
?>
<?php if (empty($services)) : ?>
<tr>
<td colspan="6" class="text-center text-muted py-4">Nessuna classe presente.</td>
</tr>
<?php else : ?>
<?php foreach ($services as $svc) : ?>
<tr>
<td style="width: 40px;"></td>
<td>
<h5 class="text-truncate font-size-14 m-0">
<a href="javascript: void(0);" class="text-dark">
<?php echo ($servicesclass->getColumnVal("servicename")); ?>
</a>
</h5>
<span class="color-dot" style="background-color: <?php echo e($svc['colorclass'] ?: '#ccc'); ?>;"></span>
<span class="font-size-14"><?php echo e($svc['servicename']); ?></span>
</td>
<td><?php echo e($svc['day']); ?></td>
<td><?php echo e($svc['time']); ?></td>
<td>
<p class="mb-0"><?php echo ($servicesclass->getColumnVal("day")); ?></p>
</td>
<td>
<p class="mb-0"><?php echo ($servicesclass->getColumnVal("time")); ?></p>
</td>
<td>
<p class="mb-0">
<i class="mdi mdi-clock-time-nine-outline align-middle font-size-16 me-1"></i>
<?php echo ($servicesclass->getColumnVal("classduration")); ?>
</p>
<?php echo e($svc['classduration']); ?>
</td>
<td>
<p class="mb-0"><?php echo ($servicesclass->getColumnVal("namecategory")); ?></p>
</td>
<td>
<button type="button" class="btn btn-primary toggle-form">
Propaga
<td><?php echo e($svc['namecategory']); ?></td>
<td class="text-end action-btns">
<button type="button" class="btn btn-primary btn-sm toggle-form">Propaga</button>
<a href="associate-services.php?id=<?php echo (int) $svc['idservice']; ?>">
<button type="button" class="btn btn-info btn-sm">Associa</button>
</a>
<a href="updateservice.php?idservice=<?php echo (int) $svc['idservice']; ?>">
<button type="button" class="btn btn-warning btn-sm">
<i class="mdi mdi-lead-pencil font-size-16 align-middle"></i>
</button>
<a href="associate-services.php?id=<?php echo ($servicesclass->getColumnVal("idservice")); ?>"><button type="button" class="btn btn-info toggle-form">
Associa
</button></a>
<?php $idservice=$servicesclass->getColumnVal("idservice"); ?>
<a href="updateservice.php?idservice=<?php echo $idservice; ?>"><button type="button" class="btn btn-warning waves-effect waves-light">
<i class="mdi mdi-lead-pencil font-size-16 align-middle me-2"></i>
</a>
<a href="cancelservice.php?idservice=<?php echo (int) $svc['idservice']; ?>">
<button type="button" class="btn btn-danger btn-sm">
<i class="bx bx-block font-size-16 align-middle"></i>
</button>
<a href="cancelservice.php?idservice=<?php echo ($servicesclass->getColumnVal("idservice")); ?>"><button type="button" class="btn btn-danger waves-effect waves-light">
<i class="bx bx-block font-size-16 align-middle me-2"></i>
</button></a>
</a>
</td>
</tr>
<tr class="expanded-row">
<td colspan="7">
<td colspan="6">
<div class="expanded-content">
<form id="calendar-form" name="calendar-form" class="calendar-form" method="post" action="nextdateclass.php">
<form class="calendar-form" method="post" action="nextdateclass.php">
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label for="propagatestartdate">Start Date:</label>
<input type="text" class="datepicker propagatestartdate form-control" name="propagatestartdate" required>
<label class="form-label">Data inizio</label>
<input type="text" class="datepicker form-control" name="propagatestartdate" required>
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label for="propagateenddate">End Date:</label>
<input type="text" class="datepicker propagateenddate form-control" name="propagateenddate" required>
<label class="form-label">Data fine</label>
<input type="text" class="datepicker form-control" name="propagateenddate" required>
</div>
</div>
</div>
<input type="hidden" class="idservice" name="idservice" value='<?php echo $servicesclass->getColumnVal("idservice"); ?>'>
<input type="hidden" class="timeclass" name="timeclass" value='<?php echo $servicesclass->getColumnVal("time"); ?>'>
<input type="hidden" class="dayclass" name="dayclass" value='<?php echo $servicesclass->getColumnVal("day"); ?>'>
<input type="hidden" class="durationtime" name="durationtime" value='<?php echo $servicesclass->getColumnVal("classduration"); ?>'>
<div>
<button type="submit" class="btn btn-primary w-md">Invia</button>
</div>
<input type="hidden" name="idservice" value="<?php echo (int) $svc['idservice']; ?>">
<input type="hidden" name="timeclass" value="<?php echo e($svc['time']); ?>">
<input type="hidden" name="dayclass" value="<?php echo e($svc['day']); ?>">
<input type="hidden" name="durationtime" value="<?php echo e($svc['classduration']); ?>">
<button type="submit" class="btn btn-primary btn-save">Propaga sul calendario</button>
</form>
</div>
</td>
</tr>
<?php
$servicesclass->moveNext();
}
$servicesclass->moveFirst(); //return RS to the first record
unset($wa_startindex);
unset($wa_repeatcount);
?>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<!-- container-fluid -->
</div>
<!-- End Page-content -->
<?php include('include/footer.php'); ?>
</div>
<!-- end main content-->
</div>
<!-- END layout-wrapper -->
</div>
<script>
document.addEventListener("DOMContentLoaded", function() {
const toggleButtons = document.querySelectorAll(".toggle-form");
toggleButtons.forEach(button => {
button.addEventListener("click", function() {
const row = this.closest("tr");
const expandedRow = row.nextElementSibling;
const expandedContent = expandedRow.querySelector(".expanded-content");
if (expandedRow.classList.contains("expanded")) {
expandedRow.classList.remove("expanded");
} else {
expandedRow.classList.add("expanded");
// Aggiungi qui il codice per popolare il contenuto espanso
// Puoi utilizzare AJAX per ottenere i dati dinamicamente se necessario
}
});
});
});
</script>
<!-- JAVASCRIPT -->
<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>
$(document).ready(function() {
// Datepicker sui campi di propagazione
$(".datepicker").datepicker({
dateFormat: "yy-mm-dd"
});
// Color picker spectrum collegato al VERO campo colorclass (ora viene salvato)
$('#colorclass').spectrum({
preferredFormat: 'hex', // Il formato del codice del colore (es. esadecimale)
showInput: true, // Mostra l'input per inserire il codice del colore manualmente
showPalette: true, // Mostra una tavolozza di colori predefiniti
palette: [ // Esempi di colori predefiniti nella tavolozza
"#FF0000", "#00FF00", "#0000FF"
// Aggiungi altri colori se necessario
preferredFormat: 'hex',
showInput: true,
showPalette: true,
palette: [
["#FF0000", "#00FF00", "#0000FF"],
["#1ebf73", "#f39c12", "#8e44ad"]
],
change: function(color) {
$('#colorclass').val(color.toHexString()); // Imposta il valore dell'input con il codice del colore
$('#colorclass').val(color.toHexString());
}
});
});
// Espansione riga propagazione
document.addEventListener("DOMContentLoaded", function() {
document.querySelectorAll(".toggle-form").forEach(function(button) {
button.addEventListener("click", function() {
var row = this.closest("tr");
var expandedRow = row.nextElementSibling;
if (expandedRow && expandedRow.classList.contains("expanded-row")) {
expandedRow.classList.toggle("expanded");
}
});
});
});
</script>
</body>
</html>
+230 -282
View File
@@ -1,382 +1,330 @@
<?php require_once('include/headscript.php'); ?>
<?php if (isset($_POST['classinsert'])) { $formclass='Y'; } else { $formclass='N'; } ?>
<?php if (isset($_GET['id'])) { $idmain=$_GET['id']; } ?>
<?php if (isset($_POST['idservice'])) { $idservice=$_POST['idservice'];
$idmain=$idservice; }
?>
<?php if (isset($_POST['formclass'])) { $formclass=$_POST['formclass']; } ?>
<?php
if ($formclass=='Y') {
$InsertQuery = new WA_MySQLi_Query($bkngstm);
$InsertQuery->Action = "insert";
$InsertQuery->Table = "associateclass";
$InsertQuery->bindColumn("idmainservice", "i", "".((isset($_POST["idmainservice"]))?$_POST["idmainservice"]:"") ."", "WA_DEFAULT");
$InsertQuery->bindColumn("idassociateservice", "i", "".((isset($_POST["servicelist"]))?$_POST["servicelist"]:"") ."", "WA_DEFAULT");
$InsertQuery->saveInSession("");
$InsertQuery->execute();
$InsertGoTo = "";
if (function_exists("rel2abs")) $InsertGoTo = $InsertGoTo?rel2abs($InsertGoTo,dirname(__FILE__)):"";
$InsertQuery->redirect($InsertGoTo);
require_once('include/headscript.php');
/**
* Associazione di classi a una classe principale.
*/
$pdo = DBHandlerSelect::getInstance()->getConnection();
function e($value): string
{
return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
}
?>
<?php
$servicesclass = new WA_MySQLi_RS("servicesclass",$bkngstm,0);
$servicesclass->setQuery("SELECT * FROM service LEFT JOIN servicecategory on service.category=servicecategory.idservicecategory WHERE service.idservice='$idmain'");
$servicesclass->execute();
?>
<?php
$associatedclasses = new WA_MySQLi_RS("associatedclasses",$bkngstm,0);
$associatedclasses->setQuery("SELECT * FROM associateclass LEFT JOIN service on service.idservice=associateclass.idassociateservice WHERE associateclass.idmainservice='$idmain'");
$associatedclasses->execute();
?>
// --- Determina la classe principale (da GET id o POST idservice) ---
$idmain = 0;
if (isset($_GET['id'])) {
$idmain = (int) $_GET['id'];
}
if (isset($_POST['idservice'])) {
$idmain = (int) $_POST['idservice'];
}
if (isset($_POST['idmainservice'])) {
$idmain = (int) $_POST['idmainservice'];
}
$insertFeedback = null;
/* -------------------------------------------------------------------------
* Inserimento associazione (POST)
* ---------------------------------------------------------------------- */
if (isset($_POST['formclass']) && $_POST['formclass'] === 'Y') {
$idmainservice = (int) ($_POST['idmainservice'] ?? 0);
$idassociateservice = (int) ($_POST['servicelist'] ?? 0);
if ($idmainservice > 0 && $idassociateservice > 0) {
$stmt = $pdo->prepare(
"INSERT INTO associateclass (idmainservice, idassociateservice)
VALUES (:main, :assoc)"
);
$ok = $stmt->execute([
':main' => $idmainservice,
':assoc' => $idassociateservice,
]);
$insertFeedback = $ok
? ['type' => 'success', 'text' => 'Associazione aggiunta con successo.']
: ['type' => 'error', 'text' => 'Errore durante l\'associazione. Riprova.'];
} else {
$insertFeedback = ['type' => 'error', 'text' => 'Seleziona una classe da associare.'];
}
}
/* -------------------------------------------------------------------------
* Dati della classe principale
* ---------------------------------------------------------------------- */
$mainService = null;
if ($idmain > 0) {
$stmtMain = $pdo->prepare(
"SELECT service.*, servicecategory.namecategory
FROM service
LEFT JOIN servicecategory ON service.category = servicecategory.idservicecategory
WHERE service.idservice = :idmain
LIMIT 1"
);
$stmtMain->execute([':idmain' => $idmain]);
$mainService = $stmtMain->fetch();
}
/* -------------------------------------------------------------------------
* Elenco di tutte le classi (per il dropdown), escludendo la principale
* ---------------------------------------------------------------------- */
$stmtAll = $pdo->prepare(
"SELECT idservice, servicename FROM service WHERE idservice != :idmain ORDER BY servicename"
);
$stmtAll->execute([':idmain' => $idmain]);
$allServices = $stmtAll->fetchAll();
/* -------------------------------------------------------------------------
* Classi già associate alla principale
* ---------------------------------------------------------------------- */
$stmtAssoc = $pdo->prepare(
"SELECT associateclass.idassociateclass, service.servicename
FROM associateclass
LEFT JOIN service ON service.idservice = associateclass.idassociateservice
WHERE associateclass.idmainservice = :idmain"
);
$stmtAssoc->execute([':idmain' => $idmain]);
$associatedClasses = $stmtAssoc->fetchAll();
?>
<!doctype html>
<html lang="en">
<html lang="it">
<head>
<meta charset="utf-8" />
<title>YogiBook - Prenotazioni YogaSoul</title>
<title>YogiBook - Associazione Servizi</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" />
<!-- App favicon -->
<link rel="shortcut icon" href="assets/images/favicon.ico">
<!-- Bootstrap Css -->
<link href="assets/css/bootstrap.min.css" id="bootstrap-style" rel="stylesheet" type="text/css" />
<!-- Icons Css -->
<link href="assets/css/icons.min.css" rel="stylesheet" type="text/css" />
<!-- App Css-->
<link href="assets/css/app.min.css" id="app-style" rel="stylesheet" type="text/css" />
<link rel="stylesheet" href="https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/spectrum-colorpicker@1.8.1/dist/spectrum.min.css">
<script src="https://cdn.jsdelivr.net/npm/spectrum-colorpicker@1.8.1/dist/spectrum.min.js"></script>
<script>
$(document).ready(function() {
$("#datepicker").datepicker(); // Inizializza il calendario
$("#calendar-form").on("submit", function(event) {
event.preventDefault();
// Ottieni il valore della data selezionata
const selectedDate = $("input[name='selectedDate']").val();
// Esegui qui le azioni necessarie con la data selezionata
// Ad esempio, puoi inviare la data al server tramite AJAX
// $.ajax({
// url: "url_del_server",
// type: "POST",
// data: { selectedDate: selectedDate },
// success: function(response) {
// // Aggiorna la visualizzazione dei dati o effettua altre azioni
// },
// error: function(error) {
// // Gestisci l'errore
// }
// });
});
});
</script>
<style>
.calendar-input {
width: 30%;
.admin-card {
border: none;
border-radius: 14px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.06);
}
.admin-card .card-body {
padding: 26px 30px;
}
.section-title {
font-size: 15px;
font-weight: 700;
letter-spacing: 0.04em;
text-transform: uppercase;
color: #6b7280;
margin-bottom: 4px;
}
.section-lead {
color: #9ca3af;
font-size: 14px;
margin-bottom: 22px;
}
.main-service-name {
font-size: 20px;
font-weight: 700;
color: #1ebf73;
}
.form-label {
font-weight: 600;
font-size: 14px;
color: #374151;
}
.form-select {
border-radius: 8px;
border: 1px solid #e2e4e9;
padding: 10px 12px;
}
.form-select:focus {
border-color: #1ebf73;
box-shadow: 0 0 0 3px rgba(30, 191, 115, 0.12);
}
.btn-save {
border-radius: 8px;
padding: 10px 26px;
font-weight: 600;
}
.assoc-table thead th {
font-size: 12px;
text-transform: uppercase;
letter-spacing: 0.03em;
color: #6b7280;
border-bottom: 2px solid #eef0f3;
}
.assoc-table td {
vertical-align: middle;
}
.empty-state {
text-align: center;
padding: 26px 10px;
color: #9ca3af;
}
.empty-state i {
font-size: 30px;
margin-bottom: 8px;
display: block;
color: #d5d9e0;
}
</style>
<style>
.expanded-row {
display: none;
}
.expanded {
display: table-row;
transition: display 0.3s ease;
}
</style>
</head>
<body>
<!-- <body data-layout="horizontal"> -->
<!-- Begin page -->
<div id="layout-wrapper">
<!-- Top Bar -->
<header id="page-topbar" class="isvertical-topbar">
<div class="navbar-header">
<div class="d-flex">
<!-- LOGO -->
<?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>
<!-- start page title -->
<div class="page-title-box align-self-center d-none d-md-block">
<h4 class="page-title mb-0">Associazione Servizi</h4>
</div>
<!-- end page title -->
</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>
</div>
</form>
</div>
</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 class="d-flex"></div>
</div>
</div>
<div class="topnav">
<div class="container-fluid">
<nav class="navbar navbar-light navbar-expand-lg topnav-menu">
</nav>
<nav class="navbar navbar-light navbar-expand-lg topnav-menu"></nav>
</div>
</div>
</header>
<!-- ============================================================== -->
<!-- Start right Content here -->
<!-- ============================================================== -->
<div class="main-content">
<div class="page-content">
<div class="container-fluid">
<?php if ($insertFeedback) : ?>
<div class="alert alert-<?php echo $insertFeedback['type'] === 'success' ? 'success' : 'danger'; ?>" role="alert">
<?php echo e($insertFeedback['text']); ?>
</div>
<?php endif; ?>
<!-- Form associazione -->
<div class="row">
<div class="col-xl-12">
<div class="card">
<div class="col-12">
<div class="card admin-card mb-4">
<div class="card-body">
<h5>Benvenuta/o </h5>
<p>Servizio: <?php echo ($servicesclass->getColumnVal("servicename")); ?></p>
<div class="section-title">Classe principale</div>
<p class="main-service-name mb-3">
<?php echo $mainService ? e($mainService['servicename']) : 'Classe non trovata'; ?>
</p>
<p class="section-lead">Associa un'altra classe a questa, così condivideranno la programmazione.</p>
<form method="post" name="insertclass" id="insertclass">
<div class="row">
<div class="col-lg-4">
<div class="col-lg-6">
<div class="mb-3">
<label for="formrow-inputState" class="form-label">Categoria</label>
<select id="servicelist" name="servicelist" class="form-select">
<option selected="">Seleziona...</option>
<?php
// Connessione al database
// Creazione della connessione
$conn = new mysqli($servername, $username, $password, $dbname);
// Verifica della connessione
if ($conn->connect_error) {
die("Connessione al database fallita: " . $conn->connect_error);
}
// Query per selezionare dati dalla tabella service
$query = "SELECT idservice, servicename FROM service";
$result = $conn->query($query);
// Popolamento del dropdown con i dati dalla tabella service
while ($row = $result->fetch_assoc()) {
echo '<option value="' . $row['idservice'] . '">' . $row['servicename'] . '</option>';
}
// Chiusura della connessione
$conn->close();
?>
<label for="servicelist" class="form-label">Classe da associare</label>
<select id="servicelist" name="servicelist" class="form-select" required>
<option value="">Seleziona...</option>
<?php foreach ($allServices as $svc) : ?>
<option value="<?php echo (int) $svc['idservice']; ?>">
<?php echo e($svc['servicename']); ?>
</option>
<?php endforeach; ?>
</select>
<input type="hidden" id="idmainservice" name="idmainservice" value="<?php echo $idmain; ?>" />
<input type="hidden" id="formclass" name="formclass" value="Y" />
</div>
</div>
</div>
<div>
<button type="submit" class="btn btn-primary w-md">Inserisci</button>
</div>
<input type="hidden" name="idmainservice" value="<?php echo (int) $idmain; ?>">
<input type="hidden" name="formclass" value="Y">
<button type="submit" class="btn btn-primary btn-save">Associa classe</button>
</form>
</div>
</div>
</div>
</div>
</div>
<!-- container-fluid -->
</div>
<div class="container-fluid">
<!-- Elenco associazioni -->
<div class="row">
<div class="col-xl-12">
<div class="card">
<div class="col-12">
<div class="card admin-card">
<div class="card-body">
<div class="table-responsive">
<table class="table table-nowrap align-middle mb-0">
<thead class="table-light">
<tr>
<th></th>
<th>Associated Class</th>
<th>Cancel</th>
<div class="section-title">Classi associate</div>
<p class="section-lead">Classi attualmente collegate a questa principale.</p>
<div class="table-responsive">
<table class="table assoc-table align-middle mb-0">
<thead>
<tr>
<th>Classe associata</th>
<th class="text-end">Azione</th>
</tr>
</thead>
<tbody>
<?php
$wa_startindex = 0;
while (!$associatedclasses->atEnd()) {
$wa_startindex = $associatedclasses->Index;
?>
<?php if (empty($associatedClasses)) : ?>
<tr>
<td style="width: 40px;"></td>
<td>
<h5 class="text-truncate font-size-14 m-0">
<a href="javascript: void(0);" class="text-dark">
<?php echo ($associatedclasses->getColumnVal("servicename")); ?>
</a>
</h5>
</td>
<td>
<a href="cancelassociate.php?idassociateclass=<?php echo ($associatedclasses->getColumnVal("idassociateclass")); ?>&id=<?php echo $idmain; ?>"<button type="button" class="btn btn-danger toggle-form">
Rimuovi
</button>
<td colspan="2">
<div class="empty-state">
<i class="fas fa-link-slash"></i>
Nessuna classe associata a questa principale.
</div>
</td>
</tr>
<?php
$associatedclasses->moveNext();
}
$associatedclasses->moveFirst(); //return RS to the first record
unset($wa_startindex);
unset($wa_repeatcount);
?>
<?php else : ?>
<?php foreach ($associatedClasses as $assoc) : ?>
<tr>
<td>
<span class="font-size-14"><?php echo e($assoc['servicename']); ?></span>
</td>
<td class="text-end">
<a href="cancelassociate.php?idassociateclass=<?php echo (int) $assoc['idassociateclass']; ?>&id=<?php echo (int) $idmain; ?>">
<button type="button" class="btn btn-danger btn-sm">
<i class="fas fa-trash"></i> Rimuovi
</button>
</a>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<!-- container-fluid -->
</div>
<!-- End Page-content -->
<?php include('include/footer.php'); ?>
</div>
<!-- end main content-->
</div>
<!-- END layout-wrapper -->
</div>
<script>
$(document).ready(function() {
$("#colorclass").spectrum({
preferredFormat: "hex", // Formato del colore preferito
showInput: true, // Mostra il campo di input per inserire il codice del colore manualmente
showInitial: true, // Mostra il colore iniziale
chooseText: "Scegli", // Testo del pulsante "Scegli"
cancelText: "Annulla", // Testo del pulsante "Annulla"
change: function(color) {
// Questa funzione verrà chiamata quando il colore viene cambiato
$("#colorclass").val(color.toHexString()); // Imposta il valore del colore selezionato nell'input "Colore"
$("#colorpicker-hidden").val(color.toHexString()); // Imposta il valore del colore selezionato nel campo nascosto
}
});
});
</script>
<script>
document.addEventListener("DOMContentLoaded", function() {
const toggleButtons = document.querySelectorAll(".toggle-form");
toggleButtons.forEach(button => {
button.addEventListener("click", function() {
const row = this.closest("tr");
const expandedRow = row.nextElementSibling;
const expandedContent = expandedRow.querySelector(".expanded-content");
if (expandedRow.classList.contains("expanded")) {
expandedRow.classList.remove("expanded");
} else {
expandedRow.classList.add("expanded");
// Aggiungi qui il codice per popolare il contenuto espanso
// Puoi utilizzare AJAX per ottenere i dati dinamicamente se necessario
}
});
});
});
</script>
<!-- JAVASCRIPT -->
<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>
</body>
</html>
+66
View File
@@ -2031,3 +2031,69 @@ Lezione aggiunta: {"idbookingclass":22,"bookingstart":"2025-10-21T18:15:00+00:00
Lezioni per idorderbook 3: 4
Order ID: 1, Lessons count: 12
Order ID: 3, Lessons count: 4
Esecuzione dashboard: 2026-07-30 17:16:19
Database connesso: yogibookaury
Elaborazione ordine: idorderbook = 1, order_id = 1
Query lezioni per idorderbook 1: SELECT bc.idbookingclass, bc.bookingstart, bc.status, bc.lostlesson, bc.expirylesson, bc.idservice, bc.is_reprogrammed, s.servicename
FROM bookingclass bc
LEFT JOIN service s ON bc.idservice = s.idservice
WHERE bc.idorder = ?
Numero di lezioni trovate per idorderbook 1: 12
Lezione aggiunta: {"idbookingclass":1,"bookingstart":"2025-09-02T18:15:00+00:00","status":"booked","lostlesson":"N","expirylesson":"N","idservice":42,"is_reprogrammed":"N","servicename":"Aerial Yoga - intermedio"}
Lezione aggiunta: {"idbookingclass":2,"bookingstart":"2025-09-23T18:15:00+00:00","status":"booked","lostlesson":"N","expirylesson":"N","idservice":42,"is_reprogrammed":"N","servicename":"Aerial Yoga - intermedio"}
Lezione aggiunta: {"idbookingclass":3,"bookingstart":"2025-09-30T18:15:00+00:00","status":"booked","lostlesson":"N","expirylesson":"N","idservice":42,"is_reprogrammed":"N","servicename":"Aerial Yoga - intermedio"}
Lezione aggiunta: {"idbookingclass":4,"bookingstart":"2025-10-14T18:15:00+00:00","status":"booked","lostlesson":"N","expirylesson":"N","idservice":42,"is_reprogrammed":"N","servicename":"Aerial Yoga - intermedio"}
Lezione aggiunta: {"idbookingclass":7,"bookingstart":"2025-10-10T18:15:00+00:00","status":"booked","lostlesson":"Y","expirylesson":"N","idservice":42,"is_reprogrammed":"N","servicename":"Aerial Yoga - intermedio"}
Lezione aggiunta: {"idbookingclass":8,"bookingstart":"2025-10-11T12:15:00+00:00","status":"booked","lostlesson":"Y","expirylesson":"N","idservice":42,"is_reprogrammed":"N","servicename":"Aerial Yoga - intermedio"}
Lezione aggiunta: {"idbookingclass":9,"bookingstart":"2025-10-11T18:15:00+00:00","status":"booked","lostlesson":"N","expirylesson":"N","idservice":42,"is_reprogrammed":"N","servicename":"Aerial Yoga - intermedio"}
Lezione aggiunta: {"idbookingclass":10,"bookingstart":"2025-11-18T18:15:00+00:00","status":"booked","lostlesson":"N","expirylesson":"N","idservice":42,"is_reprogrammed":"N","servicename":"Aerial Yoga - intermedio"}
Lezione aggiunta: {"idbookingclass":11,"bookingstart":"2025-11-25T18:15:00+00:00","status":"booked","lostlesson":"N","expirylesson":"N","idservice":42,"is_reprogrammed":"N","servicename":"Aerial Yoga - intermedio"}
Lezione aggiunta: {"idbookingclass":14,"bookingstart":"2025-10-14T18:15:00+00:00","status":"booked","lostlesson":"N","expirylesson":"N","idservice":42,"is_reprogrammed":"N","servicename":"Aerial Yoga - intermedio"}
Lezione aggiunta: {"idbookingclass":21,"bookingstart":"2025-11-24T19:30:00+00:00","status":"booked","lostlesson":"N","expirylesson":"N","idservice":41,"is_reprogrammed":"N","servicename":"Hatha Yoga"}
Lezione aggiunta: {"idbookingclass":26,"bookingstart":"2025-12-02T18:15:00+00:00","status":"booked","lostlesson":"N","expirylesson":"N","idservice":42,"is_reprogrammed":"Y","servicename":"Aerial Yoga - intermedio"}
Lezioni per idorderbook 1: 12
Elaborazione ordine: idorderbook = 3, order_id =
Query lezioni per idorderbook 3: SELECT bc.idbookingclass, bc.bookingstart, bc.status, bc.lostlesson, bc.expirylesson, bc.idservice, bc.is_reprogrammed, s.servicename
FROM bookingclass bc
LEFT JOIN service s ON bc.idservice = s.idservice
WHERE bc.idorder = ?
Numero di lezioni trovate per idorderbook 3: 4
Lezione aggiunta: {"idbookingclass":15,"bookingstart":"2025-10-15T19:30:00+00:00","status":"booked","lostlesson":"N","expirylesson":"N","idservice":41,"is_reprogrammed":"N","servicename":"Hatha Yoga"}
Lezione aggiunta: {"idbookingclass":17,"bookingstart":"2025-11-03T19:30:00+00:00","status":"booked","lostlesson":"N","expirylesson":"N","idservice":41,"is_reprogrammed":"N","servicename":"Hatha Yoga"}
Lezione aggiunta: {"idbookingclass":18,"bookingstart":"2025-11-10T19:30:00+00:00","status":"booked","lostlesson":"N","expirylesson":"N","idservice":41,"is_reprogrammed":"N","servicename":"Hatha Yoga"}
Lezione aggiunta: {"idbookingclass":22,"bookingstart":"2025-10-21T18:15:00+00:00","status":"booked","lostlesson":"N","expirylesson":"N","idservice":42,"is_reprogrammed":"Y","servicename":"Aerial Yoga - intermedio"}
Lezioni per idorderbook 3: 4
Esecuzione dashboard: 2026-07-30 17:16:19
Database connesso: yogibookaury
Elaborazione ordine: idorderbook = 1, order_id = 1
Query lezioni per idorderbook 1: SELECT bc.idbookingclass, bc.bookingstart, bc.status, bc.lostlesson, bc.expirylesson, bc.idservice, bc.is_reprogrammed, s.servicename
FROM bookingclass bc
LEFT JOIN service s ON bc.idservice = s.idservice
WHERE bc.idorder = ?
Numero di lezioni trovate per idorderbook 1: 12
Lezione aggiunta: {"idbookingclass":1,"bookingstart":"2025-09-02T18:15:00+00:00","status":"booked","lostlesson":"N","expirylesson":"N","idservice":42,"is_reprogrammed":"N","servicename":"Aerial Yoga - intermedio"}
Lezione aggiunta: {"idbookingclass":2,"bookingstart":"2025-09-23T18:15:00+00:00","status":"booked","lostlesson":"N","expirylesson":"N","idservice":42,"is_reprogrammed":"N","servicename":"Aerial Yoga - intermedio"}
Lezione aggiunta: {"idbookingclass":3,"bookingstart":"2025-09-30T18:15:00+00:00","status":"booked","lostlesson":"N","expirylesson":"N","idservice":42,"is_reprogrammed":"N","servicename":"Aerial Yoga - intermedio"}
Lezione aggiunta: {"idbookingclass":4,"bookingstart":"2025-10-14T18:15:00+00:00","status":"booked","lostlesson":"N","expirylesson":"N","idservice":42,"is_reprogrammed":"N","servicename":"Aerial Yoga - intermedio"}
Lezione aggiunta: {"idbookingclass":7,"bookingstart":"2025-10-10T18:15:00+00:00","status":"booked","lostlesson":"Y","expirylesson":"N","idservice":42,"is_reprogrammed":"N","servicename":"Aerial Yoga - intermedio"}
Lezione aggiunta: {"idbookingclass":8,"bookingstart":"2025-10-11T12:15:00+00:00","status":"booked","lostlesson":"Y","expirylesson":"N","idservice":42,"is_reprogrammed":"N","servicename":"Aerial Yoga - intermedio"}
Lezione aggiunta: {"idbookingclass":9,"bookingstart":"2025-10-11T18:15:00+00:00","status":"booked","lostlesson":"N","expirylesson":"N","idservice":42,"is_reprogrammed":"N","servicename":"Aerial Yoga - intermedio"}
Lezione aggiunta: {"idbookingclass":10,"bookingstart":"2025-11-18T18:15:00+00:00","status":"booked","lostlesson":"N","expirylesson":"N","idservice":42,"is_reprogrammed":"N","servicename":"Aerial Yoga - intermedio"}
Lezione aggiunta: {"idbookingclass":11,"bookingstart":"2025-11-25T18:15:00+00:00","status":"booked","lostlesson":"N","expirylesson":"N","idservice":42,"is_reprogrammed":"N","servicename":"Aerial Yoga - intermedio"}
Lezione aggiunta: {"idbookingclass":14,"bookingstart":"2025-10-14T18:15:00+00:00","status":"booked","lostlesson":"N","expirylesson":"N","idservice":42,"is_reprogrammed":"N","servicename":"Aerial Yoga - intermedio"}
Lezione aggiunta: {"idbookingclass":21,"bookingstart":"2025-11-24T19:30:00+00:00","status":"booked","lostlesson":"N","expirylesson":"N","idservice":41,"is_reprogrammed":"N","servicename":"Hatha Yoga"}
Lezione aggiunta: {"idbookingclass":26,"bookingstart":"2025-12-02T18:15:00+00:00","status":"booked","lostlesson":"N","expirylesson":"N","idservice":42,"is_reprogrammed":"Y","servicename":"Aerial Yoga - intermedio"}
Lezioni per idorderbook 1: 12
Elaborazione ordine: idorderbook = 3, order_id =
Query lezioni per idorderbook 3: SELECT bc.idbookingclass, bc.bookingstart, bc.status, bc.lostlesson, bc.expirylesson, bc.idservice, bc.is_reprogrammed, s.servicename
FROM bookingclass bc
LEFT JOIN service s ON bc.idservice = s.idservice
WHERE bc.idorder = ?
Numero di lezioni trovate per idorderbook 3: 4
Lezione aggiunta: {"idbookingclass":15,"bookingstart":"2025-10-15T19:30:00+00:00","status":"booked","lostlesson":"N","expirylesson":"N","idservice":41,"is_reprogrammed":"N","servicename":"Hatha Yoga"}
Lezione aggiunta: {"idbookingclass":17,"bookingstart":"2025-11-03T19:30:00+00:00","status":"booked","lostlesson":"N","expirylesson":"N","idservice":41,"is_reprogrammed":"N","servicename":"Hatha Yoga"}
Lezione aggiunta: {"idbookingclass":18,"bookingstart":"2025-11-10T19:30:00+00:00","status":"booked","lostlesson":"N","expirylesson":"N","idservice":41,"is_reprogrammed":"N","servicename":"Hatha Yoga"}
Lezione aggiunta: {"idbookingclass":22,"bookingstart":"2025-10-21T18:15:00+00:00","status":"booked","lostlesson":"N","expirylesson":"N","idservice":42,"is_reprogrammed":"Y","servicename":"Aerial Yoga - intermedio"}
Lezioni per idorderbook 3: 4
Order ID: 1, Lessons count: 12
Order ID: 3, Lessons count: 4
+10 -9
View File
@@ -12,7 +12,6 @@ include('../extra/auth.php');
if (! Auth::check()) {
redirectTo('login');
}
$user = Auth::user();
$iduserlogin = $user->present()->id;
@@ -34,10 +33,14 @@ $lastname=$user->present()->last_name;
?>
<?php require_once('Connections/bkngstm.php'); ?>
<?php require_once('webassist/mysqli/rsobj.php'); ?>
<?php // require_once('@@RSObjectPath@@'); ?>
<?php // require_once('@@RSObjectPath@@');
?>
<?php require_once('webassist/mysqli/queryobj.php'); ?>
<?php // require_once("../webassist/form_validations/wavt_scripts_php.php"); ?>
<?php //include('generalsettings.php'); ?>
<?php require_once('class/db-functions.php'); ?>
<?php // require_once("../webassist/form_validations/wavt_scripts_php.php");
?>
<?php //include('generalsettings.php');
?>
<?php
if (session_status() == PHP_SESSION_NONE) {
session_start();
@@ -55,7 +58,9 @@ $timestampnow=time();
$temporarycode = $iduserlog . "-" . $timestampnow;
$_SESSION["tempcode"] = $temporarycode;
$tempcode = $_SESSION["tempcode"];
} else { $tempcode=$_SESSION["tempcode"]; }
} else {
$tempcode = $_SESSION["tempcode"];
}
?>
<?php
@@ -150,7 +155,3 @@ die();
} */
?>
+85 -75
View File
@@ -1,95 +1,105 @@
<?php require_once('Connections/bkngstm.php'); ?>
<?php require_once('webassist/mysqli/rsobj.php'); ?>
<?php require_once('webassist/mysqli/queryobj.php'); ?>
<?php if (isset($_POST['propagatestartdate'])) { $propagatestartdate=$_POST['propagatestartdate']; } ?>
<?php if (isset($_POST['propagateenddate'])) { $propagateenddate=$_POST['propagateenddate']; } ?>
<?php if (isset($_POST['idservice'])) { $idservice=$_POST['idservice']; } ?>
<?php if (isset($_POST['dayclass'])) { $dayclass=$_POST['dayclass']; } ?>
<?php if (isset($_POST['timeclass'])) { $timeclass=$_POST['timeclass']; } ?>
<?php if (isset($_POST['durationtime'])) { $durationtime=$_POST['durationtime']; }
?>
<?php //propagate classes
// Create connection
$conn = new mysqli($servername, $username, $password, $dbname);
?>
<?php //fake variable
//$propagateenddate="2024-12-01";
//$dayclass="Tuesday";
//$idservice="1";
//$timeclass="18:30";
//$durationtime="1";
?>
<?php
//define next day
require_once('include/headscript.php');
$nextClassDay = strtotime($propagatestartdate); // Utilizza la data di propagazione come data di inizio
/**
* Propagazione di una classe sul calendario (serviceschedule).
* Genera le occorrenze settimanali tra data inizio e data fine,
* saltando le date già presenti e i giorni di pausa (dayoff).
*
* Script di sola logica: nessun output, redirect finale.
*/
$dayOfWeek = date('w', $nextClassDay); // Ottieni il giorno della settimana della data di partenza
$pdo = DBHandlerSelect::getInstance()->getConnection();
// Mappa dei nomi dei giorni in inglese ai valori numerici dei giorni della settimana
$daysOfWeek = array(
// --- Input dal form (con default vuoti) ---
$propagatestartdate = trim($_POST['propagatestartdate'] ?? '');
$propagateenddate = trim($_POST['propagateenddate'] ?? '');
$idservice = isset($_POST['idservice']) ? (int) $_POST['idservice'] : 0;
$dayclass = trim($_POST['dayclass'] ?? '');
$timeclass = trim($_POST['timeclass'] ?? '');
$durationtime = isset($_POST['durationtime']) ? (float) $_POST['durationtime'] : 0;
// --- Mappa giorni settimana ---
$daysOfWeek = [
"Sunday" => 0,
"Monday" => 1,
"Tuesday" => 2,
"Wednesday" => 3,
"Thursday" => 4,
"Friday" => 5,
"Saturday" => 6
"Saturday" => 6,
];
// --- Validazione minima: senza questi non si procede ---
if (
$propagatestartdate === '' || $propagateenddate === ''
|| $idservice <= 0 || !isset($daysOfWeek[$dayclass])
) {
header('Location: admin-services.php?message=error');
exit;
}
// --- Calcolo della prima occorrenza del giorno desiderato ---
$startTs = strtotime($propagatestartdate);
$endTs = strtotime($propagateenddate);
if ($startTs === false || $endTs === false) {
header('Location: admin-services.php?message=error');
exit;
}
$dayOfWeekStart = (int) date('w', $startTs);
$desiredDayValue = $daysOfWeek[$dayclass];
$daysUntilNext = ($desiredDayValue + 7 - $dayOfWeekStart) % 7;
$currentTs = $startTs + $daysUntilNext * 86400;
$dateEnd = date('Y-m-d', $endTs);
// --- Statement preparati una sola volta e riusati nel loop ---
$checkClass = $pdo->prepare(
"SELECT idserviceschedule FROM serviceschedule
WHERE dateschedule = :dateschedule AND idservice = :idservice
LIMIT 1"
);
$checkDayoff = $pdo->prepare(
"SELECT iddayoff FROM dayoff WHERE dayoffdate = :dayoffdate LIMIT 1"
);
$insertSchedule = $pdo->prepare(
"INSERT INTO serviceschedule (idservice, dateschedule, scheduleday, startingtime, durationtime)
VALUES (:idservice, :dateschedule, :scheduleday, :startingtime, :durationtime)"
);
// Ottieni il valore numerico del giorno della settimana desiderato
$desiredDayValue = $daysOfWeek[$dayclass];
$inserted = 0;
// Calcola quanti giorni mancano fino al prossimo o stesso giorno della settimana desiderato
$daysUntilNextDayClass = ($desiredDayValue + 7 - $dayOfWeek) % 7;
while (date('Y-m-d', $currentTs) <= $dateEnd) {
$currentDate = date('Y-m-d', $currentTs);
$datetimeSchedule = $currentDate . ' ' . $timeclass;
// Aggiungi il numero di giorni al timestamp della data di partenza per ottenere il primo giorno della settimana desiderato
$firstDayClassTimestamp = $nextClassDay + $daysUntilNextDayClass * 24 * 60 * 60;
// La data è già schedulata per questo servizio?
$checkClass->execute([
':dateschedule' => $datetimeSchedule,
':idservice' => $idservice,
]);
$alreadyScheduled = $checkClass->fetchColumn();
$firstDayClassDate = date('Y-m-d', $firstDayClassTimestamp);
// È un giorno di pausa?
$checkDayoff->execute([':dayoffdate' => $currentDate]);
$isDayoff = $checkDayoff->fetchColumn();
$datenext = $firstDayClassDate;
$datenextTimestamp=strtotime($datenext);
$endClassDay = strtotime($propagateenddate); // Utilizza la data di propagazione come data di inizio
$dateend = date('Y-m-d', $endClassDay); // stop date
while ($datenext <= $dateend) {
// merge time with date
$datenextschedule=$datenext.' '.$timeclass;
//query to check if present
$checkdateclass = new WA_MySQLi_RS("checkdateclass",$bkngstm,0);
$checkdateclass->setQuery("SELECT * FROM serviceschedule WHERE serviceschedule.dateschedule='$datenextschedule' AND serviceschedule.idservice='$idservice'");
$checkdateclass->execute();
// Query per verificare se il giorno è un giorno di pausa
$checkdayoff = new WA_MySQLi_RS("checkdayoff", $bkngstm, 0);
$checkdayoff->setQuery("SELECT * FROM dayoff WHERE dayoffdate = '$datenext'");
$checkdayoff->execute();
if (empty($checkdateclass->getColumnVal("idserviceschedule")) && empty($checkdayoff->getColumnVal("iddayoff"))) {
$sql = "INSERT INTO serviceschedule (idservice, dateschedule, scheduleday, startingtime, durationtime)
VALUES ($idservice, '$datenextschedule', '$dayclass', '$timeclass', $durationtime)";
if ($conn->query($sql) === TRUE) {
echo "New record created successfully";
} else {
echo "Error: " . $sql . "<br>" . $conn->error;
if (!$alreadyScheduled && !$isDayoff) {
$insertSchedule->execute([
':idservice' => $idservice,
':dateschedule' => $datetimeSchedule,
':scheduleday' => $dayclass,
':startingtime' => $timeclass,
':durationtime' => $durationtime,
]);
$inserted++;
}
}
$datenextTimestamp =strtotime("+7 day", $datenextTimestamp);
$datenext=date('Y-m-d', $datenextTimestamp);
echo $nextClassDay;
echo $datenext;
// Avanza di 7 giorni
$currentTs = strtotime('+7 day', $currentTs);
}
header("Location: admin-services.php?message=success"); ?>
header('Location: admin-services.php?message=success');
exit;
+102 -365
View File
@@ -1,90 +1,71 @@
<?php
// Abilita visualizzazione errori PHP (solo per debug)
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
require_once('include/headscript.php');
// Inizializza log
$logFile = 'dashboard_log.txt';
$logMessage = "Esecuzione dashboard: " . date('Y-m-d H:i:s') . "\n";
/**
* Connessione unica PDO (singleton). $iduserlogin arriva dalla sessione
* autenticata via headscript.php. Lo forziamo comunque a intero.
*/
$pdo = DBHandlerSelect::getInstance()->getConnection();
$iduserlogin = (int) $iduserlogin;
// Verifica se è stato inviato un modulo
if ($_SERVER["REQUEST_METHOD"] == "POST") {
if (isset($_FILES["fileToUpload"]) && $_FILES["fileToUpload"]["error"] === UPLOAD_ERR_OK) {
$conn = new mysqli($servername, $username, $password, $dbname);
if ($conn->connect_error) {
$logMessage .= "Connessione al database fallita: " . $conn->connect_error . "\n";
file_put_contents($logFile, $logMessage, FILE_APPEND);
die("Connessione al database fallita: " . $conn->connect_error);
}
$iduserlogin = filter_var($_POST["iduserlogin"], FILTER_VALIDATE_INT);
$logMessage .= "ID utente ricevuto dal form: $iduserlogin\n";
$conn->close();
} else {
$logMessage .= "Errore caricamento file o iduserlogin non valido\n";
file_put_contents($logFile, $logMessage, FILE_APPEND);
}
/* -------------------------------------------------------------------------
* Helper per output sicuro in HTML (anti-XSS)
* ---------------------------------------------------------------------- */
function e($value): string
{
return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
}
// Connessione al database
$conn = new mysqli($servername, $username, $password, $dbname);
if ($conn->connect_error) {
$logMessage .= "Connessione al database fallita: " . $conn->connect_error . "\n";
file_put_contents($logFile, $logMessage, FILE_APPEND);
die("Connessione al database fallita: " . $conn->connect_error);
}
$logMessage .= "Database connesso: $dbname\n";
// Query per selezionare i dati filtrati per iduser, inclusi maxreschedule e reprogrammed
$iduserlogin = $iduserlogin; // Sostituisci con $iduserlogin in produzione
$query = "SELECT o.idorderbook, o.order_id, o.idservice, o.order_date_created, o.quantityclass, o.first_lesson_date, o.expireon, o.maxreschedule, o.reprogrammed, s.servicename, s.day, s.time
/* -------------------------------------------------------------------------
* 1) Ordini dell'utente
* ---------------------------------------------------------------------- */
$sqlOrders = "SELECT o.idorderbook, o.order_id, o.idservice, o.order_date_created,
o.quantityclass, o.first_lesson_date, o.expireon,
o.maxreschedule, o.reprogrammed,
s.servicename, s.day, s.time
FROM orderbook o
LEFT JOIN service s ON o.idservice = s.idservice
WHERE o.iduser = ?";
$stmt = $conn->prepare($query);
$stmt->bind_param("i", $iduserlogin);
$stmt->execute();
$result = $stmt->get_result();
WHERE o.iduser = :iduser
ORDER BY o.order_date_created DESC";
$documents = array();
while ($row = $result->fetch_assoc()) {
// Get lesson details for each order
$idorderbook = $row['idorderbook'];
$logMessage .= "Elaborazione ordine: idorderbook = $idorderbook, order_id = {$row['order_id']}\n";
$stmtOrders = $pdo->prepare($sqlOrders);
$stmtOrders->execute([':iduser' => $iduserlogin]);
$orders = $stmtOrders->fetchAll();
$lesson_query = "SELECT bc.idbookingclass, bc.bookingstart, bc.status, bc.lostlesson, bc.expirylesson, bc.idservice, bc.is_reprogrammed, s.servicename
/* -------------------------------------------------------------------------
* 2) Tutte le lezioni degli ordini in UNA sola query (niente N+1)
* Poi le raggruppiamo per idorder in PHP.
* ---------------------------------------------------------------------- */
$lessonsByOrder = [];
$orderIds = array_filter(array_map(fn($o) => (int) $o['idorderbook'], $orders));
if (!empty($orderIds)) {
$placeholders = implode(',', array_fill(0, count($orderIds), '?'));
$sqlLessons = "SELECT bc.idbookingclass, bc.bookingstart, bc.status, bc.lostlesson,
bc.expirylesson, bc.idservice, bc.is_reprogrammed,
bc.idorder, s.servicename
FROM bookingclass bc
LEFT JOIN service s ON bc.idservice = s.idservice
WHERE bc.idorder = ?";
$lesson_stmt = $conn->prepare($lesson_query);
$lesson_stmt->bind_param("i", $idorderbook);
$lesson_stmt->execute();
$lesson_result = $lesson_stmt->get_result();
WHERE bc.idorder IN ($placeholders)";
$stmtLessons = $pdo->prepare($sqlLessons);
$stmtLessons->execute(array_values($orderIds));
$lessons = array();
$logMessage .= "Query lezioni per idorderbook $idorderbook: $lesson_query\n";
if ($lesson_result) {
$logMessage .= "Numero di lezioni trovate per idorderbook $idorderbook: " . $lesson_result->num_rows . "\n";
while ($lesson_row = $lesson_result->fetch_assoc()) {
$lesson_row['bookingstart'] = date('c', strtotime($lesson_row['bookingstart']));
$lessons[] = $lesson_row;
$logMessage .= "Lezione aggiunta: " . json_encode($lesson_row) . "\n";
foreach ($stmtLessons->fetchAll() as $lesson) {
// Normalizziamo la data in formato ISO 8601 (come faceva date('c', ...))
if (!empty($lesson['bookingstart'])) {
$lesson['bookingstart'] = date('c', strtotime($lesson['bookingstart']));
}
} else {
$logMessage .= "Errore nella query per idorderbook $idorderbook: " . $conn->error . "\n";
$lessonsByOrder[(int) $lesson['idorder']][] = $lesson;
}
$row['lessons'] = $lessons;
$documents[] = $row;
$logMessage .= "Lezioni per idorderbook $idorderbook: " . count($lessons) . "\n";
$lesson_stmt->close();
}
$stmt->close();
file_put_contents($logFile, $logMessage, FILE_APPEND);
// Attacchiamo a ogni ordine il suo array di lezioni
foreach ($orders as &$order) {
$oid = (int) $order['idorderbook'];
$order['lessons'] = $lessonsByOrder[$oid] ?? [];
}
unset($order);
?>
<!doctype html>
<html lang="it">
@@ -109,47 +90,39 @@ file_put_contents($logFile, $logMessage, FILE_APPEND);
dateFormat: "yy-mm-dd"
});
// Handle order click for popup
$('.order-row').click(function() {
var lessons = $(this).data('lessons');
console.log('Lezioni ricevute:', lessons);
var total = $(this).data('total');
var orderId = $(this).data('order-id');
var isExpired = $(this).data('is-expired') === true; // Converti in booleano
var expireOn = $(this).data('expireon');
// ---- Funzione unica per costruire e mostrare il popup dettagli ordine ----
function showOrderDetails(data) {
var lessons = data.lessons || [];
var total = data.total;
var orderId = data.orderId;
var isExpired = data.isExpired === true;
var expireOn = data.expireOn;
// Calcolo delle date
var now = new Date();
// Calcolo dei conteggi
var completed = lessons.filter(l => {
var completed = lessons.filter(function(l) {
var lessonDate = new Date(l.bookingstart);
return (l.status === 'completed') ||
(l.status === 'booked' && lessonDate < now && l.lostlesson !== 'Y' && l.expirylesson !== 'Y');
}).length;
var lost = lessons.filter(l => l.lostlesson === 'Y').length;
var expired = lessons.filter(l => l.expirylesson === 'Y').length;
var booked = lessons.filter(l => {
var lost = lessons.filter(function(l) {
return l.lostlesson === 'Y';
}).length;
var expired = lessons.filter(function(l) {
return l.expirylesson === 'Y';
}).length;
var booked = lessons.filter(function(l) {
var lessonDate = new Date(l.bookingstart);
return (l.status === 'booked' && lessonDate >= now && l.lostlesson !== 'Y' && l.expirylesson !== 'Y');
}).length;
var toSchedule = total - (booked + completed + lost + expired);
// Se l'ordine è scaduto, sposta le lezioni "Da Programmare" in "Scadute"
// Se l'ordine è scaduto, le "Da Programmare" diventano "Scadute"
if (isExpired) {
expired += toSchedule;
toSchedule = 0;
}
console.log({
booked: booked,
completed: completed,
lost: lost,
expired: expired,
toSchedule: toSchedule,
total: total
});
var expireOnFormatted = expireOn ? new Date(expireOn).toLocaleDateString('it-IT', {
day: '2-digit',
month: '2-digit',
@@ -241,13 +214,9 @@ file_put_contents($logFile, $logMessage, FILE_APPEND);
htmlContent += `
<tr class="${index % 2 === 0 ? 'even-row' : 'odd-row'}">
<td style="padding: 8px; font-size: 13px;">${lessonDate.toLocaleString('it-IT', {
day: '2-digit',
month: '2-digit',
year: 'numeric',
hour: '2-digit',
minute: '2-digit'
day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit'
})}</td>
<td style="padding: 8px; font-size: 13px;">${lesson.servicename}</td>
<td style="padding: 8px; font-size: 13px;">${lesson.servicename ?? ''}</td>
<td style="padding: 8px;">
<span class="badge ${badgeClass}" style="color: ${badgeTextColor}; padding: 6px 10px; font-size: 12px; font-weight: 500;">${statusText}</span>
</td>
@@ -272,157 +241,28 @@ file_put_contents($logFile, $logMessage, FILE_APPEND);
popup: 'custom-modal'
}
});
}
// Estrae i dati dalla riga e chiama la funzione unica
function detailsFromRow(row) {
showOrderDetails({
lessons: row.data('lessons'),
total: row.data('total'),
orderId: row.data('order-id'),
isExpired: row.data('is-expired') === true,
expireOn: row.data('expireon')
});
}
// Click sulla riga
$('.order-row').click(function() {
detailsFromRow($(this));
});
// Handle details button click
// Click sul bottone "Dettagli" (senza propagare alla riga)
$('.details-btn').click(function(e) {
e.stopPropagation();
var row = $(this).closest('tr');
var lessons = row.data('lessons');
var total = row.data('total');
var orderId = row.data('order-id');
var isExpired = row.data('is-expired') === true;
var expireOn = row.data('expireon');
var now = new Date();
var completed = lessons.filter(l => {
var lessonDate = new Date(l.bookingstart);
return (l.status === 'completed') ||
(l.status === 'booked' && lessonDate < now && l.lostlesson !== 'Y' && l.expirylesson !== 'Y');
}).length;
var lost = lessons.filter(l => l.lostlesson === 'Y').length;
var expired = lessons.filter(l => l.expirylesson === 'Y').length;
var booked = lessons.filter(l => {
var lessonDate = new Date(l.bookingstart);
return (l.status === 'booked' && lessonDate >= now && l.lostlesson !== 'Y' && l.expirylesson !== 'Y');
}).length;
var toSchedule = total - (booked + completed + lost + expired);
// Se l'ordine è scaduto, sposta le lezioni "Da Programmare" in "Scadute"
if (isExpired) {
expired += toSchedule;
toSchedule = 0;
}
var expireOnFormatted = expireOn ? new Date(expireOn).toLocaleDateString('it-IT', {
day: '2-digit',
month: '2-digit',
year: 'numeric'
}) : 'Non specificata';
var htmlContent = `
<h4 style="margin-bottom: 20px; color: #333; font-weight: 600;">
Dettagli Ordine #${orderId}
<span class="badge ${isExpired ? 'bg-danger' : 'bg-primary'}" style="margin-left: 10px; color: white;">
${isExpired ? 'Scaduto' : 'Attivo'}
</span>
</h4>
<div style="display: flex; justify-content: space-around; margin-bottom: 30px; gap: 10px;">
<div class="stat-box" style="background-color: #d1e7dd; border: 1px solid #a3cfbb;" title="Numero di lezioni acquistate per questo ordine">
<h5 style="margin: 0; color: #0f5132; font-size: 14px;">Totale</h5>
<p style="font-size: 24px; font-weight: bold; margin: 5px 0; color: #0f5132;">${total}</p>
</div>
<div class="stat-box" style="background-color: #d4edda; border: 1px solid #b1d4b6;" title="Lezioni già praticate">
<h5 style="margin: 0; color: #155724; font-size: 14px;">Praticate</h5>
<p style="font-size: 24px; font-weight: bold; margin: 5px 0; color: #155724;">${completed}</p>
</div>
<div class="stat-box" style="background-color: #f8d7da; border: 1px solid #f1aeb5;" title="Lezioni non praticate e non riprogrammate in tempo">
<h5 style="margin: 0; color: #721c24; font-size: 14px;">Perse</h5>
<p style="font-size: 24px; font-weight: bold; margin: 5px 0; color: #721c24;">${lost}</p>
</div>
<div class="stat-box" style="background-color: #fff3cd; border: 1px solid #ffecb5;" title="Lezioni non riprogrammate entro la data di scadenza dell'ordine">
<h5 style="margin: 0; color: #856404; font-size: 14px;">Scadute</h5>
<p style="font-size: 24px; font-weight: bold; margin: 5px 0; color: #856404;">${expired}</p>
</div>
<div class="stat-box" style="background-color: #e2d3f5; border: 1px solid #c3b2d6;" title="Lezioni da programmare entro la data di scadenza del tuo ordine (${expireOnFormatted})">
<h5 style="margin: 0; color: #4c2c92; font-size: 14px;">Da Programmare</h5>
<p style="font-size: 24px; font-weight: bold; margin: 5px 0; color: #4c2c92;">${toSchedule}</p>
</div>
</div>
<div style="background-color: #f8f9fa; padding: 15px; border-radius: 8px;">
<table class="lesson-table">
<thead>
<tr>
<th>Data e Ora</th>
<th>Lezione</th>
<th>Stato</th>
<th>Riprogrammata</th>
</tr>
</thead>
<tbody>
`;
if (lessons.length === 0) {
htmlContent += `
<tr>
<td colspan="4" style="text-align: center; padding: 10px; color: #666; font-size: 13px;">
Nessuna lezione trovata per questo ordine.
</td>
</tr>
`;
} else {
lessons.forEach(function(lesson, index) {
var lessonDate = new Date(lesson.bookingstart);
var statusText = lesson.status;
var badgeClass = 'bg-primary';
var badgeTextColor = '#fff';
if (lesson.status === 'completed') {
badgeClass = 'bg-success';
badgeTextColor = '#fff';
statusText = 'Completata';
} else if (lesson.lostlesson === 'Y') {
badgeClass = 'bg-danger';
badgeTextColor = '#fff';
statusText = 'Persa';
} else if (lesson.expirylesson === 'Y') {
badgeClass = 'bg-warning';
badgeTextColor = '#000';
statusText = 'Scaduta';
} else if (lesson.status === 'booked') {
if (lessonDate < now && lesson.lostlesson !== 'Y' && lesson.expirylesson !== 'Y') {
statusText = 'Completata';
badgeClass = 'bg-success';
badgeTextColor = '#fff';
} else {
statusText = 'Programmata';
}
}
var isReprogrammedText = lesson.is_reprogrammed === 'Y' ? 'Sì' : 'No';
htmlContent += `
<tr class="${index % 2 === 0 ? 'even-row' : 'odd-row'}">
<td style="padding: 8px; font-size: 13px;">${lessonDate.toLocaleString('it-IT', {
day: '2-digit',
month: '2-digit',
year: 'numeric',
hour: '2-digit',
minute: '2-digit'
})}</td>
<td style="padding: 8px; font-size: 13px;">${lesson.servicename}</td>
<td style="padding: 8px;">
<span class="badge ${badgeClass}" style="color: ${badgeTextColor}; padding: 6px 10px; font-size: 12px; font-weight: 500;">${statusText}</span>
</td>
<td style="padding: 8px; font-size: 13px;">${isReprogrammedText}</td>
</tr>
`;
});
}
htmlContent += `
</tbody>
</table>
</div>
`;
Swal.fire({
title: '',
html: htmlContent,
confirmButtonText: 'Chiudi',
width: '1000px',
customClass: {
popup: 'custom-modal'
}
detailsFromRow($(this).closest('tr'));
});
});
@@ -442,106 +282,8 @@ file_put_contents($logFile, $logMessage, FILE_APPEND);
}
});
}
});
</script>
<style>
.custom-card {
margin: 10px auto;
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: #ff4d4f;
color: white;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 0;
font-size: 60px;
font-weight: bold;
border-top-left-radius: 8px;
border-bottom-left-radius: 8px;
}
.custom-day {
line-height: 1;
}
.custom-month {
font-size: 28px;
}
.custom-event-details {
flex: 2;
display: flex;
flex-direction: column;
padding: 10px 20px;
background-color: #e6f3ff;
}
.custom-heading {
margin-top: 0;
font-size: 24px;
}
.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;
cursor: pointer;
transition: background-color 0.2s;
}
.custom-action-button:hover {
background-color: #e0e0e0;
}
@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;
}
}
.order-row {
cursor: pointer;
}
@@ -667,7 +409,7 @@ file_put_contents($logFile, $logMessage, FILE_APPEND);
<div class="col-xl-12">
<div class="card">
<div class="card-body">
<h5>Benvenuta/o <?php echo $firstname; ?></h5>
<h5>Benvenuta/o <?php echo e($firstname); ?></h5>
<p>Di seguito puoi visualizzare i tuoi ordini</p>
<div class="table-responsive">
<table class="table table-striped mb-0">
@@ -686,33 +428,32 @@ file_put_contents($logFile, $logMessage, FILE_APPEND);
</tr>
</thead>
<tbody>
<?php foreach ($documents as $document) {
<?php foreach ($orders as $document) {
$is_expired = strtotime($document['expireon']) < time();
$logMessage .= "Order ID: {$document['idorderbook']}, Lessons count: " . count($document['lessons']) . "\n";
?>
<tr class="order-row"
data-lessons='<?php echo json_encode($document['lessons']); ?>'
data-total='<?php echo $document['quantityclass']; ?>'
data-order-id='<?php echo $document['idorderbook']; ?>'
data-lessons='<?php echo e(json_encode($document['lessons'])); ?>'
data-total='<?php echo e($document['quantityclass']); ?>'
data-order-id='<?php echo e($document['idorderbook']); ?>'
data-is-expired='<?php echo $is_expired ? 'true' : 'false'; ?>'
data-expireon='<?php echo $document['expireon']; ?>'>
<td><?php echo $document['idorderbook']; ?></td>
<td><?php echo date('d-m-Y', strtotime($document['order_date_created'])); ?></td>
<td><?php echo $document['servicename']; ?></td>
<td><?php echo $document['day'] . ' ' . $document['time']; ?></td>
<td><?php echo $document['quantityclass']; ?></td>
<td><?php echo $document['first_lesson_date'] ? date('d-m-Y', strtotime($document['first_lesson_date'])) : '-'; ?></td>
data-expireon='<?php echo e($document['expireon']); ?>'>
<td><?php echo e($document['idorderbook']); ?></td>
<td><?php echo e(date('d-m-Y', strtotime($document['order_date_created']))); ?></td>
<td><?php echo e($document['servicename']); ?></td>
<td><?php echo e($document['day'] . ' ' . $document['time']); ?></td>
<td><?php echo e($document['quantityclass']); ?></td>
<td><?php echo e($document['first_lesson_date'] ? date('d-m-Y', strtotime($document['first_lesson_date'])) : '-'); ?></td>
<td style="<?php echo $is_expired ? 'color: #dc3545;' : ''; ?>">
<?php echo date('d-m-Y', strtotime($document['expireon'])); ?>
<?php echo e(date('d-m-Y', strtotime($document['expireon']))); ?>
</td>
<td>
<span style="display: inline-block; padding: 4px 8px; font-size: 11px; font-weight: 500; color: #fff; background-color: #17a2b8; border-radius: 4px;">
<?php echo $document['maxreschedule']; ?>
<?php echo e($document['maxreschedule']); ?>
</span>
</td>
<td>
<span style="display: inline-block; padding: 4px 8px; font-size: 11px; font-weight: 500; color: #fff; background-color: <?php echo ($document['reprogrammed'] >= $document['maxreschedule']) ? '#dc3545' : '#28a745'; ?>; border-radius: 4px;">
<?php echo $document['reprogrammed']; ?>
<?php echo e($document['reprogrammed']); ?>
</span>
</td>
<td>
@@ -742,7 +483,3 @@ file_put_contents($logFile, $logMessage, FILE_APPEND);
</body>
</html>
<?php
file_put_contents($logFile, $logMessage, FILE_APPEND);
$conn->close();
?>
+160 -160
View File
@@ -1,116 +1,160 @@
<?php require_once('include/headscript.php'); ?>
<?php // require_once('Connections/bkngstm.php');
?>
<?php // require_once('webassist/mysqli/rsobj.php');
?>
<?php // require_once('webassist/mysqli/queryobj.php');
?>
<?php // optionquery
$optionquery = new WA_MySQLi_RS("optionquery", $bkngstm, 0);
$optionquery->setQuery("SELECT * FROM option");
$optionquery->execute();
?>
<?php
$bookedclass = new WA_MySQLi_RS("bookedclass", $bkngstm, 0);
/**
* Connessione unica PDO riusata per tutta la pagina.
* DBHandlerSelect è un singleton: una sola connessione per richiesta.
* $iduserlogin arriva dalla sessione autenticata (headscript.php).
*/
$pdo = DBHandlerSelect::getInstance()->getConnection();
// Verifica se è stata specificata una richiesta per cambiare il mese
// Forziamo l'id utente a intero: viene dalla sessione, ma è comunque
// buona norma non fidarsi mai e trattarlo come integer.
$iduserlogin = (int) $iduserlogin;
/* -------------------------------------------------------------------------
* 1) Calcolo del mese da visualizzare
* ---------------------------------------------------------------------- */
if (isset($_GET['prev_month'])) {
$currentMonthStart = $_GET['prev_month'] . '-01';
$currentMonthStart = preg_replace('/[^0-9\-]/', '', $_GET['prev_month']) . '-01';
} elseif (isset($_GET['next_month'])) {
$currentMonthStart = $_GET['next_month'] . '-01';
$currentMonthStart = preg_replace('/[^0-9\-]/', '', $_GET['next_month']) . '-01';
} else {
$currentMonthStart = date("Y-m-01");
}
// Validazione: se la stringa non è una data valida, torna al mese corrente
$ts = strtotime($currentMonthStart);
if ($ts === false) {
$currentMonthStart = date("Y-m-01");
}
$currentDate = date("Y-m-d");
// Modifica: Se la data odierna è dopo l'inizio del mese corrente, imposta la data odierna come inizio
// Se oggi è dopo l'inizio del mese scelto, parti da oggi
if ($currentDate > $currentMonthStart) {
$currentMonthStart = $currentDate;
}
$currentMonthEnd = date("Y-m-t", strtotime($currentMonthStart));
$bookedclass->setQuery("SELECT bookingclass.*, service.*, serviceschedule.*, orderbook.expireon
/* -------------------------------------------------------------------------
* 2) Lezioni prenotate nel range del mese
* ---------------------------------------------------------------------- */
$sqlBooked = "SELECT bookingclass.*, service.*, serviceschedule.*, orderbook.expireon
FROM bookingclass
LEFT JOIN service ON bookingclass.idservice = service.idservice
LEFT JOIN serviceschedule ON bookingclass.idserviceschedule = serviceschedule.idserviceschedule
LEFT JOIN orderbook ON bookingclass.idorder = orderbook.idorderbook
WHERE bookingclass.iduser = '$iduserlogin' AND bookingclass.status = 'booked'
AND serviceschedule.dateschedule BETWEEN '$currentMonthStart' AND DATE_ADD('$currentMonthEnd', INTERVAL 1 DAY)
ORDER BY serviceschedule.dateschedule");
WHERE bookingclass.iduser = :iduser
AND bookingclass.status = 'booked'
AND serviceschedule.dateschedule BETWEEN :monthStart AND DATE_ADD(:monthEnd, INTERVAL 1 DAY)
ORDER BY serviceschedule.dateschedule";
$bookedclass->execute();
?>
$stmtBooked = $pdo->prepare($sqlBooked);
$stmtBooked->execute([
':iduser' => $iduserlogin,
':monthStart' => $currentMonthStart,
':monthEnd' => $currentMonthEnd,
]);
$bookedRows = $stmtBooked->fetchAll();
<?php
$conn = new mysqli($servername, $username, $password, $dbname);
if ($conn->connect_error) {
die("Connessione fallita: " . $conn->connect_error);
}
// ID dell'utente per il quale vuoi filtrare gli ordini
$userid = $iduserlogin;
// Query per ottenere la somma dei ticket per ogni ordine dell'utente
$query = "SELECT iduser, idorderbook, SUM(nticket) as total_tickets
/* -------------------------------------------------------------------------
* 3) Somma dei ticket acquistati dall'utente
* ---------------------------------------------------------------------- */
$sqlTickets = "SELECT SUM(nticket) AS total_tickets
FROM orderbook
WHERE iduser = $userid
GROUP BY iduser";
WHERE iduser = :iduser";
$stmtTickets = $pdo->prepare($sqlTickets);
$stmtTickets->execute([':iduser' => $iduserlogin]);
$rowTickets = $stmtTickets->fetch();
$totalTickets = (int) ($rowTickets['total_tickets'] ?? 0);
$result = $conn->query($query);
if (!$result) {
die("Query fallita: " . $conn->error);
}
if ($result->num_rows > 0) {
while ($row = $result->fetch_assoc()) {
$idOrdine = $row["idorderbook"];
$totalTickets = $row["total_tickets"];
}
} else {
$totalTickets = 0; // Imposta a zero se non ci sono righe nella query
}
$conn->close();
?>
<?php //check tickets
// Connessione al database
$conn = new mysqli($servername, $username, $password, $dbname);
if ($conn->connect_error) {
die("Connessione al database fallita: " . $conn->connect_error);
}
// ID dell'utente per il quale si desidera eseguire la query
$iduser = $iduserlogin; // Sostituisci con l'ID utente desiderato
// Data e ora attuali
/* -------------------------------------------------------------------------
* 4) Conteggio stati lezioni (praticate / future / perse / da confermare)
* ---------------------------------------------------------------------- */
$currentDateTime = date("Y-m-d H:i:s");
// Query per contare i record con data e ora passate e future, escludendo status = 'cancelled'
$query = "SELECT COUNT(*) AS total,
SUM(CASE WHEN serviceschedule.dateschedule <= '$currentDateTime' AND bookingclass.status = 'booked' AND bookingclass.lostlesson = 'N' THEN 1 ELSE 0 END) AS passed,
SUM(CASE WHEN serviceschedule.dateschedule > '$currentDateTime' AND bookingclass.status = 'booked' AND bookingclass.lostlesson = 'N' THEN 1 ELSE 0 END) AS future,
SUM(CASE WHEN bookingclass.lostlesson = 'Y' AND bookingclass.status != 'cancelled' THEN 1 ELSE 0 END) AS lost,
$sqlCounts = "SELECT
COUNT(*) AS total,
SUM(CASE WHEN serviceschedule.dateschedule <= :now1
AND bookingclass.status = 'booked'
AND bookingclass.lostlesson = 'N' THEN 1 ELSE 0 END) AS passed,
SUM(CASE WHEN serviceschedule.dateschedule > :now2
AND bookingclass.status = 'booked'
AND bookingclass.lostlesson = 'N' THEN 1 ELSE 0 END) AS future,
SUM(CASE WHEN bookingclass.lostlesson = 'Y'
AND bookingclass.status != 'cancelled' THEN 1 ELSE 0 END) AS lost,
SUM(CASE WHEN bookingclass.status = 'pending' THEN 1 ELSE 0 END) AS pending
FROM bookingclass
LEFT JOIN serviceschedule ON bookingclass.idserviceschedule = serviceschedule.idserviceschedule
WHERE bookingclass.iduser = $iduser AND bookingclass.status != 'cancelled'";
WHERE bookingclass.iduser = :iduser
AND bookingclass.status != 'cancelled'";
$result = $conn->query($query);
if ($result) {
$row = $result->fetch_assoc();
$totalRecords = $row['total'];
$passedRecords = $row['passed'];
$futureRecords = $row['future'];
$lost = $row['lost'];
$pending = $row['pending'];
$stmtCounts = $pdo->prepare($sqlCounts);
$stmtCounts->execute([
':now1' => $currentDateTime,
':now2' => $currentDateTime,
':iduser' => $iduserlogin,
]);
$rowCounts = $stmtCounts->fetch();
$totalRecords = (int) ($rowCounts['total'] ?? 0);
$passedRecords = (int) ($rowCounts['passed'] ?? 0);
$futureRecords = (int) ($rowCounts['future'] ?? 0);
$lost = (int) ($rowCounts['lost'] ?? 0);
$pending = (int) ($rowCounts['pending'] ?? 0);
$toprogram = $totalTickets - $passedRecords - $futureRecords - $pending - $lost;
/* -------------------------------------------------------------------------
* 5) Pre-caricamento limiti di riprogrammazione per gli ordini coinvolti
* (una sola query invece di una per ogni card, dentro il loop)
* ---------------------------------------------------------------------- */
$reprogramInfo = [];
$orderIds = array_filter(array_unique(array_map(
fn($r) => (int) ($r['idorder'] ?? 0),
$bookedRows
)));
if (!empty($orderIds)) {
// Costruiamo i placeholder ?,?,? in numero pari agli id
$placeholders = implode(',', array_fill(0, count($orderIds), '?'));
$sqlReprog = "SELECT idorderbook, maxreschedule, reprogrammed
FROM orderbook
WHERE idorderbook IN ($placeholders)";
$stmtReprog = $pdo->prepare($sqlReprog);
$stmtReprog->execute(array_values($orderIds));
foreach ($stmtReprog->fetchAll() as $r) {
$reprogramInfo[(int) $r['idorderbook']] = [
'max' => (int) ($r['maxreschedule'] ?? 0),
'done' => (int) ($r['reprogrammed'] ?? 0),
];
}
}
// Chiusura della connessione
$conn->close();
?>
/* -------------------------------------------------------------------------
* Helper output sicuro (evita XSS quando stampiamo dati DB nell'HTML)
* ---------------------------------------------------------------------- */
function e($value): string
{
return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
}
$italianMonths = [
"January" => "Gennaio",
"February" => "Febbraio",
"March" => "Marzo",
"April" => "Aprile",
"May" => "Maggio",
"June" => "Giugno",
"July" => "Luglio",
"August" => "Agosto",
"September" => "Settembre",
"October" => "Ottobre",
"November" => "Novembre",
"December" => "Dicembre"
];
?>
<!doctype html>
<html lang="en">
@@ -120,14 +164,10 @@ $conn->close();
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta content="YogiBook - Prenotazione facile YogaSOul" name="description" />
<meta content="Advanced Creative Solutions" name="author" />
<!-- App favicon -->
<link rel="shortcut icon" href="assets/images/favicon.ico">
<!-- Bootstrap Css -->
<link href="assets/css/bootstrap.min.css" id="bootstrap-style" rel="stylesheet" type="text/css" />
<!-- Icons Css -->
<link href="assets/css/icons.min.css" rel="stylesheet" type="text/css" />
<!-- App 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">
@@ -230,8 +270,7 @@ $conn->close();
font-size: 24px;
}
}
</style>
<style>
.month-navigation {
display: flex;
justify-content: center;
@@ -254,8 +293,7 @@ $conn->close();
.card {
width: 100%;
}
</style>
<style>
.pastel-color {
border: 1px solid #D1C4CC;
padding: 0px;
@@ -338,6 +376,7 @@ $conn->close();
});
}
</script>
</head>
<body>
<div id="layout-wrapper">
@@ -379,15 +418,12 @@ $conn->close();
<div class="card-body">
<h5>Benvenuta/o </h5>
<p>Di seguito puoi vedere lo stato delle tue prenotazioni</p>
<?php
$toprogram = $totalTickets - $passedRecords - $futureRecords - $pending - $lost;
?>
<div class="row">
<div class="col-md-2">
<div class="card pastel-color acquistate">
<div class="card-body">
<h5 style="font-size: 0.9em;">Lezioni acquistate</h5>
<p><?php echo $totalTickets; ?></p>
<p><?php echo e($totalTickets); ?></p>
</div>
</div>
</div>
@@ -395,7 +431,7 @@ $conn->close();
<div class="card pastel-color praticate">
<div class="card-body">
<h5 style="font-size: 0.9em;">Praticate</h5>
<p><?php echo $passedRecords; ?></p>
<p><?php echo e($passedRecords); ?></p>
</div>
</div>
</div>
@@ -403,7 +439,7 @@ $conn->close();
<div class="card pastel-color prenotate">
<div class="card-body">
<h5 style="font-size: 0.9em;">Prenotate</h5>
<p><?php echo $futureRecords; ?></p>
<p><?php echo e($futureRecords); ?></p>
</div>
</div>
</div>
@@ -411,7 +447,7 @@ $conn->close();
<div class="card pastel-color conferma">
<div class="card-body">
<h5 style="font-size: 0.9em;">Da confermare</h5>
<p><?php echo $pending; ?></p>
<p><?php echo e($pending); ?></p>
</div>
</div>
</div>
@@ -419,7 +455,7 @@ $conn->close();
<div class="card pastel-color programmare">
<div class="card-body">
<h5 style="font-size: 0.9em;">Da programmare</h5>
<p><?php echo $toprogram; ?></p>
<p><?php echo e($toprogram); ?></p>
</div>
</div>
</div>
@@ -427,7 +463,7 @@ $conn->close();
<div class="card pastel-color perse">
<div class="card-body">
<h5 style="font-size: 0.9em;">Perse</h5>
<p><?php echo $lost; ?></p>
<p><?php echo e($lost); ?></p>
</div>
</div>
</div>
@@ -457,7 +493,6 @@ $conn->close();
</div>
<?php } ?>
<div class="container-fluid">
<div class="row">
<div class="col-xl-12">
@@ -499,27 +534,11 @@ $conn->close();
<div class="container-fluid">
<div class="month-navigation">
<?php
$italianMonths = [
"January" => "Gennaio",
"February" => "Febbraio",
"March" => "Marzo",
"April" => "Aprile",
"May" => "Maggio",
"June" => "Giugno",
"July" => "Luglio",
"August" => "Agosto",
"September" => "Settembre",
"October" => "Ottobre",
"November" => "Novembre",
"December" => "Dicembre"
];
?>
<a href="?prev_month=<?php echo date('Y-m', strtotime('-1 month', strtotime($currentMonthStart))); ?>" class="arrow-link">
<a href="?prev_month=<?php echo e(date('Y-m', strtotime('-1 month', strtotime($currentMonthStart)))); ?>" class="arrow-link">
<i class="fas fa-chevron-left fa-2x"></i>
</a>
<h2><?php echo $italianMonths[date("F", strtotime($currentMonthStart))] . ' ' . date("Y", strtotime($currentMonthStart)); ?></h2>
<a href="?next_month=<?php echo date('Y-m', strtotime('+1 month', strtotime($currentMonthStart))); ?>" class="arrow-link">
<h2><?php echo e($italianMonths[date("F", strtotime($currentMonthStart))] . ' ' . date("Y", strtotime($currentMonthStart))); ?></h2>
<a href="?next_month=<?php echo e(date('Y-m', strtotime('+1 month', strtotime($currentMonthStart)))); ?>" class="arrow-link">
<i class="fas fa-chevron-right fa-2x"></i>
</a>
</div>
@@ -528,25 +547,18 @@ $conn->close();
<div class="col-xl-12">
<div class="card">
<div class="card-body">
<?php if (empty($bookedRows)) { ?>
<p>Prenotazioni non presenti per questo mese</p>
<?php
$wa_startindex = 0;
if ($bookedclass->TotalRows == 0) {
echo "<p>Prenotazioni non presenti per questo mese</p>";
} else {
$conn = new mysqli($servername, $username, $password, $dbname);
if ($conn->connect_error) {
die("Connessione fallita: " . $conn->connect_error);
}
while (!$bookedclass->atEnd()) {
$wa_startindex = $bookedclass->Index;
$dateschedule = $bookedclass->getColumnVal("dateschedule");
foreach ($bookedRows as $row) {
$dateschedule = $row['dateschedule'];
$dateObj = new DateTime($dateschedule);
$dayInItalian = $dateObj->format("d");
$monthInItalian = $dateObj->format("F");
$monthInItalian = $italianMonths[$monthInItalian];
$monthInItalian = $italianMonths[$dateObj->format("F")];
$newDateFormat = $dateObj->format("d-m-Y H:i");
// Calcola se la lezione può essere riprogrammata
// Calcolo finestra di cancellazione
$currentTime = new DateTime();
$classTime = new DateTime($dateschedule);
$isSameDay = $classTime->format('Y-m-d') === $currentTime->format('Y-m-d');
@@ -554,72 +566,60 @@ $conn->close();
$classMinute = (int) $classTime->format('i');
$isBefore1700 = ($classHour < 17) || ($classHour === 17 && $classMinute === 0);
// Definisci il limite per la riprogrammazione
if ($isSameDay) {
if ($isBefore1700) {
// Lezioni prima delle 17:00: cancellazione valida fino alle 00:01 dello stesso giorno
$deadline = new DateTime($classTime->format('Y-m-d 00:01:00'));
} else {
// Lezioni alle 17:00 o dopo: cancellazione valida fino alle 12:00 dello stesso giorno
$deadline = new DateTime($classTime->format('Y-m-d 12:00:00'));
}
$canBeDeleted = $currentTime <= $deadline;
} else {
// Per lezioni in giorni futuri, la riprogrammazione è sempre consentita
$canBeDeleted = true;
}
// Verifica il limite di riprogrammazioni per l'ordine
$idorder = $bookedclass->getColumnVal("idorder");
$query = "SELECT maxreschedule, reprogrammed FROM orderbook WHERE idorderbook = '$idorder'";
$result = $conn->query($query);
// Limite riprogrammazioni (dai dati pre-caricati, niente query nel loop)
$idorder = (int) ($row['idorder'] ?? 0);
$canReprogram = true;
if ($result && $result->num_rows > 0) {
$row = $result->fetch_assoc();
$maxreschedule = $row['maxreschedule'] ?? 0;
$reprogrammed = $row['reprogrammed'] ?? 0;
$canReprogram = $reprogrammed < $maxreschedule;
if (isset($reprogramInfo[$idorder])) {
$canReprogram = $reprogramInfo[$idorder]['done'] < $reprogramInfo[$idorder]['max'];
}
$idbookingclass = (int) $row['idbookingclass'];
$idservice = (int) $row['idservice'];
$expirydate = date("d/m/Y", strtotime($row['expireon']));
$colorclass = $row['colorclass'] ?? '#1ebf73';
$servicename = $row['servicename'] ?? '';
?>
<div class="custom-card" onclick="toggleCard(this)">
<div class="custom-date-box" style="background-color:#1ebf73">
<div class="custom-day"><?php echo $dayInItalian; ?></div>
<div class="custom-month"><?php echo $monthInItalian; ?></div>
<div class="custom-day"><?php echo e($dayInItalian); ?></div>
<div class="custom-month"><?php echo e($monthInItalian); ?></div>
</div>
<div class="custom-event-details" style="background-color:<?php echo ($bookedclass->getColumnVal("colorclass")); ?>">
<h2 class="custom-heading"><?php echo ($bookedclass->getColumnVal("servicename")); ?></h2>
<p class="custom-paragraph">Quando: <?php echo $newDateFormat; ?></p>
<div class="custom-event-details" style="background-color:<?php echo e($colorclass); ?>">
<h2 class="custom-heading"><?php echo e($servicename); ?></h2>
<p class="custom-paragraph">Quando: <?php echo e($newDateFormat); ?></p>
<p class="custom-paragraph">Luogo: via Valassina 62/B Seregno - Sala Contesto Yoga</p>
<div class="custom-actions">
<button class="custom-action-button" onclick="addToCalendar(this)" data-eventname="<?php echo ($bookedclass->getColumnVal("servicename")); ?>" data-eventdate="<?php echo $newDateFormat; ?>">
<button class="custom-action-button" onclick="addToCalendar(this)" data-eventname="<?php echo e($servicename); ?>" data-eventdate="<?php echo e($newDateFormat); ?>">
<i class="far fa-calendar-plus"></i> Cal
</button>
<?php $idbookingclass = $bookedclass->getColumnVal("idbookingclass"); ?>
<?php $idservice = $bookedclass->getColumnVal("idservice"); ?>
<?php $expirydate = date("d/m/Y", strtotime($bookedclass->getColumnVal("expireon"))); ?>
<?php if ($canBeDeleted && $canReprogram) : ?>
<button class="custom-action-button" onclick="confirmDelete(<?php echo $idbookingclass; ?>, <?php echo $idservice; ?>, 'bookingpanel.php')">
<i class="fas fa-calendar-alt"></i> Riprogramma
</button>
<button class="custom-action-button"
onclick="confirmDeleteOnly(<?php echo $idbookingclass; ?>, '<?php echo $expirydate; ?>')">
onclick="confirmDeleteOnly(<?php echo $idbookingclass; ?>, '<?php echo e($expirydate); ?>')">
<i class="fas fa-trash"></i> Cancella
</button>
<?php else : ?>
<button class="custom-action-button"><i class="fas fa-exclamation-circle"></i> Non puoi riprogrammare</button>
<?php endif; ?>
</div>
</div>
</div>
<?php
$bookedclass->moveNext();
}
$conn->close();
}
$bookedclass->moveFirst();
unset($wa_startindex);
unset($wa_repeatcount);
?>
</div>
</div>
+292 -324
View File
@@ -1,121 +1,126 @@
<?php require_once('include/headscript.php'); ?>
<?php // optionquery
$optionquery = new WA_MySQLi_RS("optionquery",$bkngstm,0);
$optionquery->setQuery("SELECT * FROM option");
$optionquery->execute();
?>
<?php
if (isset($_GET['message'])) { $message=$_GET['message']; } else { $message=""; }
// Verifica se è stato inviato un modulo
if ($_SERVER["REQUEST_METHOD"] == "POST") {
// Verifica se è stato caricato un file correttamente
if (isset($_FILES["fileToUpload"]) && $_FILES["fileToUpload"]["error"] === UPLOAD_ERR_OK) {
require_once('include/headscript.php');
/**
* Connessione unica PDO (singleton). $iduserlogin dalla sessione autenticata.
*/
$pdo = DBHandlerSelect::getInstance()->getConnection();
$iduserlogin = (int) $iduserlogin;
// Crea la connessione al database
$conn = new mysqli($servername, $username, $password, $dbname);
// Verifica la connessione
if ($conn->connect_error) {
die("Connessione al database fallita: " . $conn->connect_error);
function e($value): string
{
return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
}
// Ottieni l'ID dell'utente (da dove viene?)
$iduserlogin = $_POST["iduserlogin"];
$message = isset($_GET['message']) ? $_GET['message'] : '';
$uploadFeedback = null; // ['type' => 'success'|'error', 'text' => '...']
// Altre informazioni sul documento
$documentDescription = $_POST["documentDescription"];
$expiryDate = $_POST["expiryDate"];
$originalFileName = $_FILES["fileToUpload"]["name"];
$fileExtension = pathinfo($originalFileName, PATHINFO_EXTENSION);
$timestamp = time(); // Timestamp corrente
$newFileName = "{$timestamp}_{$originalFileName}"; // Aggiungi timestamp al nome del file
$fileTmpName = $_FILES["fileToUpload"]["tmp_name"];
$fileDestination = "user/document/" . $newFileName;
/* -------------------------------------------------------------------------
* Gestione upload certificato (POST) con validazione robusta
* ---------------------------------------------------------------------- */
$allowedExt = ['pdf', 'jpg', 'jpeg', 'png'];
$allowedMime = ['application/pdf', 'image/jpeg', 'image/png'];
$maxBytes = 8 * 1024 * 1024; // 8 MB
$uploadDir = 'user/document/';
// Sposta il file nella cartella di destinazione
if (move_uploaded_file($fileTmpName, $fileDestination)) {
// Inserisci i dati nel database
$sql = "INSERT INTO certificateuserprofile (iduser, documentdescription, filenamedocument, expirydatedocument)
VALUES ('$iduserlogin', '$documentDescription', '$newFileName', '$expiryDate')";
if (
$_SERVER['REQUEST_METHOD'] === 'POST'
&& isset($_FILES['fileToUpload'])
&& $_FILES['fileToUpload']['error'] === UPLOAD_ERR_OK
) {
$file = $_FILES['fileToUpload'];
$documentDescription = trim($_POST['documentDescription'] ?? '');
$expiryDate = trim($_POST['expiryDate'] ?? '');
if ($conn->query($sql) === TRUE) {
echo "Documento inserito correttamente nel database.";
// 1) Dimensione
if ($file['size'] > $maxBytes) {
$uploadFeedback = ['type' => 'error', 'text' => 'Il file supera la dimensione massima di 8 MB.'];
} else {
echo "Errore nell'esecuzione della query: " . $conn->error;
}
// 2) Estensione
$ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
// 3) Tipo MIME reale (non ci fidiamo dell'estensione dichiarata)
$finfo = new finfo(FILEINFO_MIME_TYPE);
$realMime = $finfo->file($file['tmp_name']);
if (!in_array($ext, $allowedExt, true) || !in_array($realMime, $allowedMime, true)) {
$uploadFeedback = ['type' => 'error', 'text' => 'Formato non consentito. Carica un PDF, JPG o PNG.'];
} else {
echo "Errore nel caricamento del file.";
// 4) Nome file sicuro generato dal server (niente input utente nel percorso)
$safeName = bin2hex(random_bytes(16)) . '.' . $ext;
$destination = $uploadDir . $safeName;
if (!is_dir($uploadDir)) {
@mkdir($uploadDir, 0755, true);
}
// Chiudi la connessione al database
$conn->close();
if (move_uploaded_file($file['tmp_name'], $destination)) {
// 5) INSERT con prepared statement
$sql = "INSERT INTO certificateuserprofile
(iduser, documentdescription, filenamedocument, expirydatedocument)
VALUES (:iduser, :descr, :fname, :expiry)";
$stmt = $pdo->prepare($sql);
$ok = $stmt->execute([
':iduser' => $iduserlogin,
':descr' => $documentDescription,
':fname' => $safeName,
':expiry' => $expiryDate !== '' ? $expiryDate : null,
]);
$uploadFeedback = $ok
? ['type' => 'success', 'text' => 'Documento caricato correttamente.']
: ['type' => 'error', 'text' => 'Errore nel salvataggio del documento. Riprova.'];
} else {
$uploadFeedback = ['type' => 'error', 'text' => 'Caricamento del file non riuscito. Riprova.'];
}
}
?>
<?php
$iduser=$iduserlogin;
// Crea la connessione al database
$conn = new mysqli($servername, $username, $password, $dbname);
// Verifica la connessione
if ($conn->connect_error) {
die("Connessione al database fallita: " . $conn->connect_error);
}
} elseif (
$_SERVER['REQUEST_METHOD'] === 'POST'
&& isset($_FILES['fileToUpload'])
&& $_FILES['fileToUpload']['error'] !== UPLOAD_ERR_NO_FILE
) {
// Un file è stato scelto ma l'upload è fallito lato PHP
$uploadFeedback = ['type' => 'error', 'text' => 'Si è verificato un problema durante il caricamento. Riprova.'];
}
/* -------------------------------------------------------------------------
* Dati profilo utente
* ---------------------------------------------------------------------- */
$idprofile = null;
$datebirthFromDatabase = '';
$yogaforFromDatabase = '';
$healthissueFromDatabase = '';
$generalcommentFromDatabase = '';
$stmtProfile = $pdo->prepare("SELECT * FROM userprofile WHERE iduser = :iduser LIMIT 1");
$stmtProfile->execute([':iduser' => $iduserlogin]);
$rowdata = $stmtProfile->fetch();
// Query per selezionare i dati filtrati per iduser
$query = "SELECT * FROM certificateuserprofile WHERE iduser = $iduserlogin";
$result = $conn->query($query);
// Array per memorizzare i risultati
$documents = array();
while ($row = $result->fetch_assoc()) {
$documents[] = $row;
}
// Ottieni i dati dal database in base all'id dell'utente
$sqldata = "SELECT * FROM userprofile WHERE iduser = '$iduser'";
$resultdata = $conn->query($sqldata);
if ($resultdata->num_rows > 0) {
$rowdata = $resultdata->fetch_assoc();
if ($rowdata) {
$idprofile = $rowdata['iduserprofile'];
$datebirthFromDatabase = $rowdata['datebirth'];
$yogaforFromDatabase = $rowdata['yogafor'];
$healthissueFromDatabase = $rowdata['healthissue'];
$generalcommentFromDatabase = $rowdata['generalcomment'];
$datebirthFromDatabase = $rowdata['datebirth'] ?? '';
$yogaforFromDatabase = $rowdata['yogafor'] ?? '';
$healthissueFromDatabase = $rowdata['healthissue'] ?? '';
$generalcommentFromDatabase = $rowdata['generalcomment'] ?? '';
}
$isUpdate = !empty($idprofile);
$currentYear = date('Y');
?>
<!doctype html>
<html lang="en">
<html lang="it">
<head>
<meta charset="utf-8" />
<title>YogiBook - Prenotazioni YogaSoul</title>
<title>YogiBook - Profilo Utente</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" />
<!-- App favicon -->
<link rel="shortcut icon" href="assets/images/favicon.ico">
<!-- Bootstrap Css -->
<link href="assets/css/bootstrap.min.css" id="bootstrap-style" rel="stylesheet" type="text/css" />
<!-- Icons Css -->
<link href="assets/css/icons.min.css" rel="stylesheet" type="text/css" />
<!-- App 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">
@@ -125,318 +130,281 @@ if ($resultdata->num_rows > 0) {
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<script>
// Inizializza il datepicker
$(function() {
$("#datebirth").datepicker({
changeYear: true, // Abilita la selezione dell'anno
yearRange: "1900:{{TUA_ANNO_CORRENTE}}", // Specifica l'intervallo di anni
dateFormat: "yy-mm-dd" // Formato della data
changeYear: true,
changeMonth: true,
yearRange: "1900:<?php echo $currentYear; ?>",
dateFormat: "yy-mm-dd"
});
$("#expiryDate").datepicker({
changeYear: true,
changeMonth: true,
dateFormat: "yy-mm-dd"
});
});
// Mostra il nome del file scelto e valida lato client (feedback immediato)
function handleFileChange(input) {
var label = document.getElementById('fileChosenLabel');
if (!input.files.length) {
label.textContent = 'Nessun file selezionato';
return;
}
var f = input.files[0];
var allowed = ['application/pdf', 'image/jpeg', 'image/png'];
var maxBytes = 8 * 1024 * 1024;
if (allowed.indexOf(f.type) === -1) {
label.textContent = 'Formato non valido: usa PDF, JPG o PNG';
label.style.color = '#dc3545';
input.value = '';
return;
}
if (f.size > maxBytes) {
label.textContent = 'File troppo grande (max 8 MB)';
label.style.color = '#dc3545';
input.value = '';
return;
}
label.textContent = f.name;
label.style.color = '#198754';
}
</script>
<script>
$(function() {
$("#expiryDate").datepicker({ dateFormat: "yy-mm-dd" });
});
</script>
<style>
.custom-card {
margin: 10px auto;
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;
align-items: center;
padding: 0;
font-size: 60px;
font-weight: bold;
border-top-left-radius: 8px;
border-bottom-left-radius: 8px;
}
.custom-day {
line-height: 1;
}
.custom-month {
font-size: 28px;
}
.custom-event-details {
flex: 2;
display: flex;
flex-direction: column;
padding: 10px 20px;
background-color: lightblue;
}
.custom-heading {
margin-top: 0;
font-size: 24px;
}
.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;
cursor: pointer;
transition: background-color 0.2s;
}
.custom-action-button:hover {
background-color: #e0e0e0;
}
@media (max-width: 768px) {
.custom-card {
flex-direction: column;
}
.custom-date-box, .custom-event-details {
.profile-wrap {
width: 100%;
border-radius: 0;
}
.custom-event-time {
font-size: 24px;
.profile-card {
border: none;
border-radius: 14px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.06);
}
.profile-card .card-body {
padding: 28px 32px;
}
.section-title {
font-size: 15px;
font-weight: 700;
letter-spacing: 0.04em;
text-transform: uppercase;
color: #6b7280;
margin-bottom: 4px;
}
.section-lead {
color: #9ca3af;
font-size: 14px;
margin-bottom: 22px;
}
.form-field {
margin-bottom: 20px;
}
.form-field label {
display: block;
font-weight: 600;
font-size: 14px;
color: #374151;
margin-bottom: 6px;
}
.form-field .form-control {
border-radius: 8px;
border: 1px solid #e2e4e9;
padding: 10px 12px;
}
.form-field .form-control:focus {
border-color: #1ebf73;
box-shadow: 0 0 0 3px rgba(30, 191, 115, 0.12);
}
.divider {
height: 1px;
background: #eef0f3;
margin: 30px 0;
}
/* Area upload */
.upload-drop {
border: 2px dashed #d5d9e0;
border-radius: 12px;
padding: 24px;
text-align: center;
transition: border-color 0.2s, background 0.2s;
background: #fafbfc;
}
.upload-drop:hover {
border-color: #1ebf73;
background: #f6fdf9;
}
.upload-icon {
font-size: 30px;
color: #1ebf73;
margin-bottom: 8px;
}
.file-chosen {
display: block;
margin-top: 10px;
font-size: 13px;
color: #6b7280;
}
.btn-save {
border-radius: 8px;
padding: 10px 26px;
font-weight: 600;
}
.hint {
font-size: 12px;
color: #9ca3af;
margin-top: 6px;
}
</style>
<script>
function confirmDelete(id, deletePageUrl) {
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) {
// Reindirizza direttamente alla pagina di cancellazione con l'ID come parametro.
window.location.href = `deleteclass.php?id=${id}`;
}
});
}
</script>
</head>
<body>
<!-- <body data-layout="horizontal"> -->
<!-- Begin page -->
<div id="layout-wrapper">
<!-- Top Bar -->
<header id="page-topbar" class="isvertical-topbar">
<div class="navbar-header">
<div class="d-flex">
<!-- LOGO -->
<?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>
<!-- start page title -->
<div class="page-title-box align-self-center d-none d-md-block">
<h4 class="page-title mb-0">Profilo Utente</h4>
</div>
<!-- end page title -->
</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>
</div>
</form>
</div>
</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 class="d-flex"></div>
</div>
</div>
<div class="topnav">
<div class="container-fluid">
<nav class="navbar navbar-light navbar-expand-lg topnav-menu">
</nav>
<nav class="navbar navbar-light navbar-expand-lg topnav-menu"></nav>
</div>
</div>
</header>
<!-- ============================================================== -->
<!-- Start right Content here -->
<!-- ============================================================== -->
<div class="main-content">
<div class="page-content">
<div class="container-fluid">
<div class="profile-wrap">
<div class="row">
<div class="col-xl-12">
<div class="card">
<div class="card-body">
<?php if ($message=='success') { ?>
<?php if ($message === 'success') : ?>
<div class="alert alert-success" role="alert">
Profilo aggiornato con successo!
Profilo aggiornato con successo.
</div>
<?php } ?>
<p>Di seguito puoi compilare o aggiornare il tuo profilo</p>
<div class="table-responsive">
<?php if (empty($idprofile)) { ?>
<?php endif; ?>
<?php if ($uploadFeedback) : ?>
<div class="alert alert-<?php echo $uploadFeedback['type'] === 'success' ? 'success' : 'danger'; ?>" role="alert">
<?php echo e($uploadFeedback['text']); ?>
</div>
<?php endif; ?>
<!-- Dati profilo -->
<div class="card profile-card mb-4">
<div class="card-body">
<div class="section-title">I tuoi dati</div>
<p class="section-lead">Compila o aggiorna le informazioni del tuo profilo.</p>
<form action="process.php" method="post">
<input type="hidden" class="form-control" name="iduser" value="<?php echo $iduserlogin; ?>">
<input type="hidden" class="form-control" name="kind" value="insert">
<label>Data di nascita:</label>
<input type="text" id="datebirth" class="form-control" name="datebirth" value="<?php if (isset($datebirthFromDatabase)) { echo $ $datebirthFromDatabase; } ?>" required><br>
<input type="hidden" name="iduser" value="<?php echo e($iduserlogin); ?>">
<input type="hidden" name="kind" value="<?php echo $isUpdate ? 'update' : 'insert'; ?>">
<label>Yoga praticato:</label>
<textarea name="yogafor" class="form-control" rows="4" cols="50"></textarea><br>
<div class="form-field">
<label for="datebirth">Data di nascita</label>
<input type="text" id="datebirth" class="form-control" name="datebirth"
value="<?php echo e($datebirthFromDatabase); ?>" placeholder="AAAA-MM-GG" required>
</div>
<label>Problemi di salute:</label>
<textarea name="healthissue"class="form-control" rows="4" cols="50"></textarea><br>
<div class="form-field">
<label for="yogafor">Yoga praticato</label>
<textarea id="yogafor" name="yogafor" class="form-control" rows="3"><?php echo e($yogaforFromDatabase); ?></textarea>
</div>
<label>Commenti generali:</label>
<textarea name="generalcomment" class="form-control" rows="4" cols="50"></textarea><br>
<div class="form-field">
<label for="healthissue">Problemi di salute</label>
<textarea id="healthissue" name="healthissue" class="form-control" rows="3"><?php echo e($healthissueFromDatabase); ?></textarea>
</div>
<input type="submit" class="btn btn-primary w-md" name="submit" value="Inserisci">
<div class="form-field">
<label for="generalcomment">Commenti generali</label>
<textarea id="generalcomment" name="generalcomment" class="form-control" rows="3"><?php echo e($generalcommentFromDatabase); ?></textarea>
</div>
<button type="submit" class="btn btn-primary btn-save" name="submit">
<?php echo $isUpdate ? 'Aggiorna profilo' : 'Salva profilo'; ?>
</button>
</form>
<?php } else { ?>
<form action="process.php" method="post">
<input type="hidden" class="form-control" name="iduser" value="<?php echo $iduserlogin; ?>">
<input type="hidden" class="form-control" name="kind" value="update">
<label>Data di nascita:</label>
<input type="text" id="datebirth" class="form-control" name="datebirth" value="<?php if (isset($datebirthFromDatabase)) { echo $datebirthFromDatabase; } ?>" required><br>
</div>
</div>
<label>Yoga praticato:</label>
<textarea name="yogafor" class="form-control" rows="4" cols="50"><?php if (isset($yogaforFromDatabase)) { echo $yogaforFromDatabase; } ?></textarea><br>
<!-- Upload certificato -->
<div class="card profile-card">
<div class="card-body">
<div class="section-title">Certificati e documenti</div>
<p class="section-lead">Carica il tuo certificato medico o altri documenti (PDF, JPG o PNG, max 8 MB).</p>
<label>Problemi di salute:</label>
<textarea name="healthissue"class="form-control" rows="4" cols="50"><?php if (isset($healthissueFromDatabase)) { echo $healthissueFromDatabase; } ?></textarea><br>
<form action="" method="post" enctype="multipart/form-data">
<div class="form-field">
<label for="documentDescription">Descrizione documento</label>
<input type="text" id="documentDescription" class="form-control"
name="documentDescription" placeholder="Es. Certificato medico 2026" required>
</div>
<label>Commenti generali:</label>
<textarea name="generalcomment" class="form-control" rows="4" cols="50"><?php if (isset($generalcommentFromDatabase)) { echo $generalcommentFromDatabase; } ?></textarea><br>
<div class="form-field">
<label for="expiryDate">Data di scadenza</label>
<input type="text" id="expiryDate" class="form-control" name="expiryDate" placeholder="AAAA-MM-GG">
<div class="hint">Lascia vuoto se il documento non ha scadenza.</div>
</div>
<input type="submit" class="btn btn-primary w-md" name="submit" value="Aggiorna">
<div class="form-field">
<label>File</label>
<label class="upload-drop d-block" for="fileToUpload" style="cursor:pointer;">
<div class="upload-icon"><i class="fas fa-cloud-arrow-up"></i></div>
<div>Clicca per selezionare un file</div>
<span class="file-chosen" id="fileChosenLabel">Nessun file selezionato</span>
</label>
<input type="file" id="fileToUpload" name="fileToUpload" class="d-none"
accept=".pdf,.jpg,.jpeg,.png" onchange="handleFileChange(this)" required>
</div>
<button type="submit" class="btn btn-primary btn-save">Carica documento</button>
</form>
<?php } ?>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- container-fluid -->
</div>
<?php include('include/footer.php'); ?>
</div>
<!-- end main content-->
</div>
<!-- END layout-wrapper -->
</div>
<!-- JAVASCRIPT -->
<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>
</body>
</html>
+311 -325
View File
@@ -1,161 +1,94 @@
<?php require_once('include/headscript.php'); ?>
<?php
// optionquery
$optionquery = new WA_MySQLi_RS("optionquery", $bkngstm, 0);
$optionquery->setQuery("SELECT * FROM option");
$optionquery->execute();
?>
<?php
$bookedclass = new WA_MySQLi_RS("bookedclass", $bkngstm, 0);
$bookedclass->setQuery("SELECT * FROM bookingclass LEFT JOIN service on bookingclass.idservice=service.idservice LEFT JOIN serviceschedule ON bookingclass.idserviceschedule=serviceschedule.idserviceschedule WHERE bookingclass.iduser='1'");
$bookedclass->execute();
?>
<?php
// Verifica se è stato inviato un modulo
if ($_SERVER["REQUEST_METHOD"] == "POST") {
// Verifica se tutti i campi obbligatori sono presenti
if (isset($_FILES["fileToUpload"]) && $_FILES["fileToUpload"]["error"] === UPLOAD_ERR_OK && !empty($_POST["documentDescription"]) && !empty($_POST["expiryDate"])) {
// Crea la connessione al database
$conn = new mysqli($servername, $username, $password, $dbname);
require_once('include/headscript.php');
// Verifica la connessione
if ($conn->connect_error) {
$error_message = "Connessione al database fallita: " . $conn->connect_error;
echo "<script>var errorMessage = '" . addslashes($error_message) . "';</script>";
echo "<script>var uploadStatus = 'db_connection_error';</script>";
die();
/**
* Connessione unica PDO (singleton). $iduserlogin dalla sessione autenticata.
*/
$pdo = DBHandlerSelect::getInstance()->getConnection();
$iduserlogin = (int) $iduserlogin;
function e($value): string
{
return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
}
// Ottieni l'ID dell'utente
$iduserlogin = $_POST["iduserlogin"];
$documentDescription = $conn->real_escape_string($_POST["documentDescription"]);
$expiryDate = $conn->real_escape_string($_POST["expiryDate"]);
$uploadedAt = date("Y-m-d"); // Data corrente per uploaded_at
$originalFileName = $_FILES["fileToUpload"]["name"];
$fileExtension = pathinfo($originalFileName, PATHINFO_EXTENSION);
$timestamp = time(); // Timestamp corrente
$newFileName = "{$timestamp}_{$originalFileName}"; // Aggiungi timestamp al nome del file
$fileTmpName = $_FILES["fileToUpload"]["tmp_name"];
$fileDestination = "user/document/" . $newFileName;
$uploadFeedback = null; // ['type' => 'success'|'error', 'text' => '...']
// Sposta il file nella cartella di destinazione
if (move_uploaded_file($fileTmpName, $fileDestination)) {
// Inserisci i dati nel database usando prepared statement
$sql = "INSERT INTO certificateuserprofile (iduser, documentdescription, filenamedocument, expirydatedocument, uploaded_at)
VALUES (?, ?, ?, ?, ?)";
$stmt = $conn->prepare($sql);
$stmt->bind_param("issss", $iduserlogin, $documentDescription, $newFileName, $expiryDate, $uploadedAt);
/* -------------------------------------------------------------------------
* Upload certificato (POST) con validazione robusta
* ---------------------------------------------------------------------- */
$allowedExt = ['pdf', 'jpg', 'jpeg', 'png'];
$allowedMime = ['application/pdf', 'image/jpeg', 'image/png'];
$maxBytes = 16 * 1024 * 1024; // 16 MB
$uploadDir = 'user/document/';
if ($stmt->execute()) {
echo "<script>var uploadStatus = 'success';</script>";
if (
$_SERVER['REQUEST_METHOD'] === 'POST'
&& isset($_FILES['fileToUpload'])
&& $_FILES['fileToUpload']['error'] === UPLOAD_ERR_OK
) {
$file = $_FILES['fileToUpload'];
$documentDescription = trim($_POST['documentDescription'] ?? '');
$expiryDate = trim($_POST['expiryDate'] ?? '');
if ($documentDescription === '' || $expiryDate === '') {
$uploadFeedback = ['type' => 'error', 'text' => 'Descrizione e data di scadenza sono obbligatorie.'];
} elseif ($file['size'] > $maxBytes) {
$uploadFeedback = ['type' => 'error', 'text' => 'Il file supera la dimensione massima di 16 MB.'];
} else {
$error_message = "Errore durante l'inserimento nel database: " . $conn->error;
echo "<script>var errorMessage = '" . addslashes($error_message) . "';</script>";
echo "<script>var uploadStatus = 'db_insert_error';</script>";
}
$stmt->close();
$ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
$finfo = new finfo(FILEINFO_MIME_TYPE);
$realMime = $finfo->file($file['tmp_name']);
if (!in_array($ext, $allowedExt, true) || !in_array($realMime, $allowedMime, true)) {
$uploadFeedback = ['type' => 'error', 'text' => 'Formato non consentito. Carica un PDF, JPG o PNG.'];
} else {
$error_message = "Errore nel caricamento del file.";
echo "<script>var errorMessage = '" . addslashes($error_message) . "';</script>";
echo "<script>var uploadStatus = 'file_upload_error';</script>";
$safeName = bin2hex(random_bytes(16)) . '.' . $ext;
$destination = $uploadDir . $safeName;
if (!is_dir($uploadDir)) {
@mkdir($uploadDir, 0755, true);
}
// Chiudi la connessione al database
$conn->close();
if (move_uploaded_file($file['tmp_name'], $destination)) {
$sql = "INSERT INTO certificateuserprofile
(iduser, documentdescription, filenamedocument, expirydatedocument, uploaded_at)
VALUES (:iduser, :descr, :fname, :expiry, :uploaded)";
$stmt = $pdo->prepare($sql);
$ok = $stmt->execute([
':iduser' => $iduserlogin,
':descr' => $documentDescription,
':fname' => $safeName,
':expiry' => $expiryDate,
':uploaded' => date('Y-m-d'),
]);
$uploadFeedback = $ok
? ['type' => 'success', 'text' => 'Documento caricato correttamente.']
: ['type' => 'error', 'text' => 'Errore nel salvataggio del documento. Riprova.'];
} else {
$error_message = "Tutti i campi sono obbligatori: descrizione, data di scadenza e file.";
echo "<script>var errorMessage = '" . addslashes($error_message) . "';</script>";
echo "<script>var uploadStatus = 'validation_error';</script>";
$uploadFeedback = ['type' => 'error', 'text' => 'Caricamento del file non riuscito. Riprova.'];
}
}
?>
<?php
$conn = new mysqli($servername, $username, $password, $dbname);
if ($conn->connect_error) {
die("Connessione fallita: " . $conn->connect_error);
}
} elseif (
$_SERVER['REQUEST_METHOD'] === 'POST'
&& isset($_FILES['fileToUpload'])
&& $_FILES['fileToUpload']['error'] !== UPLOAD_ERR_NO_FILE
) {
$uploadFeedback = ['type' => 'error', 'text' => 'Si è verificato un problema durante il caricamento. Riprova.'];
}
// ID dell'utente per il quale vuoi filtrare gli ordini
$userid = 1;
// Query per ottenere la somma dei ticket per ogni ordine dell'utente
$query = "SELECT iduser, idorderbook, SUM(nticket) as total_tickets
FROM orderbook
WHERE iduser = $userid
GROUP BY iduser";
$result = $conn->query($query);
if (!$result) {
die("Query fallita: " . $conn->error);
}
if ($result->num_rows > 0) {
while ($row = $result->fetch_assoc()) {
$idOrdine = $row["idorderbook"];
$totalTickets = $row["total_tickets"];
}
}
$conn->close();
?>
<?php
// Connessione al database
$conn = new mysqli($servername, $username, $password, $dbname);
if ($conn->connect_error) {
die("Connessione al database fallita: " . $conn->connect_error);
}
// ID dell'utente per il quale si desidera eseguire la query
$iduser = 1; // Sostituisci con l'ID utente desiderato
// Data e ora attuali
$currentDateTime = date("Y-m-d H:i:s");
// Query per contare i record con data e ora passate e future
$query = "SELECT COUNT(*) AS total,
SUM(CASE WHEN serviceschedule.dateschedule <= '$currentDateTime' THEN 1 ELSE 0 END) AS passed,
SUM(CASE WHEN serviceschedule.dateschedule > '$currentDateTime' THEN 1 ELSE 0 END) AS future
FROM bookingclass
LEFT JOIN serviceschedule ON bookingclass.idserviceschedule = serviceschedule.idserviceschedule
WHERE bookingclass.iduser = $iduser";
$result = $conn->query($query);
if ($result) {
$row = $result->fetch_assoc();
$totalRecords = $row['total'];
$passedRecords = $row['passed'];
$futureRecords = $row['future'];
}
// Chiusura della connessione
$conn->close();
?>
<?php
// Crea la connessione al database
$conn = new mysqli($servername, $username, $password, $dbname);
// Verifica la connessione
if ($conn->connect_error) {
die("Connessione al database fallita: " . $conn->connect_error);
}
// Query per selezionare i dati filtrati per iduser
$query = "SELECT * FROM certificateuserprofile WHERE iduser = $iduserlogin";
$result = $conn->query($query);
// Array per memorizzare i risultati
$documents = array();
while ($row = $result->fetch_assoc()) {
$documents[] = $row;
}
$conn->close();
/* -------------------------------------------------------------------------
* Elenco documenti dell'utente
* ---------------------------------------------------------------------- */
$stmtDocs = $pdo->prepare("SELECT * FROM certificateuserprofile WHERE iduser = :iduser ORDER BY uploaded_at DESC");
$stmtDocs->execute([':iduser' => $iduserlogin]);
$documents = $stmtDocs->fetchAll();
?>
<!doctype html>
<html lang="en">
<html lang="it">
<head>
<meta charset="utf-8" />
@@ -163,69 +96,37 @@ $conn->close();
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta content="YogiBook - Prenotazione facile YogaSoul" name="description" />
<meta content="Advanced Creative Solutions" name="author" />
<!-- App favicon -->
<link rel="shortcut icon" href="assets/images/favicon.ico">
<!-- Bootstrap Css -->
<link href="assets/css/bootstrap.min.css" id="bootstrap-style" rel="stylesheet" type="text/css" />
<!-- Icons Css -->
<link href="assets/css/icons.min.css" rel="stylesheet" type="text/css" />
<!-- App 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">
<!-- SweetAlert2 CDN -->
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@10"></script>
<!-- jQuery and jQuery UI -->
<link rel="stylesheet" href="https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<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() {
$("#expiryDate").datepicker({
dateFormat: "yy-mm-dd",
minDate: 0 // Impedisce la selezione di date passate
changeYear: true,
changeMonth: true,
minDate: 0
});
});
// Handle upload status and display modals
$(document).ready(function() {
if (typeof Swal === 'undefined') {
console.error('SweetAlert2 non è caricato correttamente.');
alert('Errore: SweetAlert2 non è disponibile. Controlla la connessione al CDN.');
return;
}
if (typeof uploadStatus !== 'undefined') {
if (uploadStatus === 'success') {
Swal.fire({
icon: 'success',
title: 'Successo',
text: 'Documento caricato con successo!',
confirmButtonText: 'OK'
}).then(() => {
window.location.href = window.location.href; // Ricarica la pagina
});
} else if (uploadStatus === 'db_connection_error' || uploadStatus === 'db_insert_error' || uploadStatus === 'file_upload_error' || uploadStatus === 'validation_error') {
Swal.fire({
icon: 'error',
title: 'Errore',
text: errorMessage || 'Si è verificato un errore sconosciuto.',
confirmButtonText: 'OK'
});
}
}
});
function confirmDeleteCertificate(id) {
Swal.fire({
title: "Sei sicuro?",
text: "Questo certificato verrà cancellato definitivamente!",
text: "Questo certificato verrà cancellato definitivamente.",
icon: "warning",
showCancelButton: true,
confirmButtonColor: "#d33",
cancelButtonColor: "#3085d6",
confirmButtonText: "Sì, cancella!",
confirmButtonText: "Sì, cancella",
cancelButtonText: "Annulla"
}).then((result) => {
if (result.isConfirmed) {
@@ -233,125 +134,177 @@ $conn->close();
}
});
}
function handleFileChange(input) {
var label = document.getElementById('fileChosenLabel');
if (!input.files.length) {
label.textContent = 'Nessun file selezionato';
return;
}
var f = input.files[0];
var allowed = ['application/pdf', 'image/jpeg', 'image/png'];
var maxBytes = 16 * 1024 * 1024;
if (allowed.indexOf(f.type) === -1) {
label.textContent = 'Formato non valido: usa PDF, JPG o PNG';
label.style.color = '#dc3545';
input.value = '';
return;
}
if (f.size > maxBytes) {
label.textContent = 'File troppo grande (max 16 MB)';
label.style.color = '#dc3545';
input.value = '';
return;
}
label.textContent = f.name;
label.style.color = '#198754';
}
</script>
<style>
.custom-card {
margin: 10px auto;
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;
align-items: center;
padding: 0;
font-size: 60px;
font-weight: bold;
border-top-left-radius: 8px;
border-bottom-left-radius: 8px;
}
.custom-day {
line-height: 1;
}
.custom-month {
font-size: 28px;
}
.custom-event-details {
flex: 2;
display: flex;
flex-direction: column;
padding: 10px 20px;
background-color: lightblue;
}
.custom-heading {
margin-top: 0;
font-size: 24px;
}
.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;
.cert-card {
border: none;
padding: 8px 12px;
border-radius: 4px;
cursor: pointer;
transition: background-color 0.2s;
border-radius: 14px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.06);
}
.custom-action-button:hover {
background-color: #e0e0e0;
.cert-card .card-body {
padding: 26px 30px;
}
@media (max-width: 768px) {
.custom-card {
flex-direction: column;
.section-title {
font-size: 15px;
font-weight: 700;
letter-spacing: 0.04em;
text-transform: uppercase;
color: #6b7280;
margin-bottom: 4px;
}
.custom-date-box,
.custom-event-details {
width: 100%;
border-radius: 0;
.section-lead {
color: #9ca3af;
font-size: 14px;
margin-bottom: 22px;
}
.custom-event-time {
font-size: 24px;
.cert-table {
margin-bottom: 0;
}
.cert-table thead th {
font-size: 12px;
text-transform: uppercase;
letter-spacing: 0.03em;
color: #6b7280;
border-bottom: 2px solid #eef0f3;
}
.cert-table td {
vertical-align: middle;
}
.doc-link {
display: inline-flex;
align-items: center;
gap: 6px;
color: #1ebf73;
font-weight: 600;
text-decoration: none;
}
.doc-link:hover {
text-decoration: underline;
}
.empty-state {
text-align: center;
padding: 30px 10px;
color: #9ca3af;
}
.empty-state i {
font-size: 34px;
margin-bottom: 10px;
display: block;
color: #d5d9e0;
}
.form-field {
margin-bottom: 20px;
}
.form-field label {
display: block;
font-weight: 600;
font-size: 14px;
color: #374151;
margin-bottom: 6px;
}
.form-field .form-control {
border-radius: 8px;
border: 1px solid #e2e4e9;
padding: 10px 12px;
}
.form-field .form-control:focus {
border-color: #1ebf73;
box-shadow: 0 0 0 3px rgba(30, 191, 115, 0.12);
}
.upload-drop {
border: 2px dashed #d5d9e0;
border-radius: 12px;
padding: 24px;
text-align: center;
transition: border-color 0.2s, background 0.2s;
background: #fafbfc;
}
.upload-drop:hover {
border-color: #1ebf73;
background: #f6fdf9;
}
.upload-icon {
font-size: 30px;
color: #1ebf73;
margin-bottom: 8px;
}
.file-chosen {
display: block;
margin-top: 10px;
font-size: 13px;
color: #6b7280;
}
.btn-save {
border-radius: 8px;
padding: 10px 26px;
font-weight: 600;
}
.privacy-note {
font-size: 12px;
color: #9ca3af;
margin: 12px 0 18px;
}
</style>
</head>
<body>
<!-- Begin page -->
<div id="layout-wrapper">
<!-- Top Bar -->
<header id="page-topbar" class="isvertical-topbar">
<div class="navbar-header">
<div class="d-flex">
<!-- LOGO -->
<?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>
<!-- start page title -->
<div class="page-title-box align-self-center d-none d-md-block">
<h4 class="page-title mb-0">Prenotazione Classi</h4>
<h4 class="page-title mb-0">Certificati Medici</h4>
</div>
<!-- end page title -->
</div>
<div class="d-flex">
<?php include('include/languageselection.php'); ?>
@@ -360,7 +313,6 @@ $conn->close();
</div>
</header>
<?php include('include/sidebar.php'); ?>
<header class="ishorizontal-topbar">
<div class="navbar-header">
<div class="d-flex"></div>
@@ -372,44 +324,70 @@ $conn->close();
</div>
</header>
<!-- Start right Content here -->
<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">
<?php if (isset($_GET['message']) && $_GET['message'] == 'success') { ?>
<?php if (isset($_GET['message']) && $_GET['message'] === 'success') : ?>
<div class="alert alert-success" role="alert">
Certificato rimosso con successo
Certificato rimosso con successo.
</div>
<?php } ?>
<h5>Benvenuta/o <?php echo $firstname; ?> </h5>
<p>Di seguito puoi visualizzare o caricare i certificati medici di liberatoria alla pratica Yoga</p>
<?php endif; ?>
<?php if ($uploadFeedback) : ?>
<div class="alert alert-<?php echo $uploadFeedback['type'] === 'success' ? 'success' : 'danger'; ?>" role="alert">
<?php echo e($uploadFeedback['text']); ?>
</div>
<?php endif; ?>
<!-- Elenco documenti -->
<div class="row">
<div class="col-12">
<div class="card cert-card mb-4">
<div class="card-body">
<div class="section-title">I tuoi documenti</div>
<p class="section-lead">
Ciao <?php echo e($firstname); ?>, qui trovi i certificati medici di liberatoria alla pratica Yoga che hai caricato.
</p>
<div class="table-responsive">
<table class="table table-striped mb-0">
<table class="table cert-table">
<thead>
<tr>
<th>Descrizione del Documento</th>
<th>Data di Scadenza</th>
<th>Descrizione</th>
<th>Scadenza</th>
<th>Documento</th>
<th>Azione</th>
<th class="text-end">Azione</th>
</tr>
</thead>
<tbody>
<?php foreach ($documents as $document) { ?>
<?php if (empty($documents)) : ?>
<tr>
<td><?php echo $document['documentdescription']; ?></td>
<td><?php echo $document['expirydatedocument']; ?></td>
<td><a href="user/document/<?php echo $document['filenamedocument']; ?>" target="_blank">Documento</a></td>
<td colspan="4">
<div class="empty-state">
<i class="fas fa-folder-open"></i>
Nessun documento caricato. Usa il modulo qui sotto per aggiungere il primo.
</div>
</td>
</tr>
<?php else : ?>
<?php foreach ($documents as $document) : ?>
<tr>
<td><?php echo e($document['documentdescription']); ?></td>
<td><?php echo e($document['expirydatedocument']); ?></td>
<td>
<button class="btn btn-danger btn-sm" onclick="confirmDeleteCertificate(<?php echo $document['idcertificateuserprofile']; ?>)">
<a class="doc-link" href="user/document/<?php echo e(rawurlencode($document['filenamedocument'])); ?>" target="_blank" rel="noopener">
<i class="fas fa-file-arrow-down"></i> Apri
</a>
</td>
<td class="text-end">
<button class="btn btn-danger btn-sm" onclick="confirmDeleteCertificate(<?php echo (int) $document['idcertificateuserprofile']; ?>)">
<i class="fas fa-trash"></i> Cancella
</button>
</td>
</tr>
<?php } ?>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
@@ -417,52 +395,60 @@ $conn->close();
</div>
</div>
</div>
<!-- container-fluid -->
<!-- Upload -->
<div class="row">
<div class="col-12">
<div class="card cert-card">
<div class="card-body">
<div class="section-title">Carica un nuovo documento</div>
<p class="section-lead">Formati accettati: PDF, JPG o PNG (max 16 MB).</p>
<form method="post" enctype="multipart/form-data">
<div class="row">
<div class="col-md-6">
<div class="form-field">
<label for="documentDescription">Descrizione del documento</label>
<input type="text" id="documentDescription" class="form-control"
name="documentDescription" value="Certificato Medico" required>
</div>
</div>
<div class="col-md-6">
<div class="form-field">
<label for="expiryDate">Data di scadenza</label>
<input type="text" id="expiryDate" class="form-control" name="expiryDate" placeholder="AAAA-MM-GG" required>
</div>
</div>
</div>
<div class="container-fluid">
<div class="row">
<div class="col-xl-12">
<div class="card">
<div class="card-body">
<div class="">
<div class="row mb-12">
<div class="col-xl-12 col-md-12">
<div class="pb-3 pb-xl-0">
<div class="position-relative">
<h3>Carica documenti</h3>
<div class="form-field">
<label>File</label>
<label class="upload-drop d-block" for="fileToUpload" style="cursor:pointer;">
<div class="upload-icon"><i class="fas fa-cloud-arrow-up"></i></div>
<div>Clicca per selezionare un file</div>
<span class="file-chosen" id="fileChosenLabel">Nessun file selezionato</span>
</label>
<input type="file" id="fileToUpload" name="fileToUpload" class="d-none"
accept=".pdf,.jpg,.jpeg,.png" onchange="handleFileChange(this)" required>
</div>
<form method="post" enctype="multipart/form-data">
<input type="hidden" name="iduserlogin" class="form-control" value="<?php echo $iduserlogin; ?>">
<label for="documentDescription">Descrizione del Documento:</label>
<input type="text" class="form-control" name="documentDescription" value="Certificato Medico" required><br>
<label for="expiryDate">Data di Scadenza:</label>
<input type="text" id="expiryDate" class="form-control" name="expiryDate" required><br>
<label for="fileToUpload">Seleziona un File: (peso massimo 16 MB)</label>
<input type="file" class="form-control" name="fileToUpload" required><br>
I documenti caricati sono solo a fini di sicurezza e cliccando su carica documento accetti il nostro regolamento privacy <br><br>
<input type="submit" class="btn btn-primary w-md" value="Carica Documento" name="submit">
<p class="privacy-note">
I documenti caricati sono trattati solo a fini di sicurezza. Caricando il documento accetti il regolamento sulla privacy.
</p>
<button type="submit" class="btn btn-primary btn-save" name="submit">Carica documento</button>
</form>
</div>
</div>
<div class="col-xl-9 col-md-12"></div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- container-fluid -->
</div>
<!-- End Page-content -->
</div>
<?php include('include/footer.php'); ?>
</div>
<!-- end main content-->
</div>
<!-- END layout-wrapper -->
</div>
<!-- JAVASCRIPT -->
<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>