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

+315
View File
@@ -0,0 +1,315 @@
<?php
require_once __DIR__ . '/../includes/init_praticien.php';
require_once '../includes/header.php';
$pdo->prepare("
INSERT INTO push_badge_counters (compte_id, msg_unread, rdv_unread, updated_at)
VALUES (:cid, 0, 0, NOW())
ON DUPLICATE KEY UPDATE
rdv_unread = 0,
updated_at = NOW()
")->execute([':cid' => (int)$_SESSION['compte_id']]);
$praticien_id = $_SESSION['reference_id'];
$id_rdv = isset($_GET['id']) ? intval($_GET['id']) : null;
// Suppression si demandée (via POST confirmé par SweetAlert2 côté client)
if ($id_rdv && isset($_POST['delete'])) {
$stmt = $pdo->prepare("DELETE FROM rendez_vous
WHERE id = ? AND statut = 'annulé' AND praticien_id = ?");
$stmt->execute([$id_rdv, $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>'.htmlspecialchars($translations['rdv_supprime'] ?? 'Rendez-vous supprimé').'</h4>';
echo ' </div>';
echo ' <a href="/public/praticien/rendez_vous.php" class="btn btn-primary">';
echo ' <i class="bi bi-arrow-left-circle"></i> '.htmlspecialchars($translations['retour_a_la_liste'] ?? 'Retour à la liste').'
</a>';
echo ' </div>';
echo '</div>';
echo '</div>';
exit;
}
// Charger les rendez-vous
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 = ? AND rv.praticien_id = ?
");
$stmt->execute([$id_rdv, $praticien_id]);
$rdvs = $stmt->fetchAll();
} 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 = ?
ORDER BY rv.date_rdv DESC
");
$stmt->execute([$praticien_id]);
$rdvs = $stmt->fetchAll();
}
// 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_TITLE = $translations['mes_rdv_title'] ?? 'Mes rendez-vous';
$HEAD_EXTRA = <<<HTML
<script src="/js/sweetalert2@11.js"></script>
HTML;
?>
<div class="main1">
<div class="container py-5">
<div class="shadow p-4 rounded bg-light">
<h2 class="mb-4 text-center"><?= htmlspecialchars($translations['mes_rdv_title'] ?? 'Mes rendez-vous') ?></h2>
<?php if (!$id_rdv): ?>
<a href="/public/praticien/rendez_vous_du_jour.php" class="btn btn-outline-secondary mb-4">
<?= htmlspecialchars($translations['voir_rdv_du_jour'] ?? 'Voir les RDV du jour') ?>
</a>
<div class="mb-3">
<label for="filtre_statut" class="form-label"><?= htmlspecialchars($translations['filtrer_par_statut'] ?? 'Filtrer par statut') ?></label>
<select id="filtre_statut" class="form-select w-auto d-inline-block" onchange="filtrer()">
<option value=""><?= htmlspecialchars($translations['option_tous'] ?? 'Tous') ?></option>
<?php foreach (['prévu','confirmé','annulé','terminé'] as $s): ?>
<option value="<?= $s ?>"><?= htmlspecialchars($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><?= htmlspecialchars($translations['col_date'] ?? 'Date') ?></th>
<th><?= htmlspecialchars($translations['col_heure'] ?? 'Heure') ?></th>
<th><?= htmlspecialchars($translations['col_client'] ?? 'Client') ?></th>
<th><?= htmlspecialchars($translations['col_duree'] ?? 'Durée') ?></th>
<th><?= htmlspecialchars($translations['col_statut'] ?? 'Statut') ?></th>
<?php if (!$id_rdv): ?><th><?= htmlspecialchars($translations['col_action'] ?? 'Action') ?></th><?php endif; ?>
<th><?= htmlspecialchars($translations['btn_chat'] ?? 'Chat') ?></th>
<th><?= htmlspecialchars($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="<?= htmlspecialchars($r['statut']) ?>">
<td><?= htmlspecialchars($dt->format('d/m/Y')) ?></td>
<td><?= htmlspecialchars($dt->format('H:i')) ?></td>
<td><?= htmlspecialchars($r['prenom_client'] . ' ' . $r['nom_client']) ?></td>
<td><?= (int)$r['duree_minutes'] . ' ' . htmlspecialchars($translations['min_abbr'] ?? 'min') ?></td>
<td>
<select class="form-select form-select-sm" onchange="changerStatut(<?= $id ?>, this.value)">
<?php foreach (['prévu','confirmé','annulé','terminé'] as $s): ?>
<option value="<?= $s ?>" <?= ($r['statut'] === $s ? 'selected' : '') ?>>
<?= htmlspecialchars($statutMap[$s] ?? ucfirst($s)) ?>
</option>
<?php endforeach; ?>
</select>
</td>
<?php if (!$id_rdv): ?>
<td>
<?php if ($r['statut'] === 'annulé'): ?>
<form method="POST" action="?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">
<?= htmlspecialchars($translations['btn_supprimer'] ?? 'Supprimer') ?>
</button>
</form>
<?php else: ?>
<a href="?id=<?= $id ?>" class="btn btn-outline-secondary btn-sm btn-table">
<?= htmlspecialchars($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'] ?>">
<?= htmlspecialchars($translations['chat_praticien'] ?? 'Ouvrir le chat') ?>
</a>
</td>
<!-- ====== Colonne Visio / Cabinet ====== -->
<td>
<?php
$lieu = strtolower(trim((string)($r['lieu'] ?? '')));
if ($lieu === 'online') {
// --- on garde l'ancienne logique visio telle quelle ---
$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">
<?= htmlspecialchars($translations['visio_nachalo'] ?? 'Démarrer la visio') ?>
</a>
<?php
} else {
?>
<small class="text-muted d-block mt-1">
<?php
if ($now < $open) echo htmlspecialchars(($translations['ouvert_a'] ?? 'Ouvert à') . ' ' . $open->format('H:i'));
elseif ($now > $close) echo htmlspecialchars(($translations['ferme_a'] ?? 'Fermé à') . ' ' . $close->format('H:i'));
else echo htmlspecialchars(($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">
<?= htmlspecialchars($translations['votre_rdv_jdet'] ?? 'En attente de confirmation') ?>
</small>
<?php
} else {
?>
<small class="text-muted d-block mt-1">
<?= htmlspecialchars(($translations['zaverchen_a'] ?? 'Terminé') . ' ' . $close->format('H:i')) ?>
</small>
<?php
}
} else {
// --- offline : juste afficher "Очно" / libellé cabinet ---
echo '<span class="text-nowrap">'.htmlspecialchars($translations['option_cabinet'] ?? 'Очно').'</span>';
}
?>
</td>
<!-- ===================================== -->
</tr>
<?php endforeach; else: ?>
<tr>
<td colspan="<?= $id_rdv ? 6 : 8 ?>"><?= htmlspecialchars($translations['aucun_rdv_trouve'] ?? 'Aucun rendez-vous trouvé') ?></td>
</tr>
<?php endif; ?>
</tbody>
</table>
</div>
<?php if ($id_rdv): ?>
<a href="/public/praticien/rendez_vous.php" class="btn btn-outline-secondary mb-4">
<?= htmlspecialchars($translations['retour_a_la_liste'] ?? 'Retour à la liste') ?>
</a>
<?php endif; ?>
</div>
</div>
<script>
function changerStatut(id, statut) {
fetch('/public/praticien/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) })
})
.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'] ?? '&copy; '.date('Y') ?></p>
</div>
</footer>
</body>
</html>