From e3a42c8342bba0db15e2ca9a78911121e5d539da Mon Sep 17 00:00:00 2001 From: polo Date: Thu, 29 Jan 2026 22:51:19 +0100 Subject: =?UTF-8?q?classe=20CalendarModalView,=20contr=C3=B4les=20getEleme?= =?UTF-8?q?ntOrThrow=20et=20assertElementType?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/css/calendar.css | 8 +- public/index.php | 11 +- public/js/CalendarModalView.js | 120 ++++++++++++ public/js/calendar.js | 110 +++++------ public/js/calendar_admin.js | 410 ++++++++++++++++++----------------------- public/js/main.js | 16 ++ 6 files changed, 370 insertions(+), 305 deletions(-) create mode 100644 public/js/CalendarModalView.js create mode 100644 public/js/main.js (limited to 'public') diff --git a/public/css/calendar.css b/public/css/calendar.css index b24fe5b..c96b91e 100644 --- a/public/css/calendar.css +++ b/public/css/calendar.css @@ -1,7 +1,8 @@ -.event{ +#event{ border: 2px double; border-radius: 5px; - width: max-content; + max-width: 250px; + /*width: max-content;*/ padding: 5px; } #calendar_zone{ @@ -33,6 +34,9 @@ td .fc-timegrid-axis{ .fc-day-other{ background-color: #f0f0f0; } +.fc-daygrid-day:not(.fc-day-other, .fc-day-today){ + background-color: #ffffff; +} .fc-daygrid-day-top{ justify-content: center; } diff --git a/public/index.php b/public/index.php index 540e8ab..a721db5 100644 --- a/public/index.php +++ b/public/index.php @@ -11,6 +11,8 @@ require '../src/controller/calendar.php'; + + @@ -18,15 +20,14 @@ require '../src/controller/calendar.php'; ' . "\n"; +if($_SESSION['admin']){ + echo '' . "\n"; } -else{ - echo '' . "\n"; +else{ + echo '' . "\n"; } ?> -
diff --git a/public/js/CalendarModalView.js b/public/js/CalendarModalView.js new file mode 100644 index 0000000..a7afba9 --- /dev/null +++ b/public/js/CalendarModalView.js @@ -0,0 +1,120 @@ +// js/CalendarModalView.js + +class CalendarModalView { + constructor(data) { + this.modal = ''; + if (data.mode !== 'show' && data.mode !== 'new' && data.mode !== 'edit') { + throw new Error("unknown mode in CalendarModal's constructor"); + } + this.data = data; + } + getView() { + switch (this.data.mode) { + case 'show': + this.makeShowView(); + break; + case 'new': + case 'edit': + this.makeEditView(); + break; + default: + throw new Error("unknown mode set in CalendarModal"); + } + return this.modal; + } + makeShowView() { + if (this.data.mode !== 'show') { + throw new Error(''); + } + if (this.data.all_day) { + this.data.end.setDate(this.data.end.getDate() - 1); // jour de fin modifié pour ne pas faire bizarre pour l'utilisateur + } + this.modal += `
+

` + this.escapeHtml(this.data.title) + `

+

` + this.escapeHtml(this.data.description) + `

`; + // allDay un jour + if (this.data.all_day && (this.data.start.getTime() === this.data.end.getTime())) { // comparaison des timestamps + this.modal += `

le ` + this.escapeHtml(this.displayDate(this.data.start)) + `

`; // affichage simplifié évènement d'un jour + } + // allDay plusieurs jours + else if (this.data.all_day) { + this.modal += `

du ` + this.escapeHtml(this.displayDate(this.data.start)) + `
+ au ` + this.escapeHtml(this.displayDate(this.data.end)) + `

`; + } + // non allDay + else { + this.modal += `

du ` + this.escapeHtml(this.displayDate(this.data.start)) + `
+ à ` + this.escapeHtml(this.displayHour(this.data.start)) + `

+

au ` + this.escapeHtml(this.displayDate(this.data.end)) + `
+ à ` + this.escapeHtml(this.displayHour(this.data.end)) + `

`; + } + this.modal += ` +
`; + } + displayDate(date) { + return date.getDate().toString().padStart(2, '0') + '/' + (date.getMonth() + 1).toString().padStart(2, '0') + '/' + date.getFullYear(); + } + displayHour(date) { + return date.getHours().toString().padStart(2, '0') + 'h' + date.getMinutes().toString().padStart(2, '0'); + } + makeEditView() { + if (this.data.mode === 'show') { + throw new Error(''); + } + if (this.data.all_day) { + this.data.end.setDate(this.data.end.getDate() - 1); // jour de fin modifié pour ne pas faire bizarre pour l'utilisateur + } + const title = this.data.mode === 'edit' ? this.data.title : ''; + const id = this.data.mode === 'edit' ? this.data.id : ''; + const description = this.data.mode === 'edit' ? this.data.description : ''; + const color = this.data.mode === 'edit' ? this.data.color : '#3788D8'; // bleu par défaut + this.modal += `
+
+

` + (this.data.mode === 'edit' ? "Modifier un" : "Nouvel") + ` évènement

+
+
+ + + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
` + + (this.data.mode === 'edit' + ? '' + : '') + + ` +
`; + } + makeInputValue(date) { + return date.getFullYear() + '-' + (date.getMonth() + 1).toString().padStart(2, '0') + '-' + date.getDate().toString().padStart(2, '0') + + (this.data.all_day ? '' : 'T' + date.getHours().toString().padStart(2, '0') + ':' + date.getMinutes().toString().padStart(2, '0')); + } + escapeHtml(html) { + if (html === null || html === undefined) + return ''; + return String(html) + .replace(/&/g, "&") // & en premier + .replace(//g, ">") + .replace(/"/g, """) + .replace(/'/g, "'"); + } +} diff --git a/public/js/calendar.js b/public/js/calendar.js index a092632..ff1a401 100644 --- a/public/js/calendar.js +++ b/public/js/calendar.js @@ -1,115 +1,97 @@ // js/calendar.js -document.addEventListener('DOMContentLoaded', function(){ - const calendarEl = document.getElementById('calendar'); +document.addEventListener('DOMContentLoaded', function () { + const calendarEl = getElementOrThrow('calendar'); + const modal = getElementOrThrow('event_modal'); let selected_start_string = null; - - const calendar = new FullCalendar.Calendar(calendarEl,{ + const calendar = new FullCalendar.Calendar(calendarEl, { editable: true, locale: 'fr', //timeZone: 'local', // à modifier pour être à l'heure d'un autre pays initialView: 'dayGridMonth', - headerToolbar:{ + headerToolbar: { left: 'prev,next today', center: 'title', - right: 'dayGridMonth,timeGridWeek,timeGridDay,listWeek' - //right: 'dayGridMonth,timeGridWeek' + //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 + selectable: true, // sélection de jours en cliquant dessus + longPressDelay: 300, // 1000ms par défaut 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){ + select: function (info) { selected_start_string = info.startStr; // variable "globale" hideModal(); }, - //unselect: function(event, view){}, - - eventClick: function(info){ - const aside = document.querySelector('aside'); - const checked = info.event.allDay ? 'checked' : ''; - - // 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 start = formatDate(info.event.start); - const start_date = start.split('T')[0]; - const start_hour = (info.event.allDay ? '' : '
à ' + start.split('T')[1]).replace(":", "h"); - const formated_start = start_date.split('-')[2] + '/' + start_date.split('-')[1] + '/' + start_date.split('-')[0] + start_hour; - const end = formatDate(info.event.allDay ? minusOneDay(info.event.end) : info.event.end, info.event.allDay); - const end_date = end.split('T')[0]; - const end_hour = (info.event.allDay ? '' : '
à ' + end.split('T')[1]).replace(":", "h"); - const formated_end = end_date.split('-')[2] + '/' + end_date.split('-')[1] + '/' + end_date.split('-')[0] + end_hour; - - let aside_content = `
-

` + info.event.title + `

-

` + info.event.extendedProps.description + `

`; - if(checked && (formated_start === formated_end)){ // affichage simplifié évènement d'un jour - aside_content = aside_content + `

le ` + formated_start + `

-
`; + // sélection d'une date simple sur mobile, évite des problèmes de conflit avec eventClick + dateClick: function (info) { + if (window.matchMedia('(pointer: coarse)').matches) { + const end = new Date(info.date.getTime()); + calendar.view.type === 'dayGridMonth' ? end.setDate(end.getDate() + 1) : end.setMinutes(end.getMinutes() + 30); + // vue date: la fin est une date exclue + // vue semaine: durée de 30min par défaut + calendar.select(info.date, end); // appeler select() avec un seul paramètre ne marche pas avec la vue "mois" } - else{ - aside_content = aside_content + `

du ` + formated_start + `

-

au ` + formated_end + `

-
`; + }, + //unselect: function(event, view){}, + eventClick: function (info) { + if (!info.event.start || !info.event.end) { + throw new Error('modale non conforme'); } - - aside.innerHTML = aside_content; + const modal_view = new CalendarModalView({ + mode: 'show', + title: info.event.title, + description: info.event.extendedProps.description, + color: info.event.backgroundColor, + all_day: info.event.allDay, + start: info.event.start, + end: info.event.end + }); + modal.innerHTML = modal_view.getView(); calendar.updateSize(); }, - viewDidMount: function(info){ // déclenché lorsque qu'une nouvelle vue est chargée (mois, semaine...) - if(selected_start_string){ + viewDidMount: function (_info) { + hideModal(); + 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'); - aside.innerHTML = ''; + function hideModal() { + modal.innerHTML = ''; calendar.updateSize(); } - - document.addEventListener('keydown', function(event){ - if(event.key === 'Escape') { + document.addEventListener('keydown', function (event) { + if (event.key === 'Escape') { hideModal(); } }); - // 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')){ + document.addEventListener('click', function (event) { + if (!event.target) { + throw new Error('évènement click non conforme'); + } + assertElementType(event.target, HTMLElement); + if (event.target.classList.contains('event_close_button')) { hideModal(); } }); - calendar.render(); }); \ No newline at end of file diff --git a/public/js/calendar_admin.js b/public/js/calendar_admin.js index a99b069..df8e19d 100644 --- a/public/js/calendar_admin.js +++ b/public/js/calendar_admin.js @@ -1,220 +1,164 @@ // js/calendar_admin.js -document.addEventListener('DOMContentLoaded', function(){ - const calendarEl = document.getElementById('calendar'); +document.addEventListener('DOMContentLoaded', function () { + const calendarEl = getElementOrThrow('calendar'); + const modal = getElementOrThrow('event_modal'); let selected_start_string = null; let event_selected = false; // pour event.remove() - - const calendar = new FullCalendar.Calendar(calendarEl,{ + const calendar = new FullCalendar.Calendar(calendarEl, { editable: true, locale: 'fr', //timeZone: 'local', // à modifier pour être à l'heure d'un autre pays initialView: 'dayGridMonth', - headerToolbar:{ + headerToolbar: { left: 'prev,next today', center: 'title', - right: 'dayGridMonth,timeGridWeek,timeGridDay,listWeek' - //right: 'dayGridMonth,timeGridWeek' + //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 + selectable: true, // sélection de jours en cliquant dessus + longPressDelay: 300, // 1000ms par défaut 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){ + 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; + let all_day = false; + let input_type = 'datetime-local'; // input datetime-local = date + heure + const start = new Date(info.startStr); const end = new Date(info.endStr); - - if(calendar.view.type == 'dayGridMonth'){ - start_value = info.startStr + 'T10:00'; + if (calendar.view.type == 'dayGridMonth') { 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'; + end.setHours(start.getTime() === end.getTime() ? 11 : 10); + start.setHours(10); } - 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); - } + else if (calendar.view.type == 'timeGridWeek' && info.startStr.split("T").length === 1) { // clic sur la ligne "Journée" => évènement allDay + all_day = true; + input_type = 'date'; } - - const aside_content = `
-
-

Nouvel évènement

-
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
- - -
`; - aside.innerHTML = aside_content; + //else if(calendar.view.type == 'timeGridDay'){} + const modal_view = new CalendarModalView({ + mode: 'new', + all_day: all_day, + input_type: input_type, + start: start, + end: end + }); + modal.innerHTML = modal_view.getView(); calendar.updateSize(); }, + // sélection d'une date simple sur mobile, évite des problèmes de conflit avec eventClick + dateClick: function (info) { + if (window.matchMedia('(pointer: coarse)').matches) { + const end = new Date(info.date); + calendar.view.type === 'dayGridMonth' ? end.setDate(end.getDate() + 1) : end.setMinutes(end.getMinutes() + 30); + // vue date: la fin est une date exclue + // vue semaine: durée de 30min par défaut + const local_end_date_array = end.toLocaleDateString().split('/'); + let local_end_str = local_end_date_array[2] + '-' + local_end_date_array[1] + '-' + local_end_date_array[0]; + if (calendar.view.type != 'dayGridMonth') { + local_end_str += 'T' + end.toLocaleTimeString(); + } + // on a besoin de deux chaînes représentant l'heure locale pour obtenir le même objet "info" qu'avec l'évènement select + // la vue "dayGridMonth" nécessite deux paramètres, par sécurité on en enverra toujours deux + calendar.select(info.dateStr, local_end_str); + } + }, //unselect: function(event, view){}, - eventClick: function(info){ + 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; + if (!info.event.start || !info.event.end) { + throw new Error("info.event.start ou info.event.end est null"); } - - 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; + const modal_view = new CalendarModalView({ + mode: 'edit', + input_type: info.event.allDay ? 'date' : 'datetime-local', + id: info.event.id, + title: info.event.title, + description: info.event.extendedProps.description, + color: info.event.backgroundColor, + all_day: info.event.allDay, + start: info.event.start, + end: info.event.end + }); + modal.innerHTML = modal_view.getView(); calendar.updateSize(); }, - viewDidMount: function(info){ // déclenché lorsque qu'une nouvelle vue est chargée (mois, semaine...) - if(selected_start_string){ + viewDidMount: function (_info) { + hideModal(); + 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'); + function hideModal() { event_selected = false; - aside.innerHTML = ''; + modal.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){ + function submitEvent(new_event) { + const event_title_input = getElementOrThrow('event_title'); + const event_description_input = getElementOrThrow('event_description'); + const event_all_day_input = getElementOrThrow('event_all_day'); + const event_start_input = getElementOrThrow('event_start'); + const event_end_input = getElementOrThrow('event_end'); + const event_color_input = getElementOrThrow('event_color'); + assertElementType(event_title_input, HTMLInputElement); + assertElementType(event_description_input, HTMLTextAreaElement); + assertElementType(event_all_day_input, HTMLInputElement); + assertElementType(event_start_input, HTMLInputElement); + assertElementType(event_end_input, HTMLInputElement); + assertElementType(event_color_input, HTMLInputElement); + const event_title = event_title_input.value; + const event_description = event_description_input.value; + const event_all_day = event_all_day_input.checked; + let event_start = new Date(event_start_input.value); + let event_end = new Date(event_end_input.value); + const event_color = event_color_input.value; // #3788d8 par défaut + let event_id = ''; + if (!new_event) { + //const event_id_input: HTMLElement|null = document.getElementById('event_id'); + const event_id_input = getElementOrThrow('event_id'); + assertElementType(event_id_input, HTMLInputElement); + event_id = event_id_input.value; + } + // contrôle de saisie + if (event_title.length !== 0 && event_start_input.value.length !== 0 && event_end_input.value.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]; + event_end.setDate(event_end.getDate() + 1); } - else{ - event_start = new Date(event_start).toISOString(); - event_end = new Date(event_end).toISOString(); + else { + event_start = new Date(event_start); + event_end = new Date(event_end); } - console.log(event_end); - - if(event_start > event_end || (!event_all_day && event_start == event_end)){ + // contrôle date/heure de fin après le début + if (event_start > event_end || (!event_all_day && event_start == event_end)) { + console.log("Erreur: la fin de l'évènement doit se situer après son début."); return; } - // création - if(new_event){ + if (new_event) { const event = { + id: '', title: event_title, description: event_description, allDay: event_all_day, @@ -222,7 +166,6 @@ document.addEventListener('DOMContentLoaded', function(){ end: event_end, color: event_color }; - fetch('index.php?action=new_event', { method: 'POST', headers: { @@ -230,146 +173,145 @@ document.addEventListener('DOMContentLoaded', function(){ }, body: JSON.stringify(event), }) - .then(response => response.json()) - .then(data => { - if(data.success){ + .then(response => response.json()) + .then(data => { + if (data.success) { event.id = data.id; calendar.addEvent(event); hideModal(); } }) - .catch((error) => { + .catch((error) => { console.error('Error:', error); }); - } // modification - else{ + 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), - }) + if (!event) { + throw new Error("Événement non trouvé !"); + } + 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(); - } - }) + 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é !"); - } + console.error('Error:', error); + }); } } - else{ + 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'); - + function checkAllDay() { + const event_start_input = getElementOrThrow('event_start'); + const event_end_input = getElementOrThrow('event_end'); + const event_all_day = getElementOrThrow('event_all_day'); + assertElementType(event_start_input, HTMLInputElement); + assertElementType(event_end_input, HTMLInputElement); + assertElementType(event_all_day, HTMLInputElement); const start = event_start_input.value; const end = event_end_input.value; - - if(document.getElementById('event_all_day').checked){ + if (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{ + 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?")){ + function removeEvent() { + if (!confirm("Voulez-vous vraiment supprimer cet évènement du calendrier?")) { return; } - const event_id = document.getElementById('event_id').value; + const event_tag = getElementOrThrow('event_id'); // cible input hidden + assertElementType(event_tag, HTMLInputElement); + const event_id = event_tag.value; const event = calendar.getEventById(event_id); - + if (!event) { + throw new Error("Événement non trouvé !"); + } fetch('index.php?action=remove_event', { method: 'POST', headers: { 'Content-Type': 'application/json', }, - body: JSON.stringify({'id': event_id}), + body: JSON.stringify({ 'id': event_id }), }) - .then(response => response.json()) - .then(data => { - if(data.success){ + .then(response => response.json()) + .then(data => { + if (data.success) { event.remove(); hideModal(); } }) - .catch((error) => { + .catch((error) => { console.error('Error:', error); }); event_selected = false; } - // touches de clavier - document.addEventListener('keydown', function(event){ - if(event.key === 'Escape'){ + document.addEventListener('keydown', function (event) { + if (event.key === 'Escape') { hideModal(); } - else if(event.key === 'Delete' && event_selected === true){ + 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')){ + document.addEventListener('click', function (event) { + if (!event.target) { + throw new Error('évènement click non conforme'); + } + assertElementType(event.target, HTMLElement); + if (event.target.classList.contains('event_close_button')) { hideModal(); } - else if(event.target.classList.contains('event_all_day')){ + else if (event.target.classList.contains('event_all_day')) { checkAllDay(); } - else if(event.target.classList.contains('submit_new_event')){ + else if (event.target.classList.contains('submit_new_event')) { submitEvent(true); } - else if(event.target.classList.contains('submit_update_event')){ + else if (event.target.classList.contains('submit_update_event')) { submitEvent(false); } - else if(event.target.classList.contains('delete_event')){ + else if (event.target.classList.contains('delete_event')) { removeEvent(); } }); - calendar.render(); }); \ No newline at end of file diff --git a/public/js/main.js b/public/js/main.js new file mode 100644 index 0000000..50eedf6 --- /dev/null +++ b/public/js/main.js @@ -0,0 +1,16 @@ +// js/main.js + +// réussite de getElementById +function getElementOrThrow(id) { + const elem = document.getElementById(id); + if (!elem) { + throw new Error("l'élément d'id: " + id + " non trouvé"); + } + return elem; +} +// l'erreur attribut "value" non trouvé devient l'élement attrapé n'a pas le bon type (c'est un peu plus clair) +function assertElementType(elem, ctor) { + if (!(elem instanceof ctor)) { + throw new Error(`type attendu: ${ctor.name}, type obtenu: ${elem.tagName}`); + } +} -- cgit v1.2.3