341 lines
12 KiB
PHP
341 lines
12 KiB
PHP
<?php
|
||
declare (strict_types = 1);
|
||
|
||
require_once __DIR__ . '/../includes/init_praticien.php';
|
||
|
||
$praticien_id = (int) ($_SESSION['reference_id'] ?? 0);
|
||
if ($praticien_id <= 0) {
|
||
http_response_code(403);
|
||
exit('Forbidden');
|
||
}
|
||
|
||
// Durée RDV praticien
|
||
$stmt = $pdo->prepare("SELECT duree_rdv FROM praticien WHERE id = ?");
|
||
$stmt->execute([$praticien_id]);
|
||
$duree_minutes = (int) ($stmt->fetchColumn() ?: 0);
|
||
if ($duree_minutes <= 0) {
|
||
$duree_minutes = 30;
|
||
}
|
||
|
||
$erreur = null;
|
||
|
||
$toMin = function (string $hhmm): ?int {
|
||
if (! preg_match('/^\d{2}:\d{2}$/', $hhmm)) {
|
||
return null;
|
||
}
|
||
|
||
[$h, $m] = array_map('intval', explode(':', $hhmm));
|
||
if ($h < 0 || $h > 23 || $m < 0 || $m > 59) {
|
||
return null;
|
||
}
|
||
|
||
return $h * 60 + $m;
|
||
};
|
||
|
||
// ---- Ajouter un créneau (POST) ----
|
||
if (
|
||
$_SERVER['REQUEST_METHOD'] === 'POST'
|
||
&& ($_POST['action'] ?? '') === 'add'
|
||
&& isset($_POST['jour'], $_POST['heure_debut'], $_POST['heure_fin'])
|
||
) {
|
||
require_post_csrf();
|
||
|
||
$jour = trim((string) $_POST['jour']);
|
||
$debut = trim((string) $_POST['heure_debut']);
|
||
$fin = trim((string) $_POST['heure_fin']);
|
||
|
||
$md = $toMin($debut);
|
||
$mf = $toMin($fin);
|
||
|
||
if ($md === null || $mf === null) {
|
||
$erreur = $translations['erreur_format_heure'] ?? "Format d'heure invalide.";
|
||
} elseif ($md >= $mf) {
|
||
$erreur = $translations['erreur_debut_avant_fin'] ?? "Heure de début après l’heure de fin.";
|
||
} else {
|
||
$window = $mf - $md;
|
||
|
||
if ($window < $duree_minutes) {
|
||
$erreur = sprintf(
|
||
$translations['erreur_plage_trop_courte'] ?? "Créneau trop court (%d min). Minimum : %d min.",
|
||
$window,
|
||
$duree_minutes
|
||
);
|
||
} elseif (($window % $duree_minutes) !== 0) {
|
||
$erreur = sprintf(
|
||
$translations['erreur_plage_incompatible_duree'] ?? "Créneau incompatible. La durée doit être un multiple de %d min.",
|
||
$duree_minutes
|
||
);
|
||
} else {
|
||
$stmt = $pdo->prepare(
|
||
"INSERT INTO disponibilite (praticien_id, jour_semaine, heure_debut, heure_fin)
|
||
VALUES (?, ?, ?, ?)"
|
||
);
|
||
$stmt->execute([$praticien_id, $jour, $debut, $fin]);
|
||
|
||
header('Location: /public/praticien/disponibilites.php?ok=added', true, 303);
|
||
exit;
|
||
}
|
||
}
|
||
}
|
||
|
||
// ---- Récupérer les créneaux ----
|
||
$stmt = $pdo->prepare(
|
||
"SELECT *
|
||
FROM disponibilite
|
||
WHERE praticien_id = ?
|
||
ORDER BY FIELD(jour_semaine,'lundi','mardi','mercredi','jeudi','vendredi','samedi','dimanche'), heure_debut"
|
||
);
|
||
$stmt->execute([$praticien_id]);
|
||
$dispos = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||
|
||
// Traductions jours
|
||
$jourKeyMap = [
|
||
'lundi' => 'jour_lundi',
|
||
'mardi' => 'jour_mardi',
|
||
'mercredi' => 'jour_mercredi',
|
||
'jeudi' => 'jour_jeudi',
|
||
'vendredi' => 'jour_vendredi',
|
||
'samedi' => 'jour_samedi',
|
||
'dimanche' => 'jour_dimanche',
|
||
];
|
||
|
||
$PAGE_TITLE = $translations['titre_disponibilites'] ?? 'Disponibilités';
|
||
$HEAD_EXTRA = <<<HTML
|
||
<script src="/js/sweetalert2@11.js"></script>
|
||
HTML;
|
||
|
||
require_once __DIR__ . '/../includes/header.php';
|
||
?>
|
||
|
||
<div class="main1">
|
||
<div class="container py-5">
|
||
<div class="shadow p-4 rounded bg-light">
|
||
<h2 class="mb-4 text-center"><?php echo htmlspecialchars($translations['h2_disponibilites'] ?? 'Mes disponibilités'); ?></h2>
|
||
|
||
<div class="text-end">
|
||
<a href="/public/praticien/disponibilites_date.php" class="btn btn-outline-secondary mb-4">
|
||
<?php echo htmlspecialchars($translations['btn_ajouter_par_date'] ?? 'Ajouter par date'); ?>
|
||
</a>
|
||
</div>
|
||
|
||
<div class="table-responsive mb-4">
|
||
<table class="table table-bordered table-striped align-middle text-center mobile-cards" id="table_rdv">
|
||
<thead class="table-light">
|
||
<tr>
|
||
<th><?php echo htmlspecialchars($translations['table_jour'] ?? 'Jour'); ?></th>
|
||
<th><?php echo htmlspecialchars($translations['table_debut'] ?? 'Début'); ?></th>
|
||
<th><?php echo htmlspecialchars($translations['table_fin'] ?? 'Fin'); ?></th>
|
||
<th><?php echo htmlspecialchars($translations['table_action'] ?? 'Action'); ?></th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<?php if ($dispos): ?>
|
||
<?php foreach ($dispos as $d): ?>
|
||
<tr>
|
||
<td>
|
||
<?php
|
||
$key = $jourKeyMap[$d['jour_semaine']] ?? null;
|
||
echo htmlspecialchars($key ? ($translations[$key] ?? ucfirst($d['jour_semaine'])) : ucfirst($d['jour_semaine']));
|
||
?>
|
||
</td>
|
||
<td><?php echo htmlspecialchars(substr((string) $d['heure_debut'], 0, 5)); ?></td>
|
||
<td><?php echo htmlspecialchars(substr((string) $d['heure_fin'], 0, 5)); ?></td>
|
||
<td>
|
||
<button
|
||
type="button"
|
||
class="btn btn-sm btn-danger js-del"
|
||
data-id="<?php echo (int) $d['id']; ?>">
|
||
<?php echo htmlspecialchars($translations['btn_supprimer'] ?? 'Supprimer'); ?>
|
||
</button>
|
||
</td>
|
||
</tr>
|
||
<?php endforeach; ?>
|
||
<?php else: ?>
|
||
<tr>
|
||
<td colspan="4"><?php echo htmlspecialchars($translations['aucun_creneau'] ?? 'Aucun créneau.'); ?></td>
|
||
</tr>
|
||
<?php endif; ?>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<div class="card shadow-sm p-4">
|
||
<h4 class="mb-3 text-center"><?php echo htmlspecialchars($translations['card_ajouter_creneau'] ?? 'Ajouter un créneau'); ?></h4>
|
||
|
||
<form method="POST">
|
||
<input type="hidden" name="action" value="add">
|
||
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars(ensure_csrf_token(), ENT_QUOTES, 'UTF-8'); ?>">
|
||
|
||
<div class="mb-3">
|
||
<label class="form-label" for="jour"><?php echo htmlspecialchars($translations['label_jour'] ?? 'Jour'); ?></label>
|
||
<select name="jour" id="jour" class="form-select" required>
|
||
<option value=""><?php echo htmlspecialchars($translations['option_choisir'] ?? '— Choisir —'); ?></option>
|
||
<?php
|
||
$jours = [
|
||
'lundi' => $translations['jour_lundi'] ?? 'Lundi',
|
||
'mardi' => $translations['jour_mardi'] ?? 'Mardi',
|
||
'mercredi' => $translations['jour_mercredi'] ?? 'Mercredi',
|
||
'jeudi' => $translations['jour_jeudi'] ?? 'Jeudi',
|
||
'vendredi' => $translations['jour_vendredi'] ?? 'Vendredi',
|
||
'samedi' => $translations['jour_samedi'] ?? 'Samedi',
|
||
'dimanche' => $translations['jour_dimanche'] ?? 'Dimanche',
|
||
];
|
||
foreach ($jours as $val => $label):
|
||
?>
|
||
<option value="<?php echo htmlspecialchars($val); ?>"><?php echo htmlspecialchars($label); ?></option>
|
||
<?php endforeach; ?>
|
||
</select>
|
||
</div>
|
||
|
||
<div class="mb-3">
|
||
<label class="form-label" for="heure_debut"><?php echo htmlspecialchars($translations['label_heure_debut'] ?? 'Heure de début'); ?></label>
|
||
<input type="time" name="heure_debut" id="heure_debut" class="form-control" required>
|
||
</div>
|
||
|
||
<div class="mb-3">
|
||
<label class="form-label" for="heure_fin"><?php echo htmlspecialchars($translations['label_heure_fin'] ?? 'Heure de fin'); ?></label>
|
||
<input type="time" name="heure_fin" id="heure_fin" class="form-control" required>
|
||
</div>
|
||
|
||
<button type="submit" class="btn btn-primary w-100">
|
||
<?php echo htmlspecialchars($translations['btn_ajouter'] ?? 'Ajouter'); ?>
|
||
</button>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
const csrfToken = <?php echo json_encode(ensure_csrf_token(), JSON_UNESCAPED_UNICODE); ?>;
|
||
|
||
document.addEventListener('DOMContentLoaded', function () {
|
||
const okParam = <?php echo json_encode(isset($_GET['ok']) ? (string) $_GET['ok'] : '', JSON_UNESCAPED_UNICODE); ?>;
|
||
const erreurMsg = <?php echo json_encode($erreur ?: '', JSON_UNESCAPED_UNICODE); ?>;
|
||
|
||
if (okParam === 'added') {
|
||
Swal.fire({
|
||
icon: 'success',
|
||
title: <?php echo json_encode($translations['msg_creneau_ajoute'] ?? 'Créneau ajouté.', JSON_UNESCAPED_UNICODE); ?>,
|
||
timer: 1800,
|
||
showConfirmButton: false
|
||
});
|
||
|
||
if (window.history.replaceState) {
|
||
const cleanUrl = window.location.pathname;
|
||
window.history.replaceState({}, document.title, cleanUrl);
|
||
}
|
||
}
|
||
|
||
if (erreurMsg) {
|
||
Swal.fire({
|
||
icon: 'error',
|
||
title: <?php echo json_encode($translations['erreur_titre'] ?? 'Erreur', JSON_UNESCAPED_UNICODE); ?>,
|
||
html: erreurMsg,
|
||
showCancelButton: true,
|
||
confirmButtonText: <?php echo json_encode($translations['btn_modifier_profil'] ?? 'Modifier mon profil', JSON_UNESCAPED_UNICODE); ?>,
|
||
cancelButtonText: 'OK'
|
||
}).then((result) => {
|
||
if (result.isConfirmed) {
|
||
window.location.href = '/public/praticien/modifier.php';
|
||
}
|
||
});
|
||
}
|
||
});
|
||
|
||
document.addEventListener('click', async function (e) {
|
||
const btn = e.target.closest('.js-del');
|
||
if (!btn) return;
|
||
|
||
const id = btn.dataset.id;
|
||
if (!id) return;
|
||
|
||
const result = await Swal.fire({
|
||
icon: 'warning',
|
||
title: <?php echo json_encode($translations['confirm_title'] ?? 'Confirmation', JSON_UNESCAPED_UNICODE); ?>,
|
||
text: <?php echo json_encode($translations['confirm_supprimer_creneau'] ?? 'Supprimer ce créneau ?', JSON_UNESCAPED_UNICODE); ?>,
|
||
showCancelButton: true,
|
||
confirmButtonText: <?php echo json_encode($translations['btn_da_udalit'] ?? 'Oui', JSON_UNESCAPED_UNICODE); ?>,
|
||
cancelButtonText: <?php echo json_encode($translations['btn_annuler'] ?? 'Annuler', JSON_UNESCAPED_UNICODE); ?>,
|
||
reverseButtons: true
|
||
});
|
||
|
||
if (!result.isConfirmed) return;
|
||
|
||
const formData = new FormData();
|
||
formData.append('id', String(id));
|
||
formData.append('csrf_token', csrfToken);
|
||
|
||
fetch('/public/praticien/supprimer_disponibilite.php', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Accept': 'application/json',
|
||
'X-Requested-With': 'XMLHttpRequest'
|
||
},
|
||
body: formData
|
||
})
|
||
.then(async r => {
|
||
const text = await r.text();
|
||
try {
|
||
return JSON.parse(text);
|
||
} catch (e) {
|
||
return { ok: false, message: text };
|
||
}
|
||
})
|
||
.then(data => {
|
||
if (data.ok) {
|
||
Swal.fire({
|
||
icon: 'success',
|
||
title: <?php echo json_encode($translations['msg_creneau_supprime'] ?? 'Créneau supprimé.', JSON_UNESCAPED_UNICODE); ?>,
|
||
timer: 1400,
|
||
showConfirmButton: false
|
||
}).then(() => {
|
||
window.location.reload();
|
||
});
|
||
} else {
|
||
Swal.fire({
|
||
icon: 'error',
|
||
title: <?php echo json_encode($translations['erreur_titre'] ?? 'Erreur', JSON_UNESCAPED_UNICODE); ?>,
|
||
text: data.message || 'Suppression impossible.'
|
||
});
|
||
}
|
||
})
|
||
.catch(() => {
|
||
Swal.fire({
|
||
icon: 'error',
|
||
title: 'Erreur réseau',
|
||
text: 'Veuillez réessayer.'
|
||
});
|
||
});
|
||
});
|
||
</script>
|
||
|
||
<script defer>
|
||
(function(){
|
||
function initMobileCards(tbl){
|
||
if (!tbl) return;
|
||
const heads = Array.from(tbl.querySelectorAll('thead th')).map(th => th.textContent.trim());
|
||
tbl.querySelectorAll('tbody tr').forEach(tr => {
|
||
Array.from(tr.children).forEach((td, i) => {
|
||
if (!td.hasAttribute('data-label')) td.setAttribute('data-label', heads[i] || '');
|
||
});
|
||
});
|
||
document.documentElement.classList.add('mc-ready');
|
||
}
|
||
function ready(fn){
|
||
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', fn);
|
||
else fn();
|
||
}
|
||
ready(function(){
|
||
initMobileCards(document.getElementById('table_rdv'));
|
||
});
|
||
})();
|
||
</script>
|
||
|
||
<footer class="site-footer">
|
||
<div class="container">
|
||
<p><?php echo $translations['copyr'] ?? '© ' . date('Y'); ?></p>
|
||
</div>
|
||
</footer>
|
||
</body>
|
||
</html>
|