Files
yogibook_aury_new/public/products.php
T

464 lines
19 KiB
PHP

<?php
require_once('include/headscript.php');
require_once('include/csrf.php');
SchoolContext::requireSchoolAdmin();
/**
* Catalogo prodotti - elenco della scuola corrente.
* Azioni: Modifica (product_form.php), Attiva/Disattiva ed Elimina (POST + CSRF a product_action.php).
*/
$pdo = DBHandlerSelect::getInstance()->getConnection();
function e($value): string
{
return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
}
function euro($v): string
{
return number_format((float) $v, 2, ',', '.') . ' €';
}
$message = $_GET['message'] ?? '';
$st = $pdo->prepare(
"SELECT p.*,
(SELECT COUNT(*) FROM product_variants v WHERE v.idproduct = p.idproduct AND v.active = 'Y') AS nvariants,
(SELECT MIN(v.price) FROM product_variants v WHERE v.idproduct = p.idproduct AND v.active = 'Y') AS price_from,
(SELECT COUNT(*) FROM product_services ps WHERE ps.idproduct = p.idproduct) AS nclasses,
(SELECT COUNT(*) FROM orderbook o WHERE o.idproduct = p.idproduct AND o.idschool = p.idschool) AS nsold
FROM products p
WHERE p.idschool = ?
ORDER BY FIELD(p.kind, 'lessons', 'membership', 'voucher'), p.sort_order, p.title"
);
$st->execute([$idschool]);
$products = $st->fetchAll(PDO::FETCH_ASSOC);
$kindLabels = [
'lessons' => ['Lezioni', 'bx-calendar-check'],
'membership' => ['Tessere', 'bx-id-card'],
'voucher' => ['Buoni regalo', 'bx-gift'],
];
function rulesSummary(array $p): string
{
if ($p['kind'] === 'membership') {
return 'Tessera / quota associativa';
}
if ($p['kind'] === 'voucher') {
return 'Regala un pacchetto';
}
switch ($p['booking_mode']) {
case 'fixed':
$s = (int) $p['fixed_slots'] === 2 ? 'Classi fisse · 2 a settimana' : 'Classe fissa · 1 a settimana';
break;
case 'single':
$s = 'Lezione scelta all\'acquisto';
break;
default:
$s = 'Libera';
if (!empty($p['weekly_limit'])) {
$s .= ' · max ' . (int) $p['weekly_limit'] . '/sett.';
}
}
if ($p['once_per_student'] === 'Y') {
$s .= ' · una volta per allieva';
}
return $s;
}
?>
<!doctype html>
<html lang="it">
<head>
<meta charset="utf-8" />
<title>YogiBook - Prodotti</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta content="YogiBook" name="description" />
<link rel="shortcut icon" href="assets/images/favicon.ico">
<link href="assets/css/bootstrap.min.css" id="bootstrap-style" rel="stylesheet" type="text/css" />
<link href="assets/css/icons.min.css" rel="stylesheet" type="text/css" />
<link href="assets/css/app.min.css" id="app-style" rel="stylesheet" type="text/css" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<style>
.admin-card {
border: none;
border-radius: 14px;
box-shadow: 0 4px 20px rgba(0, 0, 0, .06);
}
.admin-card .card-body {
padding: 26px 30px;
}
.section-title {
font-size: 15px;
font-weight: 700;
letter-spacing: .04em;
text-transform: uppercase;
color: #6b7280;
margin-bottom: 4px;
}
.section-lead {
color: #9ca3af;
font-size: 14px;
margin-bottom: 0;
}
.list-head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
flex-wrap: wrap;
}
.btn-new {
background: #2F7F84;
border: none;
color: #fff;
border-radius: 8px;
padding: 9px 18px;
font-weight: 600;
font-size: 14px;
white-space: nowrap;
}
.btn-new:hover {
background: #1E4A4F;
color: #fff;
}
.prod-table thead th {
font-size: 12px;
text-transform: uppercase;
letter-spacing: .03em;
color: #6b7280;
border-bottom: 2px solid #eef0f3;
white-space: nowrap;
}
.prod-table td {
vertical-align: middle;
}
.kind-group td {
background: #F4FAFA;
font-weight: 700;
color: #1E4A4F;
font-size: 13px;
}
.kind-group i {
color: #2F7F84;
margin-right: 6px;
}
.prod-name {
display: flex;
align-items: center;
gap: 12px;
}
.prod-thumb {
width: 46px;
height: 46px;
border-radius: 10px;
object-fit: cover;
flex-shrink: 0;
}
.prod-thumb.ph {
background: #DBF6F9;
display: flex;
align-items: center;
justify-content: center;
color: #2F7F84;
font-size: 20px;
}
.prod-title {
font-weight: 600;
color: #1f2937;
}
.prod-sub {
color: #9ca3af;
font-size: 12px;
}
.badge-tag {
display: inline-block;
background: #F8EFE4;
color: #8a6a3f;
font-size: 11px;
font-weight: 700;
padding: 1px 8px;
border-radius: 10px;
margin-left: 6px;
}
.pill {
display: inline-block;
font-size: 12px;
font-weight: 700;
padding: 3px 10px;
border-radius: 20px;
white-space: nowrap;
}
.pill.on {
background: #e7f7ee;
color: #1a7f52;
}
.pill.off {
background: #eef0f3;
color: #6b7280;
}
.pill.web {
background: #DBF6F9;
color: #1E4A4F;
}
.btn-act {
border: 1px solid #e2e4e9;
background: #fff;
border-radius: 8px;
padding: 6px 12px;
font-size: 13px;
font-weight: 600;
color: #374151;
cursor: pointer;
white-space: nowrap;
text-decoration: none;
display: inline-block;
}
.btn-act:hover {
background: #f3f4f6;
color: #111827;
}
.btn-act.danger {
color: #c0392b;
border-color: #f2c9c4;
}
.btn-act.danger:hover {
background: #fdecea;
}
.empty-state {
text-align: center;
padding: 44px 10px;
color: #9ca3af;
}
.empty-state i {
font-size: 34px;
margin-bottom: 10px;
display: block;
color: #cfe3e4;
}
</style>
</head>
<body>
<div id="layout-wrapper">
<header id="page-topbar" class="isvertical-topbar">
<div class="navbar-header">
<div class="d-flex">
<?php include('include/logoarea.php'); ?>
<button type="button" class="btn btn-sm px-3 font-size-24 header-item waves-effect vertical-menu-btn">
<i class="bx bx-menu align-middle"></i>
</button>
<div class="page-title-box align-self-center d-none d-md-block">
<h4 class="page-title mb-0">Prodotti</h4>
</div>
</div>
<div class="d-flex">
<?php include('include/schoolswitcher.php'); ?><?php include('include/languageselection.php'); ?>
<?php include('include/profiletopbar.php'); ?>
</div>
</div>
</header>
<?php include('include/sidebar.php'); ?>
<header class="ishorizontal-topbar">
<div class="navbar-header">
<div class="d-flex"></div>
</div>
<div class="topnav">
<div class="container-fluid">
<nav class="navbar navbar-light navbar-expand-lg topnav-menu"></nav>
</div>
</div>
</header>
<div class="main-content">
<div class="page-content">
<div class="container-fluid">
<?php if ($message === 'saved') : ?>
<div class="alert alert-success">Prodotto salvato.</div>
<?php elseif ($message === 'saved_kept') : ?>
<div class="alert alert-info">Prodotto salvato. Le opzioni già vendute non sono state eliminate ma disattivate, per non perdere lo storico.</div>
<?php elseif ($message === 'toggled') : ?>
<div class="alert alert-success">Stato del prodotto aggiornato.</div>
<?php elseif ($message === 'deleted') : ?>
<div class="alert alert-warning">Prodotto eliminato.</div>
<?php elseif ($message === 'inuse') : ?>
<div class="alert alert-warning">Questo prodotto è già stato venduto o è usato in un buono regalo: non si può eliminare. Puoi disattivarlo.</div>
<?php elseif ($message === 'error') : ?>
<div class="alert alert-danger">Si è verificato un errore. Riprova.</div>
<?php endif; ?>
<div class="card admin-card">
<div class="card-body">
<div class="list-head">
<div>
<div class="section-title">Prodotti</div>
<p class="section-lead">Quello che la scuola vende: pacchetti di lezioni, carnet, flexi, lezioni singole e di prova, tessere e buoni regalo.</p>
</div>
<a href="product_form.php" class="btn btn-new"><i class="bx bx-plus align-middle"></i> Nuovo prodotto</a>
</div>
<div class="table-responsive mt-3">
<table class="table prod-table align-middle">
<thead>
<tr>
<th>Prodotto</th>
<th>Regole</th>
<th>Opzioni</th>
<th>Classi</th>
<th>Venduti</th>
<th>Stato</th>
<th class="text-end">Azioni</th>
</tr>
</thead>
<tbody>
<?php if (empty($products)) : ?>
<tr>
<td colspan="7">
<div class="empty-state">
<i class="bx bx-purchase-tag-alt"></i>
Nessun prodotto ancora. Crea il primo con "Nuovo prodotto".
</div>
</td>
</tr>
<?php else :
$currentKind = null;
foreach ($products as $p) :
if ($p['kind'] !== $currentKind) :
$currentKind = $p['kind'];
[$kLabel, $kIcon] = $kindLabels[$currentKind] ?? [$currentKind, 'bx-box']; ?>
<tr class="kind-group">
<td colspan="7"><i class="bx <?php echo e($kIcon); ?>"></i><?php echo e($kLabel); ?></td>
</tr>
<?php endif; ?>
<tr>
<td>
<div class="prod-name">
<?php if (!empty($p['cover_image'])) : ?>
<img class="prod-thumb" src="<?php echo e($p['cover_image']); ?>" alt="">
<?php else : ?>
<span class="prod-thumb ph"><i class="bx bx-image"></i></span>
<?php endif; ?>
<div>
<div class="prod-title">
<?php echo e($p['title']); ?>
<?php if (!empty($p['badge'])) : ?><span class="badge-tag"><?php echo e($p['badge']); ?></span><?php endif; ?>
</div>
<?php if (!empty($p['subtitle'])) : ?><div class="prod-sub"><?php echo e($p['subtitle']); ?></div><?php endif; ?>
</div>
</div>
</td>
<td><?php echo e(rulesSummary($p)); ?></td>
<td>
<?php echo (int) $p['nvariants']; ?>
<?php if ($p['price_from'] !== null) : ?>
<span class="prod-sub">· da <?php echo e(euro($p['price_from'])); ?></span>
<?php endif; ?>
</td>
<td><?php echo $p['kind'] === 'lessons' ? (int) $p['nclasses'] : '—'; ?></td>
<td><?php echo (int) $p['nsold']; ?></td>
<td>
<span class="pill <?php echo $p['active'] === 'Y' ? 'on' : 'off'; ?>"><?php echo $p['active'] === 'Y' ? 'Attivo' : 'Disattivo'; ?></span>
<?php if ($p['visible_online'] === 'Y') : ?><span class="pill web" title="Visibile nell'acquisto online">Online</span><?php endif; ?>
</td>
<td class="text-end">
<a href="product_form.php?id=<?php echo (int) $p['idproduct']; ?>" class="btn-act"><i class="bx bx-edit"></i> Modifica</a>
<button type="button" class="btn-act btn-toggle" data-id="<?php echo (int) $p['idproduct']; ?>">
<i class="bx <?php echo $p['active'] === 'Y' ? 'bx-hide' : 'bx-show'; ?>"></i>
<?php echo $p['active'] === 'Y' ? 'Disattiva' : 'Attiva'; ?>
</button>
<?php if ((int) $p['nsold'] === 0) : ?>
<button type="button" class="btn-act danger btn-delete" data-id="<?php echo (int) $p['idproduct']; ?>" data-title="<?php echo e($p['title']); ?>"><i class="bx bx-trash"></i> Elimina</button>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
<form id="action-form" action="product_action.php" method="POST" style="display:none;">
<?php echo yb_csrf_field(); ?>
<input type="hidden" name="id" id="action-id">
<input type="hidden" name="action" id="action-name">
</form>
</div>
<?php include('include/footer.php'); ?>
</div>
</div>
</div>
<script src="assets/libs/bootstrap/js/bootstrap.bundle.min.js"></script>
<script src="assets/libs/metismenujs/metismenujs.min.js"></script>
<script src="assets/libs/simplebar/simplebar.min.js"></script>
<script src="assets/libs/eva-icons/eva.min.js"></script>
<script src="assets/js/app.js"></script>
<script>
function sendAction(id, action) {
document.getElementById('action-id').value = id;
document.getElementById('action-name').value = action;
document.getElementById('action-form').submit();
}
document.querySelectorAll('.btn-toggle').forEach(function(btn) {
btn.addEventListener('click', function() {
sendAction(btn.getAttribute('data-id'), 'toggle');
});
});
document.querySelectorAll('.btn-delete').forEach(function(btn) {
btn.addEventListener('click', function() {
var title = document.createElement('div');
title.textContent = btn.getAttribute('data-title');
Swal.fire({
title: 'Eliminare il prodotto?',
html: 'Stai per eliminare <strong>' + title.innerHTML + '</strong> con tutte le sue opzioni.',
icon: 'warning',
showCancelButton: true,
confirmButtonText: 'Elimina',
cancelButtonText: 'Chiudi',
confirmButtonColor: '#e74c3c',
reverseButtons: true
}).then(function(r) {
if (r.isConfirmed) sendAction(btn.getAttribute('data-id'), 'delete');
});
});
});
</script>
</body>
</html>