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) ?>
Servizi -
Dashboard
Non hai ancora creato servizi.
Aggiungine uno per renderlo prenotabile.
Ordine Nome Durata Prezzo Categoria Attivo Colore Azioni
min (float)$s['price']): ?> – € No -