SchoolSettings + adminpanel filtrato per scuola
This commit is contained in:
+20
-8
@@ -7,6 +7,7 @@ require_once('include/headscript.php');
|
||||
* manuale e cancellazione completa di una classe schedulata.
|
||||
*/
|
||||
$pdo = DBHandlerSelect::getInstance()->getConnection();
|
||||
SchoolContext::requireStaff();
|
||||
|
||||
function e($value): string
|
||||
{
|
||||
@@ -28,17 +29,24 @@ function avatarColor(string $seed): string
|
||||
return $palette[$idx];
|
||||
}
|
||||
|
||||
$optRow = $pdo->query("SELECT maxbeforetimecancell, show_teacher FROM `option` LIMIT 1")->fetch();
|
||||
$timetocancel = $optRow ? (int) $optRow['maxbeforetimecancell'] : 24;
|
||||
$showTeacher = ($optRow['show_teacher'] ?? 'N') === 'Y';
|
||||
$timetocancel = (int) SchoolSettings::get('maxbeforetimecancell', 24);
|
||||
$showTeacher = SchoolSettings::get('show_teacher', 'N') === 'Y';
|
||||
|
||||
$teachersById = [];
|
||||
if ($showTeacher) {
|
||||
foreach ($pdo->query("SELECT idemployee, nameemployee, surnameemployee, photo FROM employee")->fetchAll() as $t) {
|
||||
$stT = $pdo->prepare("SELECT idemployee, nameemployee, surnameemployee, photo FROM employee WHERE idschool = ?");
|
||||
$stT->execute([$idschool]);
|
||||
foreach ($stT->fetchAll() as $t) {
|
||||
$teachersById[(int) $t['idemployee']] = $t;
|
||||
}
|
||||
}
|
||||
|
||||
// Indirizzo della scuola (da schools), al posto del testo fisso
|
||||
$schoolAddress = implode(', ', array_filter([
|
||||
trim((string) ($schoolData['address'] ?? '')),
|
||||
trim((string) ($schoolData['city'] ?? '')),
|
||||
]));
|
||||
|
||||
if (isset($_GET['prev_month'])) {
|
||||
$currentMonthStart = preg_replace('/[^0-9\-]/', '', $_GET['prev_month']) . '-01';
|
||||
} elseif (isset($_GET['next_month'])) {
|
||||
@@ -56,9 +64,10 @@ $sqlSchedule = "SELECT serviceschedule.*, service.servicename, service.colorclas
|
||||
FROM serviceschedule
|
||||
LEFT JOIN service ON serviceschedule.idservice = service.idservice
|
||||
WHERE serviceschedule.dateschedule BETWEEN :start AND DATE_ADD(:end, INTERVAL 1 DAY)
|
||||
AND serviceschedule.idschool = :idschool
|
||||
ORDER BY serviceschedule.dateschedule";
|
||||
$stmtSchedule = $pdo->prepare($sqlSchedule);
|
||||
$stmtSchedule->execute([':start' => $currentMonthStart, ':end' => $currentMonthEnd]);
|
||||
$stmtSchedule->execute([':start' => $currentMonthStart, ':end' => $currentMonthEnd, ':idschool' => $idschool]);
|
||||
$scheduleRows = $stmtSchedule->fetchAll();
|
||||
|
||||
// Tipologie di classe presenti nel mese (per la barra filtri), con relativo colore
|
||||
@@ -81,11 +90,12 @@ if (!empty($scheduleIds)) {
|
||||
auth_users.first_name, auth_users.last_name, auth_users.id AS iduser
|
||||
FROM bookingclass
|
||||
LEFT JOIN auth_users ON bookingclass.iduser = auth_users.id
|
||||
WHERE bookingclass.idserviceschedule IN ($ph)
|
||||
WHERE bookingclass.idserviceschedule IN ($ph)
|
||||
AND bookingclass.idschool = ?
|
||||
AND bookingclass.status IN ('booked', 'pending', 'waiting')
|
||||
ORDER BY bookingclass.created_at, auth_users.last_name, auth_users.first_name";
|
||||
$stmtBookings = $pdo->prepare($sqlBookings);
|
||||
$stmtBookings->execute(array_values($scheduleIds));
|
||||
$stmtBookings->execute(array_merge(array_values($scheduleIds), [$idschool]));
|
||||
foreach ($stmtBookings->fetchAll() as $b) {
|
||||
$bookingsBySchedule[(int) $b['idserviceschedule']][] = $b;
|
||||
}
|
||||
@@ -649,7 +659,9 @@ $italianMonths = [
|
||||
<?php endif; ?>
|
||||
</h3>
|
||||
<p class="class-meta"><i class="far fa-clock me-1"></i><?php echo e($whenFmt); ?></p>
|
||||
<p class="class-meta"><i class="fas fa-location-dot me-1"></i>via Valassina 62/B Seregno - Sala Contesto Yoga</p>
|
||||
<?php if ($schoolAddress !== '') : ?>
|
||||
<p class="class-meta"><i class="fas fa-location-dot me-1"></i><?php echo e($schoolAddress); ?></p>
|
||||
<?php endif; ?>
|
||||
<?php if ($showTeacher) :
|
||||
$tch = $teachersById[(int) ($sch['idteacher'] ?? 0)] ?? null; ?>
|
||||
<p class="class-meta class-teacher">
|
||||
|
||||
+117
-36
@@ -1,17 +1,18 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* SchoolContext v2
|
||||
* Determina la scuola corrente dell'utente loggato, il suo ruolo nella scuola
|
||||
* e le scuole tra cui può passare (owner multi-scuola, superadmin).
|
||||
* SchoolContext v3
|
||||
* - Scuola corrente dell'utente loggato e ruolo nella scuola
|
||||
* - Scuole tra cui può passare (owner multi-scuola, superadmin)
|
||||
* - Vista attiva (superadmin | school | teacher | student): permette a chi ha
|
||||
* permessi alti di vedere l'app come un livello inferiore. Mai superiore.
|
||||
*
|
||||
* Uso (nella headscript):
|
||||
* Uso (headscript):
|
||||
* $schoolData = SchoolContext::resolve((int)$iduserlogin);
|
||||
* $idschool = SchoolContext::id();
|
||||
*
|
||||
* Ruoli scuola: owner | admin | teacher | student
|
||||
* Il superadmin di piattaforma (auth_users.is_superadmin = 'Y') può entrare
|
||||
* in qualsiasi scuola: se non ne è membro, il suo ruolo risulta 'superadmin'.
|
||||
* I controlli isSuperadmin()/isSchoolAdmin()/isStaff() tengono conto della
|
||||
* vista attiva. viewModes() e available() usano i permessi REALI.
|
||||
*/
|
||||
|
||||
if (!class_exists('DBHandlerSelect')) {
|
||||
@@ -29,13 +30,15 @@ class SchoolContext
|
||||
/** @var int */
|
||||
private static $userId = 0;
|
||||
|
||||
/** @var bool */
|
||||
/** @var bool permesso REALE di superadmin */
|
||||
private static $superadmin = false;
|
||||
|
||||
/** @var array scuole di cui l'utente è membro */
|
||||
private static $memberships = [];
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* ================================================================== */
|
||||
/* RISOLUZIONE SCUOLA */
|
||||
/* ================================================================== */
|
||||
|
||||
public static function resolve(int $userId): array
|
||||
{
|
||||
@@ -77,9 +80,7 @@ class SchoolContext
|
||||
return self::$school;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cambia scuola corrente. Ritorna false se l'utente non può accedervi.
|
||||
*/
|
||||
/** Cambia scuola corrente. False se l'utente non può accedervi. */
|
||||
public static function switchTo(int $idschool): bool
|
||||
{
|
||||
if ($idschool <= 0) {
|
||||
@@ -96,10 +97,7 @@ class SchoolContext
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scuole tra cui l'utente può scegliere (per il selettore in topbar).
|
||||
* Superadmin: tutte le scuole. Altri: solo quelle di cui è membro.
|
||||
*/
|
||||
/** Scuole selezionabili (permessi reali). Superadmin: tutte. */
|
||||
public static function available(): array
|
||||
{
|
||||
if (!self::$superadmin) {
|
||||
@@ -119,7 +117,70 @@ class SchoolContext
|
||||
return $all;
|
||||
}
|
||||
|
||||
/* --------------------------- getter -------------------------------- */
|
||||
/* ================================================================== */
|
||||
/* VISTA ATTIVA */
|
||||
/* ================================================================== */
|
||||
|
||||
/**
|
||||
* Viste consentite in base ai permessi REALI, dalla più alta alla più bassa.
|
||||
* @return array chiave => etichetta
|
||||
*/
|
||||
public static function viewModes(): array
|
||||
{
|
||||
$real = self::realRole();
|
||||
$modes = [];
|
||||
|
||||
if (self::$superadmin) {
|
||||
$modes['superadmin'] = 'Superadmin';
|
||||
}
|
||||
if (self::$superadmin || in_array($real, ['owner', 'admin'], true)) {
|
||||
$modes['school'] = 'Scuola';
|
||||
}
|
||||
if ($real === 'teacher') {
|
||||
$modes['teacher'] = 'Insegnante';
|
||||
}
|
||||
$modes['student'] = 'Allievo';
|
||||
|
||||
return $modes;
|
||||
}
|
||||
|
||||
/** Vista attiva (se quella in sessione non è consentita: la più alta) */
|
||||
public static function viewMode(): string
|
||||
{
|
||||
$modes = self::viewModes();
|
||||
$req = $_SESSION['view_mode'] ?? '';
|
||||
if (isset($modes[$req])) {
|
||||
return $req;
|
||||
}
|
||||
return (string) array_key_first($modes);
|
||||
}
|
||||
|
||||
public static function setViewMode(string $mode): bool
|
||||
{
|
||||
if (!isset(self::viewModes()[$mode])) {
|
||||
return false;
|
||||
}
|
||||
$_SESSION['view_mode'] = $mode;
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Pagina iniziale per la vista attiva */
|
||||
public static function landingPage(): string
|
||||
{
|
||||
switch (self::viewMode()) {
|
||||
case 'superadmin':
|
||||
return 'sa-schools.php';
|
||||
case 'school':
|
||||
case 'teacher':
|
||||
return 'adminpanel.php';
|
||||
default:
|
||||
return 'userpanel.php';
|
||||
}
|
||||
}
|
||||
|
||||
/* ================================================================== */
|
||||
/* GETTER E CONTROLLI (tengono conto della vista) */
|
||||
/* ================================================================== */
|
||||
|
||||
public static function id(): int
|
||||
{
|
||||
@@ -134,64 +195,85 @@ class SchoolContext
|
||||
return self::$school;
|
||||
}
|
||||
|
||||
/** owner | admin | teacher | student | superadmin */
|
||||
public static function role(): string
|
||||
/** Ruolo REALE nella scuola: owner|admin|teacher|student|superadmin */
|
||||
public static function realRole(): string
|
||||
{
|
||||
return self::get()['school_role'];
|
||||
}
|
||||
|
||||
public static function isSuperadmin(): bool
|
||||
/** Alias storico di realRole() */
|
||||
public static function role(): string
|
||||
{
|
||||
return self::realRole();
|
||||
}
|
||||
|
||||
public static function isRealSuperadmin(): bool
|
||||
{
|
||||
return self::$superadmin;
|
||||
}
|
||||
|
||||
public static function isSuperadmin(): bool
|
||||
{
|
||||
return self::viewMode() === 'superadmin';
|
||||
}
|
||||
|
||||
public static function isOwner(): bool
|
||||
{
|
||||
return self::role() === 'owner';
|
||||
return self::realRole() === 'owner' && self::isSchoolAdmin();
|
||||
}
|
||||
|
||||
/** Può gestire la scuola: owner, admin o superadmin */
|
||||
/** Gestisce la scuola (vista superadmin o scuola) */
|
||||
public static function isSchoolAdmin(): bool
|
||||
{
|
||||
return self::$superadmin || in_array(self::role(), ['owner', 'admin'], true);
|
||||
return in_array(self::viewMode(), ['superadmin', 'school'], true);
|
||||
}
|
||||
|
||||
/** Staff della scuola: amministratori + insegnanti */
|
||||
/** Staff: amministratori + insegnanti */
|
||||
public static function isStaff(): bool
|
||||
{
|
||||
return self::isSchoolAdmin() || self::role() === 'teacher';
|
||||
return self::isSchoolAdmin() || self::viewMode() === 'teacher';
|
||||
}
|
||||
|
||||
/** Blocca la pagina se l'utente non è amministratore della scuola */
|
||||
public static function requireSchoolAdmin(): void
|
||||
{
|
||||
if (!self::isSchoolAdmin()) {
|
||||
http_response_code(403);
|
||||
die('Accesso non autorizzato.');
|
||||
self::deny();
|
||||
}
|
||||
}
|
||||
|
||||
public static function requireStaff(): void
|
||||
{
|
||||
if (!self::isStaff()) {
|
||||
self::deny();
|
||||
}
|
||||
}
|
||||
|
||||
/** Blocca la pagina se l'utente non è superadmin */
|
||||
public static function requireSuperadmin(): void
|
||||
{
|
||||
if (!self::$superadmin) {
|
||||
http_response_code(403);
|
||||
die('Accesso non autorizzato.');
|
||||
if (!self::isSuperadmin()) {
|
||||
self::deny();
|
||||
}
|
||||
}
|
||||
|
||||
/* --------------------------- interni ------------------------------- */
|
||||
/* ================================================================== */
|
||||
/* INTERNI */
|
||||
/* ================================================================== */
|
||||
|
||||
private static function deny(): void
|
||||
{
|
||||
http_response_code(403);
|
||||
$home = self::$school ? self::landingPage() : 'userpanel.php';
|
||||
die('Accesso non autorizzato con la vista attuale. <a href="' . $home . '">Torna alla pagina iniziale</a>');
|
||||
}
|
||||
|
||||
private static function pick(int $requested): ?array
|
||||
{
|
||||
// 1) scuola richiesta e l'utente ne è membro
|
||||
foreach (self::$memberships as $m) {
|
||||
if ((int) $m['idschool'] === $requested) {
|
||||
return $m;
|
||||
}
|
||||
}
|
||||
|
||||
// 2) superadmin: può entrare in qualunque scuola esistente
|
||||
if (self::$superadmin) {
|
||||
$pdo = self::pdo();
|
||||
if ($requested > 0) {
|
||||
@@ -214,7 +296,6 @@ class SchoolContext
|
||||
return null;
|
||||
}
|
||||
|
||||
// 3) prima scuola di cui è membro
|
||||
return self::$memberships[0] ?? null;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* SchoolSettings
|
||||
* Impostazioni della scuola (tabella `option`, una riga per scuola).
|
||||
* Sostituisce tutti i "SELECT * FROM `option` LIMIT 1".
|
||||
*
|
||||
* Uso:
|
||||
* $timetocancel = (int) SchoolSettings::get('maxbeforetimecancell', 24);
|
||||
* $all = SchoolSettings::all();
|
||||
* SchoolSettings::save(['show_teacher' => 'Y']);
|
||||
*
|
||||
* Nei cron (senza sessione) passare l'idschool esplicito:
|
||||
* SchoolSettings::get('show_teacher', 'N', $idschool);
|
||||
*/
|
||||
|
||||
if (!class_exists('DBHandlerSelect')) {
|
||||
require_once __DIR__ . '/db-functions.php';
|
||||
}
|
||||
|
||||
class SchoolSettings
|
||||
{
|
||||
/** @var array cache per scuola: idschool => riga option */
|
||||
private static $cache = [];
|
||||
|
||||
/** Colonne che non si possono modificare con save() */
|
||||
const PROTECTED_COLUMNS = ['idption', 'idschool'];
|
||||
|
||||
/** Tutte le impostazioni della scuola (riga creata se mancante) */
|
||||
public static function all(?int $idschool = null): array
|
||||
{
|
||||
$idschool = self::schoolId($idschool);
|
||||
|
||||
if (!isset(self::$cache[$idschool])) {
|
||||
$pdo = self::pdo();
|
||||
$st = $pdo->prepare("SELECT * FROM `option` WHERE idschool = ? LIMIT 1");
|
||||
$st->execute([$idschool]);
|
||||
$row = $st->fetch();
|
||||
|
||||
if (!$row) {
|
||||
// Nuova scuola: crea la riga con i default delle colonne
|
||||
try {
|
||||
$pdo->prepare("INSERT INTO `option` (idschool) VALUES (?)")->execute([$idschool]);
|
||||
$st->execute([$idschool]);
|
||||
$row = $st->fetch();
|
||||
} catch (PDOException $e) {
|
||||
error_log('SchoolSettings: impossibile creare option per scuola ' . $idschool . ': ' . $e->getMessage());
|
||||
$row = [];
|
||||
}
|
||||
}
|
||||
|
||||
self::$cache[$idschool] = $row ?: [];
|
||||
}
|
||||
|
||||
return self::$cache[$idschool];
|
||||
}
|
||||
|
||||
/** Singola impostazione, con default se mancante o NULL */
|
||||
public static function get(string $key, $default = null, ?int $idschool = null)
|
||||
{
|
||||
$all = self::all($idschool);
|
||||
return (array_key_exists($key, $all) && $all[$key] !== null) ? $all[$key] : $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Salva una o più impostazioni. Accetta solo colonne esistenti in `option`
|
||||
* (le altre chiavi vengono ignorate).
|
||||
*/
|
||||
public static function save(array $data, ?int $idschool = null): void
|
||||
{
|
||||
$idschool = self::schoolId($idschool);
|
||||
$current = self::all($idschool);
|
||||
|
||||
$set = [];
|
||||
$params = [];
|
||||
foreach ($data as $col => $val) {
|
||||
if (!array_key_exists($col, $current) || in_array($col, self::PROTECTED_COLUMNS, true)) {
|
||||
continue;
|
||||
}
|
||||
$set[] = "`$col` = ?";
|
||||
$params[] = $val;
|
||||
}
|
||||
if (empty($set)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$params[] = $idschool;
|
||||
self::pdo()->prepare("UPDATE `option` SET " . implode(', ', $set) . " WHERE idschool = ?")
|
||||
->execute($params);
|
||||
|
||||
unset(self::$cache[$idschool]);
|
||||
}
|
||||
|
||||
private static function schoolId(?int $idschool): int
|
||||
{
|
||||
if ($idschool !== null && $idschool > 0) {
|
||||
return $idschool;
|
||||
}
|
||||
return SchoolContext::id();
|
||||
}
|
||||
|
||||
private static function pdo(): PDO
|
||||
{
|
||||
return DBHandlerSelect::getInstance()->getConnection();
|
||||
}
|
||||
}
|
||||
@@ -55,11 +55,14 @@ $_SESSION["nameuser"] = $nameuser;
|
||||
$_SESSION["emailuser"] = $emailuser;
|
||||
// --- Scuola corrente (SaaS) ---
|
||||
require_once('class/SchoolContext.php');
|
||||
require_once('class/SchoolSettings.php');
|
||||
$schoolData = SchoolContext::resolve((int) $iduserlogin);
|
||||
$idschool = SchoolContext::id();
|
||||
$schoolname = $schoolData['name'];
|
||||
$schoolrole = SchoolContext::role(); // owner|admin|teacher|student|superadmin
|
||||
$isschooladmin = SchoolContext::isSchoolAdmin();
|
||||
$schoolrole = SchoolContext::realRole(); // ruolo reale: owner|admin|teacher|student|superadmin
|
||||
$viewmode = SchoolContext::viewMode(); // vista attiva: superadmin|school|teacher|student
|
||||
$isschooladmin = SchoolContext::isSchoolAdmin(); // tiene conto della vista
|
||||
$isstaff = SchoolContext::isStaff();
|
||||
$issuperadmin = SchoolContext::isSuperadmin();
|
||||
// ------------------------------
|
||||
if (!isset($_SESSION["tempcode"])) {
|
||||
|
||||
+174
-146
@@ -1,9 +1,120 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Sidebar - menu basato sulla VISTA attiva (SchoolContext::viewMode()):
|
||||
* superadmin -> Piattaforma + gestione scuola
|
||||
* school -> gestione scuola (owner/admin)
|
||||
* teacher -> classi e prenotazioni
|
||||
* student -> prenotazioni e ordini personali
|
||||
* "Il mio account" è sempre visibile.
|
||||
*/
|
||||
$__mode = SchoolContext::viewMode();
|
||||
$__modes = SchoolContext::viewModes();
|
||||
$__isSuper = SchoolContext::isSuperadmin();
|
||||
$__isAdmin = SchoolContext::isSchoolAdmin();
|
||||
$__isStaff = SchoolContext::isStaff();
|
||||
$__home = SchoolContext::landingPage();
|
||||
$__page = basename($_SERVER['PHP_SELF']);
|
||||
|
||||
if (empty($_SESSION['csrf_switch'])) {
|
||||
$_SESSION['csrf_switch'] = bin2hex(random_bytes(16));
|
||||
}
|
||||
|
||||
$__modeIcons = [
|
||||
'superadmin' => 'bx bx-crown',
|
||||
'school' => 'bx bxs-school',
|
||||
'teacher' => 'bx bx-chalkboard',
|
||||
'student' => 'bx bx-user',
|
||||
];
|
||||
|
||||
/** Voce di menu con evidenziazione della pagina attiva */
|
||||
if (!function_exists('sb_item')) {
|
||||
function sb_item(string $href, string $icon, string $label, string $current): void
|
||||
{
|
||||
$active = ($href === $current) ? 'mm-active' : '';
|
||||
echo '<li class="' . $active . '"><a href="' . $href . '" class="' . ($active ? 'active' : '') . '">'
|
||||
. '<i class="' . $icon . ' icon nav-icon"></i>'
|
||||
. '<span class="menu-item">' . htmlspecialchars($label, ENT_QUOTES, 'UTF-8') . '</span></a></li>';
|
||||
}
|
||||
}
|
||||
?>
|
||||
<!-- ========== Left Sidebar Start ========== -->
|
||||
<style>
|
||||
.view-switcher {
|
||||
padding: 14px 16px 6px;
|
||||
}
|
||||
|
||||
.view-switcher .vs-label {
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: .05em;
|
||||
opacity: .6;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.view-switcher form {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
background: rgba(0, 0, 0, .05);
|
||||
border-radius: 10px;
|
||||
padding: 4px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.view-switcher button {
|
||||
flex: 1;
|
||||
border: none;
|
||||
background: transparent;
|
||||
border-radius: 7px;
|
||||
padding: 6px 4px;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
color: inherit;
|
||||
opacity: .7;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
.view-switcher button i {
|
||||
font-size: 17px;
|
||||
}
|
||||
|
||||
.view-switcher button:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.view-switcher button.active {
|
||||
background: #1ebf73;
|
||||
color: #fff;
|
||||
opacity: 1;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.view-switcher .vs-school {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
margin-top: 8px;
|
||||
opacity: .75;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
body[data-sidebar-size="sm"] .view-switcher {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="vertical-menu">
|
||||
|
||||
<!-- LOGO -->
|
||||
<div class="navbar-brand-box">
|
||||
<a href="index.html" class="logo logo-dark">
|
||||
<a href="<?php echo $__home; ?>" class="logo logo-dark">
|
||||
<span class="logo-sm">
|
||||
<img src="assets/images/logo-dark-sm.png" alt="" height="30">
|
||||
</span>
|
||||
@@ -12,7 +123,7 @@
|
||||
</span>
|
||||
</a>
|
||||
|
||||
<a href="index.html" class="logo logo-light">
|
||||
<a href="<?php echo $__home; ?>" class="logo logo-light">
|
||||
<span class="logo-lg">
|
||||
<img src="assets/images/logo-light.png" alt="" height="30">
|
||||
</span>
|
||||
@@ -28,169 +139,86 @@
|
||||
|
||||
<div data-simplebar class="sidebar-menu-scroll">
|
||||
|
||||
<?php if (count($__modes) > 1) : ?>
|
||||
<!-- Selettore vista -->
|
||||
<div class="view-switcher">
|
||||
<div class="vs-label">Vista</div>
|
||||
<form method="post" action="switchview.php">
|
||||
<input type="hidden" name="token" value="<?php echo $_SESSION['csrf_switch']; ?>">
|
||||
<?php foreach ($__modes as $__k => $__lbl) : ?>
|
||||
<button type="submit" name="mode" value="<?php echo $__k; ?>"
|
||||
class="<?php echo $__k === $__mode ? 'active' : ''; ?>"
|
||||
<?php echo $__k === $__mode ? 'disabled' : ''; ?>
|
||||
title="Vedi come <?php echo htmlspecialchars($__lbl, ENT_QUOTES, 'UTF-8'); ?>">
|
||||
<i class="<?php echo $__modeIcons[$__k] ?? 'bx bx-show'; ?>"></i>
|
||||
<?php echo htmlspecialchars($__lbl, ENT_QUOTES, 'UTF-8'); ?>
|
||||
</button>
|
||||
<?php endforeach; ?>
|
||||
</form>
|
||||
<div class="vs-school" title="Scuola corrente">
|
||||
<i class="bx bx-buildings"></i> <?php echo htmlspecialchars(SchoolContext::get()['name'], ENT_QUOTES, 'UTF-8'); ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<!--- Sidemenu -->
|
||||
<div id="sidebar-menu">
|
||||
<!-- Left Menu Start -->
|
||||
<ul class="metismenu list-unstyled" id="side-menu">
|
||||
|
||||
<li class="menu-title" data-key="t-menu">Dashboard</li>
|
||||
<?php if ($__isSuper) : ?>
|
||||
<!-- Piattaforma -->
|
||||
<li class="menu-title">Piattaforma</li>
|
||||
<?php sb_item('sa-schools.php', 'bx bx-buildings', 'Scuole', $__page); ?>
|
||||
<?php endif; ?>
|
||||
|
||||
<li>
|
||||
<a href="userpanel.php" class="">
|
||||
<i class="bx bx-calendar-event icon nav-icon"></i>
|
||||
<span class="menu-item" data-key="t-email">Prenotazioni</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="orders.php" class="">
|
||||
<i class="bx bx-detail icon nav-icon"></i>
|
||||
<span class="menu-item" data-key="t-email">Ordini</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="menu-title" data-key="t-applications">Profilo Utente</li>
|
||||
|
||||
<li>
|
||||
<a href="userprofile.php" class="">
|
||||
<i class="bx bx-user-circle icon nav-icon"></i>
|
||||
<span class="menu-item" data-key="t-email">Profilo utente</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="options.php" class="">
|
||||
<i class="bx bx-cog icon nav-icon"></i>
|
||||
<span class="menu-item" data-key="t-email">Opzioni</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="userprofiledoc.php" class="">
|
||||
<i class="bx bxs-file-doc icon nav-icon"></i>
|
||||
<span class="menu-item" data-key="t-email">Certificati</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<?php if (Auth::user()->hasRole('Admin')): ?>
|
||||
<?php if (!$__isStaff) : ?>
|
||||
<!-- Area allievo -->
|
||||
<li class="menu-title">Le mie lezioni</li>
|
||||
<?php sb_item('userpanel.php', 'bx bx-calendar-event', 'Prenotazioni', $__page); ?>
|
||||
<?php sb_item('orders.php', 'bx bx-detail', 'Ordini', $__page); ?>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($__isStaff) : ?>
|
||||
<!-- Classi & Prenotazioni -->
|
||||
<li class="menu-title" data-key="t-admin-classi">Classi & Prenotazioni</li>
|
||||
|
||||
<li>
|
||||
<a href="adminpanel.php" class="">
|
||||
<i class="bx bxs-school icon nav-icon"></i>
|
||||
<span class="menu-item">Classi</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="admin-services.php" class="">
|
||||
<i class="bx bx-edit-alt icon nav-icon"></i>
|
||||
<span class="menu-item">Servizi</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="pendingbookedclass.php" class="">
|
||||
<i class="bx bx-time-five icon nav-icon"></i>
|
||||
<span class="menu-item">Pending/Waiting</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="dayoff.php" class="">
|
||||
<i class="bx bx-power-off icon nav-icon"></i>
|
||||
<span class="menu-item">Day Off</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="menu-title">Classi & Prenotazioni</li>
|
||||
<?php sb_item('adminpanel.php', 'bx bxs-school', 'Classi', $__page); ?>
|
||||
<?php if ($__isAdmin) sb_item('admin-services.php', 'bx bx-edit-alt', 'Servizi', $__page); ?>
|
||||
<?php sb_item('pendingbookedclass.php', 'bx bx-time-five', 'Pending/Waiting', $__page); ?>
|
||||
<?php if ($__isAdmin) sb_item('dayoff.php', 'bx bx-power-off', 'Day Off', $__page); ?>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($__isAdmin) : ?>
|
||||
<!-- Eventi -->
|
||||
<li class="menu-title" data-key="t-admin-eventi">Eventi</li>
|
||||
<li class="menu-title">Eventi</li>
|
||||
<?php sb_item('events.php', 'bx bx-calendar-star', 'Eventi', $__page); ?>
|
||||
<?php sb_item('event_locations.php', 'bx bx-map', 'Sedi', $__page); ?>
|
||||
<?php sb_item('event_options.php', 'bx bx-cog', 'Opzioni Eventi', $__page); ?>
|
||||
|
||||
<li>
|
||||
<a href="events.php" class="">
|
||||
<i class="bx bx-calendar-star icon nav-icon"></i>
|
||||
<span class="menu-item">Eventi</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="event_locations.php" class="">
|
||||
<i class="bx bx-map icon nav-icon"></i>
|
||||
<span class="menu-item">Sedi</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="event_options.php" class="">
|
||||
<i class="bx bx-map icon nav-icon"></i>
|
||||
<span class="menu-item">Opzione Eventi</span>
|
||||
</a>
|
||||
</li>
|
||||
<!-- Ordini -->
|
||||
<li class="menu-title" data-key="t-admin-ordini">Ordini</li>
|
||||
|
||||
<li>
|
||||
<a href="orderbooklist.php" class="">
|
||||
<i class="bx bx-list-check icon nav-icon"></i>
|
||||
<span class="menu-item">Order Pending</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="ordersummary.php" class="">
|
||||
<i class="bx bx-receipt icon nav-icon"></i>
|
||||
<span class="menu-item">Sommario Ordini</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="menu-title">Ordini</li>
|
||||
<?php sb_item('orderbooklist.php', 'bx bx-list-check', 'Ordini Pending', $__page); ?>
|
||||
<?php sb_item('ordersummary.php', 'bx bx-receipt', 'Sommario Ordini', $__page); ?>
|
||||
|
||||
<!-- Report & Certificati -->
|
||||
<li class="menu-title" data-key="t-admin-report">Report & Certificati</li>
|
||||
<li class="menu-title">Report & Certificati</li>
|
||||
<?php sb_item('situationusers.php', 'bx bx-bar-chart-alt-2', 'Situazione', $__page); ?>
|
||||
<?php sb_item('certificati.php', 'bx bx-certification', 'Certificati', $__page); ?>
|
||||
<?php sb_item('statistiche.php', 'bx bx-line-chart', 'Statistiche', $__page); ?>
|
||||
|
||||
<li>
|
||||
<a href="situationusers.php" class="">
|
||||
<i class="bx bx-bar-chart-alt-2 icon nav-icon"></i>
|
||||
<span class="menu-item">Situazione</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="certificati.php" class="">
|
||||
<i class="bx bx-certification icon nav-icon"></i>
|
||||
<span class="menu-item">Certificati</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="statistiche.php" class="">
|
||||
<i class="bx bx-certification icon nav-icon"></i>
|
||||
<span class="menu-item">Statistiche</span>
|
||||
</a>
|
||||
</li>
|
||||
<!-- Impostazioni -->
|
||||
<li class="menu-title" data-key="t-admin-settings">Impostazioni</li>
|
||||
|
||||
<li>
|
||||
<a href="optionsschool.php" class="">
|
||||
<i class="bx bx-slider-alt icon nav-icon"></i>
|
||||
<span class="menu-item">Opzioni Scuola</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="insegnanti.php" class="">
|
||||
<i class="bx bx-slider-alt icon nav-icon"></i>
|
||||
<span class="menu-item">Insegnanti</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<!-- Template (demo) -->
|
||||
<li class="menu-title" data-key="t-applications">Template</li>
|
||||
|
||||
<li>
|
||||
<a href="javascript: void(0);" class="has-arrow">
|
||||
<i class="bx bx-table icon nav-icon"></i>
|
||||
<span class="menu-item" data-key="t-tables">Tables</span>
|
||||
</a>
|
||||
<ul class="sub-menu" aria-expanded="false">
|
||||
<li><a href="copiaintegra/dist/tables-basic.html" data-key="t-basic-tables">Basic Tables</a></li>
|
||||
<li><a href="copiaintegra/dist/tables-advanced.html" data-key="t-advanced-tables">Advance Tables</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="menu-title">Impostazioni scuola</li>
|
||||
<?php sb_item('optionsschool.php', 'bx bx-slider-alt', 'Opzioni Scuola', $__page); ?>
|
||||
<?php sb_item('insegnanti.php', 'bx bx-group', 'Insegnanti', $__page); ?>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Account personale: sempre visibile -->
|
||||
<li class="menu-title">Il mio account</li>
|
||||
<?php sb_item('userprofile.php', 'bx bx-user-circle', 'Profilo utente', $__page); ?>
|
||||
<?php sb_item('userprofiledoc.php', 'bx bxs-file-doc', 'Certificati', $__page); ?>
|
||||
<?php sb_item('options.php', 'bx bx-cog', 'Opzioni', $__page); ?>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
<!-- Sidebar -->
|
||||
</div>
|
||||
</div>
|
||||
<!-- Left Sidebar End -->
|
||||
@@ -0,0 +1,517 @@
|
||||
<?php
|
||||
require_once('include/headscript.php');
|
||||
|
||||
/**
|
||||
* SUPERADMIN - Membri di una scuola (school_user)
|
||||
* Aggiunta per email, cambio ruolo, attiva/disattiva, rimozione.
|
||||
* Protezione: una scuola non può restare senza owner attivo.
|
||||
*/
|
||||
SchoolContext::requireSuperadmin();
|
||||
$pdo = DBHandlerSelect::getInstance()->getConnection();
|
||||
|
||||
if (!function_exists('e')) {
|
||||
function e($value): string
|
||||
{
|
||||
return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($_SESSION['csrf_sa'])) {
|
||||
$_SESSION['csrf_sa'] = bin2hex(random_bytes(16));
|
||||
}
|
||||
$csrf = $_SESSION['csrf_sa'];
|
||||
|
||||
$roles = [
|
||||
'owner' => 'Titolare',
|
||||
'admin' => 'Admin',
|
||||
'teacher' => 'Insegnante',
|
||||
'student' => 'Allievo',
|
||||
];
|
||||
|
||||
$sid = (int) ($_GET['id'] ?? $_POST['idschool'] ?? 0);
|
||||
$st = $pdo->prepare("SELECT * FROM schools WHERE idschool = ?");
|
||||
$st->execute([$sid]);
|
||||
$school = $st->fetch();
|
||||
if (!$school) {
|
||||
header('Location: sa-schools.php');
|
||||
exit;
|
||||
}
|
||||
$back = 'sa-school-members.php?id=' . $sid;
|
||||
|
||||
/** Owner attivi della scuola, escluso eventualmente un membro */
|
||||
function activeOwnersExcept(PDO $pdo, int $sid, int $exceptId): int
|
||||
{
|
||||
$q = $pdo->prepare(
|
||||
"SELECT COUNT(*) FROM school_user
|
||||
WHERE idschool = ? AND school_role = 'owner' AND active = 'Y' AND idschooluser <> ?"
|
||||
);
|
||||
$q->execute([$sid, $exceptId]);
|
||||
return (int) $q->fetchColumn();
|
||||
}
|
||||
|
||||
/** Riga membro (solo se appartiene a questa scuola) */
|
||||
function memberRow(PDO $pdo, int $sid, int $idsu): ?array
|
||||
{
|
||||
$q = $pdo->prepare("SELECT * FROM school_user WHERE idschooluser = ? AND idschool = ?");
|
||||
$q->execute([$idsu, $sid]);
|
||||
return $q->fetch() ?: null;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
* AZIONI
|
||||
* -------------------------------------------------------------------- */
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
if (!hash_equals($csrf, $_POST['token'] ?? '')) {
|
||||
http_response_code(400);
|
||||
die('Richiesta non valida.');
|
||||
}
|
||||
|
||||
$action = $_POST['action'] ?? '';
|
||||
$idsu = (int) ($_POST['idschooluser'] ?? 0);
|
||||
|
||||
try {
|
||||
if ($action === 'add') {
|
||||
$email = trim((string) ($_POST['email'] ?? ''));
|
||||
$role = $_POST['role'] ?? 'student';
|
||||
if (!isset($roles[$role]) || $email === '') {
|
||||
header("Location: $back&error=data");
|
||||
exit;
|
||||
}
|
||||
$u = $pdo->prepare("SELECT id FROM auth_users WHERE email = ? LIMIT 1");
|
||||
$u->execute([$email]);
|
||||
$uid = (int) $u->fetchColumn();
|
||||
if ($uid === 0) {
|
||||
header("Location: $back&error=notfound");
|
||||
exit;
|
||||
}
|
||||
$pdo->prepare(
|
||||
"INSERT INTO school_user (user_id, idschool, school_role, active)
|
||||
VALUES (?, ?, ?, 'Y')
|
||||
ON DUPLICATE KEY UPDATE school_role = ?, active = 'Y'"
|
||||
)->execute([$uid, $sid, $role, $role]);
|
||||
header("Location: $back&msg=added");
|
||||
exit;
|
||||
}
|
||||
|
||||
$row = $idsu > 0 ? memberRow($pdo, $sid, $idsu) : null;
|
||||
if (!$row) {
|
||||
header("Location: $back&error=data");
|
||||
exit;
|
||||
}
|
||||
$isActiveOwner = ($row['school_role'] === 'owner' && $row['active'] === 'Y');
|
||||
|
||||
if ($action === 'role') {
|
||||
$role = $_POST['role'] ?? '';
|
||||
if (!isset($roles[$role])) {
|
||||
header("Location: $back&error=data");
|
||||
exit;
|
||||
}
|
||||
if ($isActiveOwner && $role !== 'owner' && activeOwnersExcept($pdo, $sid, $idsu) === 0) {
|
||||
header("Location: $back&error=lastowner");
|
||||
exit;
|
||||
}
|
||||
$pdo->prepare("UPDATE school_user SET school_role = ? WHERE idschooluser = ? AND idschool = ?")
|
||||
->execute([$role, $idsu, $sid]);
|
||||
header("Location: $back&msg=role");
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($action === 'toggle') {
|
||||
if ($isActiveOwner && activeOwnersExcept($pdo, $sid, $idsu) === 0) {
|
||||
header("Location: $back&error=lastowner");
|
||||
exit;
|
||||
}
|
||||
$pdo->prepare("UPDATE school_user SET active = IF(active = 'Y', 'N', 'Y') WHERE idschooluser = ? AND idschool = ?")
|
||||
->execute([$idsu, $sid]);
|
||||
header("Location: $back&msg=toggled");
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($action === 'remove') {
|
||||
if ($isActiveOwner && activeOwnersExcept($pdo, $sid, $idsu) === 0) {
|
||||
header("Location: $back&error=lastowner");
|
||||
exit;
|
||||
}
|
||||
$pdo->prepare("DELETE FROM school_user WHERE idschooluser = ? AND idschool = ?")
|
||||
->execute([$idsu, $sid]);
|
||||
header("Location: $back&msg=removed");
|
||||
exit;
|
||||
}
|
||||
} catch (PDOException $ex) {
|
||||
error_log('sa-school-members: ' . $ex->getMessage());
|
||||
header("Location: $back&error=db");
|
||||
exit;
|
||||
}
|
||||
|
||||
header("Location: $back");
|
||||
exit;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
* ELENCO (filtri: ruolo, ricerca)
|
||||
* -------------------------------------------------------------------- */
|
||||
$fRole = $_GET['role'] ?? '';
|
||||
$fQ = trim((string) ($_GET['q'] ?? ''));
|
||||
|
||||
$where = ["su.idschool = ?"];
|
||||
$params = [$sid];
|
||||
if (isset($roles[$fRole])) {
|
||||
$where[] = "su.school_role = ?";
|
||||
$params[] = $fRole;
|
||||
}
|
||||
if ($fQ !== '') {
|
||||
$where[] = "(u.first_name LIKE ? OR u.last_name LIKE ? OR u.email LIKE ?)";
|
||||
$like = '%' . $fQ . '%';
|
||||
array_push($params, $like, $like, $like);
|
||||
}
|
||||
|
||||
$sql = "SELECT su.*, u.first_name, u.last_name, u.email
|
||||
FROM school_user su
|
||||
JOIN auth_users u ON u.id = su.user_id
|
||||
WHERE " . implode(' AND ', $where) . "
|
||||
ORDER BY FIELD(su.school_role, 'owner', 'admin', 'teacher', 'student'), u.last_name, u.first_name
|
||||
LIMIT 300";
|
||||
$stm = $pdo->prepare($sql);
|
||||
$stm->execute($params);
|
||||
$members = $stm->fetchAll();
|
||||
|
||||
$cnt = $pdo->prepare("SELECT school_role, COUNT(*) AS n FROM school_user WHERE idschool = ? GROUP BY school_role");
|
||||
$cnt->execute([$sid]);
|
||||
$counts = array_column($cnt->fetchAll(), 'n', 'school_role');
|
||||
|
||||
$msgMap = [
|
||||
'added' => 'Membro aggiunto.',
|
||||
'role' => 'Ruolo aggiornato.',
|
||||
'toggled' => 'Stato del membro aggiornato.',
|
||||
'removed' => 'Membro rimosso dalla scuola.',
|
||||
];
|
||||
$errMap = [
|
||||
'data' => 'Dati non validi.',
|
||||
'notfound' => 'Nessun utente registrato con questa email. L\'utente deve prima registrarsi.',
|
||||
'lastowner' => 'Operazione bloccata: la scuola resterebbe senza un titolare attivo. Nomina prima un altro owner.',
|
||||
'db' => 'Errore database, operazione non eseguita.',
|
||||
];
|
||||
$roleColors = [
|
||||
'owner' => ['#fff4d6', '#8a6d00'],
|
||||
'admin' => ['#e8f1fb', '#2980b9'],
|
||||
'teacher' => ['#f1e8fb', '#8e44ad'],
|
||||
'student' => ['#e3f2ec', '#1a7f52'],
|
||||
];
|
||||
?>
|
||||
<!doctype html>
|
||||
<html lang="it">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>YogiBook - Membri <?php echo e($school['name']); ?></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;
|
||||
}
|
||||
|
||||
.count-chips {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.count-chip {
|
||||
border: 1px solid #e2e4e9;
|
||||
background: #fff;
|
||||
color: #374151;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
padding: 6px 14px;
|
||||
border-radius: 20px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.count-chip.active,
|
||||
.count-chip:hover {
|
||||
background: #1ebf73;
|
||||
border-color: #1ebf73;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.member-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 10px 14px;
|
||||
border-radius: 10px;
|
||||
background: #f8fafc;
|
||||
border: 1px solid #eef0f3;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.member-row.inactive {
|
||||
opacity: .55;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
flex: 0 0 38px;
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
border-radius: 50%;
|
||||
background: #E1D0B8;
|
||||
color: #5a4630;
|
||||
font-weight: 800;
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.member-info {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.member-name {
|
||||
font-weight: 600;
|
||||
color: #1f2937;
|
||||
}
|
||||
|
||||
.member-email {
|
||||
font-size: 12px;
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
.role-pill {
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
padding: 2px 9px;
|
||||
border-radius: 20px;
|
||||
margin-left: 6px;
|
||||
}
|
||||
|
||||
.member-actions {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.member-actions select {
|
||||
font-size: 12px;
|
||||
padding: 4px 8px;
|
||||
height: auto;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.btn-soft {
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
padding: 6px 11px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.btn-soft.toggle {
|
||||
background: #eef2f7;
|
||||
color: #374151;
|
||||
}
|
||||
|
||||
.btn-soft.remove {
|
||||
background: #fdecea;
|
||||
color: #c0392b;
|
||||
}
|
||||
</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">Membri: <?php echo e($school['name']); ?></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">
|
||||
|
||||
<a href="sa-schools.php" class="d-inline-block mb-3"><i class="fas fa-arrow-left me-1"></i> Torna alle scuole</a>
|
||||
|
||||
<?php if (isset($_GET['msg'], $msgMap[$_GET['msg']])) : ?>
|
||||
<div class="alert alert-success"><?php echo e($msgMap[$_GET['msg']]); ?></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; ?>
|
||||
|
||||
<!-- AGGIUNGI MEMBRO -->
|
||||
<div class="sa-card">
|
||||
<h5><i class="fas fa-user-plus me-1" style="color:#1ebf73;"></i> Aggiungi membro</h5>
|
||||
<form method="post" action="sa-school-members.php" class="row g-2 align-items-end">
|
||||
<input type="hidden" name="token" value="<?php echo e($csrf); ?>">
|
||||
<input type="hidden" name="action" value="add">
|
||||
<input type="hidden" name="idschool" value="<?php echo $sid; ?>">
|
||||
<div class="col-md-6">
|
||||
<label>Email utente registrato</label>
|
||||
<input type="email" name="email" class="form-control" required>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label>Ruolo</label>
|
||||
<select name="role" class="form-select">
|
||||
<?php foreach ($roles as $k => $lbl) : ?>
|
||||
<option value="<?php echo $k; ?>" <?php echo $k === 'student' ? 'selected' : ''; ?>><?php echo e($lbl); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<button type="submit" class="btn btn-primary w-100"><i class="fas fa-plus me-1"></i> Aggiungi</button>
|
||||
</div>
|
||||
</form>
|
||||
<small class="text-muted d-block mt-2">Se l'utente è già membro, viene aggiornato il ruolo e riattivato.</small>
|
||||
</div>
|
||||
|
||||
<!-- ELENCO MEMBRI -->
|
||||
<div class="sa-card">
|
||||
<h5><i class="fas fa-users me-1" style="color:#1ebf73;"></i> Membri</h5>
|
||||
|
||||
<div class="count-chips">
|
||||
<a class="count-chip <?php echo $fRole === '' ? 'active' : ''; ?>" href="<?php echo $back; ?>">Tutti (<?php echo array_sum($counts); ?>)</a>
|
||||
<?php foreach ($roles as $k => $lbl) : ?>
|
||||
<a class="count-chip <?php echo $fRole === $k ? 'active' : ''; ?>" href="<?php echo $back; ?>&role=<?php echo $k; ?>"><?php echo e($lbl); ?> (<?php echo (int) ($counts[$k] ?? 0); ?>)</a>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
|
||||
<form method="get" action="sa-school-members.php" class="row g-2 mb-3">
|
||||
<input type="hidden" name="id" value="<?php echo $sid; ?>">
|
||||
<input type="hidden" name="role" value="<?php echo e($fRole); ?>">
|
||||
<div class="col-md-6">
|
||||
<input type="text" name="q" class="form-control" placeholder="Cerca per nome, cognome o email" value="<?php echo e($fQ); ?>">
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<button class="btn btn-light w-100"><i class="fas fa-search"></i> Cerca</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<?php if (empty($members)) : ?>
|
||||
<p class="text-center text-muted py-3">Nessun membro trovato.</p>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php foreach ($members as $mb) :
|
||||
$idsu = (int) $mb['idschooluser'];
|
||||
$full = trim($mb['first_name'] . ' ' . $mb['last_name']);
|
||||
$ini = mb_substr($mb['first_name'] ?? '', 0, 1) . mb_substr($mb['last_name'] ?? '', 0, 1);
|
||||
$rc = $roleColors[$mb['school_role']] ?? ['#eef2f7', '#374151'];
|
||||
$active = $mb['active'] === 'Y';
|
||||
?>
|
||||
<div class="member-row <?php echo $active ? '' : 'inactive'; ?>">
|
||||
<div class="avatar"><?php echo e($ini ?: '?'); ?></div>
|
||||
<div class="member-info">
|
||||
<span class="member-name"><?php echo e($full ?: '(senza nome)'); ?></span>
|
||||
<span class="role-pill" style="background:<?php echo $rc[0]; ?>;color:<?php echo $rc[1]; ?>;"><?php echo e($roles[$mb['school_role']] ?? $mb['school_role']); ?></span>
|
||||
<?php if (!$active) : ?><span class="role-pill" style="background:#1f2937;color:#fff;">Disattivato</span><?php endif; ?>
|
||||
<div class="member-email"><?php echo e($mb['email']); ?></div>
|
||||
</div>
|
||||
<div class="member-actions">
|
||||
<form method="post" action="sa-school-members.php" class="m-0">
|
||||
<input type="hidden" name="token" value="<?php echo e($csrf); ?>">
|
||||
<input type="hidden" name="action" value="role">
|
||||
<input type="hidden" name="idschool" value="<?php echo $sid; ?>">
|
||||
<input type="hidden" name="idschooluser" value="<?php echo $idsu; ?>">
|
||||
<select name="role" class="form-select" onchange="this.form.submit()">
|
||||
<?php foreach ($roles as $k => $lbl) : ?>
|
||||
<option value="<?php echo $k; ?>" <?php echo $k === $mb['school_role'] ? 'selected' : ''; ?>><?php echo e($lbl); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</form>
|
||||
<form method="post" action="sa-school-members.php" class="m-0">
|
||||
<input type="hidden" name="token" value="<?php echo e($csrf); ?>">
|
||||
<input type="hidden" name="action" value="toggle">
|
||||
<input type="hidden" name="idschool" value="<?php echo $sid; ?>">
|
||||
<input type="hidden" name="idschooluser" value="<?php echo $idsu; ?>">
|
||||
<button type="submit" class="btn-soft toggle" title="<?php echo $active ? 'Disattiva' : 'Riattiva'; ?>">
|
||||
<i class="fas <?php echo $active ? 'fa-user-slash' : 'fa-user-check'; ?>"></i>
|
||||
</button>
|
||||
</form>
|
||||
<form method="post" action="sa-school-members.php" class="m-0 form-remove" data-name="<?php echo e($full); ?>">
|
||||
<input type="hidden" name="token" value="<?php echo e($csrf); ?>">
|
||||
<input type="hidden" name="action" value="remove">
|
||||
<input type="hidden" name="idschool" value="<?php echo $sid; ?>">
|
||||
<input type="hidden" name="idschooluser" value="<?php echo $idsu; ?>">
|
||||
<button type="submit" class="btn-soft remove" title="Rimuovi dalla scuola"><i class="fas fa-trash"></i></button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
<?php if (count($members) === 300) : ?>
|
||||
<p class="text-muted small mt-2">Mostrati i primi 300 risultati: usa la ricerca per restringere.</p>
|
||||
<?php endif; ?>
|
||||
</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-remove', function(e) {
|
||||
e.preventDefault();
|
||||
const form = this;
|
||||
Swal.fire({
|
||||
title: 'Rimuovere dalla scuola?',
|
||||
html: '<strong>' + $('<div>').text($(form).data('name')).html() + '</strong><br><br>L\'account resta attivo sul portale, ma perde l\'accesso a questa scuola.',
|
||||
icon: 'warning',
|
||||
showCancelButton: true,
|
||||
confirmButtonColor: '#e74c3c',
|
||||
confirmButtonText: 'Sì, rimuovi',
|
||||
cancelButtonText: 'Annulla'
|
||||
}).then((r) => {
|
||||
if (r.isConfirmed) form.submit();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,494 @@
|
||||
<?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>
|
||||
@@ -26,6 +26,5 @@ if (!SchoolContext::switchTo($target)) {
|
||||
die('Non hai accesso a questa scuola.');
|
||||
}
|
||||
|
||||
$landing = SchoolContext::isStaff() ? 'adminpanel.php' : 'userpanel.php';
|
||||
header('Location: ' . $landing);
|
||||
header('Location: ' . SchoolContext::landingPage());
|
||||
exit;
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Cambio vista (superadmin / scuola / insegnante / allievo).
|
||||
* POST: mode, token. Consente solo viste permesse dal ruolo reale.
|
||||
*/
|
||||
require_once('include/headscript.php');
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
header('Location: ' . SchoolContext::landingPage());
|
||||
exit;
|
||||
}
|
||||
|
||||
$token = $_POST['token'] ?? '';
|
||||
if (empty($_SESSION['csrf_switch']) || !hash_equals($_SESSION['csrf_switch'], $token)) {
|
||||
http_response_code(400);
|
||||
die('Richiesta non valida.');
|
||||
}
|
||||
|
||||
if (!SchoolContext::setViewMode((string) ($_POST['mode'] ?? ''))) {
|
||||
http_response_code(403);
|
||||
die('Vista non consentita.');
|
||||
}
|
||||
|
||||
header('Location: ' . SchoolContext::landingPage());
|
||||
exit;
|
||||
Reference in New Issue
Block a user