yogiboook_new/public/userarea/school_profile.php
2026-01-29 08:55:33 +01:00

1017 lines
44 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?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>