diff options
| author | polo <ordipolo@gmx.fr> | 2025-06-20 23:59:10 +0200 |
|---|---|---|
| committer | polo <ordipolo@gmx.fr> | 2025-06-20 23:59:10 +0200 |
| commit | f0e0b449f2bd6c4417e467b2903b29ad03a9626d (patch) | |
| tree | 2688d2c64ff5a290f0b12da240a4416ec16045cd | |
| parent | f582e617f6fea0a543ba9e683b9637f9557bb997 (diff) | |
| download | fullcalendar-f0e0b449f2bd6c4417e467b2903b29ad03a9626d.tar.gz fullcalendar-f0e0b449f2bd6c4417e467b2903b29ad03a9626d.tar.bz2 fullcalendar-f0e0b449f2bd6c4417e467b2903b29ad03a9626d.zip | |
params au chargement des données, suppression d'un évènement
| -rw-r--r-- | public/js/calendar.js | 8 | ||||
| -rw-r--r-- | public/js/calendar_admin.js | 49 | ||||
| -rw-r--r-- | src/load-events.php | 108 |
3 files changed, 116 insertions, 49 deletions
diff --git a/public/js/calendar.js b/public/js/calendar.js index af9ffb4..f1c55c4 100644 --- a/public/js/calendar.js +++ b/public/js/calendar.js | |||
| @@ -5,6 +5,7 @@ document.addEventListener('DOMContentLoaded', function(){ | |||
| 5 | const calendar = new FullCalendar.Calendar(calendarEl,{ | 5 | const calendar = new FullCalendar.Calendar(calendarEl,{ |
| 6 | editable: true, | 6 | editable: true, |
| 7 | locale: 'fr', | 7 | locale: 'fr', |
| 8 | //timeZone: 'local', // à modifier pour être à l'heure d'un autre pays | ||
| 8 | initialView: 'dayGridMonth', | 9 | initialView: 'dayGridMonth', |
| 9 | headerToolbar:{ | 10 | headerToolbar:{ |
| 10 | left: 'prev,next today', | 11 | left: 'prev,next today', |
| @@ -32,6 +33,8 @@ document.addEventListener('DOMContentLoaded', function(){ | |||
| 32 | slotEventOverlap: true, // superposition (limitée) de deux évènements simultanés | 33 | slotEventOverlap: true, // superposition (limitée) de deux évènements simultanés |
| 33 | allDayContent: 'Journée', // texte dans la case "toute la journée" | 34 | allDayContent: 'Journée', // texte dans la case "toute la journée" |
| 34 | nowIndicator: true, // barre rouge pour maintenant | 35 | nowIndicator: true, // barre rouge pour maintenant |
| 36 | |||
| 37 | events: '../src/load-events.php', // fichier PHP qui retourne les événements | ||
| 35 | 38 | ||
| 36 | select: function(info){ | 39 | select: function(info){ |
| 37 | selected_start_string = info.startStr; // variable "globale" | 40 | selected_start_string = info.startStr; // variable "globale" |
| @@ -47,7 +50,7 @@ document.addEventListener('DOMContentLoaded', function(){ | |||
| 47 | return date.getFullYear() + '-' + (date.getMonth() + 1).toString().padStart(2, '0') + '-' + date.getDate().toString().padStart(2, '0') | 50 | return date.getFullYear() + '-' + (date.getMonth() + 1).toString().padStart(2, '0') + '-' + date.getDate().toString().padStart(2, '0') |
| 48 | + (info.event.allDay ? '' : 'T' + date.getHours().toString().padStart(2, '0') + ':' + date.getMinutes().toString().padStart(2, '0')); | 51 | + (info.event.allDay ? '' : 'T' + date.getHours().toString().padStart(2, '0') + ':' + date.getMinutes().toString().padStart(2, '0')); |
| 49 | } | 52 | } |
| 50 | function getEndMinusOneDay(date){ | 53 | function minusOneDay(date){ |
| 51 | date.setDate(date.getDate() - 1); // jour de fin modifié pour ne pas faire bizarre pour l'utilisateur | 54 | date.setDate(date.getDate() - 1); // jour de fin modifié pour ne pas faire bizarre pour l'utilisateur |
| 52 | return date; | 55 | return date; |
| 53 | } | 56 | } |
| @@ -56,7 +59,7 @@ document.addEventListener('DOMContentLoaded', function(){ | |||
| 56 | const start_date = start.split('T')[0]; | 59 | const start_date = start.split('T')[0]; |
| 57 | const start_hour = (info.event.allDay ? '' : '<br>à ' + start.split('T')[1]).replace(":", "h"); | 60 | const start_hour = (info.event.allDay ? '' : '<br>à ' + start.split('T')[1]).replace(":", "h"); |
| 58 | const formated_start = 'le ' + start_date.split('-')[2] + '/' + start_date.split('-')[1] + '/' + start_date.split('-')[0] + start_hour; | 61 | const formated_start = 'le ' + start_date.split('-')[2] + '/' + start_date.split('-')[1] + '/' + start_date.split('-')[0] + start_hour; |
| 59 | const end = formatDate(info.event.allDay ? getEndMinusOneDay(info.event.end) : info.event.end, info.event.allDay); | 62 | const end = formatDate(info.event.allDay ? minusOneDay(info.event.end) : info.event.end, info.event.allDay); |
| 60 | const end_date = end.split('T')[0]; | 63 | const end_date = end.split('T')[0]; |
| 61 | const end_hour = (info.event.allDay ? '' : '<br>à ' + end.split('T')[1]).replace(":", "h"); | 64 | const end_hour = (info.event.allDay ? '' : '<br>à ' + end.split('T')[1]).replace(":", "h"); |
| 62 | const formated_end = 'le ' + end_date.split('-')[2] + '/' + end_date.split('-')[1] + '/' + end_date.split('-')[0] + end_hour; | 65 | const formated_end = 'le ' + end_date.split('-')[2] + '/' + end_date.split('-')[1] + '/' + end_date.split('-')[0] + end_hour; |
| @@ -78,7 +81,6 @@ document.addEventListener('DOMContentLoaded', function(){ | |||
| 78 | } | 81 | } |
| 79 | }, | 82 | }, |
| 80 | //datesSet: function(info){}, // déclenché lorsque des dates affichées sont chargées (= comme viewDidMount + changement de date) | 83 | //datesSet: function(info){}, // déclenché lorsque des dates affichées sont chargées (= comme viewDidMount + changement de date) |
| 81 | events: '../src/load-events.php' // fichier PHP qui retourne les événements | ||
| 82 | }); | 84 | }); |
| 83 | 85 | ||
| 84 | function hideModal(){ | 86 | function hideModal(){ |
diff --git a/public/js/calendar_admin.js b/public/js/calendar_admin.js index 253d127..8fe91a3 100644 --- a/public/js/calendar_admin.js +++ b/public/js/calendar_admin.js | |||
| @@ -1,10 +1,12 @@ | |||
| 1 | document.addEventListener('DOMContentLoaded', function(){ | 1 | document.addEventListener('DOMContentLoaded', function(){ |
| 2 | const calendarEl = document.getElementById('calendar'); | 2 | const calendarEl = document.getElementById('calendar'); |
| 3 | let selected_start_string = null; | 3 | let selected_start_string = null; |
| 4 | let event_selected = false; // pour event.remove() | ||
| 4 | 5 | ||
| 5 | const calendar = new FullCalendar.Calendar(calendarEl,{ | 6 | const calendar = new FullCalendar.Calendar(calendarEl,{ |
| 6 | editable: true, | 7 | editable: true, |
| 7 | locale: 'fr', | 8 | locale: 'fr', |
| 9 | //timeZone: 'local', // à modifier pour être à l'heure d'un autre pays | ||
| 8 | initialView: 'dayGridMonth', | 10 | initialView: 'dayGridMonth', |
| 9 | headerToolbar:{ | 11 | headerToolbar:{ |
| 10 | left: 'prev,next today', | 12 | left: 'prev,next today', |
| @@ -32,9 +34,12 @@ document.addEventListener('DOMContentLoaded', function(){ | |||
| 32 | slotEventOverlap: true, // superposition (limitée) de deux évènements simultanés | 34 | slotEventOverlap: true, // superposition (limitée) de deux évènements simultanés |
| 33 | allDayContent: 'Journée', // texte dans la case "toute la journée" | 35 | allDayContent: 'Journée', // texte dans la case "toute la journée" |
| 34 | nowIndicator: true, // barre rouge pour maintenant | 36 | nowIndicator: true, // barre rouge pour maintenant |
| 37 | |||
| 38 | events: '../src/load-events.php', // fichier PHP qui retourne les événements | ||
| 35 | 39 | ||
| 36 | select: function(info){ | 40 | select: function(info){ |
| 37 | selected_start_string = info.startStr; // variable "globale" | 41 | selected_start_string = info.startStr; // variable "globale" |
| 42 | event_selected = false; | ||
| 38 | const aside = document.querySelector('aside'); | 43 | const aside = document.querySelector('aside'); |
| 39 | let checked = ''; | 44 | let checked = ''; |
| 40 | let input = 'datetime-local'; | 45 | let input = 'datetime-local'; |
| @@ -107,6 +112,7 @@ document.addEventListener('DOMContentLoaded', function(){ | |||
| 107 | }, | 112 | }, |
| 108 | //unselect: function(event, view){}, | 113 | //unselect: function(event, view){}, |
| 109 | eventClick: function(info){ | 114 | eventClick: function(info){ |
| 115 | event_selected = true; // variable "globale" | ||
| 110 | const aside = document.querySelector('aside'); | 116 | const aside = document.querySelector('aside'); |
| 111 | const checked = info.event.allDay ? 'checked' : ''; | 117 | const checked = info.event.allDay ? 'checked' : ''; |
| 112 | const input = info.event.allDay ? 'date' : 'datetime-local'; | 118 | const input = info.event.allDay ? 'date' : 'datetime-local'; |
| @@ -116,13 +122,13 @@ document.addEventListener('DOMContentLoaded', function(){ | |||
| 116 | return date.getFullYear() + '-' + (date.getMonth() + 1).toString().padStart(2, '0') + '-' + date.getDate().toString().padStart(2, '0') | 122 | return date.getFullYear() + '-' + (date.getMonth() + 1).toString().padStart(2, '0') + '-' + date.getDate().toString().padStart(2, '0') |
| 117 | + (info.event.allDay ? '' : 'T' + date.getHours().toString().padStart(2, '0') + ':' + date.getMinutes().toString().padStart(2, '0')); | 123 | + (info.event.allDay ? '' : 'T' + date.getHours().toString().padStart(2, '0') + ':' + date.getMinutes().toString().padStart(2, '0')); |
| 118 | } | 124 | } |
| 119 | function getEndMinusOneDay(date){ | 125 | function minusOneDay(date){ |
| 120 | date.setDate(date.getDate() - 1); // jour de fin modifié pour ne pas faire bizarre pour l'utilisateur | 126 | date.setDate(date.getDate() - 1); // jour de fin modifié pour ne pas faire bizarre pour l'utilisateur |
| 121 | return date; | 127 | return date; |
| 122 | } | 128 | } |
| 123 | 129 | ||
| 124 | const formated_start = formatDate(info.event.start); | 130 | const formated_start = formatDate(info.event.start); |
| 125 | const formated_end = formatDate(info.event.allDay ? getEndMinusOneDay(info.event.end) : info.event.end, info.event.allDay); | 131 | const formated_end = formatDate(info.event.allDay ? minusOneDay(info.event.end) : info.event.end, info.event.allDay); |
| 126 | 132 | ||
| 127 | const aside_content = `<div class="form_event"> | 133 | const aside_content = `<div class="form_event"> |
| 128 | <div class="event_title_box"> | 134 | <div class="event_title_box"> |
| @@ -155,6 +161,7 @@ document.addEventListener('DOMContentLoaded', function(){ | |||
| 155 | </div> | 161 | </div> |
| 156 | <button class="submit_update_event">Modifier</button> | 162 | <button class="submit_update_event">Modifier</button> |
| 157 | <button class="event_close_button">Annuler</button> | 163 | <button class="event_close_button">Annuler</button> |
| 164 | <button class="delete_event">Supprimer</button> | ||
| 158 | </div>`; | 165 | </div>`; |
| 159 | aside.innerHTML = aside_content; | 166 | aside.innerHTML = aside_content; |
| 160 | calendar.updateSize(); | 167 | calendar.updateSize(); |
| @@ -165,11 +172,11 @@ document.addEventListener('DOMContentLoaded', function(){ | |||
| 165 | } | 172 | } |
| 166 | }, | 173 | }, |
| 167 | //datesSet: function(info){}, // déclenché lorsque des dates affichées sont chargées (= comme viewDidMount + changement de date) | 174 | //datesSet: function(info){}, // déclenché lorsque des dates affichées sont chargées (= comme viewDidMount + changement de date) |
| 168 | events: '../src/load-events.php' // fichier PHP qui retourne les événements | ||
| 169 | }); | 175 | }); |
| 170 | 176 | ||
| 171 | function hideModal(){ | 177 | function hideModal(){ |
| 172 | const aside = document.querySelector('aside'); | 178 | const aside = document.querySelector('aside'); |
| 179 | event_selected = false; | ||
| 173 | aside.innerHTML = ''; | 180 | aside.innerHTML = ''; |
| 174 | calendar.updateSize(); | 181 | calendar.updateSize(); |
| 175 | } | 182 | } |
| @@ -304,13 +311,44 @@ document.addEventListener('DOMContentLoaded', function(){ | |||
| 304 | event_end_input.value = end + 'T11:00'; | 311 | event_end_input.value = end + 'T11:00'; |
| 305 | } | 312 | } |
| 306 | } | 313 | } |
| 314 | function removeEvent(){ | ||
| 315 | if(!confirm("Voulez-vous vraiment supprimer cet évènement du calendrier?")){ | ||
| 316 | return; | ||
| 317 | } | ||
| 318 | const event_id = document.getElementById('event_id').value; | ||
| 319 | const event = calendar.getEventById(event_id); | ||
| 320 | |||
| 321 | fetch('../src/post-ajax.php', { | ||
| 322 | method: 'POST', | ||
| 323 | headers: { | ||
| 324 | 'Content-Type': 'application/json', | ||
| 325 | }, | ||
| 326 | body: JSON.stringify(event_id), | ||
| 327 | }) | ||
| 328 | .then(response => response.json()) | ||
| 329 | .then(data => { | ||
| 330 | if(data.success){ | ||
| 331 | event.remove(); | ||
| 332 | hideModal(); | ||
| 333 | } | ||
| 334 | }) | ||
| 335 | .catch((error) => { | ||
| 336 | console.error('Error:', error); | ||
| 337 | }); | ||
| 338 | event_selected = false; | ||
| 339 | } | ||
| 307 | 340 | ||
| 341 | // touches de clavier | ||
| 308 | document.addEventListener('keydown', function(event){ | 342 | document.addEventListener('keydown', function(event){ |
| 309 | if(event.key === 'Escape') { | 343 | if(event.key === 'Escape'){ |
| 310 | hideModal(); | 344 | hideModal(); |
| 311 | } | 345 | } |
| 346 | else if(event.key === 'Delete' && event_selected === true){ | ||
| 347 | removeEvent(); | ||
| 348 | } | ||
| 312 | }); | 349 | }); |
| 313 | 350 | ||
| 351 | // boutons dans la "modale" | ||
| 314 | // technique de la délégation d'événements pour utiliser un bouton ajouté dynamiquement | 352 | // technique de la délégation d'événements pour utiliser un bouton ajouté dynamiquement |
| 315 | document.addEventListener('click', function(event){ | 353 | document.addEventListener('click', function(event){ |
| 316 | if(event.target.classList.contains('event_close_button')){ | 354 | if(event.target.classList.contains('event_close_button')){ |
| @@ -325,6 +363,9 @@ document.addEventListener('DOMContentLoaded', function(){ | |||
| 325 | else if(event.target.classList.contains('submit_update_event')){ | 363 | else if(event.target.classList.contains('submit_update_event')){ |
| 326 | submitEvent(false); | 364 | submitEvent(false); |
| 327 | } | 365 | } |
| 366 | else if(event.target.classList.contains('delete_event')){ | ||
| 367 | removeEvent(); | ||
| 368 | } | ||
| 328 | }); | 369 | }); |
| 329 | 370 | ||
| 330 | calendar.render(); | 371 | calendar.render(); |
diff --git a/src/load-events.php b/src/load-events.php index b997fce..5c71137 100644 --- a/src/load-events.php +++ b/src/load-events.php | |||
| @@ -1,43 +1,67 @@ | |||
| 1 | <?php | 1 | <?php |
| 2 | $events = [ | 2 | // réception de deux paramètres GET: 'start' et 'end' |
| 3 | [ | 3 | |
| 4 | 'id' => 1, | 4 | if(isset($_GET['start']) && isset($_GET['end']) && empty($_POST)){ |
| 5 | 'title' => 'Évènement1', | 5 | // bornes début et fin du calendrier affiché à l'heure locale |
| 6 | 'start' => '2025-06-03T05:00:00Z', // Z indique que l'heure est en UTC | 6 | // noter que la vue "planning" est similaire à la vue "semaine" |
| 7 | 'end' => '2025-06-03T09:00:00Z', | 7 | $start = new DateTime($_GET['start']); |
| 8 | 'allDay' => false, | 8 | $end = new DateTime($_GET['end']); |
| 9 | 'color' => '#ffa500', // couleur hexa, éviter les couleurs CSS qui ne fonctionnent pas dans value="" en HTML | 9 | $start->setTimezone(new DateTimeZone('UTC')); |
| 10 | //'url' => 'https://dev.nageurs-bigoudens.fr', // comportement: https://fullcalendar.io/docs/eventClick | 10 | $end->setTimezone(new DateTimeZone('UTC')); |
| 11 | 'description' => 'blablabla', | 11 | |
| 12 | ], | 12 | // recherche des évènement en BDD avec ceci: |
| 13 | [ | 13 | // WHERE end >= :start AND start <= :end |
| 14 | 'id' => 2, | 14 | // on prend les évènements se finissant après le début |
| 15 | 'title' => 'Évènement2', | 15 | // ou commençant avant la fin de la fourchette |
| 16 | 'start' => '2025-06-06T08:00:00Z', | 16 | |
| 17 | 'end' => '2025-06-07T08:00:00Z', | 17 | // affichage format ISO à l'heure UTC |
| 18 | 'allDay' => false, | 18 | //$date->format('Y-m-d\TH:i:s\Z'); |
| 19 | 'color' => '#e01b24', | 19 | |
| 20 | 'description' => 'truc machin', | 20 | // chatgpt suggère l'utilisation d'un DTO |
| 21 | ], | 21 | // => une classe de données simple et tout "public" pour des évènements obtenables autant depuis la BDD que de fichiers .ics par exemple |
| 22 | [ | 22 | |
| 23 | 'id' => 3, | 23 | $events = [ |
| 24 | 'title' => 'Évènement3', | 24 | [ |
| 25 | 'start' => '2025-06-08', | 25 | 'id' => 1, |
| 26 | 'end' => '2025-06-09', | 26 | 'title' => 'Évènement1', |
| 27 | 'allDay' => true, // pas d'heure | 27 | 'start' => '2025-06-03T05:00:00Z', // Z indique que l'heure est en UTC |
| 28 | 'color' => '#008000', | 28 | 'end' => '2025-06-03T09:00:00Z', |
| 29 | 'description' => 'ça va chier', | 29 | 'allDay' => false, |
| 30 | ], | 30 | 'color' => '#ffa500', // couleur hexa, éviter les couleurs CSS qui ne fonctionnent pas dans value="" en HTML |
| 31 | // provoque une erreur, si allDay la fin ne peut être égale au début | 31 | //'url' => 'https://dev.nageurs-bigoudens.fr', // comportement: https://fullcalendar.io/docs/eventClick |
| 32 | /*[ | 32 | 'description' => 'blablabla', |
| 33 | 'id' => 4, | 33 | ], |
| 34 | 'title' => 'Évènement4', | 34 | [ |
| 35 | 'start' => '2025-06-09', | 35 | 'id' => 2, |
| 36 | 'end' => '2025-06-09', | 36 | 'title' => 'Évènement2', |
| 37 | 'allDay' => true, | 37 | 'start' => '2025-06-06T08:00:00Z', |
| 38 | 'color' => '#1a5fb4', | 38 | 'end' => '2025-06-07T08:00:00Z', |
| 39 | ],*/ | 39 | 'allDay' => false, |
| 40 | ]; | 40 | 'color' => '#e01b24', |
| 41 | 41 | 'description' => 'truc machin', | |
| 42 | header('Content-Type: application/json'); | 42 | ], |
| 43 | echo json_encode($events); | 43 | [ |
| 44 | 'id' => 3, | ||
| 45 | 'title' => 'Évènement3', | ||
| 46 | 'start' => '2025-06-08', | ||
| 47 | 'end' => '2025-06-09', | ||
| 48 | 'allDay' => true, // pas d'heure | ||
| 49 | 'color' => '#008000', | ||
| 50 | 'description' => 'ça va chier', | ||
| 51 | ], | ||
| 52 | // provoque une erreur, si allDay la fin ne peut être égale au début | ||
| 53 | /*[ | ||
| 54 | 'id' => 4, | ||
| 55 | 'title' => 'Évènement4', | ||
| 56 | 'start' => '2025-06-09', | ||
| 57 | 'end' => '2025-06-09', | ||
| 58 | 'allDay' => true, | ||
| 59 | 'color' => '#1a5fb4', | ||
| 60 | ],*/ | ||
| 61 | ]; | ||
| 62 | |||
| 63 | header('Content-Type: application/json'); | ||
| 64 | echo json_encode($events); | ||
| 65 | } | ||
| 66 | |||
| 67 | |||
