diff options
-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 | |||