diff options
| author | polo <ordipolo@gmx.fr> | 2026-01-15 23:14:05 +0100 |
|---|---|---|
| committer | polo <ordipolo@gmx.fr> | 2026-01-15 23:14:05 +0100 |
| commit | 600fbca86951c28b4cf96a93b1a5fe5ba6149161 (patch) | |
| tree | 8035923f595cce6ef98598c072ec3297569c0dde | |
| parent | 972d0c1e95894898f6d34bebb811a2637b4e1c5a (diff) | |
| download | cms-600fbca86951c28b4cf96a93b1a5fe5ba6149161.tar.gz cms-600fbca86951c28b4cf96a93b1a5fe5ba6149161.tar.bz2 cms-600fbca86951c28b4cf96a93b1a5fe5ba6149161.zip | |
| -rw-r--r-- | public/js/calendar.js | 27 | ||||
| -rw-r--r-- | public/js/calendar_admin.js | 82 | ||||
| -rw-r--r-- | public/js/main.js | 17 |
3 files changed, 94 insertions, 32 deletions
diff --git a/public/js/calendar.js b/public/js/calendar.js index 8809246..b3aa927 100644 --- a/public/js/calendar.js +++ b/public/js/calendar.js | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | // js/calendar.js | 1 | // js/calendar.js |
| 2 | 2 | ||
| 3 | document.addEventListener('DOMContentLoaded', function(){ | 3 | document.addEventListener('DOMContentLoaded', function(){ |
| 4 | const calendarEl = document.getElementById('calendar'); | 4 | const calendarEl = getElementOrThrow('calendar'); |
| 5 | let selected_start_string = null; | 5 | let selected_start_string = null; |
| 6 | 6 | ||
| 7 | const calendar = new FullCalendar.Calendar(calendarEl,{ | 7 | const calendar = new FullCalendar.Calendar(calendarEl,{ |
| @@ -58,7 +58,10 @@ document.addEventListener('DOMContentLoaded', function(){ | |||
| 58 | //unselect: function(event, view){}, | 58 | //unselect: function(event, view){}, |
| 59 | 59 | ||
| 60 | eventClick: function(info){ | 60 | eventClick: function(info){ |
| 61 | const aside = document.querySelector('aside'); | 61 | const modal = getElementOrThrow('event_modal'); |
| 62 | if(!info.event.start || !info.event.end){ | ||
| 63 | throw new Error('modale non conforme'); | ||
| 64 | } | ||
| 62 | const checked = info.event.allDay ? 'checked' : ''; | 65 | const checked = info.event.allDay ? 'checked' : ''; |
| 63 | 66 | ||
| 64 | // change des objets Date en chaînes compatibles avec les input | 67 | // change des objets Date en chaînes compatibles avec les input |
| @@ -75,25 +78,25 @@ document.addEventListener('DOMContentLoaded', function(){ | |||
| 75 | const start_date = start.split('T')[0]; | 78 | const start_date = start.split('T')[0]; |
| 76 | const start_hour = (info.event.allDay ? '' : '<br>à ' + start.split('T')[1]).replace(":", "h"); | 79 | const start_hour = (info.event.allDay ? '' : '<br>à ' + start.split('T')[1]).replace(":", "h"); |
| 77 | const formated_start = start_date.split('-')[2] + '/' + start_date.split('-')[1] + '/' + start_date.split('-')[0] + start_hour; | 80 | const formated_start = start_date.split('-')[2] + '/' + start_date.split('-')[1] + '/' + start_date.split('-')[0] + start_hour; |
| 78 | const end = formatDate(info.event.allDay ? minusOneDay(info.event.end) : info.event.end, info.event.allDay); | 81 | const end = formatDate(info.event.allDay ? minusOneDay(info.event.end) : info.event.end); |
| 79 | const end_date = end.split('T')[0]; | 82 | const end_date = end.split('T')[0]; |
| 80 | const end_hour = (info.event.allDay ? '' : '<br>à ' + end.split('T')[1]).replace(":", "h"); | 83 | const end_hour = (info.event.allDay ? '' : '<br>à ' + end.split('T')[1]).replace(":", "h"); |
| 81 | const formated_end = end_date.split('-')[2] + '/' + end_date.split('-')[1] + '/' + end_date.split('-')[0] + end_hour; | 84 | const formated_end = end_date.split('-')[2] + '/' + end_date.split('-')[1] + '/' + end_date.split('-')[0] + end_hour; |
| 82 | 85 | ||
| 83 | let aside_content = `<div id="event" style="border-color: ` + info.event.backgroundColor +`;"> | 86 | let modal_content = `<div id="event" style="border-color: ` + info.event.backgroundColor +`;"> |
| 84 | <h3>` + info.event.title + `</h3> | 87 | <h3>` + info.event.title + `</h3> |
| 85 | <p><i>` + info.event.extendedProps.description + `</i></p>`; | 88 | <p><i>` + info.event.extendedProps.description + `</i></p>`; |
| 86 | if(checked && (formated_start === formated_end)){ // affichage simplifié évènement d'un jour | 89 | if(checked && (formated_start === formated_end)){ // affichage simplifié évènement d'un jour |
| 87 | aside_content = aside_content + `<p>le ` + formated_start + `</p>`; | 90 | modal_content = modal_content + `<p>le ` + formated_start + `</p>`; |
| 88 | } | 91 | } |
| 89 | else{ | 92 | else{ |
| 90 | aside_content = aside_content + `<p>du ` + formated_start + `</p> | 93 | modal_content = modal_content + `<p>du ` + formated_start + `</p> |
| 91 | <p>au ` + formated_end + `</p>`; | 94 | <p>au ` + formated_end + `</p>`; |
| 92 | } | 95 | } |
| 93 | aside_content += `<button class="event_close_button">Fermer</button> | 96 | modal_content += `<button class="event_close_button">Fermer</button> |
| 94 | </div>`; | 97 | </div>`; |
| 95 | 98 | ||
| 96 | aside.innerHTML = aside_content; | 99 | modal.innerHTML = modal_content; |
| 97 | calendar.updateSize(); | 100 | calendar.updateSize(); |
| 98 | }, | 101 | }, |
| 99 | viewDidMount: function(info){ // déclenché lorsque qu'une nouvelle vue est chargée (mois, semaine...) | 102 | viewDidMount: function(info){ // déclenché lorsque qu'une nouvelle vue est chargée (mois, semaine...) |
| @@ -105,8 +108,8 @@ document.addEventListener('DOMContentLoaded', function(){ | |||
| 105 | }); | 108 | }); |
| 106 | 109 | ||
| 107 | function hideModal(){ | 110 | function hideModal(){ |
| 108 | const aside = document.querySelector('aside'); | 111 | const modal = getElementOrThrow('event_modal'); |
| 109 | aside.innerHTML = ''; | 112 | modal.innerHTML = ''; |
| 110 | calendar.updateSize(); | 113 | calendar.updateSize(); |
| 111 | } | 114 | } |
| 112 | 115 | ||
| @@ -118,6 +121,10 @@ document.addEventListener('DOMContentLoaded', function(){ | |||
| 118 | 121 | ||
| 119 | // technique de la délégation d'événements pour utiliser un bouton ajouté dynamiquement | 122 | // technique de la délégation d'événements pour utiliser un bouton ajouté dynamiquement |
| 120 | document.addEventListener('click', function(event){ | 123 | document.addEventListener('click', function(event){ |
| 124 | if(!event.target){ | ||
| 125 | throw new Error('évènement click non conforme'); | ||
| 126 | } | ||
| 127 | assertElementType(event.target, HTMLElement); | ||
| 121 | if(event.target.classList.contains('event_close_button')){ | 128 | if(event.target.classList.contains('event_close_button')){ |
| 122 | hideModal(); | 129 | hideModal(); |
| 123 | } | 130 | } |
diff --git a/public/js/calendar_admin.js b/public/js/calendar_admin.js index 17ae0c2..0600954 100644 --- a/public/js/calendar_admin.js +++ b/public/js/calendar_admin.js | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | // js/calendar_admin.js | 1 | // js/calendar_admin.js |
| 2 | 2 | ||
| 3 | document.addEventListener('DOMContentLoaded', function(){ | 3 | document.addEventListener('DOMContentLoaded', function(){ |
| 4 | const calendarEl = document.getElementById('calendar'); | 4 | const calendarEl = getElementOrThrow('calendar'); |
| 5 | let selected_start_string = null; | 5 | let selected_start_string = null; |
| 6 | let event_selected = false; // pour event.remove() | 6 | let event_selected = false; // pour event.remove() |
| 7 | 7 | ||
| @@ -44,7 +44,7 @@ document.addEventListener('DOMContentLoaded', function(){ | |||
| 44 | select: function(info){ | 44 | select: function(info){ |
| 45 | selected_start_string = info.startStr; // variable "globale" | 45 | selected_start_string = info.startStr; // variable "globale" |
| 46 | event_selected = false; | 46 | event_selected = false; |
| 47 | const aside = document.querySelector('aside'); | 47 | const modal = getElementOrThrow('event_modal'); |
| 48 | let checked = ''; | 48 | let checked = ''; |
| 49 | let input = 'datetime-local'; | 49 | let input = 'datetime-local'; |
| 50 | 50 | ||
| @@ -81,7 +81,7 @@ document.addEventListener('DOMContentLoaded', function(){ | |||
| 81 | } | 81 | } |
| 82 | } | 82 | } |
| 83 | 83 | ||
| 84 | const aside_content = `<div id="form_event"> | 84 | const modal_content = `<div id="form_event"> |
| 85 | <div class="event_title_box"> | 85 | <div class="event_title_box"> |
| 86 | <h2>Nouvel évènement</h2> | 86 | <h2>Nouvel évènement</h2> |
| 87 | </div> | 87 | </div> |
| @@ -112,7 +112,7 @@ document.addEventListener('DOMContentLoaded', function(){ | |||
| 112 | <button class="submit_new_event">Enregistrer</button> | 112 | <button class="submit_new_event">Enregistrer</button> |
| 113 | <button class="event_close_button">Annuler</button> | 113 | <button class="event_close_button">Annuler</button> |
| 114 | </div>`; | 114 | </div>`; |
| 115 | aside.innerHTML = aside_content; | 115 | modal.innerHTML = modal_content; |
| 116 | calendar.updateSize(); | 116 | calendar.updateSize(); |
| 117 | }, | 117 | }, |
| 118 | // sélection d'une date simple sur mobile, évite des problèmes de conflit avec eventClick | 118 | // sélection d'une date simple sur mobile, évite des problèmes de conflit avec eventClick |
| @@ -138,7 +138,7 @@ document.addEventListener('DOMContentLoaded', function(){ | |||
| 138 | //unselect: function(event, view){}, | 138 | //unselect: function(event, view){}, |
| 139 | eventClick: function(info){ | 139 | eventClick: function(info){ |
| 140 | event_selected = true; // variable "globale" | 140 | event_selected = true; // variable "globale" |
| 141 | const aside = document.querySelector('aside'); | 141 | const modal = getElementOrThrow('event_modal'); |
| 142 | const checked = info.event.allDay ? 'checked' : ''; | 142 | const checked = info.event.allDay ? 'checked' : ''; |
| 143 | const input = info.event.allDay ? 'date' : 'datetime-local'; | 143 | const input = info.event.allDay ? 'date' : 'datetime-local'; |
| 144 | 144 | ||
| @@ -153,9 +153,9 @@ document.addEventListener('DOMContentLoaded', function(){ | |||
| 153 | } | 153 | } |
| 154 | 154 | ||
| 155 | const formated_start = formatDate(info.event.start); | 155 | const formated_start = formatDate(info.event.start); |
| 156 | const formated_end = formatDate(info.event.allDay ? minusOneDay(info.event.end) : info.event.end, info.event.allDay); | 156 | const formated_end = formatDate(info.event.allDay ? minusOneDay(info.event.end) : info.event.end); |
| 157 | 157 | ||
| 158 | const aside_content = `<div id="form_event" style="border-color: ` + info.event.backgroundColor +`;"> | 158 | const modal_content = `<div id="form_event" style="border-color: ` + info.event.backgroundColor +`;"> |
| 159 | <div class="event_title_box"> | 159 | <div class="event_title_box"> |
| 160 | <h2>Modifier un évènement</h2> | 160 | <h2>Modifier un évènement</h2> |
| 161 | </div> | 161 | </div> |
| @@ -188,7 +188,7 @@ document.addEventListener('DOMContentLoaded', function(){ | |||
| 188 | <button class="event_close_button">Annuler</button> | 188 | <button class="event_close_button">Annuler</button> |
| 189 | <button class="delete_event">Supprimer</button> | 189 | <button class="delete_event">Supprimer</button> |
| 190 | </div>`; | 190 | </div>`; |
| 191 | aside.innerHTML = aside_content; | 191 | modal.innerHTML = modal_content; |
| 192 | calendar.updateSize(); | 192 | calendar.updateSize(); |
| 193 | }, | 193 | }, |
| 194 | viewDidMount: function(info){ // déclenché lorsque qu'une nouvelle vue est chargée (mois, semaine...) | 194 | viewDidMount: function(info){ // déclenché lorsque qu'une nouvelle vue est chargée (mois, semaine...) |
| @@ -200,21 +200,41 @@ document.addEventListener('DOMContentLoaded', function(){ | |||
| 200 | }); | 200 | }); |
| 201 | 201 | ||
| 202 | function hideModal(){ | 202 | function hideModal(){ |
| 203 | const aside = document.querySelector('aside'); | 203 | const modal = getElementOrThrow('event_modal'); |
| 204 | event_selected = false; | 204 | event_selected = false; |
| 205 | aside.innerHTML = ''; | 205 | modal.innerHTML = ''; |
| 206 | calendar.updateSize(); | 206 | calendar.updateSize(); |
| 207 | } | 207 | } |
| 208 | 208 | ||
| 209 | function submitEvent(new_event){ | 209 | function submitEvent(new_event){ |
| 210 | const event_title = document.getElementById('event_title').value; | 210 | const event_title_input = getElementOrThrow('event_title'); |
| 211 | const event_description = document.getElementById('event_description').value; | 211 | const event_description_input = getElementOrThrow('event_description'); |
| 212 | const event_all_day = document.getElementById('event_all_day').checked; | 212 | const event_all_day_input = getElementOrThrow('event_all_day'); |
| 213 | let event_start = document.getElementById('event_start').value; | 213 | const event_start_input = getElementOrThrow('event_start'); |
| 214 | let event_end = document.getElementById('event_end').value; | 214 | const event_end_input = getElementOrThrow('event_end'); |
| 215 | const event_color = document.getElementById('event_color').value; // #3788d8 par défaut | 215 | const event_color_input = getElementOrThrow('event_color'); |
| 216 | const event_id = new_event ? '' : document.getElementById('event_id').value; | 216 | assertElementType(event_title_input, HTMLInputElement); |
| 217 | assertElementType(event_description_input, HTMLTextAreaElement); | ||
| 218 | assertElementType(event_all_day_input, HTMLInputElement); | ||
| 219 | assertElementType(event_start_input, HTMLInputElement); | ||
| 220 | assertElementType(event_end_input, HTMLInputElement); | ||
| 221 | assertElementType(event_color_input, HTMLInputElement); | ||
| 217 | 222 | ||
| 223 | const event_title = event_title_input.value; | ||
| 224 | const event_description = event_description_input.value; | ||
| 225 | const event_all_day = event_all_day_input.checked; | ||
| 226 | let event_start = event_start_input.value; | ||
| 227 | let event_end = event_end_input.value; | ||
| 228 | const event_color = event_color_input.value; // #3788d8 par défaut | ||
| 229 | |||
| 230 | let event_id = ''; | ||
| 231 | if(!new_event){ | ||
| 232 | const event_id_input = getElementOrThrow('event_id'); | ||
| 233 | assertElementType(event_id_input, HTMLInputElement); | ||
| 234 | event_id = event_id_input.value; | ||
| 235 | } | ||
| 236 | |||
| 237 | // contrôle de saisie | ||
| 218 | if(event_title.length !== 0 && event_start.length !== 0 && event_end.length !== 0 && event_color.length !== 0 | 238 | if(event_title.length !== 0 && event_start.length !== 0 && event_end.length !== 0 && event_color.length !== 0 |
| 219 | && (new_event || event_id.length !== 0)) | 239 | && (new_event || event_id.length !== 0)) |
| 220 | { | 240 | { |
| @@ -238,6 +258,7 @@ document.addEventListener('DOMContentLoaded', function(){ | |||
| 238 | // création | 258 | // création |
| 239 | if(new_event){ | 259 | if(new_event){ |
| 240 | const event = { | 260 | const event = { |
| 261 | id: '', | ||
| 241 | title: event_title, | 262 | title: event_title, |
| 242 | description: event_description, | 263 | description: event_description, |
| 243 | allDay: event_all_day, | 264 | allDay: event_all_day, |
| @@ -269,7 +290,9 @@ document.addEventListener('DOMContentLoaded', function(){ | |||
| 269 | // modification | 290 | // modification |
| 270 | else{ | 291 | else{ |
| 271 | const event = calendar.getEventById(event_id); | 292 | const event = calendar.getEventById(event_id); |
| 272 | 293 | if(!event){ | |
| 294 | throw new Error("Événement non trouvé !"); | ||
| 295 | } | ||
| 273 | if(event){ | 296 | if(event){ |
| 274 | const event_copy = { | 297 | const event_copy = { |
| 275 | id: parseInt(event.id), | 298 | id: parseInt(event.id), |
| @@ -316,13 +339,17 @@ document.addEventListener('DOMContentLoaded', function(){ | |||
| 316 | } | 339 | } |
| 317 | 340 | ||
| 318 | function checkAllDay(){ | 341 | function checkAllDay(){ |
| 319 | const event_start_input = document.getElementById('event_start'); | 342 | const event_start_input = getElementOrThrow('event_start'); |
| 320 | const event_end_input = document.getElementById('event_end'); | 343 | const event_end_input = getElementOrThrow('event_end'); |
| 344 | const event_all_day = getElementOrThrow('event_all_day'); | ||
| 345 | assertElementType(event_start_input, HTMLInputElement); | ||
| 346 | assertElementType(event_end_input, HTMLInputElement); | ||
| 347 | assertElementType(event_all_day, HTMLInputElement); | ||
| 321 | 348 | ||
| 322 | const start = event_start_input.value; | 349 | const start = event_start_input.value; |
| 323 | const end = event_end_input.value; | 350 | const end = event_end_input.value; |
| 324 | 351 | ||
| 325 | if(document.getElementById('event_all_day').checked){ | 352 | if(event_all_day.checked){ |
| 326 | event_start_input.type = 'date'; | 353 | event_start_input.type = 'date'; |
| 327 | event_end_input.type = 'date'; | 354 | event_end_input.type = 'date'; |
| 328 | 355 | ||
| @@ -341,8 +368,15 @@ document.addEventListener('DOMContentLoaded', function(){ | |||
| 341 | if(!confirm("Voulez-vous vraiment supprimer cet évènement du calendrier?")){ | 368 | if(!confirm("Voulez-vous vraiment supprimer cet évènement du calendrier?")){ |
| 342 | return; | 369 | return; |
| 343 | } | 370 | } |
| 344 | const event_id = document.getElementById('event_id').value; | 371 | const event_tag = document.getElementById('event_id'); // cible input hidden |
| 372 | if(!event_tag || !(event_tag instanceof HTMLInputElement)){ | ||
| 373 | return; | ||
| 374 | } | ||
| 375 | const event_id = event_tag.value; | ||
| 345 | const event = calendar.getEventById(event_id); | 376 | const event = calendar.getEventById(event_id); |
| 377 | if(!event){ | ||
| 378 | throw new Error("Événement non trouvé !"); | ||
| 379 | } | ||
| 346 | 380 | ||
| 347 | fetch('index.php?action=remove_event', { | 381 | fetch('index.php?action=remove_event', { |
| 348 | method: 'POST', | 382 | method: 'POST', |
| @@ -377,6 +411,10 @@ document.addEventListener('DOMContentLoaded', function(){ | |||
| 377 | // boutons dans la "modale" | 411 | // boutons dans la "modale" |
| 378 | // technique de la délégation d'événements pour utiliser un bouton ajouté dynamiquement | 412 | // technique de la délégation d'événements pour utiliser un bouton ajouté dynamiquement |
| 379 | document.addEventListener('click', function(event){ | 413 | document.addEventListener('click', function(event){ |
| 414 | if(!event.target){ | ||
| 415 | throw new Error('évènement click non conforme'); | ||
| 416 | } | ||
| 417 | assertElementType(event.target, HTMLElement); | ||
| 380 | if(event.target.classList.contains('event_close_button')){ | 418 | if(event.target.classList.contains('event_close_button')){ |
| 381 | hideModal(); | 419 | hideModal(); |
| 382 | } | 420 | } |
diff --git a/public/js/main.js b/public/js/main.js index c8e10b0..825699f 100644 --- a/public/js/main.js +++ b/public/js/main.js | |||
| @@ -36,6 +36,23 @@ function toastNotify(message){ | |||
| 36 | setTimeout(function(){ toast.className = toast.className.replace('show', ''); }, 5000); | 36 | setTimeout(function(){ toast.className = toast.className.replace('show', ''); }, 5000); |
| 37 | } | 37 | } |
| 38 | 38 | ||
| 39 | |||
| 40 | // réussite de getElementById | ||
| 41 | function getElementOrThrow(id) { | ||
| 42 | const elem = document.getElementById(id); | ||
| 43 | if (!elem) { | ||
| 44 | throw new Error("l'élément d'id: " + id + " non trouvé"); | ||
| 45 | } | ||
| 46 | return elem; | ||
| 47 | } | ||
| 48 | // l'erreur attribut "value" non trouvé devient l'élement attrapé n'a pas le bon type (c'est un peu plus clair) | ||
| 49 | function assertElementType(elem, ctor) { | ||
| 50 | if (!(elem instanceof ctor)) { | ||
| 51 | throw new Error(`l'élement attrapé n'a pas le bon type. type attendu: ${ctor.name}, type obtenu: ${elem.tagName}`); | ||
| 52 | } | ||
| 53 | } | ||
| 54 | |||
| 55 | |||
| 39 | function controlURL(input){ | 56 | function controlURL(input){ |
| 40 | const url = input.value.trim(); | 57 | const url = input.value.trim(); |
| 41 | if(!url){ | 58 | if(!url){ |
