Files
yogibook_aury_new/public/sa-schools.php
T

494 lines
22 KiB
PHP

<?php
require_once('include/headscript.php');
/**
* SUPERADMIN - Gestione scuole della piattaforma
* Elenco, creazione, modifica, sospensione/riattivazione, accesso rapido.
*/
SchoolContext::requireSuperadmin();
$pdo = DBHandlerSelect::getInstance()->getConnection();
if (!function_exists('e')) {
function e($value): string
{
return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
}
}
function sa_slugify(string $s): string
{
$t = @iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $s);
if ($t === false) {
$t = $s;
}
$t = strtolower(preg_replace('/[^a-zA-Z0-9]+/', '-', $t));
return trim($t, '-');
}
if (empty($_SESSION['csrf_sa'])) {
$_SESSION['csrf_sa'] = bin2hex(random_bytes(16));
}
if (empty($_SESSION['csrf_switch'])) {
$_SESSION['csrf_switch'] = bin2hex(random_bytes(16));
}
$csrf = $_SESSION['csrf_sa'];
$fields = ['name', 'slug', 'email', 'phone', 'address', 'city', 'postcode', 'country', 'vat_number'];
/* ----------------------------------------------------------------------
* AZIONI (POST -> redirect)
* -------------------------------------------------------------------- */
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (!hash_equals($csrf, $_POST['token'] ?? '')) {
http_response_code(400);
die('Richiesta non valida.');
}
$action = $_POST['action'] ?? '';
$id = (int) ($_POST['idschool'] ?? 0);
try {
if ($action === 'save') {
$data = [];
foreach ($fields as $f) {
$v = trim((string) ($_POST[$f] ?? ''));
$data[$f] = ($v === '') ? null : $v;
}
if ($data['name'] === null) {
header('Location: sa-schools.php?error=name' . ($id ? '&edit=' . $id : ''));
exit;
}
$data['slug'] = sa_slugify($data['slug'] ?? $data['name']);
if ($data['slug'] === '') {
header('Location: sa-schools.php?error=slug' . ($id ? '&edit=' . $id : ''));
exit;
}
$data['country'] = strtoupper(substr($data['country'] ?? 'IT', 0, 2));
$chk = $pdo->prepare("SELECT COUNT(*) FROM schools WHERE slug = ? AND idschool <> ?");
$chk->execute([$data['slug'], $id]);
if ((int) $chk->fetchColumn() > 0) {
header('Location: sa-schools.php?error=slugdup' . ($id ? '&edit=' . $id : ''));
exit;
}
if ($id > 0) {
$set = implode(', ', array_map(fn($f) => "`$f` = :$f", $fields));
$data['idschool'] = $id;
$pdo->prepare("UPDATE schools SET $set WHERE idschool = :idschool")->execute($data);
} else {
$cols = implode(', ', array_map(fn($f) => "`$f`", $fields));
$vals = implode(', ', array_map(fn($f) => ":$f", $fields));
$pdo->prepare("INSERT INTO schools ($cols) VALUES ($vals)")->execute($data);
$id = (int) $pdo->lastInsertId();
}
// Owner opzionale: associa per email
$warn = '';
$ownerEmail = trim((string) ($_POST['owner_email'] ?? ''));
if ($ownerEmail !== '') {
$u = $pdo->prepare("SELECT id FROM auth_users WHERE email = ? LIMIT 1");
$u->execute([$ownerEmail]);
$uid = (int) $u->fetchColumn();
if ($uid > 0) {
$pdo->prepare(
"INSERT INTO school_user (user_id, idschool, school_role, active)
VALUES (?, ?, 'owner', 'Y')
ON DUPLICATE KEY UPDATE school_role = 'owner', active = 'Y'"
)->execute([$uid, $id]);
} else {
$warn = '&warn=ownernotfound';
}
}
header('Location: sa-schools.php?msg=saved' . $warn);
exit;
}
if ($action === 'toggle_status' && $id > 0) {
$pdo->prepare(
"UPDATE schools SET status = IF(status = 'suspended', 'active', 'suspended') WHERE idschool = ?"
)->execute([$id]);
header('Location: sa-schools.php?msg=status');
exit;
}
} catch (PDOException $ex) {
error_log('sa-schools: ' . $ex->getMessage());
header('Location: sa-schools.php?error=db');
exit;
}
header('Location: sa-schools.php');
exit;
}
/* ----------------------------------------------------------------------
* DATI
* -------------------------------------------------------------------- */
$edit = null;
if (isset($_GET['edit'])) {
$st = $pdo->prepare("SELECT * FROM schools WHERE idschool = ?");
$st->execute([(int) $_GET['edit']]);
$edit = $st->fetch() ?: null;
}
$schools = $pdo->query(
"SELECT s.*,
(SELECT COUNT(*) FROM school_user su
WHERE su.idschool = s.idschool AND su.school_role = 'student' AND su.active = 'Y') AS n_students,
(SELECT COUNT(*) FROM school_user su
WHERE su.idschool = s.idschool AND su.school_role IN ('admin','teacher') AND su.active = 'Y') AS n_staff,
(SELECT GROUP_CONCAT(CONCAT(u.first_name, ' ', u.last_name) SEPARATOR ', ')
FROM school_user su JOIN auth_users u ON u.id = su.user_id
WHERE su.idschool = s.idschool AND su.school_role = 'owner') AS owners
FROM schools s
ORDER BY s.name"
)->fetchAll();
$msgMap = [
'saved' => ['success', 'Scuola salvata correttamente.'],
'status' => ['success', 'Stato della scuola aggiornato.'],
];
$errMap = [
'name' => 'Il nome della scuola è obbligatorio.',
'slug' => 'Slug non valido.',
'slugdup' => 'Questo slug è già usato da un\'altra scuola.',
'db' => 'Errore database, operazione non eseguita.',
];
$currentId = SchoolContext::id();
?>
<!doctype html>
<html lang="it">
<head>
<meta charset="utf-8" />
<title>YogiBook - Scuole (piattaforma)</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="shortcut icon" href="assets/images/favicon.ico">
<link href="assets/css/bootstrap.min.css" id="bootstrap-style" rel="stylesheet" type="text/css" />
<link href="assets/css/icons.min.css" rel="stylesheet" type="text/css" />
<link href="assets/css/app.min.css" id="app-style" rel="stylesheet" type="text/css" />
<link 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/jquery-3.6.0.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<style>
.sa-card {
background: #fff;
border-radius: 14px;
box-shadow: 0 3px 16px rgba(0, 0, 0, 0.06);
padding: 22px;
margin-bottom: 22px;
}
.sa-card h5 {
font-weight: 700;
color: #1f2937;
margin-bottom: 16px;
}
.sa-card label {
font-size: 13px;
font-weight: 600;
color: #374151;
}
.school-table th {
font-size: 12px;
text-transform: uppercase;
letter-spacing: .03em;
color: #6b7280;
border-bottom-width: 1px;
}
.school-table td {
vertical-align: middle;
}
.school-name {
font-weight: 700;
color: #1f2937;
}
.school-slug {
font-size: 12px;
color: #9ca3af;
}
.pill {
display: inline-block;
font-size: 12px;
font-weight: 700;
padding: 3px 10px;
border-radius: 20px;
}
.pill.active {
background: #e3f2ec;
color: #1a7f52;
}
.pill.suspended {
background: #fdecec;
color: #c0392b;
}
.pill.trial {
background: #fff4d6;
color: #8a6d00;
}
.pill.current {
background: #eef2f7;
color: #374151;
margin-left: 6px;
}
.btn-soft {
border: none;
border-radius: 8px;
padding: 6px 12px;
font-size: 12px;
font-weight: 600;
cursor: pointer;
white-space: nowrap;
}
.btn-soft:hover {
filter: brightness(0.94);
}
.btn-soft.edit {
background: #eef2f7;
color: #374151;
}
.btn-soft.members {
background: #e8f1fb;
color: #2980b9;
}
.btn-soft.enter {
background: #e3f2ec;
color: #1a7f52;
}
.btn-soft.suspend {
background: #fdecea;
color: #c0392b;
}
.btn-soft.reactivate {
background: #fff4d6;
color: #8a6d00;
}
.actions {
display: flex;
gap: 6px;
flex-wrap: wrap;
justify-content: flex-end;
}
</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">Scuole della piattaforma</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'); ?>
<div class="main-content">
<div class="page-content">
<div class="container-fluid">
<?php if (isset($_GET['msg'], $msgMap[$_GET['msg']])) : $m = $msgMap[$_GET['msg']]; ?>
<div class="alert alert-<?php echo $m[0]; ?>"><?php echo e($m[1]); ?></div>
<?php endif; ?>
<?php if (($_GET['warn'] ?? '') === 'ownernotfound') : ?>
<div class="alert alert-warning">Scuola salvata, ma nessun utente trovato con l'email indicata come owner: l'owner deve prima registrarsi, poi puoi associarlo dalla pagina <strong>Membri</strong>.</div>
<?php endif; ?>
<?php if (isset($_GET['error'])) : ?>
<div class="alert alert-danger"><?php echo e($errMap[$_GET['error']] ?? 'Si è verificato un errore.'); ?></div>
<?php endif; ?>
<!-- FORM CREA / MODIFICA -->
<div class="sa-card">
<h5>
<i class="fas <?php echo $edit ? 'fa-pen' : 'fa-plus-circle'; ?> me-1" style="color:#1ebf73;"></i>
<?php echo $edit ? 'Modifica scuola: ' . e($edit['name']) : 'Nuova scuola'; ?>
</h5>
<form method="post" action="sa-schools.php">
<input type="hidden" name="token" value="<?php echo e($csrf); ?>">
<input type="hidden" name="action" value="save">
<input type="hidden" name="idschool" value="<?php echo (int) ($edit['idschool'] ?? 0); ?>">
<div class="row g-3">
<div class="col-md-5">
<label>Nome scuola *</label>
<input type="text" name="name" class="form-control" required value="<?php echo e($edit['name'] ?? ''); ?>">
</div>
<div class="col-md-3">
<label>Slug (URL)</label>
<input type="text" name="slug" class="form-control" placeholder="auto dal nome" value="<?php echo e($edit['slug'] ?? ''); ?>">
</div>
<div class="col-md-4">
<label>Partita IVA / CF</label>
<input type="text" name="vat_number" class="form-control" value="<?php echo e($edit['vat_number'] ?? ''); ?>">
</div>
<div class="col-md-4">
<label>Email scuola</label>
<input type="email" name="email" class="form-control" value="<?php echo e($edit['email'] ?? ''); ?>">
</div>
<div class="col-md-3">
<label>Telefono</label>
<input type="text" name="phone" class="form-control" value="<?php echo e($edit['phone'] ?? ''); ?>">
</div>
<div class="col-md-5">
<label>Indirizzo</label>
<input type="text" name="address" class="form-control" value="<?php echo e($edit['address'] ?? ''); ?>">
</div>
<div class="col-md-4">
<label>Città</label>
<input type="text" name="city" class="form-control" value="<?php echo e($edit['city'] ?? ''); ?>">
</div>
<div class="col-md-2">
<label>CAP</label>
<input type="text" name="postcode" class="form-control" value="<?php echo e($edit['postcode'] ?? ''); ?>">
</div>
<div class="col-md-2">
<label>Nazione</label>
<input type="text" name="country" class="form-control" maxlength="2" value="<?php echo e($edit['country'] ?? 'IT'); ?>">
</div>
<div class="col-md-4">
<label>Email owner <span class="text-muted fw-normal">(facoltativa)</span></label>
<input type="email" name="owner_email" class="form-control" placeholder="utente già registrato">
</div>
</div>
<div class="mt-3 d-flex gap-2">
<button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i> Salva</button>
<?php if ($edit) : ?>
<a href="sa-schools.php" class="btn btn-light">Annulla</a>
<?php endif; ?>
</div>
</form>
</div>
<!-- ELENCO SCUOLE -->
<div class="sa-card">
<h5><i class="fas fa-building me-1" style="color:#1ebf73;"></i> Scuole (<?php echo count($schools); ?>)</h5>
<div class="table-responsive">
<table class="table school-table mb-0">
<thead>
<tr>
<th>Scuola</th>
<th>Città</th>
<th>Owner</th>
<th class="text-center">Staff</th>
<th class="text-center">Allievi</th>
<th>Stato</th>
<th class="text-end">Azioni</th>
</tr>
</thead>
<tbody>
<?php foreach ($schools as $s) :
$sid = (int) $s['idschool'];
$status = $s['status'] ?: 'active';
$statusLabel = ['active' => 'Attiva', 'suspended' => 'Sospesa', 'trial' => 'Prova'][$status] ?? $status;
?>
<tr>
<td>
<span class="school-name"><?php echo e($s['name']); ?></span>
<?php if ($sid === $currentId) : ?><span class="pill current">corrente</span><?php endif; ?>
<div class="school-slug">#<?php echo $sid; ?> · <?php echo e($s['slug']); ?></div>
</td>
<td><?php echo e($s['city'] ?? ''); ?></td>
<td>
<?php if (!empty($s['owners'])) : ?>
<?php echo e($s['owners']); ?>
<?php else : ?>
<span style="color:#e67e22;font-weight:600;">Nessun owner</span>
<?php endif; ?>
</td>
<td class="text-center"><?php echo (int) $s['n_staff']; ?></td>
<td class="text-center"><?php echo (int) $s['n_students']; ?></td>
<td><span class="pill <?php echo e($status); ?>"><?php echo e($statusLabel); ?></span></td>
<td>
<div class="actions">
<a class="btn-soft edit" href="sa-schools.php?edit=<?php echo $sid; ?>"><i class="fas fa-pen"></i> Modifica</a>
<a class="btn-soft members" href="sa-school-members.php?id=<?php echo $sid; ?>"><i class="fas fa-users"></i> Membri</a>
<?php if ($sid !== $currentId && $status !== 'suspended') : ?>
<form method="post" action="switchschool.php" class="m-0">
<input type="hidden" name="idschool" value="<?php echo $sid; ?>">
<input type="hidden" name="token" value="<?php echo e($_SESSION['csrf_switch']); ?>">
<button type="submit" class="btn-soft enter"><i class="fas fa-right-to-bracket"></i> Entra</button>
</form>
<?php endif; ?>
<form method="post" action="sa-schools.php" class="m-0 form-toggle" data-name="<?php echo e($s['name']); ?>" data-suspend="<?php echo $status === 'suspended' ? '0' : '1'; ?>">
<input type="hidden" name="token" value="<?php echo e($csrf); ?>">
<input type="hidden" name="action" value="toggle_status">
<input type="hidden" name="idschool" value="<?php echo $sid; ?>">
<?php if ($status === 'suspended') : ?>
<button type="submit" class="btn-soft reactivate"><i class="fas fa-play"></i> Riattiva</button>
<?php else : ?>
<button type="submit" class="btn-soft suspend"><i class="fas fa-pause"></i> Sospendi</button>
<?php endif; ?>
</form>
</div>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
</div>
<?php include('include/footer.php'); ?>
</div>
</div>
</div>
<script src="assets/libs/bootstrap/js/bootstrap.bundle.min.js"></script>
<script src="assets/libs/metismenujs/metismenujs.min.js"></script>
<script src="assets/libs/simplebar/simplebar.min.js"></script>
<script src="assets/libs/eva-icons/eva.min.js"></script>
<script src="assets/js/app.js"></script>
<script>
$(document).on('submit', '.form-toggle', function(e) {
e.preventDefault();
const form = this;
const suspend = $(form).data('suspend') == 1;
Swal.fire({
title: suspend ? 'Sospendere la scuola?' : 'Riattivare la scuola?',
html: '<strong>' + $('<div>').text($(form).data('name')).html() + '</strong><br><br>' +
(suspend ? 'Owner, staff e allievi non potranno più accedere a questa scuola.' : 'La scuola tornerà accessibile a tutti i suoi membri.'),
icon: suspend ? 'warning' : 'question',
showCancelButton: true,
confirmButtonColor: suspend ? '#e74c3c' : '#1ebf73',
confirmButtonText: suspend ? 'Sì, sospendi' : 'Sì, riattiva',
cancelButtonText: 'Annulla'
}).then((r) => {
if (r.isConfirmed) form.submit();
});
});
</script>
</body>
</html>