Init Git Prod Mars 2026
This commit is contained in:
commit
d81efe93b1
362 files changed
+86627
No files matched your search
@@ -0,0 +1,235 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../includes/init_praticien.php';
|
||||
|
||||
$praticien_id = (int)($_SESSION['reference_id'] ?? 0);
|
||||
$lang = $_SESSION['lang'] ?? 'fr';
|
||||
$fcLocale = ($lang === 'ru') ? 'ru' : 'fr';
|
||||
|
||||
// ✅ ne pas htmlspecialchars ici
|
||||
$PAGE_TITLE = $translations['titre_agenda_page'] ?? 'Agenda';
|
||||
|
||||
$HEAD_EXTRA = <<<HTML
|
||||
<!-- FullCalendar (bundle + toutes les locales) -->
|
||||
<!--<link href="https://cdn.jsdelivr.net/npm/fullcalendar@6.1.8/index.global.min.css" rel="stylesheet" />-->
|
||||
<script src="/js/index.global.min.js"></script>
|
||||
<!--<script src="https://cdn.jsdelivr.net/npm/fullcalendar@6.1.8/locales-all.global.min.js"></script>-->
|
||||
|
||||
<!-- SweetAlert2 -->
|
||||
<script src="/js/sweetalert2@11.js"></script>
|
||||
<link href="/css/sweetalert2.min.css" rel="stylesheet">
|
||||
|
||||
<style>
|
||||
/* ====== Theme léger & responsive ====== */
|
||||
:root{
|
||||
--card-bg:#fff;
|
||||
--muted:#6c757d;
|
||||
--brand:#38378B;
|
||||
--accent:#e9ecef;
|
||||
--text:#222;
|
||||
--border:#e9ecef;
|
||||
}
|
||||
body{ background:#8CA9FF; color:var(--text); }
|
||||
.agenda-wrap{ width:1000px; max-width:1100px; margin:clamp(8px,2vw,24px) auto; padding:clamp(8px,2vw,24px); }
|
||||
.agenda-card{ background:var(--card-bg); border:1px solid var(--border); border-radius:16px; box-shadow:0 6px 18px rgba(0,0,0,.06); padding:clamp(12px,2.4vw,28px); }
|
||||
.agenda-title{ margin:0 0 12px; text-align:center; font-weight:700; letter-spacing:.2px; }
|
||||
#calendar{ min-height:420px; }
|
||||
.fc .fc-toolbar.fc-header-toolbar{ gap:8px; flex-wrap:wrap; }
|
||||
.fc .fc-toolbar-title{ font-size:clamp(18px,2.4vw,22px); color:var(--brand); }
|
||||
.fc .fc-button{ border-radius:999px; border:1px solid var(--border); background:#fff; color:#111; box-shadow:0 2px 6px rgba(0,0,0,.05); padding:6px 10px; }
|
||||
.fc .fc-button-primary:not(:disabled).fc-button-active,
|
||||
.fc .fc-button-primary:not(:disabled):active,
|
||||
.fc .fc-button-primary:hover{ background:var(--accent); border-color:transparent; color:#000; }
|
||||
.fc .fc-col-header-cell-cushion{ color:var(--brand); font-weight:600; }
|
||||
.fc .fc-timegrid-slot-label, .fc .fc-timegrid-axis-cushion{ color:var(--muted); }
|
||||
.fc .fc-event{ border-radius:12px; border:0; padding:2px 4px; }
|
||||
.fc .fc-daygrid-event{ padding:2px 6px; }
|
||||
.empty-state{ text-align:center; color:var(--muted); padding:28px 12px 8px; font-size:15px; }
|
||||
.fc .fc-timegrid-now-indicator-line{ border-color:#ff5a5f; }
|
||||
@media (max-width: 768px){
|
||||
.agenda-card{ max-width:720px; border-radius:14px; }
|
||||
.fc .fc-toolbar.fc-header-toolbar{ justify-content:center; }
|
||||
.agenda-wrap{ width:750px; max-width:768px; }
|
||||
}
|
||||
</style>
|
||||
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"><?= htmlspecialchars($translations['h2_mon_agenda']) ?></h2>
|
||||
<div id="calendar" role="application" aria-label="calendar"></div>
|
||||
<div id="emptyMsg" class="empty-state" style="display:none;">
|
||||
<?= htmlspecialchars($translations['agenda_aucun_evenement'] ?? 'Aucun rendez-vous à afficher.') ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer class="site-footer">
|
||||
<div class="container">
|
||||
<p><?= $translations['copyr'] ?></p>
|
||||
</div>
|
||||
</footer>
|
||||
<!-- Bootstrap JS -->
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const calendarEl = document.getElementById('calendar');
|
||||
const emptyMsgEl = document.getElementById('emptyMsg');
|
||||
|
||||
// Expose translations to JS
|
||||
const t = <?= json_encode([
|
||||
'rdv_avec' => $translations['sw_rdv_avec'],
|
||||
'date' => $translations['sw_date'],
|
||||
'heure' => $translations['sw_heure'],
|
||||
'statut' => $translations['sw_statut'],
|
||||
'gerer_rdv' => $translations['sw_gerer_rdv'],
|
||||
'btn_fermer' => $translations['sw_btn_fermer'],
|
||||
'err_chargement' => $translations['erreur_chargement_rdv'],
|
||||
'statut_confirme' => $translations['statut_confirme'],
|
||||
'statut_prevu' => $translations['statut_prevu'],
|
||||
'statut_annule' => $translations['statut_annule'],
|
||||
], JSON_UNESCAPED_UNICODE) ?>;
|
||||
|
||||
const fcLocale = <?= json_encode($fcLocale) ?>;
|
||||
|
||||
// Vue par défaut selon la largeur : liste sur mobile, semaine sur desktop
|
||||
const isMobile = window.matchMedia('(max-width: 768px)').matches;
|
||||
const initialView = isMobile ? 'listWeek' : 'timeGridWeek';
|
||||
|
||||
const calendar = new window.FullCalendar.Calendar(calendarEl, {
|
||||
locale: fcLocale,
|
||||
timeZone: 'Europe/Paris',
|
||||
initialView,
|
||||
nowIndicator: true,
|
||||
stickyHeaderDates: true,
|
||||
expandRows: true,
|
||||
slotMinTime: "07:00:00",
|
||||
slotMaxTime: "21:00:00",
|
||||
firstDay: 1, // Lundi
|
||||
height: isMobile ? 'auto' : 680,
|
||||
customButtons: {
|
||||
btnJour: {
|
||||
text: '<?= addslashes($translations['btn_jour'] ?? 'Jour') ?>',
|
||||
click() { calendar.changeView('listDay'); }
|
||||
},
|
||||
btnSemaine: {
|
||||
text: '<?= addslashes($translations['btn_semaine'] ?? 'Semaine') ?>',
|
||||
click() { calendar.changeView('listWeek'); }
|
||||
}
|
||||
},
|
||||
headerToolbar: {
|
||||
left: 'prev,next today',
|
||||
center: 'title',
|
||||
right: isMobile
|
||||
? 'btnJour,btnSemaine,dayGridMonth' // ✅ affiche: Jour | Semaine | Mois
|
||||
: 'timeGridWeek,dayGridMonth'
|
||||
},
|
||||
buttonText: {
|
||||
today: '<?= addslashes($translations['btn_auj'] ?? "Aujourd'hui") ?>',
|
||||
month: '<?= addslashes($translations['btn_mois'] ?? 'Mois') ?>',
|
||||
week: '<?= addslashes($translations['btn_semaine'] ?? 'Semaine') ?>',
|
||||
day: '<?= addslashes($translations['btn_jour'] ?? 'Jour') ?>',
|
||||
list: '<?= addslashes($translations['btn_liste'] ?? 'Liste') ?>'
|
||||
},
|
||||
// 👉 clé pour corriger "Liste Liste"
|
||||
views: {
|
||||
listDay: { buttonText: '<?= addslashes($translations['btn_jour'] ?? 'Jour') ?>' },
|
||||
listWeek: { buttonText: '<?= addslashes($translations['btn_semaine'] ?? 'Semaine') ?>' }
|
||||
},
|
||||
// Événements
|
||||
events: {
|
||||
url: '/public/praticien/events.php',
|
||||
method: 'POST',
|
||||
extraParams: { praticien_id: <?= json_encode($praticien_id) ?> },
|
||||
failure: function () {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Oops…',
|
||||
text: t.err_chargement
|
||||
});
|
||||
}
|
||||
},
|
||||
// Ajuste l’état vide
|
||||
loading: function(isLoading) {
|
||||
if (isLoading) return;
|
||||
// Quand le chargement est fini, teste s’il y a des events visibles
|
||||
const hasEvents = calendar.getEvents().length > 0;
|
||||
emptyMsgEl.style.display = hasEvents ? 'none' : 'block';
|
||||
},
|
||||
// Rendu compact des événements
|
||||
eventContent: function(arg){
|
||||
// Sur listView, FullCalendar gère déjà bien. En grille, compactons.
|
||||
if (arg.view.type.includes('timeGrid') || arg.view.type.includes('dayGrid')) {
|
||||
const title = document.createElement('div');
|
||||
title.style.fontWeight = '600';
|
||||
title.style.fontSize = '12px';
|
||||
title.textContent = arg.event.title;
|
||||
|
||||
const time = document.createElement('div');
|
||||
time.style.fontSize = '11px';
|
||||
time.style.opacity = '.9';
|
||||
// arg.timeText -> "10:00 - 10:30"
|
||||
time.textContent = arg.timeText;
|
||||
|
||||
return { domNodes: [title, time] };
|
||||
}
|
||||
return true;
|
||||
},
|
||||
// Modale SweetAlert
|
||||
eventClick: function(info) {
|
||||
const start = new Date(info.event.start);
|
||||
const end = new Date(info.event.end);
|
||||
|
||||
const dateStr = start.toLocaleDateString(<?= json_encode($lang === 'ru' ? 'ru-RU' : 'fr-FR') ?>, {
|
||||
weekday: 'long', year: 'numeric', month: 'long', day: 'numeric'
|
||||
});
|
||||
const heureDebut = start.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
|
||||
const heureFin = end.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
|
||||
|
||||
const raw = (info.event.extendedProps.statut || '').trim().toLowerCase();
|
||||
const fr2key = { 'prévu': 'planned', 'confirmé': 'confirmed', 'annulé': 'cancelled' };
|
||||
const key = fr2key[raw] || raw;
|
||||
|
||||
const statutMap = {
|
||||
planned: t.statut_prevu,
|
||||
confirmed: t.statut_confirme,
|
||||
cancelled: t.statut_annule
|
||||
};
|
||||
const statutLabel = statutMap[key] ?? key;
|
||||
|
||||
Swal.fire({
|
||||
title: `${t.rdv_avec} ${info.event.title}`,
|
||||
html: `
|
||||
<p><strong>${t.date} :</strong> ${dateStr}</p>
|
||||
<p><strong>${t.heure} :</strong> ${heureDebut} – ${heureFin}</p>
|
||||
<p><strong>${t.statut} :</strong> ${statutLabel}</p>
|
||||
<a class="swal2-confirm swal2-styled" style="display:inline-block;margin-top:8px;border-radius:999px" href="/public/praticien/rendez_vous.php?id=${info.event.id}" target="_blank">${t.gerer_rdv}</a>
|
||||
`,
|
||||
showConfirmButton: true,
|
||||
confirmButtonText: t.btn_fermer
|
||||
});
|
||||
},
|
||||
// Accessibilité basique
|
||||
eventDidMount: function(info){
|
||||
info.el.setAttribute('aria-label', info.event.title);
|
||||
},
|
||||
// Switch auto si on redimensionne
|
||||
windowResize: function(view){
|
||||
const mobile = window.matchMedia('(max-width: 768px)').matches;
|
||||
calendar.setOption('height', mobile ? 'auto' : 680);
|
||||
// Garde la vue “liste” sur mobile si l’utilisateur n’a pas changé manuellement
|
||||
if (mobile && !calendar.view.type.startsWith('list')) {
|
||||
calendar.changeView('listWeek');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
calendar.render();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in new issue
Block a user