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 primo salone (o quello attivo)
$stmt = $pdo->prepare("
SELECT id, name
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'];
$shop_name = $shop['name'];
// ===========================
// Helpers
// ===========================
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;
}
// slug semplice e robusto (senza intl)
function makeSlug(string $str): string
{
$str = trim($str);
$str = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $str);
$str = strtolower($str);
$str = preg_replace('/[^a-z0-9]+/', '-', $str);
$str = trim($str, '-');
return $str ?: 'servizio';
}
// assicura slug unico per shop
function uniqueSlug(PDO $pdo, int $shop_id, string $baseSlug, int $excludeId = 0): string
{
$slug = $baseSlug;
$i = 1;
while (true) {
if ($excludeId > 0) {
$stmt = $pdo->prepare("SELECT COUNT(*) FROM services WHERE shop_id = ? AND slug = ? AND id != ?");
$stmt->execute([$shop_id, $slug, $excludeId]);
} else {
$stmt = $pdo->prepare("SELECT COUNT(*) FROM services WHERE shop_id = ? AND slug = ?");
$stmt->execute([$shop_id, $slug]);
}
if ((int)$stmt->fetchColumn() === 0) break;
$i++;
$slug = $baseSlug . '-' . $i;
}
return $slug;
}
function clampInt($val, int $min, int $max, int $fallback): int
{
if ($val === null || $val === '') return $fallback;
if (!is_numeric($val)) return $fallback;
$n = (int)$val;
if ($n < $min) return $min;
if ($n > $max) return $max;
return $n;
}
function normalizeMoney($val): ?string
{
if ($val === null) return null;
$val = trim((string)$val);
if ($val === '') return null;
$val = str_replace(',', '.', $val);
if (!preg_match('/^\d+(\.\d{1,2})?$/', $val)) return null;
return $val;
}
// ===========================
// POST actions (add/edit/delete)
// ===========================
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
$action = $_POST['action'];
try {
if ($action === 'add' || $action === 'edit') {
$id = ($action === 'edit') ? (int)($_POST['id'] ?? 0) : 0;
$name = trim($_POST['name'] ?? '');
$description = trim($_POST['description'] ?? '');
$duration = clampInt($_POST['duration_minutes'] ?? null, 5, 600, 30);
$price = normalizeMoney($_POST['price'] ?? null);
$price_max = normalizeMoney($_POST['price_max'] ?? null);
$category = trim($_POST['category'] ?? '');
$color_hex = trim($_POST['color_hex'] ?? '');
$order = clampInt($_POST['order'] ?? null, 0, 9999, 0);
$is_active = isset($_POST['is_active']) ? 1 : 0;
// Validazioni base
if ($name === '') {
setFlash('danger', "Il nome del servizio è obbligatorio.");
header("Location: services.php");
exit;
}
if ($price === null) {
setFlash('danger', "Prezzo non valido (usa es. 20 o 20.00).");
header("Location: services.php");
exit;
}
if ($price_max !== null && (float)$price_max < (float)$price) {
setFlash('danger', "Il prezzo massimo non può essere inferiore al prezzo.");
header("Location: services.php");
exit;
}
if ($color_hex !== '' && !preg_match('/^#[0-9A-Fa-f]{6}$/', $color_hex)) {
setFlash('danger', "Colore non valido. Usa formato tipo #FFAA00.");
header("Location: services.php");
exit;
}
// slug
$baseSlug = makeSlug($name);
$slug = uniqueSlug($pdo, $shop_id, $baseSlug, $id);
if ($action === 'add') {
$stmt = $pdo->prepare("
INSERT INTO services
(shop_id, name, slug, description, duration_minutes, price, price_max, category, is_active, color_hex, `order`)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
");
$ok = $stmt->execute([
$shop_id,
$name,
$slug,
$description !== '' ? $description : null,
$duration,
$price,
$price_max,
$category !== '' ? $category : null,
$is_active,
$color_hex !== '' ? $color_hex : null,
$order
]);
setFlash($ok ? 'success' : 'danger', $ok ? "Servizio aggiunto!" : "Errore durante l'aggiunta.");
header("Location: services.php");
exit;
} else {
if ($id <= 0) {
setFlash('danger', "ID non valido.");
header("Location: services.php");
exit;
}
$stmt = $pdo->prepare("
UPDATE services
SET name = ?, slug = ?, description = ?, duration_minutes = ?, price = ?, price_max = ?,
category = ?, is_active = ?, color_hex = ?, `order` = ?, updated_at = NOW()
WHERE id = ? AND shop_id = ?
");
$ok = $stmt->execute([
$name,
$slug,
$description !== '' ? $description : null,
$duration,
$price,
$price_max,
$category !== '' ? $category : null,
$is_active,
$color_hex !== '' ? $color_hex : null,
$order,
$id,
$shop_id
]);
setFlash($ok ? 'success' : 'danger', $ok ? "Servizio aggiornato!" : "Errore durante l'aggiornamento.");
header("Location: services.php");
exit;
}
}
if ($action === 'delete') {
$id = (int)($_POST['id'] ?? 0);
if ($id <= 0) {
setFlash('danger', "ID non valido.");
header("Location: services.php");
exit;
}
$stmt = $pdo->prepare("DELETE FROM services WHERE id = ? AND shop_id = ?");
$ok = $stmt->execute([$id, $shop_id]);
setFlash($ok ? 'success' : 'danger', $ok ? "Servizio eliminato!" : "Errore durante l'eliminazione.");
header("Location: services.php");
exit;
}
setFlash('danger', "Azione non valida.");
header("Location: services.php");
exit;
} catch (Throwable $e) {
setFlash('danger', "Errore: " . $e->getMessage());
header("Location: services.php");
exit;
}
}
// ===========================
// Fetch services
// ===========================
$stmt = $pdo->prepare("
SELECT id, name, slug, description, duration_minutes, price, price_max, category, is_active, color_hex, `order`
FROM services
WHERE shop_id = ?
ORDER BY `order` ASC, name ASC
");
$stmt->execute([$shop_id]);
$services = $stmt->fetchAll(PDO::FETCH_ASSOC);
$flash = getFlash();
?>
Servizi - = htmlspecialchars($shop_name) ?>
= htmlspecialchars($flash['text']) ?>
Non hai ancora creato servizi.
Aggiungine uno per renderlo prenotabile.
| Ordine |
Nome |
Durata |
Prezzo |
Categoria |
Attivo |
Colore |
Azioni |
| = (int)$s['order'] ?> |
= htmlspecialchars($s['name']) ?>
= htmlspecialchars($s['description']) ?>
|
= (int)$s['duration_minutes'] ?> min |
€ = htmlspecialchars(number_format((float)$s['price'], 2, ',', '.')) ?>
(float)$s['price']): ?>
– € = htmlspecialchars(number_format((float)$s['price_max'], 2, ',', '.')) ?>
|
= htmlspecialchars($s['category'] ?? '-') ?> |
Sì
No
|
= htmlspecialchars($s['color_hex']) ?>
-
|
|