552 lines
24 KiB
PHP
552 lines
24 KiB
PHP
<?php
|
|
// Forza la visualizzazione degli errori (solo dev)
|
|
ini_set('display_errors', 1);
|
|
ini_set('display_startup_errors', 1);
|
|
error_reporting(E_ALL);
|
|
|
|
if (session_status() === PHP_SESSION_NONE) {
|
|
session_start();
|
|
}
|
|
|
|
include('include/headscript.php');
|
|
|
|
// Connessione DB
|
|
$dbHandler = DBHandlerSelect::getInstance();
|
|
$pdo = $dbHandler->getConnection();
|
|
|
|
// Verifica utente loggato
|
|
if (!isset($iduserlogin)) {
|
|
header("Location: login.php");
|
|
exit;
|
|
}
|
|
|
|
// Controlla se esiste almeno un salone
|
|
$stmt = $pdo->prepare("SELECT COUNT(*) FROM shops WHERE owner_id = ?");
|
|
$stmt->execute([$iduserlogin]);
|
|
if ((int)$stmt->fetchColumn() === 0) {
|
|
header("Location: onboarding_salon.php");
|
|
exit;
|
|
}
|
|
|
|
// Prendi il salone
|
|
$stmt = $pdo->prepare("
|
|
SELECT *
|
|
FROM shops
|
|
WHERE owner_id = ?
|
|
ORDER BY created_at ASC
|
|
LIMIT 1
|
|
");
|
|
$stmt->execute([$iduserlogin]);
|
|
$shop = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
|
|
if (!$shop) {
|
|
die("Errore: salone non trovato.");
|
|
}
|
|
|
|
$shop_id = (int)$shop['id'];
|
|
|
|
// ===========================
|
|
// Helper: Flash messages
|
|
// ===========================
|
|
function setFlash(string $type, string $text): void
|
|
{
|
|
$_SESSION['flash'] = ['type' => $type, 'text' => $text];
|
|
}
|
|
|
|
function getFlash(): ?array
|
|
{
|
|
if (!isset($_SESSION['flash'])) return null;
|
|
$f = $_SESSION['flash'];
|
|
unset($_SESSION['flash']);
|
|
return $f;
|
|
}
|
|
|
|
// ===========================
|
|
// Helper: Ridimensiona immagine (come nel tuo template)
|
|
// ===========================
|
|
function resizeImage($source_path, $dest_path, $max_width = 800)
|
|
{
|
|
list($width, $height, $type) = getimagesize($source_path);
|
|
if ($width <= $max_width) {
|
|
copy($source_path, $dest_path);
|
|
return;
|
|
}
|
|
$new_width = $max_width;
|
|
$new_height = (int)(($height * $new_width) / $width);
|
|
switch ($type) {
|
|
case IMAGETYPE_JPEG:
|
|
$source = imagecreatefromjpeg($source_path);
|
|
break;
|
|
case IMAGETYPE_PNG:
|
|
$source = imagecreatefrompng($source_path);
|
|
break;
|
|
case IMAGETYPE_GIF:
|
|
$source = imagecreatefromgif($source_path);
|
|
break;
|
|
default:
|
|
throw new Exception("Formato non supportato.");
|
|
}
|
|
$dest = imagecreatetruecolor($new_width, $new_height);
|
|
if ($type == IMAGETYPE_PNG) {
|
|
imagealphablending($dest, false);
|
|
imagesavealpha($dest, true);
|
|
}
|
|
imagecopyresampled($dest, $source, 0, 0, 0, 0, $new_width, $new_height, $width, $height);
|
|
switch ($type) {
|
|
case IMAGETYPE_JPEG:
|
|
imagejpeg($dest, $dest_path, 90);
|
|
break;
|
|
case IMAGETYPE_PNG:
|
|
imagepng($dest, $dest_path);
|
|
break;
|
|
case IMAGETYPE_GIF:
|
|
imagegif($dest, $dest_path);
|
|
break;
|
|
}
|
|
imagedestroy($source);
|
|
imagedestroy($dest);
|
|
}
|
|
|
|
// ===========================
|
|
// Helper: Geolocalizzazione con Nominatim
|
|
// ===========================
|
|
function geocodeAddress(string $address): array
|
|
{
|
|
$url = 'https://nominatim.openstreetmap.org/search';
|
|
$params = http_build_query([
|
|
'q' => $address,
|
|
'format' => 'json',
|
|
'limit' => 1,
|
|
'addressdetails' => 1,
|
|
'countrycodes' => 'it',
|
|
]);
|
|
|
|
$ch = curl_init($url . '?' . $params);
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
curl_setopt($ch, CURLOPT_USERAGENT, 'HairBook Salon Manager/1.0 (contact: info@hairbook.it)');
|
|
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
|
|
|
|
$response = curl_exec($ch);
|
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
curl_close($ch);
|
|
|
|
if ($httpCode !== 200 || !$response) {
|
|
return ['success' => false, 'message' => 'Errore connessione Nominatim'];
|
|
}
|
|
|
|
$data = json_decode($response, true);
|
|
|
|
if (empty($data) || !isset($data[0])) {
|
|
return ['success' => false, 'message' => 'Indirizzo non trovato'];
|
|
}
|
|
|
|
$result = $data[0];
|
|
return [
|
|
'success' => true,
|
|
'lat' => (float)$result['lat'],
|
|
'lon' => (float)$result['lon'],
|
|
'display' => $result['display_name'] ?? ''
|
|
];
|
|
}
|
|
|
|
// ===========================
|
|
// POST - Salva profilo + geocode se necessario
|
|
// ===========================
|
|
$flash = null;
|
|
if ($_SERVER['REQUEST_METHOD'] === 'POST' && !isset($_POST['action'])) { // normale submit form
|
|
try {
|
|
$name = trim($_POST['name'] ?? '');
|
|
$description = trim($_POST['description'] ?? '');
|
|
$address = trim($_POST['address'] ?? '');
|
|
$address_extra = trim($_POST['address_extra'] ?? '');
|
|
$city = trim($_POST['city'] ?? '');
|
|
$province = strtoupper(trim($_POST['province'] ?? ''));
|
|
$zip_code = trim($_POST['zip_code'] ?? '');
|
|
$phone = trim($_POST['phone'] ?? '');
|
|
$mobile = trim($_POST['mobile'] ?? '');
|
|
$email = trim($_POST['email'] ?? '');
|
|
$website = trim($_POST['website'] ?? '');
|
|
$instagram = trim(ltrim($_POST['instagram'] ?? '', '@'));
|
|
$facebook = trim($_POST['facebook'] ?? '');
|
|
$google_maps_url = trim($_POST['google_maps_url'] ?? '');
|
|
$latitude = !empty($_POST['latitude']) ? (float)$_POST['latitude'] : null;
|
|
$longitude = !empty($_POST['longitude']) ? (float)$_POST['longitude'] : null;
|
|
$active = isset($_POST['active']) ? 1 : 0;
|
|
|
|
// Validazioni
|
|
if ($name === '') {
|
|
throw new Exception("Nome salone obbligatorio.");
|
|
}
|
|
if ($city === '') {
|
|
throw new Exception("Città obbligatoria.");
|
|
}
|
|
|
|
// Tenta geocode automatico se lat/lon vuoti
|
|
if ($latitude === null || $longitude === null) {
|
|
$full_address = implode(', ', array_filter([
|
|
$address,
|
|
$address_extra,
|
|
$zip_code,
|
|
$city,
|
|
$province ? $province : '',
|
|
'Italia'
|
|
]));
|
|
|
|
$geocode = geocodeAddress($full_address);
|
|
if ($geocode['success']) {
|
|
$latitude = $geocode['lat'];
|
|
$longitude = $geocode['lon'];
|
|
setFlash('info', "Coordinate calcolate automaticamente!");
|
|
}
|
|
}
|
|
|
|
// Upload logo
|
|
$logo = $shop['logo'];
|
|
if (isset($_FILES['logo']) && $_FILES['logo']['error'] === UPLOAD_ERR_OK) {
|
|
$file = $_FILES['logo'];
|
|
$ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
|
|
if (in_array($ext, ['jpg', 'jpeg', 'png', 'gif'])) {
|
|
$new_filename = "logos/{$shop_id}-" . time() . "." . $ext;
|
|
resizeImage($file['tmp_name'], $new_filename, 400);
|
|
if ($logo && file_exists($logo)) @unlink($logo);
|
|
$logo = $new_filename;
|
|
}
|
|
}
|
|
|
|
// Upload cover
|
|
$cover = $shop['cover_image'];
|
|
if (isset($_FILES['cover_image']) && $_FILES['cover_image']['error'] === UPLOAD_ERR_OK) {
|
|
$file = $_FILES['cover_image'];
|
|
$ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
|
|
if (in_array($ext, ['jpg', 'jpeg', 'png', 'gif'])) {
|
|
$new_filename = "covers/{$shop_id}-" . time() . "." . $ext;
|
|
resizeImage($file['tmp_name'], $new_filename, 1200);
|
|
if ($cover && file_exists($cover)) @unlink($cover);
|
|
$cover = $new_filename;
|
|
}
|
|
}
|
|
|
|
// Update DB
|
|
$stmt = $pdo->prepare("
|
|
UPDATE shops SET
|
|
name = ?, description = ?, address = ?, address_extra = ?, city = ?, province = ?,
|
|
zip_code = ?, phone = ?, mobile = ?, email = ?, website = ?, instagram = ?, facebook = ?,
|
|
google_maps_url = ?, latitude = ?, longitude = ?, logo = ?, cover_image = ?, active = ?,
|
|
updated_at = NOW()
|
|
WHERE id = ? AND owner_id = ?
|
|
");
|
|
$ok = $stmt->execute([
|
|
$name,
|
|
$description ?: null,
|
|
$address,
|
|
$address_extra ?: null,
|
|
$city,
|
|
$province,
|
|
$zip_code,
|
|
$phone ?: null,
|
|
$mobile ?: null,
|
|
$email ?: null,
|
|
$website ?: null,
|
|
$instagram ?: null,
|
|
$facebook ?: null,
|
|
$google_maps_url ?: null,
|
|
$latitude,
|
|
$longitude,
|
|
$logo,
|
|
$cover,
|
|
$active,
|
|
$shop_id,
|
|
$iduserlogin
|
|
]);
|
|
|
|
setFlash($ok ? 'success' : 'danger', $ok ? "Profilo aggiornato!" : "Errore salvataggio.");
|
|
header("Location: salon_profile.php");
|
|
exit;
|
|
} catch (Exception $e) {
|
|
setFlash('danger', $e->getMessage());
|
|
header("Location: salon_profile.php");
|
|
exit;
|
|
}
|
|
}
|
|
|
|
// Flash
|
|
$flash = getFlash();
|
|
?>
|
|
|
|
<!doctype html>
|
|
<html lang="it">
|
|
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<link rel="icon" href="assets/images/favicon-32x32.png" type="image/png" />
|
|
<?php include('cssinclude.php'); ?>
|
|
<?php include('siteinfo.php'); ?>
|
|
<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>
|
|
<title>Profilo Salone - <?= htmlspecialchars($shop['name']) ?></title>
|
|
<style>
|
|
#map {
|
|
height: 350px;
|
|
border: 1px solid #dee2e6;
|
|
border-radius: 8px;
|
|
margin-top: 10px;
|
|
}
|
|
|
|
.form-label {
|
|
font-weight: 600;
|
|
}
|
|
|
|
.input-group-text {
|
|
background: #f8f9fa;
|
|
}
|
|
</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-light d-flex justify-content-between align-items-center">
|
|
<h6 class="mb-0">Modifica Profilo Salone</h6>
|
|
<a href="salon_dashboard.php" class="btn btn-outline-secondary">
|
|
<i class="bx bx-arrow-back me-1"></i> Dashboard
|
|
</a>
|
|
</div>
|
|
|
|
<div class="card-body">
|
|
<?php if ($flash): ?>
|
|
<div class="alert alert-<?= htmlspecialchars($flash['type']) ?> alert-dismissible fade show">
|
|
<?= htmlspecialchars($flash['text']) ?>
|
|
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<form action="" method="POST" enctype="multipart/form-data">
|
|
<div class="row g-4">
|
|
<!-- Colonna sinistra: dati principali -->
|
|
<div class="col-lg-8">
|
|
<div class="mb-3">
|
|
<label class="form-label">Nome Salone <span class="text-danger">*</span></label>
|
|
<input type="text" class="form-control" name="name" value="<?= htmlspecialchars($shop['name']) ?>" required>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label class="form-label">Descrizione</label>
|
|
<textarea class="form-control" name="description" rows="4"><?= htmlspecialchars($shop['description'] ?? '') ?></textarea>
|
|
</div>
|
|
|
|
<div class="row g-3">
|
|
<div class="col-md-8">
|
|
<label class="form-label">Indirizzo <span class="text-danger">*</span></label>
|
|
<input type="text" class="form-control" name="address" id="address" value="<?= htmlspecialchars($shop['address']) ?>" required>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label">Indirizzo extra</label>
|
|
<input type="text" class="form-control" name="address_extra" value="<?= htmlspecialchars($shop['address_extra'] ?? '') ?>">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row g-3">
|
|
<div class="col-md-6">
|
|
<label class="form-label">Città <span class="text-danger">*</span></label>
|
|
<input type="text" class="form-control" name="city" id="city" value="<?= htmlspecialchars($shop['city']) ?>" required>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<label class="form-label">Provincia</label>
|
|
<input type="text" class="form-control text-uppercase" name="province" maxlength="2" value="<?= htmlspecialchars($shop['province'] ?? '') ?>">
|
|
</div>
|
|
<div class="col-md-3">
|
|
<label class="form-label">CAP</label>
|
|
<input type="text" class="form-control" name="zip_code" value="<?= htmlspecialchars($shop['zip_code']) ?>">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row g-3">
|
|
<div class="col-md-6">
|
|
<label class="form-label">Telefono</label>
|
|
<input type="text" class="form-control" name="phone" value="<?= htmlspecialchars($shop['phone'] ?? '') ?>">
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label class="form-label">Cellulare / WhatsApp</label>
|
|
<input type="text" class="form-control" name="mobile" value="<?= htmlspecialchars($shop['mobile'] ?? '') ?>">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row g-3">
|
|
<div class="col-md-6">
|
|
<label class="form-label">Email</label>
|
|
<input type="email" class="form-control" name="email" value="<?= htmlspecialchars($shop['email'] ?? '') ?>">
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label class="form-label">Sito Web</label>
|
|
<input type="url" class="form-control" name="website" value="<?= htmlspecialchars($shop['website'] ?? '') ?>">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row g-3">
|
|
<div class="col-md-6">
|
|
<label class="form-label">Instagram (solo username)</label>
|
|
<div class="input-group">
|
|
<span class="input-group-text">@</span>
|
|
<input type="text" class="form-control" name="instagram" value="<?= htmlspecialchars($shop['instagram'] ?? '') ?>">
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label class="form-label">Facebook</label>
|
|
<input type="text" class="form-control" name="facebook" value="<?= htmlspecialchars($shop['facebook'] ?? '') ?>">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label class="form-label">Link Google Maps (opzionale)</label>
|
|
<input type="url" class="form-control" name="google_maps_url" value="<?= htmlspecialchars($shop['google_maps_url'] ?? '') ?>">
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Colonna destra: immagini + mappa -->
|
|
<div class="col-lg-4">
|
|
<div class="mb-3">
|
|
<label class="form-label">Logo Salone (quadrato)</label>
|
|
<?php if ($shop['logo']): ?>
|
|
<img src="<?= htmlspecialchars($shop['logo']) ?>" alt="Logo" class="img-fluid rounded mb-2" style="max-height:120px;">
|
|
<?php endif; ?>
|
|
<input type="file" class="form-control" name="logo" accept="image/*">
|
|
</div>
|
|
|
|
<div class="mb-4">
|
|
<label class="form-label">Immagine di Copertina (orizzontale)</label>
|
|
<?php if ($shop['cover_image']): ?>
|
|
<img src="<?= htmlspecialchars($shop['cover_image']) ?>" alt="Cover" class="img-fluid rounded mb-2" style="max-height:120px; width:100%;">
|
|
<?php endif; ?>
|
|
<input type="file" class="form-control" name="cover_image" accept="image/*">
|
|
</div>
|
|
|
|
<!-- Geolocalizzazione -->
|
|
<div class="mb-3">
|
|
<label class="form-label">Coordinate</label>
|
|
<div class="input-group mb-2">
|
|
<input type="number" step="any" class="form-control" name="latitude" id="lat"
|
|
value="<?= htmlspecialchars($shop['latitude'] ?? '') ?>" placeholder="Latitudine">
|
|
<input type="number" step="any" class="form-control" name="longitude" id="lng"
|
|
value="<?= htmlspecialchars($shop['longitude'] ?? '') ?>" placeholder="Longitudine">
|
|
</div>
|
|
<button type="button" class="btn btn-outline-info w-100" id="geocodeBtn">
|
|
<i class="bx bx-current-location"></i> Geolocalizza indirizzo
|
|
</button>
|
|
<small class="form-text text-muted mt-1 d-block">
|
|
Clicca per calcolare lat/lon da indirizzo (usa OpenStreetMap)
|
|
</small>
|
|
</div>
|
|
|
|
<!-- Mappa -->
|
|
<div id="map"></div>
|
|
|
|
<div class="form-check mt-4">
|
|
<input class="form-check-input" type="checkbox" name="active" id="active" <?= $shop['active'] ? 'checked' : '' ?>>
|
|
<label class="form-check-label fw-bold" for="active">
|
|
Salone attivo e visibile online
|
|
</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="d-grid mt-5">
|
|
<button type="submit" class="btn btn-primary btn-lg">Salva Modifiche</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<?php include('include/footer.php'); ?>
|
|
</div>
|
|
|
|
<?php include('jsinclude.php'); ?>
|
|
|
|
<script>
|
|
// Inizializza mappa
|
|
var map = L.map('map').setView([45.4642, 9.1900], 13); // default Milano
|
|
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
|
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>'
|
|
}).addTo(map);
|
|
|
|
var marker = null;
|
|
|
|
<?php if ($shop['latitude'] && $shop['longitude']): ?>
|
|
var initLat = <?= (float)$shop['latitude'] ?>;
|
|
var initLng = <?= (float)$shop['longitude'] ?>;
|
|
map.setView([initLat, initLng], 15);
|
|
marker = L.marker([initLat, initLng]).addTo(map);
|
|
<?php endif; ?>
|
|
|
|
// Click su mappa → aggiorna coordinate
|
|
map.on('click', function(e) {
|
|
document.getElementById('lat').value = e.latlng.lat.toFixed(8);
|
|
document.getElementById('lng').value = e.latlng.lng.toFixed(8);
|
|
if (marker) map.removeLayer(marker);
|
|
marker = L.marker(e.latlng).addTo(map);
|
|
});
|
|
|
|
// Bottone Geolocalizza
|
|
document.getElementById('geocodeBtn').addEventListener('click', function() {
|
|
const addr = document.querySelector('[name="address"]').value.trim();
|
|
const extra = document.querySelector('[name="address_extra"]').value.trim();
|
|
const zip = document.querySelector('[name="zip_code"]').value.trim();
|
|
const city = document.querySelector('[name="city"]').value.trim();
|
|
const prov = document.querySelector('[name="province"]').value.trim().toUpperCase();
|
|
|
|
if (!addr || !city) {
|
|
alert("Compila almeno Indirizzo e Città.");
|
|
return;
|
|
}
|
|
|
|
const full = [addr, extra, zip, city, prov ? prov : '', 'Italia'].filter(Boolean).join(', ');
|
|
|
|
fetch('', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/x-www-form-urlencoded'
|
|
},
|
|
body: 'action=geocode&address=' + encodeURIComponent(full)
|
|
})
|
|
.then(r => r.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
document.getElementById('lat').value = data.lat;
|
|
document.getElementById('lng').value = data.lon;
|
|
map.setView([data.lat, data.lon], 15);
|
|
if (marker) map.removeLayer(marker);
|
|
marker = L.marker([data.lat, data.lon]).addTo(map);
|
|
alert("Trovato!\nLat: " + data.lat + "\nLon: " + data.lon);
|
|
} else {
|
|
alert("Non trovato: " + (data.message || "Indirizzo non riconosciuto"));
|
|
}
|
|
})
|
|
.catch(err => alert("Errore: " + err));
|
|
});
|
|
</script>
|
|
</body>
|
|
|
|
</html>
|
|
|
|
<?php
|
|
// Gestione AJAX geocode (in fondo alla pagina)
|
|
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'geocode') {
|
|
$addr = trim($_POST['address'] ?? '');
|
|
if ($addr === '') {
|
|
echo json_encode(['success' => false, 'message' => 'Indirizzo vuoto']);
|
|
exit;
|
|
}
|
|
|
|
$result = geocodeAddress($addr);
|
|
header('Content-Type: application/json');
|
|
echo json_encode($result);
|
|
exit;
|
|
}
|
|
?>
|