comelifacciamo/public/userarea/shop_hours.php
2026-02-01 20:37:49 +01:00

620 lines
25 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 primo salone
$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 flash
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;
}
// Fetch orari esistenti
$stmt = $pdo->prepare("SELECT * FROM shop_hours WHERE shop_id = ? ORDER BY day_of_week");
$stmt->execute([$shop_id]);
$hours_raw = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Organizza per giorno
$hours = [];
for ($d = 0; $d <= 6; $d++) {
$hours[$d] = [
'is_open' => 0,
'open_time' => '09:00',
'close_time' => '19:00',
'open_time_2' => null,
'close_time_2' => null,
'notes' => ''
];
}
foreach ($hours_raw as $h) {
$d = (int)$h['day_of_week'];
$hours[$d] = $h;
}
$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'); ?>
<title>Orari di Apertura - <?= htmlspecialchars($shop_name) ?></title>
<style>
.hours-card {
border: none;
border-radius: 16px;
overflow: hidden;
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.08);
}
.hours-header {
background: linear-gradient(135deg, #8b5cf6 0%, #7c3aed 100%);
color: white;
padding: 1.8rem 2rem;
}
.day-row {
padding: 1.25rem 2rem;
border-bottom: 1px solid #f3f4f6;
}
.day-row:last-child {
border-bottom: none;
}
.day-name {
font-weight: 700;
font-size: 1.15rem;
min-width: 140px;
}
.time-input {
width: 120px;
}
.form-check-input-lg {
width: 2.8rem;
height: 1.5rem;
}
.form-check-input-lg:checked {
background-color: #8b5cf6;
border-color: #8b5cf6;
}
.preview-week {
background: #f9fafb;
border-radius: 12px;
padding: 1.5rem;
margin-top: 2rem;
}
.preview-day {
display: flex;
align-items: center;
padding: 0.75rem 0;
border-bottom: 1px dashed #e5e7eb;
}
.preview-day:last-child {
border-bottom: none;
}
.copy-day.active {
animation: pulse 0.5s;
}
@keyframes pulse {
0%,
100% {
transform: scale(1);
}
50% {
transform: scale(1.1);
}
}
.paste-day:disabled {
opacity: 0.5;
cursor: not-allowed;
}
</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 hours-card">
<div class="hours-header d-flex justify-content-between align-items-center">
<h5 class="mb-0">Orari di Apertura - <?= htmlspecialchars($shop_name) ?></h5>
<a href="salon_dashboard.php" class="btn btn-light btn-sm px-4">
<i class="bx bx-arrow-back me-2"></i> Dashboard
</a>
</div>
<div class="form-section p-4">
<?php if ($flash): ?>
<div class="alert alert-<?= $flash['type'] ?> alert-dismissible fade show mb-4" role="alert">
<?= htmlspecialchars($flash['text']) ?>
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
<?php endif; ?>
<form action="" method="POST">
<?php
$days_names = ['Domenica', 'Lunedì', 'Martedì', 'Mercoledì', 'Giovedì', 'Venerdì', 'Sabato'];
foreach ($days_names as $d => $name):
$h = $hours[$d];
?>
<div class="day-row d-flex align-items-center gap-4 flex-wrap" data-day="<?= $d ?>">
<div class="day-name"><?= $name ?></div>
<div class="form-check form-switch form-switch-lg">
<input class="form-check-input form-check-input-lg" type="checkbox" name="is_open_<?= $d ?>" id="open_<?= $d ?>" <?= $h['is_open'] ? 'checked' : '' ?>>
<label class="form-check-label fw-bold" for="open_<?= $d ?>">
Aperto
</label>
</div>
<div class="d-flex gap-3 align-items-center">
<input type="time" class="form-control time-input" name="open_time_<?= $d ?>" value="<?= htmlspecialchars($h['open_time'] ?? '09:00') ?>">
<span>-</span>
<input type="time" class="form-control time-input" name="close_time_<?= $d ?>" value="<?= htmlspecialchars($h['close_time'] ?? '19:00') ?>">
</div>
<div class="d-flex gap-3 align-items-center">
<input type="time" class="form-control time-input" name="open_time_2_<?= $d ?>" value="<?= htmlspecialchars($h['open_time_2'] ?? '') ?>" placeholder="Opzionale">
<span>-</span>
<input type="time" class="form-control time-input" name="close_time_2_<?= $d ?>" value="<?= htmlspecialchars($h['close_time_2'] ?? '') ?>" placeholder="Opzionale">
</div>
<input type="text" class="form-control flex-grow-1" name="notes_<?= $d ?>" value="<?= htmlspecialchars($h['notes'] ?? '') ?>" placeholder="Note (es. chiuso per pranzo dalle 13:00 alle 14:30)">
<!-- PULSANTI COPIA/INCOLLA -->
<div class="d-flex gap-2">
<button type="button" class="btn btn-sm btn-outline-primary copy-day" data-day="<?= $d ?>" title="Copia questi orari">
<i class="bx bx-copy"></i>
</button>
<button type="button" class="btn btn-sm btn-outline-success paste-day" data-day="<?= $d ?>" title="Incolla orari copiati">
<i class="bx bx-paste"></i>
</button>
</div>
</div>
<?php endforeach; ?>
<div class="d-grid mt-5">
<button type="submit" class="btn btn-primary btn-lg">
<i class="bx bx-save me-2"></i> Salva Orari di Apertura
</button>
</div>
</form>
<!-- Anteprima settimanale -->
<div class="preview-week">
<h6 class="mb-3">Anteprima Settimanale</h6>
<?php foreach ($days_names as $d => $name):
$h = $hours[$d];
$status = $h['is_open'] ? 'Aperto' : 'Chiuso';
$times = $h['is_open']
? ($h['open_time'] . ' - ' . $h['close_time'] . ($h['open_time_2'] ? ' / ' . $h['open_time_2'] . ' - ' . $h['close_time_2'] : ''))
: '—';
?>
<div class="preview-day d-flex justify-content-between align-items-center">
<strong><?= $name ?></strong>
<span class="badge <?= $h['is_open'] ? 'bg-success' : 'bg-secondary' ?> px-3 py-2">
<?= $status ?> <?= $times ?>
</span>
</div>
<?php endforeach; ?>
</div>
</div>
</div>
</div>
</div>
<?php include('include/footer.php'); ?>
</div>
<?php include('jsinclude.php'); ?>
<script>
document.addEventListener('DOMContentLoaded', function() {
const form = document.querySelector('form[method="POST"]');
const submitBtn = form.querySelector('button[type="submit"]');
// NASCONDI il pulsante salva
submitBtn.style.display = 'none';
// Clipboard per gli orari
let copiedData = null;
// Timeout per debounce
let saveTimeout = null;
// ========== AUTO-SAVE ==========
function autoSave() {
clearTimeout(saveTimeout);
showSavingIndicator();
saveTimeout = setTimeout(() => {
const formData = new FormData(form);
fetch('shop_hours_save.php', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
showSaveResult(data);
})
.catch(error => {
console.error('Errore:', error);
showSaveResult({
type: 'danger',
message: 'Errore durante il salvataggio'
});
});
}, 800);
}
function showSavingIndicator() {
removeAlerts();
const alertDiv = document.createElement('div');
alertDiv.className = 'alert alert-info mb-4 saving-indicator';
alertDiv.innerHTML = '<i class="bx bx-loader-alt bx-spin me-2"></i> Salvando...';
const formSection = document.querySelector('.form-section');
formSection.insertBefore(alertDiv, formSection.firstChild);
}
function showSaveResult(data) {
removeAlerts();
const alertDiv = document.createElement('div');
alertDiv.className = `alert alert-${data.type} alert-dismissible fade show mb-4`;
alertDiv.innerHTML = `
${data.message}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
`;
const formSection = document.querySelector('.form-section');
formSection.insertBefore(alertDiv, formSection.firstChild);
if (data.type === 'success') {
setTimeout(() => alertDiv.remove(), 3000);
}
}
function removeAlerts() {
document.querySelectorAll('.alert').forEach(alert => alert.remove());
}
// ========== COPIA/INCOLLA ==========
// Funzione per ottenere i dati di un giorno
function getDayData(day) {
const row = document.querySelector(`.day-row[data-day="${day}"]`);
return {
is_open: row.querySelector(`input[name="is_open_${day}"]`).checked,
open_time: row.querySelector(`input[name="open_time_${day}"]`).value,
close_time: row.querySelector(`input[name="close_time_${day}"]`).value,
open_time_2: row.querySelector(`input[name="open_time_2_${day}"]`).value,
close_time_2: row.querySelector(`input[name="close_time_2_${day}"]`).value,
notes: row.querySelector(`input[name="notes_${day}"]`).value
};
}
// Funzione per impostare i dati di un giorno
function setDayData(day, data) {
const row = document.querySelector(`.day-row[data-day="${day}"]`);
row.querySelector(`input[name="is_open_${day}"]`).checked = data.is_open;
row.querySelector(`input[name="open_time_${day}"]`).value = data.open_time;
row.querySelector(`input[name="close_time_${day}"]`).value = data.close_time;
row.querySelector(`input[name="open_time_2_${day}"]`).value = data.open_time_2;
row.querySelector(`input[name="close_time_2_${day}"]`).value = data.close_time_2;
row.querySelector(`input[name="notes_${day}"]`).value = data.notes;
}
// Gestione COPIA
document.querySelectorAll('.copy-day').forEach(btn => {
btn.addEventListener('click', function() {
const day = this.dataset.day;
copiedData = getDayData(day);
// Feedback visivo
document.querySelectorAll('.copy-day').forEach(b => {
b.classList.remove('active', 'btn-primary');
b.classList.add('btn-outline-primary');
});
this.classList.remove('btn-outline-primary');
this.classList.add('btn-primary', 'active');
// Alert temporaneo
const tempAlert = document.createElement('div');
tempAlert.className = 'alert alert-success position-fixed top-0 start-50 translate-middle-x mt-3';
tempAlert.style.zIndex = '9999';
tempAlert.innerHTML = '<i class="bx bx-check me-2"></i> Orari copiati!';
document.body.appendChild(tempAlert);
setTimeout(() => tempAlert.remove(), 1500);
});
});
// Gestione INCOLLA
document.querySelectorAll('.paste-day').forEach(btn => {
btn.addEventListener('click', function() {
if (!copiedData) {
alert('Nessun orario copiato! Prima clicca su "Copia" su un giorno.');
return;
}
const day = this.dataset.day;
setDayData(day, copiedData);
// Trigger auto-save
autoSave();
// Feedback visivo
this.classList.add('btn-success');
setTimeout(() => {
this.classList.remove('btn-success');
}, 1000);
});
});
// ========== LISTENER AUTO-SAVE ==========
const allInputs = form.querySelectorAll('input, select, textarea');
allInputs.forEach(input => {
if (input.type === 'checkbox') {
input.addEventListener('change', autoSave);
} else {
input.addEventListener('input', autoSave);
}
});
});
</script>
<script>
document.addEventListener('DOMContentLoaded', function() {
const form = document.querySelector('form[method="POST"]');
const submitBtn = form.querySelector('button[type="submit"]');
// NASCONDI il pulsante salva
submitBtn.style.display = 'none';
// Clipboard per gli orari
let copiedData = null;
// Timeout per debounce
let saveTimeout = null;
// Nomi dei giorni
const daysNames = ['Domenica', 'Lunedì', 'Martedì', 'Mercoledì', 'Giovedì', 'Venerdì', 'Sabato'];
// ========== AGGIORNA ANTEPRIMA ==========
function updatePreview() {
for (let d = 0; d <= 6; d++) {
const isOpen = document.querySelector(`input[name="is_open_${d}"]`).checked;
const openTime = document.querySelector(`input[name="open_time_${d}"]`).value;
const closeTime = document.querySelector(`input[name="close_time_${d}"]`).value;
const openTime2 = document.querySelector(`input[name="open_time_2_${d}"]`).value;
const closeTime2 = document.querySelector(`input[name="close_time_2_${d}"]`).value;
// Trova il badge nella preview
const previewDays = document.querySelectorAll('.preview-day');
const badge = previewDays[d].querySelector('.badge');
if (isOpen) {
let times = `${openTime} - ${closeTime}`;
if (openTime2 && closeTime2) {
times += ` / ${openTime2} - ${closeTime2}`;
}
badge.className = 'badge bg-success px-3 py-2';
badge.textContent = `Aperto ${times}`;
} else {
badge.className = 'badge bg-secondary px-3 py-2';
badge.textContent = 'Chiuso —';
}
}
}
// ========== AUTO-SAVE ==========
function autoSave() {
clearTimeout(saveTimeout);
showSavingIndicator();
// AGGIORNA ANTEPRIMA IMMEDIATAMENTE
updatePreview();
saveTimeout = setTimeout(() => {
const formData = new FormData(form);
fetch('shop_hours_save.php', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
showSaveResult(data);
})
.catch(error => {
console.error('Errore:', error);
showSaveResult({
type: 'danger',
message: 'Errore durante il salvataggio'
});
});
}, 800);
}
function showSavingIndicator() {
removeAlerts();
const alertDiv = document.createElement('div');
alertDiv.className = 'alert alert-info mb-4 saving-indicator';
alertDiv.innerHTML = '<i class="bx bx-loader-alt bx-spin me-2"></i> Salvando...';
const formSection = document.querySelector('.form-section');
formSection.insertBefore(alertDiv, formSection.firstChild);
}
function showSaveResult(data) {
removeAlerts();
const alertDiv = document.createElement('div');
alertDiv.className = `alert alert-${data.type} alert-dismissible fade show mb-4`;
alertDiv.innerHTML = `
${data.message}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
`;
const formSection = document.querySelector('.form-section');
formSection.insertBefore(alertDiv, formSection.firstChild);
if (data.type === 'success') {
setTimeout(() => alertDiv.remove(), 3000);
}
}
function removeAlerts() {
document.querySelectorAll('.alert').forEach(alert => alert.remove());
}
// ========== COPIA/INCOLLA ==========
function getDayData(day) {
const row = document.querySelector(`.day-row[data-day="${day}"]`);
return {
is_open: row.querySelector(`input[name="is_open_${day}"]`).checked,
open_time: row.querySelector(`input[name="open_time_${day}"]`).value,
close_time: row.querySelector(`input[name="close_time_${day}"]`).value,
open_time_2: row.querySelector(`input[name="open_time_2_${day}"]`).value,
close_time_2: row.querySelector(`input[name="close_time_2_${day}"]`).value,
notes: row.querySelector(`input[name="notes_${day}"]`).value
};
}
function setDayData(day, data) {
const row = document.querySelector(`.day-row[data-day="${day}"]`);
row.querySelector(`input[name="is_open_${day}"]`).checked = data.is_open;
row.querySelector(`input[name="open_time_${day}"]`).value = data.open_time;
row.querySelector(`input[name="close_time_${day}"]`).value = data.close_time;
row.querySelector(`input[name="open_time_2_${day}"]`).value = data.open_time_2;
row.querySelector(`input[name="close_time_2_${day}"]`).value = data.close_time_2;
row.querySelector(`input[name="notes_${day}"]`).value = data.notes;
}
document.querySelectorAll('.copy-day').forEach(btn => {
btn.addEventListener('click', function() {
const day = this.dataset.day;
copiedData = getDayData(day);
document.querySelectorAll('.copy-day').forEach(b => {
b.classList.remove('active', 'btn-primary');
b.classList.add('btn-outline-primary');
});
this.classList.remove('btn-outline-primary');
this.classList.add('btn-primary', 'active');
const tempAlert = document.createElement('div');
tempAlert.className = 'alert alert-success position-fixed top-0 start-50 translate-middle-x mt-3';
tempAlert.style.zIndex = '9999';
tempAlert.innerHTML = '<i class="bx bx-check me-2"></i> Orari copiati!';
document.body.appendChild(tempAlert);
setTimeout(() => tempAlert.remove(), 1500);
});
});
document.querySelectorAll('.paste-day').forEach(btn => {
btn.addEventListener('click', function() {
if (!copiedData) {
alert('Nessun orario copiato! Prima clicca su "Copia" su un giorno.');
return;
}
const day = this.dataset.day;
setDayData(day, copiedData);
// AGGIORNA ANTEPRIMA IMMEDIATAMENTE
updatePreview();
// Trigger auto-save
autoSave();
this.classList.add('btn-success');
setTimeout(() => {
this.classList.remove('btn-success');
}, 1000);
});
});
// ========== LISTENER AUTO-SAVE + PREVIEW ==========
const allInputs = form.querySelectorAll('input, select, textarea');
allInputs.forEach(input => {
if (input.type === 'checkbox') {
input.addEventListener('change', autoSave);
} else {
input.addEventListener('input', autoSave);
}
});
});
</script>
</body>
</html>