Init Git Prod Mars 2026

This commit is contained in:
Oleg Barykin committed 2026-03-05 10:25:39 +01:00
commit d81efe93b1
362 files changed
+86627

No files matched your search

+283
View File
@@ -0,0 +1,283 @@
<?php
// /public/praticien/disponibilites.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 (garde-fou strict)
$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;
}
// fallback sécurité
// ---- Ajouter un créneau (POST) ----
$erreur = null;
$toMin = function (string $hhmm): ?int {
// input type="time" renvoie souvent HH:MM
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;
};
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['jour'], $_POST['heure_debut'], $_POST['heure_fin'])) {
$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; // minutes
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;
}
}
}
// ---- Supprimer un créneau (GET) ----
// (idéalement: POST + CSRF, on garde GET pour aller vite)
if (isset($_GET['supprimer'])) {
$id = (int) $_GET['supprimer'];
$stmt = $pdo->prepare("DELETE FROM disponibilite WHERE id = ? AND praticien_id = ?");
$stmt->execute([$id, $praticien_id]);
header('Location: /public/praticien/disponibilites.php?ok=deleted', 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);
// Prépare les traductions de jours
$jourKeyMap = [
'lundi' => 'jour_lundi', 'mardi' => 'jour_mardi', 'mercredi' => 'jour_mercredi',
'jeudi' => 'jour_jeudi', 'vendredi' => 'jour_vendredi', 'samedi' => 'jour_samedi', 'dimanche' => 'jour_dimanche',
];
// 1️⃣ Titre de la page
$PAGE_TITLE = $translations['titre_disponibilites'] ?? 'Disponibilités';
// 2️⃣ Ce qui doit aller dans <head> EN PLUS du header global
$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>
<!-- Plus d'alertes Bootstrap ici : on affiche via Swal selon ?ok=... et $erreur -->
<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): 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;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">
<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>
// --- SweetAlert2: messages succès / erreur depuis PHP ---
document.addEventListener('DOMContentLoaded', function(){
const okParam = '<?php echo isset($_GET['ok']) ? addslashes((string) $_GET['ok']) : '' ?>';
const erreurMsg = '<?php echo $erreur ? addslashes((string) $erreur) : '' ?>';
if (okParam === 'added') {
Swal.fire({
icon: 'success',
title: '<?php echo addslashes($translations["msg_creneau_ajoute"] ?? "Créneau ajouté.") ?>',
timer: 1800,
showConfirmButton: false
});
} else if (okParam === 'deleted') {
Swal.fire({
icon: 'success',
title: '<?php echo addslashes($translations["msg_creneau_supprime"] ?? "Créneau supprimé.") ?>',
timer: 1800,
showConfirmButton: false
});
}
if (erreurMsg) {
Swal.fire({
icon: 'error',
title: '<?php echo addslashes($translations["erreur_titre"] ?? "Erreur") ?>',
html: erreurMsg,
showCancelButton: true,
confirmButtonText: '<?php echo addslashes($translations["btn_modifier_profil"] ?? "Modifier mon profil") ?>',
cancelButtonText: 'OK'
}).then((result) => {
if (result.isConfirmed) {
window.location.href = '/public/praticien/modifier.php';
}
});
}
});
// --- Confirmation de suppression avec SweetAlert2 ---
document.addEventListener('click', async function(e){
const btn = e.target.closest('.js-del');
if (!btn) return;
const id = btn.dataset.id;
const result = await Swal.fire({
icon: 'warning',
title: '<?php echo addslashes($translations["confirm_title"] ?? "Confirmation") ?>',
text: '<?php echo addslashes($translations["confirm_supprimer_creneau"] ?? "Supprimer ce créneau ?") ?>',
showCancelButton: true,
confirmButtonText: '<?php echo addslashes($translations["btn_da_udalit"] ?? "Oui") ?>',
cancelButtonText: '<?php echo addslashes($translations["btn_annuler"] ?? "Annuler") ?>',
reverseButtons: true
});
if (result.isConfirmed) {
// Suppression côté serveur (GET) — garde la mécanique existante
window.location.href = '?supprimer=' + encodeURIComponent(id);
}
});
</script>
<!--*********Adaptation des tables pour un affichage mobile***************-->
<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>