Files
2026-09-18 15:36:44 +02:00

964 lines
48 KiB
PHP
Raw Permalink 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
require_once('include/headscript.php');
/**
* Modulo Eventi - form creazione/modifica (una sola pagina).
* - event_form.php -> nuovo evento
* - event_form.php?id=123 -> modifica evento esistente
* Invia a event_save.php (insert/update + redirect).
*
* Extra rispetto alla v1:
* - flatpickr per data/ora (fine = inizio + 1h automatica)
* - sede da event_locations (default scuola) o indirizzo personalizzato
* - "Calcola coordinate" da indirizzo (geocoding via event_geocode.php)
* - copertina drag&drop -> event_upload_cover.php -> assets/uploads/events/
*/
$pdo = DBHandlerSelect::getInstance()->getConnection();
function e($value): string
{
return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
}
$id = isset($_GET['id']) ? (int) $_GET['id'] : 0;
$ev = null;
if ($id > 0) {
$stmt = $pdo->prepare("SELECT * FROM events WHERE id = ?");
$stmt->execute([$id]);
$ev = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$ev) {
header('Location: events.php?message=error');
exit();
}
}
// Default dal record unico di `option`
$opt = $pdo->query("SELECT * FROM `option` LIMIT 1")->fetch(PDO::FETCH_ASSOC);
$defaultColor = $opt['event_default_color'] ?? '#4CAF50';
$defaultMaxPerBooking = (int) ($opt['event_default_max_per_booking'] ?? 1);
$defReminderEnabled = ($opt['event_reminder_enabled'] ?? 'N') === 'Y';
$defReminderHours = (int) ($opt['event_reminder_hours'] ?? 24);
$defOfferHours = (int) ($opt['event_offer_expiry_hours'] ?? 24);
// Sedi salvate (se la tabella non esiste ancora, si degrada su indirizzo libero)
$locations = [];
try {
$locations = $pdo->query("SELECT * FROM event_locations ORDER BY is_default DESC, name ASC")->fetchAll(PDO::FETCH_ASSOC);
} catch (Throwable $e) {
$locations = [];
}
$val = [
'title' => $ev['title'] ?? '',
'slug' => $ev['slug'] ?? '',
'organizer_name' => $ev['organizer_name'] ?? '',
'starts_at' => !empty($ev['starts_at']) ? date('Y-m-d H:i', strtotime($ev['starts_at'])) : '',
'ends_at' => !empty($ev['ends_at']) ? date('Y-m-d H:i', strtotime($ev['ends_at'])) : '',
'address' => $ev['address'] ?? '',
'lat' => $ev['lat'] ?? '',
'lng' => $ev['lng'] ?? '',
'cost' => $ev['cost'] ?? '0.00',
'currency' => $ev['currency'] ?? 'EUR',
'max_seats' => $ev['max_seats'] ?? '',
'max_per_booking' => $ev['max_per_booking'] ?? $defaultMaxPerBooking,
'color' => $ev['color'] ?? $defaultColor,
'cover_image' => $ev['cover_image'] ?? '',
'status' => $ev['status'] ?? 'draft',
'description' => $ev['description'] ?? '',
'instructions' => $ev['instructions'] ?? '',
'what_to_bring' => $ev['what_to_bring'] ?? '',
'suitable_for' => $ev['suitable_for'] ?? '',
'gallery' => $ev['gallery'] ?? '',
'waitlist_enabled' => ($ev['waitlist_enabled'] ?? 'Y') === 'Y',
'reminder_enabled' => $ev['reminder_enabled'] ?? null,
'reminder_hours' => $ev['reminder_hours'] ?? '',
'offer_expiry_hours' => $ev['offer_expiry_hours'] ?? '',
'schedule_enabled' => (($ev['status'] ?? '') === 'scheduled') || !empty($ev['publish_at']),
'publish_at' => !empty($ev['publish_at']) ? date('Y-m-d H:i', strtotime($ev['publish_at'])) : '',
];
$isEdit = $id > 0;
$pageTitle = $isEdit ? 'Modifica evento' : 'Nuovo evento';
?>
<!doctype html>
<html lang="it">
<head>
<meta charset="utf-8" />
<title>YogiBook - <?php echo e($pageTitle); ?></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">
<!-- Date/ora -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/flatpickr/4.6.13/flatpickr.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/flatpickr/4.6.13/flatpickr.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/flatpickr/4.6.13/l10n/it.min.js"></script>
<!-- Editor testo ricco -->
<link href="https://cdn.jsdelivr.net/npm/quill@2/dist/quill.snow.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/quill@2/dist/quill.js"></script>
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<style>
.admin-card {
border: none;
border-radius: 14px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.06);
}
.admin-card .card-body {
padding: 26px 30px;
}
.section-title {
font-size: 15px;
font-weight: 700;
letter-spacing: 0.04em;
text-transform: uppercase;
color: #6b7280;
margin-bottom: 4px;
}
.section-lead {
color: #9ca3af;
font-size: 14px;
margin-bottom: 22px;
}
.form-label {
font-weight: 600;
font-size: 14px;
color: #374151;
}
.form-control,
.form-select {
border-radius: 8px;
border: 1px solid #e2e4e9;
padding: 10px 12px;
}
.form-control:focus,
.form-select:focus {
border-color: #1ebf73;
box-shadow: 0 0 0 3px rgba(30, 191, 115, 0.12);
}
.form-control[readonly] {
background: #f7f8fa;
color: #6b7280;
}
.hint {
color: #9ca3af;
font-size: 12px;
margin-top: 4px;
}
#editor {
height: 240px;
border-bottom-left-radius: 8px;
border-bottom-right-radius: 8px;
}
.ql-toolbar.ql-snow,
.ql-container.ql-snow {
border-color: #e2e4e9;
}
.ql-toolbar.ql-snow {
border-top-left-radius: 8px;
border-top-right-radius: 8px;
}
input[type="color"].form-control {
padding: 4px;
height: 44px;
width: 100%;
}
.btn-geocode {
border: 1px solid #e2e4e9;
background: #fff;
border-radius: 8px;
padding: 10px 14px;
font-weight: 600;
font-size: 13px;
color: #2f7be7;
white-space: nowrap;
}
.btn-geocode:hover {
background: #f3f4f6;
}
.btn-save {
border-radius: 8px;
padding: 10px 26px;
font-weight: 600;
}
.btn-back {
border-radius: 8px;
padding: 10px 20px;
font-weight: 600;
}
/* Dropzone copertina */
.dropzone {
border: 2px dashed #d5d9e0;
border-radius: 12px;
background: #fafbfc;
padding: 22px;
text-align: center;
transition: border-color 0.15s, background 0.15s;
cursor: pointer;
}
.dropzone.dragover {
border-color: #1ebf73;
background: #eef6f1;
}
.dz-empty i {
font-size: 34px;
color: #b6bcc6;
display: block;
margin-bottom: 8px;
}
.dz-empty p {
margin: 0;
color: #4b5563;
font-size: 14px;
}
.dz-empty small {
color: #9ca3af;
}
.dz-browse {
color: #2f7be7;
font-weight: 600;
text-decoration: underline;
}
.dz-preview img {
max-height: 170px;
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.12);
}
.dz-remove {
margin-top: 12px;
border: 1px solid #f2c9c4;
background: #fff;
color: #c0392b;
border-radius: 8px;
padding: 6px 14px;
font-weight: 600;
font-size: 13px;
cursor: pointer;
}
.dz-remove:hover {
background: #fdecea;
}
.dz-progress {
color: #6b7280;
font-weight: 600;
}
.coord-flash {
border-color: #1ebf73 !important;
box-shadow: 0 0 0 3px rgba(30, 191, 115, 0.18) !important;
transition: box-shadow .2s;
}
.gallery-grid {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-top: 12px;
}
.gallery-cell {
position: relative;
width: 92px;
height: 92px;
border-radius: 10px;
overflow: hidden;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.10);
}
.gallery-cell img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}
.gallery-remove {
position: absolute;
top: 4px;
right: 4px;
width: 22px;
height: 22px;
border: none;
border-radius: 50%;
background: rgba(0, 0, 0, 0.55);
color: #fff;
font-size: 15px;
line-height: 1;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
}
.gallery-remove:hover {
background: #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"><?php echo e($pageTitle); ?></h4>
</div>
</div>
<div class="d-flex">
<?php include('include/languageselection.php'); ?>
<?php include('include/profiletopbar.php'); ?>
</div>
</div>
</header>
<?php include('include/sidebar.php'); ?>
<header class="ishorizontal-topbar">
<div class="navbar-header">
<div class="d-flex"></div>
</div>
<div class="topnav">
<div class="container-fluid">
<nav class="navbar navbar-light navbar-expand-lg topnav-menu"></nav>
</div>
</div>
</header>
<div class="main-content">
<div class="page-content">
<div class="container-fluid">
<div class="row">
<div class="col-lg-9">
<div class="card admin-card">
<div class="card-body">
<div class="section-title"><?php echo e($pageTitle); ?></div>
<p class="section-lead">Compila i dati dell'evento. I campi con * sono obbligatori.</p>
<form action="event_save.php" method="POST" id="event-form">
<input type="hidden" name="id" value="<?php echo (int) $id; ?>">
<div class="row">
<div class="col-md-8 mb-3">
<label class="form-label">Titolo *</label>
<input type="text" class="form-control" name="title" required
value="<?php echo e($val['title']); ?>" placeholder="Es. Yoga in Gravidanza & Amaca">
</div>
<div class="col-md-4 mb-3">
<label class="form-label">Slug</label>
<input type="text" class="form-control" name="slug"
value="<?php echo e($val['slug']); ?>" placeholder="generato dal titolo">
<div class="hint">URL pubblico del widget. Vuoto = generato dal titolo.</div>
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label">Data e ora inizio *</label>
<input type="text" class="form-control" id="starts_at" name="starts_at" required
value="<?php echo e($val['starts_at']); ?>" placeholder="Scegli data e ora" autocomplete="off">
</div>
<div class="col-md-6 mb-3">
<label class="form-label">Data e ora fine</label>
<input type="text" class="form-control" id="ends_at" name="ends_at"
value="<?php echo e($val['ends_at']); ?>" placeholder="Auto: inizio + 1h" autocomplete="off">
</div>
</div>
<!-- Sede / indirizzo -->
<div class="row">
<?php if (!empty($locations)) : ?>
<div class="col-md-5 mb-3">
<label class="form-label">Sede</label>
<select id="location-select" class="form-select">
<?php foreach ($locations as $loc) : ?>
<option value="<?php echo (int) $loc['id']; ?>"
data-address="<?php echo e($loc['address']); ?>"
data-lat="<?php echo e($loc['lat']); ?>"
data-lng="<?php echo e($loc['lng']); ?>">
<?php echo e($loc['name']); ?>
</option>
<?php endforeach; ?>
<option value="custom">➕ Indirizzo personalizzato</option>
</select>
</div>
<div class="col-md-7 mb-3">
<label class="form-label">Indirizzo</label>
<div class="d-flex gap-2">
<input type="text" class="form-control" id="address-input" name="address"
value="<?php echo e($val['address']); ?>" placeholder="Via, numero, città">
<button type="button" class="btn-geocode" id="btn-geocode"><i class="bx bx-map-pin"></i> Calcola</button>
</div>
<div class="hint" id="geo-hint">Per un indirizzo personalizzato, premi "Calcola" per ottenere le coordinate mappa.</div>
</div>
<?php else : ?>
<div class="col-md-12 mb-3">
<label class="form-label">Indirizzo</label>
<div class="d-flex gap-2">
<input type="text" class="form-control" id="address-input" name="address"
value="<?php echo e($val['address']); ?>" placeholder="Via, numero, città">
<button type="button" class="btn-geocode" id="btn-geocode"><i class="bx bx-map-pin"></i> Calcola coordinate</button>
</div>
<div class="hint" id="geo-hint">Premi "Calcola coordinate" per ottenere lat/lng dall'indirizzo.</div>
</div>
<?php endif; ?>
</div>
<div class="row" id="coords-row">
<div class="col-md-3 mb-3">
<label class="form-label">Latitudine</label>
<input type="text" class="form-control" id="lat-input" name="lat" value="<?php echo e($val['lat']); ?>" placeholder="45.6600">
</div>
<div class="col-md-3 mb-3">
<label class="form-label">Longitudine</label>
<input type="text" class="form-control" id="lng-input" name="lng" value="<?php echo e($val['lng']); ?>" placeholder="8.7900">
</div>
<div class="col-md-6 mb-3">
<label class="form-label">Organizzatore</label>
<input type="text" class="form-control" name="organizer_name"
value="<?php echo e($val['organizer_name']); ?>" placeholder="Lascia vuoto = tu">
</div>
</div>
<div class="mb-3">
<label class="form-label">Descrizione</label>
<div id="editor"></div>
<input type="hidden" name="description" id="description-input">
<div class="hint">Testo formattato, elenchi, link e immagini.</div>
</div>
<!-- Dettagli aggiuntivi (facoltativi) -->
<div class="row">
<div class="col-md-4 mb-3">
<label class="form-label">Istruzioni</label>
<textarea class="form-control" name="instructions" rows="3" placeholder="Come arrivare, orari, note pratiche…"><?php echo e($val['instructions']); ?></textarea>
</div>
<div class="col-md-4 mb-3">
<label class="form-label">Cosa portare</label>
<textarea class="form-control" name="what_to_bring" rows="3" placeholder="Tappetino, acqua, abbigliamento comodo…"><?php echo e($val['what_to_bring']); ?></textarea>
</div>
<div class="col-md-4 mb-3">
<label class="form-label">Per chi è adatto</label>
<textarea class="form-control" name="suitable_for" rows="3" placeholder="Principianti, gravidanza, tutti i livelli…"><?php echo e($val['suitable_for']); ?></textarea>
</div>
</div>
<p class="hint" style="margin-top:-8px;">Questi campi sono facoltativi e verranno usati anche nella card pubblica e nelle email.</p>
<!-- Copertina drag&drop -->
<div class="mb-3">
<label class="form-label">Immagine di copertina</label>
<div class="dropzone" id="cover-dropzone">
<div class="dz-empty" id="dz-empty" style="<?php echo $val['cover_image'] ? 'display:none;' : ''; ?>">
<i class="bx bx-cloud-upload"></i>
<p>Trascina qui l'immagine oppure <span class="dz-browse">sfoglia</span></p>
<small>JPG, PNG, WEBP o GIF · max 5 MB</small>
</div>
<div class="dz-preview" id="dz-preview" style="<?php echo $val['cover_image'] ? '' : 'display:none;'; ?>">
<img id="dz-img" src="<?php echo e($val['cover_image']); ?>" alt="Copertina">
<div><button type="button" class="dz-remove" id="dz-remove"><i class="bx bx-trash"></i> Rimuovi</button></div>
</div>
<div class="dz-progress" id="dz-progress" style="display:none;"><i class="bx bx-loader bx-spin"></i> Caricamento…</div>
<input type="file" id="cover-file" accept="image/*" hidden>
</div>
<input type="hidden" name="cover_image" id="cover_image" value="<?php echo e($val['cover_image']); ?>">
<div class="hint">Immagine principale mostrata nella card.</div>
</div>
<!-- Galleria foto -->
<div class="mb-3">
<label class="form-label">Galleria foto</label>
<div class="dropzone" id="gallery-dropzone">
<div class="dz-empty">
<i class="bx bx-images"></i>
<p>Trascina qui le immagini oppure <span class="dz-browse">sfoglia</span></p>
<small>Più immagini insieme · JPG, PNG, WEBP o GIF · max 5 MB l'una</small>
</div>
<input type="file" id="gallery-file" accept="image/*" multiple hidden>
</div>
<div class="gallery-grid" id="gallery-grid"></div>
<input type="hidden" name="gallery" id="gallery-input" value="<?php echo e($val['gallery']); ?>">
</div>
<div class="row">
<div class="col-md-3 mb-3">
<label class="form-label">Costo</label>
<input type="number" step="0.01" min="0" class="form-control" name="cost" value="<?php echo e($val['cost']); ?>">
<div class="hint">0 = gratuito.</div>
</div>
<div class="col-md-3 mb-3">
<label class="form-label">Valuta</label>
<select name="currency" class="form-select">
<?php foreach (['EUR', 'USD', 'GBP', 'CHF'] as $cur) : ?>
<option value="<?php echo $cur; ?>" <?php echo $val['currency'] === $cur ? 'selected' : ''; ?>><?php echo $cur; ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-3 mb-3">
<label class="form-label">Posti totali *</label>
<input type="number" min="1" class="form-control" name="max_seats" required value="<?php echo e($val['max_seats']); ?>" placeholder="Es. 15">
</div>
<div class="col-md-3 mb-3">
<label class="form-label">Max posti per persona</label>
<input type="number" min="1" class="form-control" name="max_per_booking" value="<?php echo e($val['max_per_booking']); ?>">
</div>
</div>
<div class="row">
<div class="col-md-3 mb-3">
<label class="form-label">Colore card</label>
<input type="color" class="form-control" name="color" value="<?php echo e($val['color']); ?>">
</div>
<div class="col-md-5 mb-3">
<label class="form-label">Stato</label>
<select name="status" class="form-select">
<option value="draft" <?php echo $val['status'] === 'draft' ? 'selected' : ''; ?>>Bozza</option>
<option value="published" <?php echo $val['status'] === 'published' ? 'selected' : ''; ?>>Pubblicato</option>
<option value="cancelled" <?php echo $val['status'] === 'cancelled' ? 'selected' : ''; ?>>Annullato</option>
</select>
</div>
<div class="col-md-4 mb-3">
<label class="form-label">Lista d'attesa</label>
<div class="form-check form-switch" style="padding-top:8px;">
<input class="form-check-input" type="checkbox" role="switch" id="waitlist_enabled" name="waitlist_enabled" value="Y" <?php echo $val['waitlist_enabled'] ? 'checked' : ''; ?>>
<label class="form-check-label" for="waitlist_enabled" style="font-weight:400;">Consenti iscrizione a evento pieno</label>
</div>
</div>
</div>
<!-- Apertura programmata -->
<div class="row">
<div class="col-md-5 mb-3">
<label class="form-label">Apertura prenotazioni</label>
<div class="form-check form-switch" style="padding-top:8px;">
<input class="form-check-input" type="checkbox" role="switch" id="schedule_enabled" name="schedule_enabled" value="Y" <?php echo $val['schedule_enabled'] ? 'checked' : ''; ?>>
<label class="form-check-label" for="schedule_enabled" style="font-weight:400;">Apri in una data futura (programmato)</label>
</div>
</div>
<div class="col-md-4 mb-3" id="publish-at-wrap" style="<?php echo $val['schedule_enabled'] ? '' : 'display:none;'; ?>">
<label class="form-label">Data/ora apertura</label>
<input type="text" class="form-control" id="publish_at" name="publish_at" value="<?php echo e($val['publish_at']); ?>" placeholder="Quando aprire le prenotazioni" autocomplete="off">
</div>
</div>
<p class="hint" style="margin-top:-8px;">Se attivo, l'evento resta "Programmato" (visibile ma non prenotabile) finché il cron non lo apre alla data indicata.</p>
<!-- Promemoria e lista d'attesa (override per evento) -->
<div class="row">
<div class="col-md-4 mb-3">
<label class="form-label">Promemoria automatico</label>
<select name="reminder_enabled" class="form-select">
<option value="">Predefinito (scuola: <?php echo $defReminderEnabled ? 'attivo' : 'disattivo'; ?>)</option>
<option value="Y" <?php echo $val['reminder_enabled'] === 'Y' ? 'selected' : ''; ?>>Attivo</option>
<option value="N" <?php echo $val['reminder_enabled'] === 'N' ? 'selected' : ''; ?>>Disattivo</option>
</select>
</div>
<div class="col-md-4 mb-3">
<label class="form-label">Promemoria: ore prima</label>
<input type="number" min="1" max="168" class="form-control" name="reminder_hours" value="<?php echo e($val['reminder_hours']); ?>" placeholder="Default: <?php echo $defReminderHours; ?>">
</div>
<div class="col-md-4 mb-3">
<label class="form-label">Scadenza offerta lista d'attesa (ore)</label>
<input type="number" min="1" max="336" class="form-control" name="offer_expiry_hours" value="<?php echo e($val['offer_expiry_hours']); ?>" placeholder="Default: <?php echo $defOfferHours; ?>">
</div>
</div>
<p class="hint" style="margin-top:-8px;">Lascia vuoto o "Predefinito" per usare le impostazioni generali (Opzioni Eventi).</p>
<div class="d-flex justify-content-between mt-2">
<a href="events.php" class="btn btn-light btn-back"><i class="bx bx-arrow-back"></i> Indietro</a>
<button type="submit" class="btn btn-primary btn-save">
<i class="bx bx-save"></i> <?php echo $isEdit ? 'Salva modifiche' : 'Crea evento'; ?>
</button>
</div>
</form>
</div>
</div>
</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>
/* ---------- Date/ora con flatpickr (fine = inizio + 1h) ---------- */
var fpOpts = {
enableTime: true,
time_24hr: true,
minuteIncrement: 15,
dateFormat: "Y-m-d H:i",
altInput: true,
altFormat: "l j F Y, H:i",
locale: "it"
};
var endPicker = flatpickr("#ends_at", fpOpts);
flatpickr("#publish_at", fpOpts);
document.getElementById('schedule_enabled').addEventListener('change', function() {
document.getElementById('publish-at-wrap').style.display = this.checked ? '' : 'none';
});
var startPicker = flatpickr("#starts_at", Object.assign({}, fpOpts, {
onChange: function(dates) {
if (!dates[0]) return;
endPicker.set('minDate', dates[0]);
var end = endPicker.selectedDates[0];
if (!end || end <= dates[0]) {
endPicker.setDate(new Date(dates[0].getTime() + 60 * 60 * 1000), false);
}
}
}));
/* ---------- Sede / indirizzo ---------- */
var addressInput = document.getElementById('address-input');
var latInput = document.getElementById('lat-input');
var lngInput = document.getElementById('lng-input');
var geoHint = document.getElementById('geo-hint');
var locationSelect = document.getElementById('location-select');
var currentAddress = <?php echo json_encode($val['address'], JSON_UNESCAPED_UNICODE); ?>;
function clearCoords() {
latInput.value = '';
lngInput.value = '';
}
// Dichiarazione (hoisted): usata anche in fase di preselezione qui sotto.
function applyLocation() {
if (!locationSelect) return;
var opt = locationSelect.options[locationSelect.selectedIndex];
if (locationSelect.value === 'custom') {
// Indirizzo personalizzato: campo vuoto, nessuna coordinata finché non premi "Calcola"
addressInput.value = '';
clearCoords();
geoHint.textContent = 'Scrivi l\'indirizzo e premi "Calcola" per le coordinate.';
addressInput.focus();
} else {
// Sede salvata: indirizzo e coordinate REALI della sede
addressInput.value = opt.getAttribute('data-address') || '';
latInput.value = opt.getAttribute('data-lat') || '';
lngInput.value = opt.getAttribute('data-lng') || '';
geoHint.textContent = (latInput.value && lngInput.value) ?
'Coordinate della sede caricate.' :
'Questa sede non ha coordinate salvate: premi "Calcola".';
}
}
if (locationSelect) {
var matched = null;
Array.prototype.forEach.call(locationSelect.options, function(opt) {
if (opt.value !== 'custom' && currentAddress !== '' && opt.getAttribute('data-address') === currentAddress) {
matched = opt.value;
}
});
if (currentAddress === '') {
locationSelect.selectedIndex = 0; // nuovo evento -> sede di default (scuola)
applyLocation();
} else if (matched) {
locationSelect.value = matched; // l'indirizzo salvato combacia con una sede
applyLocation();
} else {
locationSelect.value = 'custom'; // indirizzo personalizzato salvato: NON toccare i valori esistenti
}
locationSelect.addEventListener('change', applyLocation);
}
// Se modifichi l'indirizzo a mano, le vecchie coordinate non valgono più:
// le azzero subito, così non restano valori "di default" fuorvianti.
addressInput.addEventListener('input', function() {
clearCoords();
geoHint.textContent = 'Indirizzo modificato: premi "Calcola" per aggiornare le coordinate.';
});
/* ---------- Geocoding indirizzo -> lat/lng ---------- */
document.getElementById('btn-geocode').addEventListener('click', function() {
var q = addressInput.value.trim();
if (q === '') {
addressInput.focus();
return;
}
geoHint.textContent = 'Ricerca coordinate…';
fetch('event_geocode.php?q=' + encodeURIComponent(q))
.then(function(r) {
return r.json();
})
.then(function(data) {
if (data.ok) {
latInput.value = data.lat;
lngInput.value = data.lng;
latInput.classList.add('coord-flash');
lngInput.classList.add('coord-flash');
setTimeout(function() {
latInput.classList.remove('coord-flash');
lngInput.classList.remove('coord-flash');
}, 900);
geoHint.innerHTML = '<span style="color:#1a7f52;font-weight:600;">✓ Coordinate aggiornate:</span> ' +
data.lat + ', ' + data.lng + (data.display ? ' — ' + data.display : '');
} else {
geoHint.textContent = data.error || 'Indirizzo non trovato.';
}
})
.catch(function() {
geoHint.textContent = 'Errore durante il geocoding.';
});
});
/* ---------- Editor Quill ---------- */
var quill = new Quill('#editor', {
theme: 'snow',
modules: {
toolbar: [
[{
'header': [2, 3, false]
}],
['bold', 'italic', 'underline'],
[{
'list': 'ordered'
}, {
'list': 'bullet'
}],
['link', 'image'],
['clean']
]
}
});
var initialHtml = <?php echo json_encode($val['description'], JSON_UNESCAPED_UNICODE | JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP); ?>;
if (initialHtml && initialHtml.trim() !== '') {
quill.clipboard.dangerouslyPasteHTML(initialHtml);
}
/* ---------- Copertina drag&drop ---------- */
var dz = document.getElementById('cover-dropzone');
var fileInput = document.getElementById('cover-file');
var dzEmpty = document.getElementById('dz-empty');
var dzPreview = document.getElementById('dz-preview');
var dzProgress = document.getElementById('dz-progress');
var dzImg = document.getElementById('dz-img');
var coverHidden = document.getElementById('cover_image');
dz.addEventListener('click', function(ev) {
if (ev.target.id === 'dz-remove' || ev.target.closest('#dz-remove')) return;
fileInput.click();
});
['dragenter', 'dragover'].forEach(function(t) {
dz.addEventListener(t, function(ev) {
ev.preventDefault();
dz.classList.add('dragover');
});
});
['dragleave', 'drop'].forEach(function(t) {
dz.addEventListener(t, function(ev) {
ev.preventDefault();
dz.classList.remove('dragover');
});
});
dz.addEventListener('drop', function(ev) {
if (ev.dataTransfer.files.length) uploadCover(ev.dataTransfer.files[0]);
});
fileInput.addEventListener('change', function() {
if (fileInput.files.length) uploadCover(fileInput.files[0]);
});
document.getElementById('dz-remove').addEventListener('click', function() {
coverHidden.value = '';
dzImg.src = '';
dzPreview.style.display = 'none';
dzEmpty.style.display = '';
fileInput.value = '';
});
function uploadCover(file) {
if (!file.type.startsWith('image/')) {
Swal.fire('Formato non valido', 'Seleziona un file immagine.', 'warning');
return;
}
if (file.size > 5 * 1024 * 1024) {
Swal.fire('File troppo grande', "L'immagine supera i 5 MB.", 'warning');
return;
}
dzEmpty.style.display = 'none';
dzPreview.style.display = 'none';
dzProgress.style.display = 'block';
var fd = new FormData();
fd.append('cover', file);
fetch('event_upload_cover.php', {
method: 'POST',
body: fd
})
.then(function(r) {
return r.json();
})
.then(function(data) {
dzProgress.style.display = 'none';
if (data.ok) {
coverHidden.value = data.path;
dzImg.src = data.path + '?t=' + Date.now();
dzPreview.style.display = 'block';
} else {
dzEmpty.style.display = '';
Swal.fire('Errore', data.error || 'Caricamento non riuscito.', 'error');
}
})
.catch(function() {
dzProgress.style.display = 'none';
dzEmpty.style.display = '';
Swal.fire('Errore', 'Caricamento non riuscito.', 'error');
});
}
/* ---------- Galleria foto (multi-upload) ---------- */
var galleryInput = document.getElementById('gallery-input');
var galleryGrid = document.getElementById('gallery-grid');
var galleryFile = document.getElementById('gallery-file');
var galleryDz = document.getElementById('gallery-dropzone');
var galleryItems = [];
try {
galleryItems = JSON.parse(galleryInput.value || '[]');
if (!Array.isArray(galleryItems)) galleryItems = [];
} catch (e) {
galleryItems = [];
}
function renderGallery() {
galleryGrid.innerHTML = '';
galleryItems.forEach(function(path, idx) {
var cell = document.createElement('div');
cell.className = 'gallery-cell';
var img = document.createElement('img');
img.src = path;
var btn = document.createElement('button');
btn.type = 'button';
btn.className = 'gallery-remove';
btn.setAttribute('data-idx', idx);
btn.innerHTML = '&times;';
cell.appendChild(img);
cell.appendChild(btn);
galleryGrid.appendChild(cell);
});
galleryInput.value = JSON.stringify(galleryItems);
}
renderGallery();
galleryGrid.addEventListener('click', function(ev) {
var btn = ev.target.closest('.gallery-remove');
if (!btn) return;
galleryItems.splice(parseInt(btn.getAttribute('data-idx'), 10), 1);
renderGallery();
});
galleryDz.addEventListener('click', function() {
galleryFile.click();
});
['dragenter', 'dragover'].forEach(function(t) {
galleryDz.addEventListener(t, function(ev) {
ev.preventDefault();
galleryDz.classList.add('dragover');
});
});
['dragleave', 'drop'].forEach(function(t) {
galleryDz.addEventListener(t, function(ev) {
ev.preventDefault();
galleryDz.classList.remove('dragover');
});
});
galleryDz.addEventListener('drop', function(ev) {
if (ev.dataTransfer.files.length) uploadGallery(ev.dataTransfer.files);
});
galleryFile.addEventListener('change', function() {
if (galleryFile.files.length) uploadGallery(galleryFile.files);
});
function uploadGallery(files) {
Array.prototype.forEach.call(files, function(file) {
if (!file.type.startsWith('image/')) return;
if (file.size > 5 * 1024 * 1024) {
Swal.fire('File troppo grande', file.name + ' supera i 5 MB.', 'warning');
return;
}
var fd = new FormData();
fd.append('cover', file); // stesso handler della copertina
fetch('event_upload_cover.php', {
method: 'POST',
body: fd
})
.then(function(r) {
return r.json();
})
.then(function(data) {
if (data.ok) {
galleryItems.push(data.path);
renderGallery();
} else {
Swal.fire('Errore', data.error || 'Caricamento non riuscito.', 'error');
}
})
.catch(function() {
Swal.fire('Errore', 'Caricamento non riuscito.', 'error');
});
});
galleryFile.value = '';
}
/* ---------- Slug suggerito dal titolo ---------- */
(function() {
var titleInput = document.querySelector('input[name="title"]');
var slugInput = document.querySelector('input[name="slug"]');
if (!titleInput || !slugInput) return;
// In modifica lo slug esiste già: lo consideriamo "toccato" e non lo sovrascriviamo.
var slugEdited = slugInput.value.trim() !== '';
function slugify(s) {
return s.toString().normalize('NFD').replace(/[\u0300-\u036f]/g, '')
.toLowerCase().trim().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');
}
slugInput.addEventListener('input', function() {
slugEdited = slugInput.value.trim() !== '';
});
titleInput.addEventListener('input', function() {
if (!slugEdited) slugInput.value = slugify(titleInput.value);
});
})();
/* ---------- Submit ---------- */
document.getElementById('event-form').addEventListener('submit', function() {
var html = quill.root.innerHTML;
document.getElementById('description-input').value = (html === '<p><br></p>') ? '' : html;
});
</script>
</body>
</html>