326 lines
13 KiB
PHP
326 lines
13 KiB
PHP
<?php
|
|
// /public/cabinet/rendez_vous.php (VERSION SECRÉTAIRE)
|
|
require_once __DIR__ . '/../includes/init_secretaire.php';
|
|
|
|
|
|
if (!function_exists('h')) { function h($s){ return htmlspecialchars((string)$s, ENT_QUOTES, 'UTF-8'); } }
|
|
|
|
// --- Contexte secrétaire + praticien obligatoire
|
|
$compteId = (int)($_SESSION['compte_id'] ?? 0);
|
|
$userRole = strtolower((string)($_SESSION['role'] ?? ''));
|
|
$praticien_id = isset($_GET['praticien_id']) ? (int)$_GET['praticien_id'] : 0;
|
|
if ($userRole !== 'secretaire' || $praticien_id <= 0) { http_response_code(403); exit('Forbidden'); }
|
|
|
|
// --- Vérifier pivot + droit can_manage_rdv
|
|
$st = $pdo->prepare("
|
|
SELECT can_manage_rdv
|
|
FROM secretaire_praticien
|
|
WHERE secretaire_user_id = :u
|
|
AND praticien_id = :p
|
|
AND is_active = 1
|
|
LIMIT 1
|
|
");
|
|
$st->execute(['u'=>$compteId,'p'=>$praticien_id]);
|
|
$pivot = $st->fetch(PDO::FETCH_ASSOC);
|
|
if (!$pivot || (int)$pivot['can_manage_rdv'] !== 1) { http_response_code(403); exit('Forbidden'); }
|
|
|
|
// --- Paramètres
|
|
$id_rdv = isset($_GET['id']) ? (int)$_GET['id'] : null;
|
|
|
|
// --- Suppression (POST) : bornée au praticien + statut 'annulé'
|
|
if ($id_rdv && isset($_POST['delete'])) {
|
|
// delete uniquement si le RDV appartient à ce praticien et est annulé
|
|
$del = $pdo->prepare("DELETE FROM rendez_vous WHERE id = :id AND praticien_id = :p AND statut = 'annulé'");
|
|
$del->execute(['id'=>$id_rdv,'p'=>$praticien_id]);
|
|
|
|
echo '<div class="main1">';
|
|
echo '<div class="container py-5">';
|
|
echo ' <div class="shadow p-4 rounded bg-light text-center">';
|
|
echo ' <div class="alert alert mb-4" role="alert">';
|
|
echo ' <h4>'.h($translations['rdv_supprime'] ?? 'Rendez-vous supprimé').'</h4>';
|
|
echo ' </div>';
|
|
echo ' <a href="/public/cabinet/rendez_vous.php?praticien_id='. (int)$praticien_id .'" class="btn btn-primary">';
|
|
echo ' <i class="bi bi-arrow-left-circle"></i> '.h($translations['retour_a_la_liste'] ?? 'Retour à la liste').'
|
|
</a>';
|
|
echo ' </div>';
|
|
echo '</div>';
|
|
echo '</div>';
|
|
exit;
|
|
}
|
|
|
|
// --- Charger les RDV (bornés au praticien)
|
|
if ($id_rdv) {
|
|
$stmt = $pdo->prepare("
|
|
SELECT rv.*, c.nom AS nom_client, c.prenom AS prenom_client
|
|
FROM rendez_vous rv
|
|
JOIN client c ON rv.client_id = c.id
|
|
WHERE rv.id = :id
|
|
AND rv.praticien_id = :p
|
|
LIMIT 1
|
|
");
|
|
$stmt->execute(['id'=>$id_rdv,'p'=>$praticien_id]);
|
|
$one = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
$rdvs = $one ? [$one] : [];
|
|
} else {
|
|
$stmt = $pdo->prepare("
|
|
SELECT rv.*, c.nom AS nom_client, c.prenom AS prenom_client
|
|
FROM rendez_vous rv
|
|
JOIN client c ON rv.client_id = c.id
|
|
WHERE rv.praticien_id = :p
|
|
ORDER BY rv.date_rdv DESC
|
|
");
|
|
$stmt->execute(['p'=>$praticien_id]);
|
|
$rdvs = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|
}
|
|
|
|
// --- Map statuts (valeurs BDD -> libellés traduits)
|
|
$statutMap = [
|
|
'prévu' => $translations['statut_prevu'] ?? 'Prévu',
|
|
'confirmé' => $translations['statut_confirme'] ?? 'Confirmé',
|
|
'annulé' => $translations['statut_annule'] ?? 'Annulé',
|
|
'terminé' => $translations['statut_termine'] ?? 'Terminé',
|
|
];
|
|
|
|
$PAGE_TITRE = $translations['mes_rdv_title'] ?? 'Mes rendez-vous';
|
|
$HEAD_EXTRA = <<<HTML
|
|
<script src="/js/sweetalert2@11.js"></script>
|
|
HTML;
|
|
require_once __DIR__ . '/../includes/header_cabinet.php';
|
|
?>
|
|
<div class="main1">
|
|
<div class="container py-5">
|
|
<div class="shadow p-4 rounded bg-light">
|
|
<h2 class="mb-4 text-center"><?= htmlspecialchars($currentPraticienName, ENT_QUOTES, 'UTF-8') ?> - <?= h($translations['vse_consult'] ?? 'Mes rendez-vous') ?></h2>
|
|
|
|
<?php if (!$id_rdv): ?>
|
|
<a href="/public/cabinet/rendez_vous_du_jour.php?praticien_id=<?= (int)$praticien_id ?>" class="btn btn-outline-secondary mb-4">
|
|
<?= h($translations['voir_rdv_du_jour'] ?? 'Voir les RDV du jour') ?>
|
|
</a>
|
|
|
|
<div class="mb-3">
|
|
<label for="filtre_statut" class="form-label"><?= h($translations['filtrer_par_statut'] ?? 'Filtrer par statut') ?></label>
|
|
<select id="filtre_statut" class="form-select w-auto d-inline-block" onchange="filtrer()">
|
|
<option value=""><?= h($translations['option_tous'] ?? 'Tous') ?></option>
|
|
<?php foreach (['prévu','confirmé','annulé','terminé'] as $s): ?>
|
|
<option value="<?= $s ?>"><?= h($statutMap[$s] ?? ucfirst($s)) ?></option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<div class="table-responsive">
|
|
<table class="table table-bordered table-striped align-middle text-center mobile-cards" id="table_rdv">
|
|
<thead class="table-light">
|
|
<tr>
|
|
<th><?= h($translations['col_date'] ?? 'Date') ?></th>
|
|
<th><?= h($translations['col_heure'] ?? 'Heure') ?></th>
|
|
<th><?= h($translations['col_client'] ?? 'Client') ?></th>
|
|
<th><?= h($translations['col_duree'] ?? 'Durée') ?></th>
|
|
<th><?= h($translations['col_statut'] ?? 'Statut') ?></th>
|
|
<?php if (!$id_rdv): ?><th><?= h($translations['col_action'] ?? 'Action') ?></th><?php endif; ?>
|
|
<!--<th><?= h($translations['btn_chat'] ?? 'Chat') ?></th>
|
|
<th><?= h($translations['status_online'] ?? 'Visio') ?></th>-->
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php if (!empty($rdvs)): foreach ($rdvs as $r):
|
|
$dt = new DateTime($r['date_rdv']);
|
|
$id = (int)$r['id'];
|
|
?>
|
|
<tr data-statut="<?= h($r['statut']) ?>">
|
|
<td><?= h($dt->format('d/m/Y')) ?></td>
|
|
<td><?= h($dt->format('H:i')) ?></td>
|
|
<td><?= h(($r['prenom_client'] ?? '').' '.($r['nom_client'] ?? '')) ?></td>
|
|
<td><?= (int)$r['duree_minutes'] . ' ' . h($translations['min_abbr'] ?? 'min') ?></td>
|
|
<td>
|
|
<select class="form-select form-select-sm" onchange="changerStatut(<?= $id ?>, this.value, <?= (int)$praticien_id ?>)">
|
|
<?php foreach (['prévu','confirmé','annulé','terminé'] as $s): ?>
|
|
<option value="<?= $s ?>" <?= ($r['statut'] === $s ? 'selected' : '') ?>>
|
|
<?= h($statutMap[$s] ?? ucfirst($s)) ?>
|
|
</option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
</td>
|
|
|
|
<?php if (!$id_rdv): ?>
|
|
<td>
|
|
<?php if (($r['statut'] ?? '') === 'annulé'): ?>
|
|
<form method="POST" action="?praticien_id=<?= (int)$praticien_id ?>&id=<?= $id ?>" class="js-del-form d-inline">
|
|
<input type="hidden" name="delete" value="1">
|
|
<button type="button" class="btn btn-sm btn-table_del btn-table_del-danger js-del-btn">
|
|
<?= h($translations['btn_supprimer'] ?? 'Supprimer') ?>
|
|
</button>
|
|
</form>
|
|
<?php else: ?>
|
|
<a href="?praticien_id=<?= (int)$praticien_id ?>&id=<?= $id ?>" class="btn btn-outline-secondary btn-sm btn-table">
|
|
<?= h($translations['btn_voir'] ?? 'Voir') ?>
|
|
</a>
|
|
<?php endif; ?>
|
|
</td>
|
|
<?php endif; ?>
|
|
|
|
<!--<td>
|
|
<a class="btn btn-outline-secondary btn-sm btn-table" href="/messages/start.php?client_id=<?= (int)$r['client_id'] ?>">
|
|
<?= h($translations['chat_praticien'] ?? 'Ouvrir le chat') ?>
|
|
</a>
|
|
</td>-->
|
|
|
|
<!-- ====== Colonne Visio / Cabinet ======
|
|
<td>
|
|
<?php
|
|
$lieu = strtolower(trim((string)($r['lieu'] ?? '')));
|
|
if ($lieu === 'online') {
|
|
$statut = $r['statut'];
|
|
$tzParis = new DateTimeZone('Europe/Paris');
|
|
$now = new DateTime('now', $tzParis);
|
|
$start = new DateTime($r['date_rdv'], $tzParis);
|
|
$open = (clone $start)->modify('-15 minutes');
|
|
$close = (clone $start)->modify('+'.(int)($r['duree_minutes'] ?? 120).' minutes');
|
|
|
|
if ($statut === 'confirmé') {
|
|
if ($now >= $open && $now <= $close) {
|
|
?>
|
|
<a href="/visio/join.php?rdv=<?= (int)$r['id'] ?>&start=1" class="btn btn-outline-secondary btn-sm btn-table">
|
|
<?= h($translations['visio_nachalo'] ?? 'Démarrer la visio') ?>
|
|
</a>
|
|
<?php
|
|
} else {
|
|
?>
|
|
<small class="text-muted d-block mt-1">
|
|
<?php
|
|
if ($now < $open) echo h(($translations['ouvert_a'] ?? 'Ouvert à').' '.$open->format('H:i'));
|
|
elseif ($now > $close) echo h(($translations['ferme_a'] ?? 'Fermé à').' '.$close->format('H:i'));
|
|
else echo h(($translations['prev_a'] ?? 'Prévu le').' '.$start->format('d/m/Y \à H:i').'.');
|
|
?>
|
|
</small>
|
|
<?php
|
|
}
|
|
} elseif ($statut === 'prévu') {
|
|
?>
|
|
<small class="text-danger d-block mt-1"><?= h($translations['votre_rdv_jdet'] ?? 'En attente de confirmation') ?></small>
|
|
<?php
|
|
} else {
|
|
?>
|
|
<small class="text-muted d-block mt-1"><?= h(($translations['zaverchen_a'] ?? 'Terminé').' '.$close->format('H:i')) ?></small>
|
|
<?php
|
|
}
|
|
} else {
|
|
echo '<span class="text-nowrap">'.h($translations['option_cabinet'] ?? 'Очно').'</span>';
|
|
}
|
|
?>
|
|
</td>
|
|
===================================== -->
|
|
</tr>
|
|
<?php endforeach; else: ?>
|
|
<tr>
|
|
<td colspan="<?= $id_rdv ? 6 : 8 ?>"><?= h($translations['aucun_rdv_trouve'] ?? 'Aucun rendez-vous trouvé') ?></td>
|
|
</tr>
|
|
<?php endif; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<?php if ($id_rdv): ?>
|
|
<a href="/public/cabinet/rendez_vous.php?praticien_id=<?= (int)$praticien_id ?>" class="btn btn-outline-secondary mb-4">
|
|
<?= h($translations['retour_a_la_liste'] ?? 'Retour à la liste') ?>
|
|
</a>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
function changerStatut(id, statut, praticienId) {
|
|
fetch('/public/cabinet/changer_statut.php', {
|
|
method: 'POST',
|
|
headers: {'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'},
|
|
body: new URLSearchParams({ id: String(id), statut: String(statut), praticien_id: String(praticienId) })
|
|
})
|
|
.then(async (r) => {
|
|
const text = await r.text();
|
|
let data;
|
|
try { data = JSON.parse(text); }
|
|
catch { throw new Error('Réponse invalide: ' + text.slice(0,180)); }
|
|
|
|
if (!data.ok) throw new Error(data.message || 'Erreur');
|
|
|
|
Swal.fire({
|
|
icon: 'success',
|
|
title: data.message || '<?= addslashes($translations['maj_ok'] ?? "Mise à jour réussie") ?>',
|
|
timer: 1800,
|
|
showConfirmButton: false
|
|
});
|
|
})
|
|
.catch((e) => {
|
|
Swal.fire({
|
|
icon: 'error',
|
|
title: '<?= addslashes($translations['erreur_mise_a_jour'] ?? "Erreur de mise à jour") ?>',
|
|
text: e.message || '<?= addslashes($translations['erreur_mise_a_jour'] ?? "Erreur de mise à jour.") ?>',
|
|
confirmButtonText: 'OK'
|
|
});
|
|
});
|
|
}
|
|
</script>
|
|
|
|
<script>
|
|
function filtrer() {
|
|
const valeur = document.getElementById("filtre_statut").value;
|
|
const lignes = document.querySelectorAll("#table_rdv tbody tr");
|
|
lignes.forEach(tr => {
|
|
const statut = tr.dataset.statut;
|
|
tr.style.display = (!valeur || statut === valeur) ? '' : 'none';
|
|
});
|
|
}
|
|
</script>
|
|
|
|
<script>
|
|
document.addEventListener('click', async function(e){
|
|
const btn = e.target.closest('.js-del-btn');
|
|
if (!btn) return;
|
|
const form = btn.closest('.js-del-form');
|
|
if (!form) return;
|
|
|
|
const result = await Swal.fire({
|
|
icon: 'warning',
|
|
title: '<?= addslashes($translations["confirm_title"] ?? "Confirmation") ?>',
|
|
text: '<?= addslashes($translations["confirm_supprimer_rdv"] ?? "Supprimer définitivement ce rendez-vous annulé ?") ?>',
|
|
showCancelButton: true,
|
|
confirmButtonText: '<?= addslashes($translations["btn_da_udalit"] ?? "Oui") ?>',
|
|
cancelButtonText: '<?= addslashes($translations["btn_annuler"] ?? "Annuler") ?>',
|
|
reverseButtons: true
|
|
});
|
|
|
|
if (result.isConfirmed) form.submit();
|
|
});
|
|
</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(){
|
|
const tbl = document.getElementById('table_rdv') || document.querySelector('table.mobile-cards');
|
|
initMobileCards(tbl);
|
|
});
|
|
})();
|
|
</script>
|
|
</div>
|
|
<footer class="site-footer">
|
|
<div class="container">
|
|
<p><?= $translations['copyr'] ?? '© '.date('Y') ?></p>
|
|
</div>
|
|
</footer>
|
|
</body>
|
|
</html>
|