Files

319 lines
11 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare (strict_types = 1);
// /public/praticien/disponibilites_date.php
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 une dispo ponctuelle (POST + CSRF)
if (
$_SERVER['REQUEST_METHOD'] === 'POST'
&& ($_POST['action'] ?? '') === 'add'
&& isset($_POST['date_dispo'], $_POST['heure_debut'], $_POST['heure_fin'])
) {
require_post_csrf();
$date_dispo = trim((string) $_POST['date_dispo']); // YYYY-MM-DD
$debut = trim((string) $_POST['heure_debut']); // HH:MM
$fin = trim((string) $_POST['heure_fin']); // HH:MM
$dt = DateTimeImmutable::createFromFormat('Y-m-d', $date_dispo);
if (! $dt || $dt->format('Y-m-d') !== $date_dispo) {
$erreur = $translations['erreur_format_date'] ?? "Format de date invalide.";
} else {
$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_date (praticien_id, date_dispo, heure_debut, heure_fin)
VALUES (?, ?, ?, ?)
");
$stmt->execute([$praticien_id, $date_dispo, $debut, $fin]);
header('Location: /public/praticien/disponibilites_date.php?ok=added', true, 303);
exit;
}
}
}
}
// ---- Récupérer les plages datées
$stmt = $pdo->prepare("
SELECT id, date_dispo, heure_debut, heure_fin
FROM disponibilite_date
WHERE praticien_id = ?
ORDER BY date_dispo ASC, heure_debut ASC
");
$stmt->execute([$praticien_id]);
$dates = $stmt->fetchAll(PDO::FETCH_ASSOC);
$PAGE_TITLE = $translations['titre_disponibilites_date'] ?? 'Disponibilités (dates)';
$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_date'] ?? 'Mes disponibilités ponctuelles'); ?>
</h2>
<div class="d-flex justify-content-between align-items-center mb-3">
<a class="btn btn-outline-secondary" href="/public/praticien/disponibilites.php">
<?php echo htmlspecialchars($translations['retour_au_planning'] ?? 'Retour'); ?>
</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['cl_date_rdv'] ?? 'Date'); ?></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 ($dates): ?>
<?php foreach ($dates as $d): ?>
<tr>
<td><?php echo htmlspecialchars(date('d/m/Y', strtotime((string) $d['date_dispo']))); ?></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_date'] ?? 'Ajouter une disponibilité ponctuelle'); ?>
</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="date_dispo"><?php echo htmlspecialchars($translations['cl_date_rdv'] ?? 'Date'); ?></label>
<input type="date" name="date_dispo" id="date_dispo" class="form-control" required>
</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) {
window.history.replaceState({}, document.title, window.location.pathname);
}
}
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_date.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'] ?? '&copy; ' . date('Y'); ?></p>
</div>
</footer>
</body>
</html>