1017 lines
44 KiB
PHP
1017 lines
44 KiB
PHP
<?php
|
||
// school_profile.php
|
||
|
||
ini_set('display_errors', 1);
|
||
ini_set('display_startup_errors', 1);
|
||
error_reporting(E_ALL);
|
||
|
||
include('include/headscript.php');
|
||
|
||
$dbHandler = DBHandlerSelect::getInstance();
|
||
$pdo = $dbHandler->getConnection();
|
||
|
||
if (!isset($iduserlogin)) {
|
||
die("Errore: ID utente non definito.");
|
||
}
|
||
|
||
// Recupera utente
|
||
$stmt = $pdo->prepare("SELECT id, first_name, last_name, email FROM auth_users WHERE id = ? LIMIT 1");
|
||
$stmt->execute([$iduserlogin]);
|
||
$user = $stmt->fetch(PDO::FETCH_ASSOC);
|
||
|
||
if (!$user) {
|
||
die("Errore: Utente non trovato.");
|
||
}
|
||
|
||
// Recupera eventuale scuola del proprietario
|
||
$stmt = $pdo->prepare("SELECT * FROM schools WHERE owner_id = ? ORDER BY id DESC LIMIT 1");
|
||
$stmt->execute([$iduserlogin]);
|
||
$school = $stmt->fetch(PDO::FETCH_ASSOC);
|
||
|
||
$is_new = !$school;
|
||
|
||
if ($is_new) {
|
||
$school = [
|
||
'id' => null,
|
||
'owner_id' => $iduserlogin,
|
||
'name' => '',
|
||
'slug' => '',
|
||
'website' => '',
|
||
'email' => $user['email'] ?? '', // default utile
|
||
'phone' => '',
|
||
'description' => '',
|
||
'address_street' => '',
|
||
'address_city' => '',
|
||
'address_postal_code' => '',
|
||
'address_province' => '',
|
||
'address_country' => 'Italia',
|
||
'latitude' => null,
|
||
'longitude' => null,
|
||
'owner_name' => trim(($user['first_name'] ?? '') . ' ' . ($user['last_name'] ?? '')),
|
||
'vat_number' => '',
|
||
'logo' => '',
|
||
'status' => 'active',
|
||
];
|
||
} else {
|
||
// se esiste, sincronizza school_id in sessione
|
||
$_SESSION['school_id'] = (int)$school['id'];
|
||
|
||
// Carica foto esistenti
|
||
$stmtPhotos = $pdo->prepare("SELECT id, filename FROM school_photos WHERE school_id = ? ORDER BY sort_order ASC, id ASC");
|
||
$stmtPhotos->execute([$school['id']]);
|
||
$existingPhotos = $stmtPhotos->fetchAll(PDO::FETCH_ASSOC);
|
||
}
|
||
|
||
|
||
// Generatore slug
|
||
function generateSlug($string)
|
||
{
|
||
$slug = iconv('UTF-8', 'ASCII//TRANSLIT', $string);
|
||
$slug = preg_replace('/[^a-z0-9 -]/i', '', $slug);
|
||
$slug = trim($slug);
|
||
$slug = preg_replace('/ +/', '-', $slug);
|
||
$slug = strtolower($slug);
|
||
return $slug;
|
||
}
|
||
|
||
// Ridimensiona immagine con GD (max 1920 px lato lungo, qualità 82%)
|
||
function resizeAndSaveImage($tmp_name, $target_path, $maxDimension = 1920, $quality = 82)
|
||
{
|
||
list($width, $height, $type) = getimagesize($tmp_name);
|
||
|
||
if ($width <= $maxDimension && $height <= $maxDimension) {
|
||
return move_uploaded_file($tmp_name, $target_path);
|
||
}
|
||
|
||
$ratio = min($maxDimension / $width, $maxDimension / $height);
|
||
$newW = (int)($width * $ratio);
|
||
$newH = (int)($height * $ratio);
|
||
|
||
$src = null;
|
||
switch ($type) {
|
||
case IMAGETYPE_JPEG:
|
||
$src = imagecreatefromjpeg($tmp_name);
|
||
break;
|
||
case IMAGETYPE_PNG:
|
||
$src = imagecreatefrompng($tmp_name);
|
||
break;
|
||
case IMAGETYPE_GIF:
|
||
$src = imagecreatefromgif($tmp_name);
|
||
break;
|
||
default:
|
||
return false;
|
||
}
|
||
if (!$src) return false;
|
||
|
||
$dst = imagecreatetruecolor($newW, $newH);
|
||
|
||
// Trasparenza per PNG
|
||
if ($type == IMAGETYPE_PNG) {
|
||
imagealphablending($dst, false);
|
||
imagesavealpha($dst, true);
|
||
$transparent = imagecolorallocatealpha($dst, 255, 255, 255, 127);
|
||
imagefilledrectangle($dst, 0, 0, $newW, $newH, $transparent);
|
||
}
|
||
|
||
imagecopyresampled($dst, $src, 0, 0, 0, 0, $newW, $newH, $width, $height);
|
||
|
||
$success = false;
|
||
switch ($type) {
|
||
case IMAGETYPE_JPEG:
|
||
$success = imagejpeg($dst, $target_path, $quality);
|
||
break;
|
||
case IMAGETYPE_PNG:
|
||
$success = imagepng($dst, $target_path, (int)(9 - ($quality / 10)));
|
||
break;
|
||
case IMAGETYPE_GIF:
|
||
$success = imagegif($dst, $target_path);
|
||
break;
|
||
}
|
||
|
||
imagedestroy($src);
|
||
imagedestroy($dst);
|
||
return $success;
|
||
}
|
||
|
||
// POST - Salvataggio
|
||
$success_message = $error = null;
|
||
|
||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && !isset($_POST['action'])) {
|
||
$name = trim($_POST['name'] ?? '');
|
||
$slug = generateSlug(trim($_POST['slug'] ?? $name));
|
||
$website = trim($_POST['website'] ?? '');
|
||
$email = trim($_POST['email'] ?? '');
|
||
$phone = trim($_POST['phone'] ?? '');
|
||
$description = trim($_POST['description'] ?? '');
|
||
$address_street = trim($_POST['address_street'] ?? '');
|
||
$address_city = trim($_POST['address_city'] ?? '');
|
||
$address_postal_code = trim($_POST['address_postal_code'] ?? '');
|
||
$address_province = trim($_POST['address_province'] ?? '');
|
||
$address_country = trim($_POST['address_country'] ?? 'Italia');
|
||
$latitude = !empty($_POST['latitude']) ? floatval($_POST['latitude']) : null;
|
||
$longitude = !empty($_POST['longitude']) ? floatval($_POST['longitude']) : null;
|
||
$owner_name = trim($_POST['owner_name'] ?? '');
|
||
$vat_number = trim($_POST['vat_number'] ?? '');
|
||
$rawStatus = $_POST['status'] ?? 'active';
|
||
$status = in_array($rawStatus, ['active', 'inactive', 'suspended'], true) ? $rawStatus : 'active';
|
||
|
||
// Validazioni
|
||
if (empty($name)) $error = "Il nome della scuola è obbligatorio.";
|
||
elseif (empty($slug)) $error = "Lo slug non può essere vuoto.";
|
||
else {
|
||
$stmt = $pdo->prepare("SELECT COUNT(*) FROM schools WHERE slug = ? AND id != ?");
|
||
$stmt->execute([$slug, $school['id'] ?? 0]);
|
||
if ($stmt->fetchColumn() > 0) {
|
||
$error = "Lo slug '$slug' è già in uso.";
|
||
}
|
||
}
|
||
|
||
// Logo
|
||
$logo = $school['logo'] ?? '';
|
||
if (!empty($_FILES['logo']['name']) && $_FILES['logo']['error'] === UPLOAD_ERR_OK) {
|
||
$ext = strtolower(pathinfo($_FILES['logo']['name'], PATHINFO_EXTENSION));
|
||
if (in_array($ext, ['jpg', 'jpeg', 'png', 'gif'])) {
|
||
$new_name = "photoschool/{$iduserlogin}-" . time() . "-logo.$ext";
|
||
if (move_uploaded_file($_FILES['logo']['tmp_name'], $new_name)) {
|
||
if ($logo && file_exists($logo) && !$is_new) @unlink($logo);
|
||
$logo = $new_name;
|
||
} else {
|
||
$error = "Errore caricamento logo.";
|
||
}
|
||
} else {
|
||
$error = "Solo JPG, PNG, GIF ammessi.";
|
||
}
|
||
}
|
||
|
||
if (!isset($error)) {
|
||
$params = [
|
||
$name,
|
||
$slug,
|
||
$website ?: null,
|
||
$email,
|
||
$phone ?: null,
|
||
$description,
|
||
$address_street,
|
||
$address_city,
|
||
$address_postal_code,
|
||
$address_province,
|
||
$address_country,
|
||
$latitude,
|
||
$longitude,
|
||
$owner_name,
|
||
$vat_number,
|
||
$logo,
|
||
$status
|
||
];
|
||
|
||
if ($is_new) {
|
||
$stmt = $pdo->prepare("
|
||
INSERT INTO schools (
|
||
owner_id, name, slug, website, email, phone, description,
|
||
address_street, address_city, address_postal_code, address_province, address_country,
|
||
latitude, longitude, owner_name, vat_number, logo, status
|
||
) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
||
");
|
||
array_unshift($params, $iduserlogin);
|
||
$success = $stmt->execute($params);
|
||
|
||
if ($success) {
|
||
|
||
// 1) Prendi ID appena creato
|
||
$newSchoolId = (int)$pdo->lastInsertId();
|
||
|
||
// 2) Salvalo in sessione (FONDAMENTALE per evitare redirect onboarding)
|
||
$_SESSION['school_id'] = $newSchoolId;
|
||
|
||
// 3) Crea record settings base (se non esiste già)
|
||
try {
|
||
$stmtSet = $pdo->prepare("INSERT INTO school_settings (school_id) VALUES (?)");
|
||
$stmtSet->execute([$newSchoolId]);
|
||
} catch (Exception $e) {
|
||
// se esiste già, ignoriamo
|
||
}
|
||
|
||
// 4) Ricarica la scuola appena creata
|
||
$stmt = $pdo->prepare("SELECT * FROM schools WHERE id = ? LIMIT 1");
|
||
$stmt->execute([$newSchoolId]);
|
||
$school = $stmt->fetch(PDO::FETCH_ASSOC);
|
||
|
||
$success_message = "Scuola creata con successo!";
|
||
$is_new = false;
|
||
} else {
|
||
$error = "Errore creazione scuola.";
|
||
}
|
||
} else {
|
||
$params[] = $school['id'];
|
||
$stmt = $pdo->prepare("
|
||
UPDATE schools SET
|
||
name=?, slug=?, website=?, email=?, phone=?, description=?,
|
||
address_street=?, address_city=?, address_postal_code=?, address_province=?,
|
||
address_country=?, latitude=?, longitude=?, owner_name=?, vat_number=?,
|
||
logo=?, status=?
|
||
WHERE id=?
|
||
");
|
||
$success = $stmt->execute($params);
|
||
|
||
if ($success) {
|
||
$success_message = "Profilo aggiornato con successo!";
|
||
$stmt = $pdo->prepare("SELECT * FROM schools WHERE id = ?");
|
||
$stmt->execute([$school['id']]);
|
||
$school = $stmt->fetch(PDO::FETCH_ASSOC);
|
||
} else {
|
||
$error = "Errore aggiornamento.";
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// AJAX per gestione foto
|
||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
|
||
header('Content-Type: application/json; charset=utf-8');
|
||
|
||
if ($is_new || empty($school['id'])) {
|
||
echo json_encode(['success' => false, 'error' => 'Prima salva il profilo scuola']);
|
||
exit;
|
||
}
|
||
|
||
$school_id = (int)$school['id'];
|
||
|
||
if ($_POST['action'] === 'upload_photos') {
|
||
$currentCount = $pdo->query("SELECT COUNT(*) FROM school_photos WHERE school_id = $school_id")->fetchColumn();
|
||
$canAdd = 5 - $currentCount;
|
||
|
||
if ($canAdd <= 0) {
|
||
echo json_encode(['success' => false, 'error' => 'Limite di 5 foto raggiunto']);
|
||
exit;
|
||
}
|
||
|
||
$uploaded = [];
|
||
$errors = [];
|
||
|
||
foreach ($_FILES['photos']['tmp_name'] ?? [] as $i => $tmp) {
|
||
if ($canAdd <= count($uploaded)) break;
|
||
if (empty($tmp) || $_FILES['photos']['error'][$i] !== 0) continue;
|
||
|
||
$origName = $_FILES['photos']['name'][$i];
|
||
$ext = strtolower(pathinfo($origName, PATHINFO_EXTENSION));
|
||
if (!in_array($ext, ['jpg', 'jpeg', 'png', 'gif'])) {
|
||
$errors[] = $origName . ' - formato non supportato';
|
||
continue;
|
||
}
|
||
|
||
$safeName = preg_replace('/[^a-z0-9._-]/i', '', pathinfo($origName, PATHINFO_FILENAME));
|
||
$newFilename = "photoschool/{$school_id}_" . time() . "_{$safeName}.{$ext}";
|
||
|
||
if (resizeAndSaveImage($tmp, $newFilename)) {
|
||
$stmt = $pdo->prepare("INSERT INTO school_photos
|
||
(school_id, filename, original_name, mime_type, file_size, sort_order)
|
||
VALUES (?, ?, ?, ?, ?, ?)");
|
||
$stmt->execute([
|
||
$school_id,
|
||
$newFilename,
|
||
$origName,
|
||
$_FILES['photos']['type'][$i] ?: 'image/jpeg',
|
||
(int)$_FILES['photos']['size'][$i],
|
||
$currentCount + count($uploaded)
|
||
]);
|
||
|
||
$uploaded[] = [
|
||
'id' => $pdo->lastInsertId(),
|
||
'filename' => $newFilename
|
||
];
|
||
} else {
|
||
$errors[] = $origName . ' - errore elaborazione';
|
||
}
|
||
}
|
||
|
||
echo json_encode([
|
||
'success' => !empty($uploaded),
|
||
'uploaded' => $uploaded,
|
||
'errors' => $errors,
|
||
'remaining' => 5 - ($currentCount + count($uploaded))
|
||
]);
|
||
exit;
|
||
}
|
||
|
||
if ($_POST['action'] === 'delete_photo' && !empty($_POST['photo_id'])) {
|
||
$photoId = (int)$_POST['photo_id'];
|
||
$stmt = $pdo->prepare("SELECT filename FROM school_photos WHERE id = ? AND school_id = ?");
|
||
$stmt->execute([$photoId, $school_id]);
|
||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||
|
||
if ($row) {
|
||
if (file_exists($row['filename'])) @unlink($row['filename']);
|
||
$pdo->prepare("DELETE FROM school_photos WHERE id = ?")->execute([$photoId]);
|
||
}
|
||
echo json_encode(['success' => true]);
|
||
exit;
|
||
}
|
||
|
||
echo json_encode(['success' => false, 'error' => 'Azione non valida']);
|
||
exit;
|
||
}
|
||
?>
|
||
|
||
<!doctype html>
|
||
<html lang="it">
|
||
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<title><?php echo $is_new ? 'Crea' : 'Modifica'; ?> Profilo Scuola</title>
|
||
<?php include('cssinclude.php'); ?>
|
||
|
||
<!-- Quill.js CDN -->
|
||
<link href="https://cdn.jsdelivr.net/npm/quill@2.0.2/dist/quill.snow.css" rel="stylesheet" />
|
||
|
||
<style>
|
||
#map {
|
||
height: 380px;
|
||
border-radius: 10px;
|
||
margin-top: 12px;
|
||
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.08);
|
||
}
|
||
|
||
/* Editor Quill */
|
||
.ql-container {
|
||
min-height: 260px;
|
||
font-size: 15px;
|
||
border: 1px solid #ced4da;
|
||
border-radius: 0.375rem;
|
||
height: auto;
|
||
margin-bottom: 14px;
|
||
}
|
||
|
||
.ql-editor {
|
||
min-height: 260px;
|
||
margin-bottom: 10px;
|
||
}
|
||
|
||
.form-label {
|
||
font-weight: 500;
|
||
}
|
||
|
||
.ql-toolbar {
|
||
border-radius: 0.375rem 0.375rem 0 0;
|
||
border-color: #ced4da;
|
||
}
|
||
|
||
/* Logo rettangolare, non arrotondato */
|
||
.school-logo {
|
||
max-width: 100%;
|
||
height: auto;
|
||
max-height: 220px;
|
||
object-fit: contain;
|
||
border: 1px solid #dee2e6;
|
||
border-radius: 8px;
|
||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.08);
|
||
background: #fff;
|
||
padding: 10px;
|
||
display: block;
|
||
margin: 0 auto 1rem;
|
||
}
|
||
|
||
/* Placeholder logo quando non presente */
|
||
.logo-placeholder {
|
||
width: 100%;
|
||
max-height: 220px;
|
||
height: 220px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
border: 1px dashed #cfd4da;
|
||
border-radius: 8px;
|
||
background: #f8f9fa;
|
||
color: #6c757d;
|
||
font-weight: 700;
|
||
font-size: 28px;
|
||
letter-spacing: 1px;
|
||
margin: 0 auto 1rem;
|
||
}
|
||
|
||
/* Thumbnails foto scuola */
|
||
.school-photo-thumb {
|
||
cursor: zoom-in;
|
||
transition: transform .15s ease, box-shadow .15s ease;
|
||
box-shadow: 0 2px 10px rgba(0, 0, 0, .08);
|
||
}
|
||
|
||
.school-photo-thumb:hover {
|
||
transform: scale(1.03);
|
||
box-shadow: 0 10px 25px rgba(0, 0, 0, .16);
|
||
}
|
||
</style>
|
||
</head>
|
||
|
||
<body>
|
||
<div class="wrapper">
|
||
<?php include('include/navbar.php'); ?>
|
||
<?php include('include/topbar.php'); ?>
|
||
|
||
<div class="page-wrapper">
|
||
<div class="page-content">
|
||
<div class="card radius-10">
|
||
<div class="card-header bg-gradient-primary text-white">
|
||
<h5 class="mb-0"><?php echo $is_new ? 'Crea il tuo profilo scuola' : 'Gestisci profilo scuola'; ?></h5>
|
||
</div>
|
||
|
||
<div class="card-body">
|
||
<?php if ($success_message): ?>
|
||
<div class="alert alert-success alert-dismissible fade show">
|
||
<?php echo htmlspecialchars($success_message); ?>
|
||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||
</div>
|
||
<?php endif; ?>
|
||
|
||
<?php if ($error): ?>
|
||
<div class="alert alert-danger alert-dismissible fade show">
|
||
<?php echo htmlspecialchars($error); ?>
|
||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||
</div>
|
||
<?php endif; ?>
|
||
|
||
<form method="POST" enctype="multipart/form-data" id="schoolForm">
|
||
<div class="row g-4">
|
||
<!-- Colonna sinistra: solo logo -->
|
||
<div class="col-lg-4 text-center">
|
||
<?php if (!empty($school['logo'])): ?>
|
||
<img id="logoPreview"
|
||
src="<?php echo htmlspecialchars($school['logo']); ?>"
|
||
alt="Logo Scuola"
|
||
class="school-logo mb-3">
|
||
<?php else: ?>
|
||
<div id="logoPreviewPlaceholder" class="logo-placeholder">NA</div>
|
||
<img id="logoPreview" src="" alt="Logo Scuola" class="school-logo mb-3" style="display:none;">
|
||
<?php endif; ?>
|
||
|
||
|
||
<div class="mb-3">
|
||
<label class="form-label">Cambia Logo (opzionale)</label>
|
||
<input type="file" class="form-control" name="logo" id="logoInput" accept="image/jpeg,image/png,image/gif">
|
||
<small class="text-muted d-block mt-1">Max 2MB – JPG, PNG, GIF</small>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Colonna destra: tutti i campi inclusa descrizione con Quill -->
|
||
<div class="col-lg-8">
|
||
<div class="row g-3">
|
||
<div class="col-md-6">
|
||
<label class="form-label">Nome Scuola <span class="text-danger">*</span></label>
|
||
<input type="text" class="form-control" name="name" required
|
||
value="<?php echo htmlspecialchars($school['name'] ?? ''); ?>">
|
||
</div>
|
||
|
||
<div class="col-md-6">
|
||
<label class="form-label">Slug (URL personalizzato) <span class="text-danger">*</span></label>
|
||
<div class="input-group">
|
||
<span class="input-group-text">yogiboook.com/</span>
|
||
<input type="text" class="form-control" name="slug" id="slug" required
|
||
value="<?php echo htmlspecialchars($school['slug'] ?? ''); ?>">
|
||
</div>
|
||
</div>
|
||
|
||
<div class="col-md-6">
|
||
<label class="form-label">Sito web</label>
|
||
<input type="url" class="form-control" name="website"
|
||
value="<?php echo htmlspecialchars($school['website'] ?? ''); ?>">
|
||
</div>
|
||
|
||
<div class="col-md-6">
|
||
<label class="form-label">Email scuola <span class="text-danger">*</span></label>
|
||
<input type="email" class="form-control" name="email" required
|
||
value="<?php echo htmlspecialchars($school['email'] ?? ''); ?>">
|
||
</div>
|
||
|
||
<div class="col-md-6">
|
||
<label class="form-label">Telefono</label>
|
||
<input type="tel" class="form-control" name="phone"
|
||
value="<?php echo htmlspecialchars($school['phone'] ?? ''); ?>">
|
||
</div>
|
||
|
||
<!-- Editor Quill qui, nella colonna destra -->
|
||
<div class="col-12">
|
||
<label class="form-label">Descrizione scuola</label>
|
||
<div id="quill-editor"></div>
|
||
<input type="hidden" name="description" id="description-hidden">
|
||
</div>
|
||
|
||
<div class="col-12">
|
||
<hr class="my-4">
|
||
<h6 class="mb-3">Indirizzo sede</h6>
|
||
</div>
|
||
|
||
<div class="col-12">
|
||
<label class="form-label">Via e numero civico</label>
|
||
<input type="text" class="form-control" name="address_street"
|
||
value="<?php echo htmlspecialchars($school['address_street'] ?? ''); ?>">
|
||
</div>
|
||
|
||
<div class="col-md-4">
|
||
<label class="form-label">Città</label>
|
||
<input type="text" class="form-control" name="address_city"
|
||
value="<?php echo htmlspecialchars($school['address_city'] ?? ''); ?>">
|
||
</div>
|
||
|
||
<div class="col-md-4">
|
||
<label class="form-label">CAP</label>
|
||
<input type="text" class="form-control" name="address_postal_code"
|
||
value="<?php echo htmlspecialchars($school['address_postal_code'] ?? ''); ?>">
|
||
</div>
|
||
|
||
<div class="col-md-4">
|
||
<label class="form-label">Provincia</label>
|
||
<input type="text" class="form-control" name="address_province"
|
||
value="<?php echo htmlspecialchars($school['address_province'] ?? ''); ?>">
|
||
</div>
|
||
|
||
<div class="col-md-6">
|
||
<label class="form-label">Nazione</label>
|
||
<input type="text" class="form-control" name="address_country"
|
||
value="<?php echo htmlspecialchars($school['address_country'] ?? 'Italia'); ?>">
|
||
</div>
|
||
|
||
<div class="col-md-6 mt-4 pt-2">
|
||
<button type="button" class="btn btn-outline-primary w-100" id="geocodeBtn">
|
||
<i class="bx bx-map-pin me-1"></i> Trova coordinate sulla mappa
|
||
</button>
|
||
</div>
|
||
|
||
<div class="col-12">
|
||
<div id="map"></div>
|
||
</div>
|
||
|
||
<div class="col-md-6">
|
||
<label class="form-label">Latitudine</label>
|
||
<input type="text" class="form-control" name="latitude" id="latitude"
|
||
value="<?php echo htmlspecialchars($school['latitude'] ?? ''); ?>" readonly>
|
||
</div>
|
||
|
||
<div class="col-md-6">
|
||
<label class="form-label">Longitudine</label>
|
||
<input type="text" class="form-control" name="longitude" id="longitude"
|
||
value="<?php echo htmlspecialchars($school['longitude'] ?? ''); ?>" readonly>
|
||
</div>
|
||
|
||
<div class="col-12">
|
||
<hr class="my-4">
|
||
<h6 class="mb-3">Dati amministrativi</h6>
|
||
</div>
|
||
|
||
<div class="col-md-6">
|
||
<label class="form-label">Nome proprietario / legale</label>
|
||
<input type="text" class="form-control" name="owner_name"
|
||
value="<?php echo htmlspecialchars($school['owner_name'] ?? ''); ?>">
|
||
</div>
|
||
|
||
<div class="col-md-6">
|
||
<label class="form-label">Partita IVA</label>
|
||
<input type="text" class="form-control" name="vat_number"
|
||
value="<?php echo htmlspecialchars($school['vat_number'] ?? ''); ?>">
|
||
</div>
|
||
|
||
<div class="col-md-6">
|
||
<label class="form-label">Stato</label>
|
||
<select class="form-select" name="status">
|
||
<option value="active" <?php echo ($school['status'] ?? '') === 'active' ? 'selected' : ''; ?>>Attiva</option>
|
||
<option value="inactive" <?php echo ($school['status'] ?? '') === 'inactive' ? 'selected' : ''; ?>>Inattiva</option>
|
||
<option value="suspended" <?php echo ($school['status'] ?? '') === 'suspended' ? 'selected' : ''; ?>>Sospesa</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div class="col-12 mt-5">
|
||
<button type="submit" class="btn btn-primary btn-lg px-5">
|
||
<i class="bx bx-save me-2"></i>
|
||
<?php echo $is_new ? 'Crea Scuola' : 'Salva Modifiche'; ?>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<!-- Sezione foto scuole -->
|
||
<?php if (!$is_new): ?>
|
||
<div class="row mt-5">
|
||
<div class="col-12">
|
||
<hr class="my-4">
|
||
<h5>Foto della scuola (max 5)</h5>
|
||
|
||
<div id="photos-dropzone" style="border: 2px dashed #adb5bd; border-radius: 10px; padding: 30px; text-align: center; background: #f8f9fa; min-height: 160px; cursor: pointer;">
|
||
<p class="mb-2">Trascina le immagini qui oppure</p>
|
||
<button type="button" class="btn btn-outline-primary btn-sm" id="btn-select-photos">Seleziona file</button>
|
||
<input type="file" id="photos-input" name="photos[]" multiple accept="image/jpeg,image/png,image/gif" style="display:none;">
|
||
<div class="mt-3 text-muted">Foto rimanenti: <strong id="photos-remaining"><?php echo 5 - count($existingPhotos ?? []); ?></strong></div>
|
||
</div>
|
||
|
||
<div id="photos-preview" class="mt-4 d-flex flex-wrap gap-3">
|
||
<?php foreach ($existingPhotos ?? [] as $photo): ?>
|
||
<div class="position-relative" style="width:160px; height:160px;" data-photo-id="<?php echo $photo['id']; ?>">
|
||
<img class="school-photo-thumb"
|
||
src="<?php echo htmlspecialchars($photo['filename']); ?>"
|
||
alt="Foto scuola"
|
||
style="width:100%; height:100%; object-fit:cover; border-radius:8px; border:1px solid #dee2e6;">
|
||
|
||
<button type="button" class="btn btn-danger btn-sm position-absolute top-0 end-0 translate-middle rounded-circle p-0" style="width:28px; height:28px; font-size:18px; line-height:1;" data-delete-id="<?php echo $photo['id']; ?>">×</button>
|
||
</div>
|
||
<?php endforeach; ?>
|
||
</div>
|
||
<!-- Modal zoom foto -->
|
||
<div class="modal fade" id="photoZoomModal" tabindex="-1" aria-hidden="true">
|
||
<div class="modal-dialog modal-dialog-centered modal-xl">
|
||
<div class="modal-content bg-dark border-0">
|
||
<div class="modal-header border-0">
|
||
<button type="button" class="btn-close btn-close-white ms-auto" data-bs-dismiss="modal" aria-label="Chiudi"></button>
|
||
</div>
|
||
<div class="modal-body text-center p-0 pb-4">
|
||
<img id="photoZoomImg" src="" alt="Foto scuola" style="max-width:100%; max-height:80vh; object-fit:contain;">
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
<?php endif; ?>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<?php include('include/footer.php'); ?>
|
||
</div>
|
||
|
||
<?php include('jsinclude.php'); ?>
|
||
|
||
<!-- Quill.js -->
|
||
<script src="https://cdn.jsdelivr.net/npm/quill@2.0.2/dist/quill.js"></script>
|
||
|
||
<!-- Leaflet -->
|
||
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
|
||
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
|
||
|
||
<script>
|
||
// ====================== QUILL EDITOR - colori testo e sfondo visibili ======================
|
||
const quill = new Quill('#quill-editor', {
|
||
theme: 'snow',
|
||
modules: {
|
||
toolbar: [
|
||
['bold', 'italic', 'underline', 'strike'],
|
||
['blockquote', 'code-block'],
|
||
[{
|
||
'header': [1, 2, 3, false]
|
||
}],
|
||
[{
|
||
'color': [
|
||
'#000000', '#434343', '#666666', '#999999', '#b7b7b7', '#cccccc', '#d9d9d9', '#ffffff',
|
||
'#ff0000', '#ff9900', '#ffff00', '#00ff00', '#00ffff', '#0000ff', '#9900ff', '#ff00ff',
|
||
'#ffcccc', '#ffe6cc', '#ffffcc', '#ccffcc', '#ccffff', '#ccccff', '#e6ccff', '#ffccff'
|
||
]
|
||
}, {
|
||
'background': [
|
||
'#000000', '#434343', '#666666', '#999999', '#b7b7b7', '#cccccc', '#d9d9d9', '#ffffff',
|
||
'#ff0000', '#ff9900', '#ffff00', '#00ff00', '#00ffff', '#0000ff', '#9900ff', '#ff00ff',
|
||
'#ffcccc', '#ffe6cc', '#ffffcc', '#ccffcc', '#ccffff', '#ccccff', '#e6ccff', '#ffccff'
|
||
]
|
||
}],
|
||
[{
|
||
'list': 'ordered'
|
||
}, {
|
||
'list': 'bullet'
|
||
}],
|
||
[{
|
||
'align': []
|
||
}],
|
||
['link', 'clean']
|
||
]
|
||
}
|
||
});
|
||
|
||
// Carica contenuto iniziale (HTML)
|
||
quill.root.innerHTML = `<?php echo addslashes($school['description'] ?? ''); ?>`;
|
||
|
||
// Salva HTML prima del submit
|
||
document.getElementById('schoolForm').addEventListener('submit', function(e) {
|
||
const html = quill.root.innerHTML.trim();
|
||
document.getElementById('description-hidden').value = (html === '<p><br></p>' ? '' : html);
|
||
|
||
});
|
||
|
||
// ====================== MAPPA (invariata, funziona già) ======================
|
||
let map, marker;
|
||
|
||
function initMap(lat = 45.4642, lng = 9.1900, zoom = 12) {
|
||
if (map) map.remove();
|
||
map = L.map('map').setView([lat, lng], zoom);
|
||
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>'
|
||
}).addTo(map);
|
||
marker = L.marker([lat, lng], {
|
||
draggable: true
|
||
}).addTo(map);
|
||
marker.on('dragend', function(e) {
|
||
const pos = e.target.getLatLng();
|
||
document.getElementById('latitude').value = pos.lat.toFixed(8);
|
||
document.getElementById('longitude').value = pos.lng.toFixed(8);
|
||
});
|
||
}
|
||
|
||
function updateMap(lat, lng) {
|
||
if (!map) initMap(lat, lng, 16);
|
||
else {
|
||
map.setView([lat, lng], 16);
|
||
marker.setLatLng([lat, lng]);
|
||
}
|
||
document.getElementById('latitude').value = lat.toFixed(8);
|
||
document.getElementById('longitude').value = lng.toFixed(8);
|
||
}
|
||
|
||
document.getElementById('geocodeBtn')?.addEventListener('click', function() {
|
||
const street = document.querySelector('[name="address_street"]').value.trim();
|
||
const city = document.querySelector('[name="address_city"]').value.trim();
|
||
const cap = document.querySelector('[name="address_postal_code"]').value.trim();
|
||
const country = document.querySelector('[name="address_country"]').value.trim() || 'Italia';
|
||
|
||
if (!street || !city) {
|
||
alert('Inserisci almeno Via e Città');
|
||
return;
|
||
}
|
||
|
||
const query = `${street}, ${city}${cap ? ' ' + cap : ''}, ${country}`;
|
||
const url = `https://nominatim.openstreetmap.org/search?format=json&q=${encodeURIComponent(query)}&limit=1`;
|
||
|
||
fetch(url)
|
||
.then(r => r.json())
|
||
.then(data => {
|
||
if (data?.length > 0) {
|
||
const loc = data[0];
|
||
updateMap(parseFloat(loc.lat), parseFloat(loc.lon));
|
||
} else {
|
||
alert('Indirizzo non trovato.');
|
||
}
|
||
})
|
||
.catch(() => alert('Errore durante la ricerca.'));
|
||
});
|
||
|
||
// Init mappa
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
const lat = parseFloat(document.getElementById('latitude').value);
|
||
const lng = parseFloat(document.getElementById('longitude').value);
|
||
if (!isNaN(lat) && !isNaN(lng)) {
|
||
initMap(lat, lng, 16);
|
||
} else {
|
||
initMap();
|
||
}
|
||
});
|
||
|
||
// Slug automatico (JS)
|
||
function slugify(str) {
|
||
return (str || '')
|
||
.toString()
|
||
.normalize('NFD').replace(/[\u0300-\u036f]/g, '') // rimuove accenti
|
||
.toLowerCase()
|
||
.trim()
|
||
.replace(/[^a-z0-9\s-]/g, '')
|
||
.replace(/\s+/g, '-')
|
||
.replace(/-+/g, '-');
|
||
}
|
||
|
||
const nameField = document.querySelector('[name="name"]');
|
||
const slugField = document.querySelector('[name="slug"]');
|
||
let slugTouched = false;
|
||
|
||
nameField?.addEventListener('input', function() {
|
||
if (!slugTouched) slugField.value = slugify(this.value);
|
||
});
|
||
|
||
slugField?.addEventListener('input', () => slugTouched = true);
|
||
|
||
// ====================== LOGO PREVIEW (before save) ======================
|
||
const logoInput = document.getElementById('logoInput');
|
||
const logoPreview = document.getElementById('logoPreview');
|
||
const logoPlaceholder = document.getElementById('logoPreviewPlaceholder');
|
||
|
||
logoInput?.addEventListener('change', function() {
|
||
const file = this.files && this.files[0];
|
||
if (!file) return;
|
||
|
||
// Basic type check (client-side)
|
||
if (!['image/jpeg', 'image/png', 'image/gif'].includes(file.type)) {
|
||
alert('Formato non valido. Usa JPG, PNG o GIF.');
|
||
this.value = '';
|
||
return;
|
||
}
|
||
|
||
const reader = new FileReader();
|
||
reader.onload = function(e) {
|
||
if (logoPlaceholder) logoPlaceholder.style.display = 'none';
|
||
if (logoPreview) {
|
||
logoPreview.src = e.target.result;
|
||
logoPreview.style.display = 'block';
|
||
}
|
||
};
|
||
reader.readAsDataURL(file);
|
||
});
|
||
</script>
|
||
<script>
|
||
// Gestione foto
|
||
const dropzone = document.getElementById('photos-dropzone');
|
||
const fileInput = document.getElementById('photos-input');
|
||
const btnSelect = document.getElementById('btn-select-photos');
|
||
const previewContainer = document.getElementById('photos-preview');
|
||
const remainingSpan = document.getElementById('photos-remaining');
|
||
|
||
let remainingPhotos = parseInt(remainingSpan?.innerText || '0');
|
||
|
||
function updateRemainingCount() {
|
||
if (remainingSpan) remainingSpan.innerText = remainingPhotos;
|
||
if (remainingPhotos <= 0 && dropzone) {
|
||
dropzone.style.opacity = '0.5';
|
||
dropzone.style.pointerEvents = 'none';
|
||
}
|
||
}
|
||
|
||
function addPhotoPreview(url, photoId) {
|
||
const wrapper = document.createElement('div');
|
||
wrapper.className = 'position-relative';
|
||
wrapper.style.width = '160px';
|
||
wrapper.style.height = '160px';
|
||
wrapper.dataset.photoId = photoId;
|
||
|
||
wrapper.innerHTML = `
|
||
<img class="school-photo-thumb" src="${url}" alt="Foto" style="width:100%; height:100%; object-fit:cover; border-radius:8px; border:1px solid #dee2e6;">
|
||
<button type="button" class="btn btn-danger btn-sm position-absolute top-0 end-0 translate-middle rounded-circle p-0" style="width:28px;height:28px;font-size:18px;line-height:1;" data-delete-id="${photoId}">×</button>
|
||
`;
|
||
previewContainer.appendChild(wrapper);
|
||
}
|
||
|
||
function deletePhoto(btn) {
|
||
const wrapper = btn.closest('.position-relative');
|
||
const photoId = wrapper.dataset.photoId;
|
||
|
||
if (!photoId) {
|
||
wrapper.remove();
|
||
remainingPhotos++;
|
||
updateRemainingCount();
|
||
return;
|
||
}
|
||
|
||
if (!confirm('Vuoi eliminare questa foto?')) return;
|
||
|
||
fetch('', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/x-www-form-urlencoded'
|
||
},
|
||
body: 'action=delete_photo&photo_id=' + photoId
|
||
})
|
||
.then(r => r.json())
|
||
.then(data => {
|
||
if (data.success) {
|
||
wrapper.remove();
|
||
remainingPhotos++;
|
||
updateRemainingCount();
|
||
} else {
|
||
alert('Errore durante l\'eliminazione');
|
||
}
|
||
})
|
||
.catch(() => alert('Errore di connessione'));
|
||
}
|
||
|
||
// Eventi
|
||
if (dropzone) {
|
||
dropzone.addEventListener('click', (e) => {
|
||
// se clicco sul bottone (o dentro al bottone), NON devo aprire due volte
|
||
if (e.target.closest('#btn-select-photos')) return;
|
||
fileInput.click();
|
||
});
|
||
|
||
btnSelect?.addEventListener('click', (e) => {
|
||
e.preventDefault();
|
||
e.stopPropagation(); // IMPORTANTISSIMO: evita il click anche sulla dropzone
|
||
fileInput.click();
|
||
});
|
||
|
||
|
||
['dragover', 'dragenter'].forEach(ev => dropzone.addEventListener(ev, e => {
|
||
e.preventDefault();
|
||
dropzone.classList.add('bg-light', 'border-primary');
|
||
}));
|
||
|
||
['dragleave', 'drop'].forEach(ev => dropzone.addEventListener(ev, e => {
|
||
e.preventDefault();
|
||
dropzone.classList.remove('bg-light', 'border-primary');
|
||
}));
|
||
|
||
dropzone.addEventListener('drop', e => handleFiles(e.dataTransfer.files));
|
||
|
||
fileInput.addEventListener('change', e => {
|
||
handleFiles(e.target.files);
|
||
fileInput.value = '';
|
||
});
|
||
}
|
||
|
||
function handleFiles(files) {
|
||
if (remainingPhotos <= 0) return alert('Limite di 5 foto raggiunto');
|
||
|
||
const formData = new FormData();
|
||
formData.append('action', 'upload_photos');
|
||
|
||
let added = 0;
|
||
for (let file of files) {
|
||
if (added >= remainingPhotos) break;
|
||
if (!file.type.startsWith('image/')) continue;
|
||
formData.append('photos[]', file);
|
||
added++;
|
||
}
|
||
|
||
if (added === 0) return;
|
||
|
||
fetch('', {
|
||
method: 'POST',
|
||
body: formData
|
||
})
|
||
.then(r => r.json())
|
||
.then(data => {
|
||
if (data.success) {
|
||
data.uploaded.forEach(item => {
|
||
addPhotoPreview(item.filename, item.id);
|
||
});
|
||
remainingPhotos -= data.uploaded.length;
|
||
updateRemainingCount();
|
||
}
|
||
if (data.errors && data.errors.length) {
|
||
alert('Problemi con alcuni file:\n' + data.errors.join('\n'));
|
||
}
|
||
})
|
||
.catch(err => {
|
||
console.error(err);
|
||
alert('Errore durante il caricamento');
|
||
});
|
||
}
|
||
|
||
previewContainer?.addEventListener('click', e => {
|
||
// Se clicco la X: elimina
|
||
if (e.target.hasAttribute('data-delete-id')) {
|
||
e.preventDefault();
|
||
deletePhoto(e.target);
|
||
return;
|
||
}
|
||
|
||
// Se clicco una foto: zoom
|
||
if (e.target && e.target.tagName === 'IMG') {
|
||
const modalEl = document.getElementById('photoZoomModal');
|
||
const zoomImg = document.getElementById('photoZoomImg');
|
||
if (!modalEl || !zoomImg) return;
|
||
|
||
zoomImg.src = e.target.src;
|
||
|
||
// Bootstrap modal
|
||
const modal = new bootstrap.Modal(modalEl);
|
||
modal.show();
|
||
}
|
||
});
|
||
|
||
updateRemainingCount();
|
||
</script>
|
||
</body>
|
||
|
||
</html>
|