// js/calendar_admin.js document.addEventListener('DOMContentLoaded', function(){ const calendarEl = document.getElementById('calendar'); let selected_start_string = null; let event_selected = false; // pour event.remove() const calendar = new FullCalendar.Calendar(calendarEl,{ editable: true, locale: 'fr', //timeZone: 'local', // à modifier pour être à l'heure d'un autre pays initialView: 'dayGridMonth', headerToolbar:{ left: 'prev,next today', center: 'title', //right: 'dayGridMonth,timeGridWeek,timeGridDay,listWeek' right: 'dayGridMonth,timeGridWeek,listWeek' }, slotMinWidth: 70, defaultAllDay: false, // numéros de semaine //weekNumbers: true, //weekText: 's', // vue mois contentHeight: 600, // après initialisation: calendar.setOption('contentHeight', 650); //aspectRatio: 1.5, // après initialisation: calendar.setOption('aspectRatio', 1.8); // pour recalculer la taille au redimensionnement du parent, exécuter: calendar.updateSize() stickyHeaderDates: true, // garder les en-tête de colonnes lors du scroll fixedWeekCount: false, // avec false, affiche 4, 5 ou 6 semaines selon le mois selectable: true, // sélection de jours multiples navLinks: true, // numéros de jour et de semaines clicables // vue semaine slotEventOverlap: true, // superposition (limitée) de deux évènements simultanés allDayContent: 'Journée', // texte dans la case "toute la journée" nowIndicator: true, // barre rouge pour maintenant // params en plus: https://fullcalendar.io/docs/events-json-feed events: 'index.php?action=get_events', // fichier PHP qui retourne les événements select: function(info){ selected_start_string = info.startStr; // variable "globale" event_selected = false; const aside = document.querySelector('aside'); let checked = ''; let input = 'datetime-local'; // on veut des chaines de la forme 2025-05-20T07:05 // il faut retirer les secondes et le fuseau horaire du format ISO, c'est chiant // on enverra par contre une chaine ISO au serveur pour avoir un enregistrement correct let start_value; let end_value; const end = new Date(info.endStr); if(calendar.view.type == 'dayGridMonth'){ start_value = info.startStr + 'T10:00'; end.setDate(end.getDate() - 1); // jour de fin modifié pour ne pas faire bizarre pour l'utilisateur end.setHours(11); end_value = end.toISOString().split('T')[0] + 'T11:00'; } else if(calendar.view.type == 'timeGridWeek' || calendar.view.type == 'timeGridDay'){ const start_array = info.startStr.split("T"); const end_array = info.endStr.split("T"); // clic sur la ligne "Journée", = 'dayGridMonth' if(start_array.length == 1){ checked = 'checked'; input = 'date'; start_value = info.startStr; end.setDate(end.getDate() - 1); end_value = end.toISOString().split('T')[0]; } else if(start_array.length == 2){ start_value = start_array[0] + "T" + start_array[1].substr(0,5); // format 2025-06-12T10:00 end_value = end_array[0] + "T" + end_array[1].substr(0,5); } } const aside_content = `

Nouvel évènement

`; aside.innerHTML = aside_content; calendar.updateSize(); }, //unselect: function(event, view){}, eventClick: function(info){ event_selected = true; // variable "globale" const aside = document.querySelector('aside'); const checked = info.event.allDay ? 'checked' : ''; const input = info.event.allDay ? 'date' : 'datetime-local'; // change des objets Date en chaînes compatibles avec les input function formatDate(date){ return date.getFullYear() + '-' + (date.getMonth() + 1).toString().padStart(2, '0') + '-' + date.getDate().toString().padStart(2, '0') + (info.event.allDay ? '' : 'T' + date.getHours().toString().padStart(2, '0') + ':' + date.getMinutes().toString().padStart(2, '0')); } function minusOneDay(date){ date.setDate(date.getDate() - 1); // jour de fin modifié pour ne pas faire bizarre pour l'utilisateur return date; } const formated_start = formatDate(info.event.start); const formated_end = formatDate(info.event.allDay ? minusOneDay(info.event.end) : info.event.end, info.event.allDay); const aside_content = `

Modifier un évènement

`; aside.innerHTML = aside_content; calendar.updateSize(); }, viewDidMount: function(info){ // déclenché lorsque qu'une nouvelle vue est chargée (mois, semaine...) if(selected_start_string){ calendar.gotoDate(new Date(selected_start_string)); } }, //datesSet: function(info){}, // déclenché lorsque des dates affichées sont chargées (= comme viewDidMount + changement de date) }); function hideModal(){ const aside = document.querySelector('aside'); event_selected = false; aside.innerHTML = ''; calendar.updateSize(); } function submitEvent(new_event){ const event_title = document.getElementById('event_title').value; const event_description = document.getElementById('event_description').value; const event_all_day = document.getElementById('event_all_day').checked; let event_start = document.getElementById('event_start').value; let event_end = document.getElementById('event_end').value; const event_color = document.getElementById('event_color').value; // #3788d8 par défaut const event_id = new_event ? '' : document.getElementById('event_id').value; if(event_title.length !== 0 && event_start.length !== 0 && event_end.length !== 0 && event_color.length !== 0 && (new_event || event_id.length !== 0)) { if(event_all_day){ // on remet le jour de fin exclu const tmp_object = new Date(event_end); tmp_object.setDate(tmp_object.getDate() + 1); event_end = tmp_object.toISOString().split('T')[0]; } else{ event_start = new Date(event_start).toISOString(); event_end = new Date(event_end).toISOString(); } if(event_start > event_end || (!event_all_day && event_start == event_end)){ return; } // création if(new_event){ const event = { title: event_title, description: event_description, allDay: event_all_day, start: event_start, end: event_end, color: event_color }; fetch('index.php?action=new_event', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(event), }) .then(response => response.json()) .then(data => { if(data.success){ event.id = data.id; calendar.addEvent(event); hideModal(); } }) .catch((error) => { console.error('Error:', error); }); } // modification else{ const event = calendar.getEventById(event_id); if(event){ const event_copy = { id: parseInt(event.id), description: event_description, title: event_title, allDay: event_all_day, start: event_start, end: event_end, color: event_color }; fetch('index.php?action=update_event', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(event_copy), }) .then(response => response.json()) .then(data => { if(data.success){ event.setProp('title', event_title); event.setExtendedProp('description', event_description); event.setAllDay(event_all_day); event.setStart(event_start); event.setEnd(event_end); event.setProp('color', event_color); hideModal(); } }) .catch((error) => { console.error('Error:', error); }); } else{ console.log("Événement non trouvé !"); } } } else{ // notif input vide console.log('erreur: input vide'); } } function checkAllDay(){ const event_start_input = document.getElementById('event_start'); const event_end_input = document.getElementById('event_end'); const start = event_start_input.value; const end = event_end_input.value; if(document.getElementById('event_all_day').checked){ event_start_input.type = 'date'; event_end_input.type = 'date'; event_start_input.value = start.split('T')[0]; event_end_input.value = end.split('T')[0]; } else{ event_start_input.type = 'datetime-local'; event_end_input.type = 'datetime-local'; event_start_input.value = start + 'T10:00'; event_end_input.value = end + 'T11:00'; } } function removeEvent(){ if(!confirm("Voulez-vous vraiment supprimer cet évènement du calendrier?")){ return; } const event_id = document.getElementById('event_id').value; const event = calendar.getEventById(event_id); fetch('index.php?action=remove_event', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({'id': event_id}), }) .then(response => response.json()) .then(data => { if(data.success){ event.remove(); hideModal(); } }) .catch((error) => { console.error('Error:', error); }); event_selected = false; } // touches de clavier document.addEventListener('keydown', function(event){ if(event.key === 'Escape'){ hideModal(); } else if(event.key === 'Delete' && event_selected === true){ removeEvent(); } }); // boutons dans la "modale" // technique de la délégation d'événements pour utiliser un bouton ajouté dynamiquement document.addEventListener('click', function(event){ if(event.target.classList.contains('event_close_button')){ hideModal(); } else if(event.target.classList.contains('event_all_day')){ checkAllDay(); } else if(event.target.classList.contains('submit_new_event')){ submitEvent(true); } else if(event.target.classList.contains('submit_update_event')){ submitEvent(false); } else if(event.target.classList.contains('delete_event')){ removeEvent(); } }); calendar.render(); });