2026-01-27 14:53:37 +01:00

547 lines
27 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, validazioni)
// ===========================
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;
}
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;
}
// ===========================
// 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;
$first_name = trim($_POST['first_name'] ?? '');
$last_name = trim($_POST['last_name'] ?? '');
$nickname = trim($_POST['nickname'] ?? '');
$role = trim($_POST['role'] ?? '');
$phone = trim($_POST['phone'] ?? '');
$email = trim($_POST['email'] ?? '');
$color_hex = trim($_POST['color_hex'] ?? '');
$max_app_day = clampInt($_POST['max_appointments_per_day'] ?? null, 1, 50, 10);
$can_book_online = isset($_POST['can_book_online']) ? 1 : 0;
$is_active = isset($_POST['is_active']) ? 1 : 0;
$notes = trim($_POST['notes'] ?? '');
// Validazioni base
if ($first_name === '' || $last_name === '') {
setFlash('danger', "Nome e Cognome sono obbligatori.");
header("Location: staff.php");
exit;
}
if ($color_hex !== '' && !preg_match('/^#[0-9A-Fa-f]{6}$/', $color_hex)) {
setFlash('danger', "Colore non valido. Usa formato #FFAA00.");
header("Location: staff.php");
exit;
}
if ($action === 'add') {
$stmt = $pdo->prepare("
INSERT INTO staff
(shop_id, first_name, last_name, nickname, role, phone, email, color_hex,
max_appointments_per_day, can_book_online, is_active, notes)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
");
$ok = $stmt->execute([
$shop_id,
$first_name,
$last_name,
$nickname ?: null,
$role ?: null,
$phone ?: null,
$email ?: null,
$color_hex ?: null,
$max_app_day,
$can_book_online,
$is_active,
$notes ?: null
]);
setFlash($ok ? 'success' : 'danger', $ok ? "Collaboratore aggiunto!" : "Errore durante l'aggiunta.");
header("Location: staff.php");
exit;
} else {
if ($id <= 0) {
setFlash('danger', "ID non valido.");
header("Location: staff.php");
exit;
}
$stmt = $pdo->prepare("
UPDATE staff
SET first_name = ?, last_name = ?, nickname = ?, role = ?, phone = ?, email = ?,
color_hex = ?, max_appointments_per_day = ?, can_book_online = ?, is_active = ?,
notes = ?, updated_at = NOW()
WHERE id = ? AND shop_id = ?
");
$ok = $stmt->execute([
$first_name,
$last_name,
$nickname ?: null,
$role ?: null,
$phone ?: null,
$email ?: null,
$color_hex ?: null,
$max_app_day,
$can_book_online,
$is_active,
$notes ?: null,
$id,
$shop_id
]);
setFlash($ok ? 'success' : 'danger', $ok ? "Collaboratore aggiornato!" : "Errore durante l'aggiornamento.");
header("Location: staff.php");
exit;
}
}
if ($action === 'delete') {
$id = (int)($_POST['id'] ?? 0);
if ($id <= 0) {
setFlash('danger', "ID non valido.");
header("Location: staff.php");
exit;
}
$stmt = $pdo->prepare("DELETE FROM staff WHERE id = ? AND shop_id = ?");
$ok = $stmt->execute([$id, $shop_id]);
setFlash($ok ? 'success' : 'danger', $ok ? "Collaboratore eliminato!" : "Errore durante l'eliminazione.");
header("Location: staff.php");
exit;
}
setFlash('danger', "Azione non valida.");
header("Location: staff.php");
exit;
} catch (Throwable $e) {
setFlash('danger', "Errore: " . $e->getMessage());
header("Location: staff.php");
exit;
}
}
// ===========================
// Fetch staff
// ===========================
$stmt = $pdo->prepare("
SELECT id, first_name, last_name, nickname, role, phone, email, color_hex,
max_appointments_per_day, can_book_online, is_active, notes
FROM staff
WHERE shop_id = ?
ORDER BY last_name ASC, first_name ASC
");
$stmt->execute([$shop_id]);
$staff_list = $stmt->fetchAll(PDO::FETCH_ASSOC);
$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>Staff / Parrucchieri - <?= htmlspecialchars($shop_name) ?></title>
</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 align-items-center justify-content-between">
<h6 class="mb-0">Staff / Parrucchieri - <?= htmlspecialchars($shop_name) ?></h6>
<div>
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#addStaffModal">
<i class="bx bx-plus me-1"></i> Aggiungi Collaboratore
</button>
<a href="salon_dashboard.php" class="btn btn-outline-secondary ms-2">
<i class="bx bx-arrow-back me-1"></i> Dashboard
</a>
</div>
</div>
<div class="card-body">
<?php if ($flash): ?>
<div class="alert alert-<?= htmlspecialchars($flash['type']) ?> alert-dismissible fade show" role="alert">
<?= htmlspecialchars($flash['text']) ?>
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
<?php endif; ?>
<?php if (empty($staff_list)): ?>
<div class="alert alert-info text-center py-4">
Non hai ancora aggiunto collaboratori.<br>
Aggiungine uno per gestire le prenotazioni.
</div>
<?php else: ?>
<div class="table-responsive">
<table id="staffTable" class="table table-striped table-hover table-bordered">
<thead>
<tr>
<th>Nome</th>
<th>Ruolo</th>
<th>Telefono / Email</th>
<th>Max app/giorno</th>
<th>Online</th>
<th>Attivo</th>
<th>Colore</th>
<th>Azioni</th>
</tr>
</thead>
<tbody>
<?php foreach ($staff_list as $s): ?>
<tr>
<td>
<div class="fw-bold">
<?= htmlspecialchars($s['first_name'] . ' ' . $s['last_name']) ?>
<?php if ($s['nickname']): ?>
<small class="text-muted">(@<?= htmlspecialchars($s['nickname']) ?>)</small>
<?php endif; ?>
</div>
</td>
<td><?= htmlspecialchars($s['role'] ?: '-') ?></td>
<td>
<?= htmlspecialchars($s['phone'] ?: '-') ?><br>
<small><?= htmlspecialchars($s['email'] ?: '-') ?></small>
</td>
<td><?= (int)$s['max_appointments_per_day'] ?></td>
<td>
<?php if ((int)$s['can_book_online'] === 1): ?>
<span class="badge bg-success">Sì</span>
<?php else: ?>
<span class="badge bg-secondary">No</span>
<?php endif; ?>
</td>
<td>
<?php if ((int)$s['is_active'] === 1): ?>
<span class="badge bg-success">Sì</span>
<?php else: ?>
<span class="badge bg-danger">No</span>
<?php endif; ?>
</td>
<td>
<?php if (!empty($s['color_hex'])): ?>
<span class="badge" style="background: <?= htmlspecialchars($s['color_hex']) ?>;">
<?= htmlspecialchars($s['color_hex']) ?>
</span>
<?php else: ?>
<span class="text-muted">-</span>
<?php endif; ?>
</td>
<td>
<button type="button" class="btn btn-sm btn-warning me-1"
data-bs-toggle="modal" data-bs-target="#editStaffModal"
onclick='fillEditStaffModal(<?= json_encode([
"id" => (int)$s["id"],
"first_name" => $s["first_name"],
"last_name" => $s["last_name"],
"nickname" => $s["nickname"] ?? "",
"role" => $s["role"] ?? "",
"phone" => $s["phone"] ?? "",
"email" => $s["email"] ?? "",
"color_hex" => $s["color_hex"] ?? "",
"max_appointments_per_day" => (int)$s["max_appointments_per_day"],
"can_book_online" => (int)$s["can_book_online"],
"is_active" => (int)$s["is_active"],
"notes" => $s["notes"] ?? ""
], JSON_HEX_APOS | JSON_HEX_QUOT) ?>)'>
<i class="bx bx-edit"></i> Modifica
</button>
<form action="" method="POST" style="display:inline;"
onsubmit="return confirm('Confermi l\'eliminazione di questo collaboratore?');">
<input type="hidden" name="action" value="delete">
<input type="hidden" name="id" value="<?= (int)$s['id'] ?>">
<button type="submit" class="btn btn-sm btn-danger">
<i class="bx bx-trash"></i> Elimina
</button>
</form>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
</div>
</div>
</div>
</div>
<?php include('include/footer.php'); ?>
</div>
<!-- Modal Aggiungi -->
<div class="modal fade" id="addStaffModal" tabindex="-1" aria-labelledby="addStaffModalLabel">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header bg-primary text-white">
<h5 class="modal-title" id="addStaffModalLabel">Aggiungi Collaboratore</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
</div>
<form action="" method="POST">
<div class="modal-body">
<input type="hidden" name="action" value="add">
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label fw-bold">Nome <span class="text-danger">*</span></label>
<input type="text" class="form-control" name="first_name" required>
</div>
<div class="col-md-6 mb-3">
<label class="form-label fw-bold">Cognome <span class="text-danger">*</span></label>
<input type="text" class="form-control" name="last_name" required>
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label fw-bold">Nickname / Nome d'arte</label>
<input type="text" class="form-control" name="nickname" placeholder="Es: Sara la Colorista">
</div>
<div class="col-md-6 mb-3">
<label class="form-label fw-bold">Ruolo</label>
<input type="text" class="form-control" name="role" placeholder="Es: Parrucchiere Senior">
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label fw-bold">Telefono</label>
<input type="text" class="form-control" name="phone">
</div>
<div class="col-md-6 mb-3">
<label class="form-label fw-bold">Email</label>
<input type="email" class="form-control" name="email">
</div>
</div>
<div class="row">
<div class="col-md-4 mb-3">
<label class="form-label fw-bold">Colore calendario (hex)</label>
<input type="text" class="form-control" name="color_hex" placeholder="#3788d8">
</div>
<div class="col-md-4 mb-3">
<label class="form-label fw-bold">Max app/giorno</label>
<input type="number" class="form-control" name="max_appointments_per_day" min="1" max="50" value="10">
</div>
<div class="col-md-4 mb-3 d-flex align-items-center gap-3 mt-4">
<div class="form-check">
<input class="form-check-input" type="checkbox" name="can_book_online" id="add_can_book" checked>
<label class="form-check-label" for="add_can_book">Prenotabile online</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" name="is_active" id="add_is_active" checked>
<label class="form-check-label" for="add_is_active">Attivo</label>
</div>
</div>
</div>
<div class="mb-3">
<label class="form-label fw-bold">Note interne</label>
<textarea class="form-control" name="notes" rows="2" placeholder="Es: solo su appuntamento, non lavora lunedì..."></textarea>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Annulla</button>
<button type="submit" class="btn btn-primary">Aggiungi</button>
</div>
</form>
</div>
</div>
</div>
<!-- Modal Modifica -->
<div class="modal fade" id="editStaffModal" tabindex="-1" aria-labelledby="editStaffModalLabel">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header bg-warning text-dark">
<h5 class="modal-title" id="editStaffModalLabel">Modifica Collaboratore</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<form action="" method="POST">
<div class="modal-body">
<input type="hidden" name="action" value="edit">
<input type="hidden" name="id" id="edit_id">
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label fw-bold">Nome <span class="text-danger">*</span></label>
<input type="text" class="form-control" name="first_name" id="edit_first_name" required>
</div>
<div class="col-md-6 mb-3">
<label class="form-label fw-bold">Cognome <span class="text-danger">*</span></label>
<input type="text" class="form-control" name="last_name" id="edit_last_name" required>
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label fw-bold">Nickname / Nome d'arte</label>
<input type="text" class="form-control" name="nickname" id="edit_nickname">
</div>
<div class="col-md-6 mb-3">
<label class="form-label fw-bold">Ruolo</label>
<input type="text" class="form-control" name="role" id="edit_role">
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label fw-bold">Telefono</label>
<input type="text" class="form-control" name="phone" id="edit_phone">
</div>
<div class="col-md-6 mb-3">
<label class="form-label fw-bold">Email</label>
<input type="email" class="form-control" name="email" id="edit_email">
</div>
</div>
<div class="row">
<div class="col-md-4 mb-3">
<label class="form-label fw-bold">Colore calendario (hex)</label>
<input type="text" class="form-control" name="color_hex" id="edit_color_hex">
</div>
<div class="col-md-4 mb-3">
<label class="form-label fw-bold">Max app/giorno</label>
<input type="number" class="form-control" name="max_appointments_per_day" id="edit_max_app" min="1" max="50">
</div>
<div class="col-md-4 mb-3 d-flex align-items-center gap-3 mt-4">
<div class="form-check">
<input class="form-check-input" type="checkbox" name="can_book_online" id="edit_can_book">
<label class="form-check-label" for="edit_can_book">Prenotabile online</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" name="is_active" id="edit_is_active">
<label class="form-check-label" for="edit_is_active">Attivo</label>
</div>
</div>
</div>
<div class="mb-3">
<label class="form-label fw-bold">Note interne</label>
<textarea class="form-control" name="notes" id="edit_notes" rows="2"></textarea>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Annulla</button>
<button type="submit" class="btn btn-warning">Salva Modifiche</button>
</div>
</form>
</div>
</div>
</div>
<?php include('jsinclude.php'); ?>
<script>
$(document).ready(function() {
$('#staffTable').DataTable({
language: {
url: '//cdn.datatables.net/plug-ins/1.13.6/i18n/it-IT.json'
},
order: [
[1, 'asc']
]
});
});
function fillEditStaffModal(data) {
document.getElementById('edit_id').value = data.id;
document.getElementById('edit_first_name').value = data.first_name || '';
document.getElementById('edit_last_name').value = data.last_name || '';
document.getElementById('edit_nickname').value = data.nickname || '';
document.getElementById('edit_role').value = data.role || '';
document.getElementById('edit_phone').value = data.phone || '';
document.getElementById('edit_email').value = data.email || '';
document.getElementById('edit_color_hex').value = data.color_hex || '';
document.getElementById('edit_max_app').value = data.max_appointments_per_day || 10;
document.getElementById('edit_can_book').checked = (parseInt(data.can_book_online, 10) === 1);
document.getElementById('edit_is_active').checked = (parseInt(data.is_active, 10) === 1);
document.getElementById('edit_notes').value = data.notes || '';
}
</script>
</body>
</html>