summaryrefslogtreecommitdiff
path: root/public/js/calendar_admin.js
diff options
context:
space:
mode:
Diffstat (limited to 'public/js/calendar_admin.js')
-rw-r--r--public/js/calendar_admin.js331
1 files changed, 331 insertions, 0 deletions
diff --git a/public/js/calendar_admin.js b/public/js/calendar_admin.js
new file mode 100644
index 0000000..253d127
--- /dev/null
+++ b/public/js/calendar_admin.js
@@ -0,0 +1,331 @@
1document.addEventListener('DOMContentLoaded', function(){
2 const calendarEl = document.getElementById('calendar');
3 let selected_start_string = null;
4
5 const calendar = new FullCalendar.Calendar(calendarEl,{
6 editable: true,
7 locale: 'fr',
8 initialView: 'dayGridMonth',
9 headerToolbar:{
10 left: 'prev,next today',
11 center: 'title',
12 right: 'dayGridMonth,timeGridWeek,timeGridDay,listWeek'
13 //right: 'dayGridMonth,timeGridWeek'
14 },
15 slotMinWidth: 70,
16 defaultAllDay: false,
17
18 // numéros de semaine
19 //weekNumbers: true,
20 //weekText: 's',
21
22 // vue mois
23 contentHeight: 600, // après initialisation: calendar.setOption('contentHeight', 650);
24 //aspectRatio: 1.5, // après initialisation: calendar.setOption('aspectRatio', 1.8);
25 // pour recalculer la taille au redimensionnement du parent, exécuter: calendar.updateSize()
26 stickyHeaderDates: true, // garder les en-tête de colonnes lors du scroll
27 fixedWeekCount: false, // avec false, affiche 4, 5 ou 6 semaines selon le mois
28 selectable: true, // sélection de jours multiples
29 navLinks: true, // numéros de jour et de semaines clicables
30
31 // vue semaine
32 slotEventOverlap: true, // superposition (limitée) de deux évènements simultanés
33 allDayContent: 'Journée', // texte dans la case "toute la journée"
34 nowIndicator: true, // barre rouge pour maintenant
35
36 select: function(info){
37 selected_start_string = info.startStr; // variable "globale"
38 const aside = document.querySelector('aside');
39 let checked = '';
40 let input = 'datetime-local';
41
42 // on veut des chaines de la forme 2025-05-20T07:05
43 // il faut retirer les secondes et le fuseau horaire du format ISO, c'est chiant
44 // on enverra par contre une chaine ISO au serveur pour avoir un enregistrement correct
45
46 let start_value;
47 let end_value;
48 const end = new Date(info.endStr);
49
50 if(calendar.view.type == 'dayGridMonth'){
51 start_value = info.startStr + 'T10:00';
52 end.setDate(end.getDate() - 1); // jour de fin modifié pour ne pas faire bizarre pour l'utilisateur
53 end.setHours(11);
54 end_value = end.toISOString().split('T')[0] + 'T11:00';
55 }
56 else if(calendar.view.type == 'timeGridWeek' || calendar.view.type == 'timeGridDay'){
57 const start_array = info.startStr.split("T");
58 const end_array = info.endStr.split("T");
59
60 // clic sur la ligne "Journée", = 'dayGridMonth'
61 if(start_array.length == 1){
62 checked = 'checked';
63 input = 'date';
64 start_value = info.startStr;
65 end.setDate(end.getDate() - 1);
66 end_value = end.toISOString().split('T')[0];
67 }
68 else if(start_array.length == 2){
69 start_value = start_array[0] + "T" + start_array[1].substr(0,5); // format 2025-06-12T10:00
70 end_value = end_array[0] + "T" + end_array[1].substr(0,5);
71 }
72 }
73
74 const aside_content = `<div class="form_event">
75 <div class="event_title_box">
76 <h2>Nouvel évènement</h2>
77 </div>
78 <div class="">
79 <label for="event_title">Nom</label>
80 <input type="text" id="event_title">
81 </div>
82 <div class="">
83 <label for="event_description">Description</label>
84 <textarea id="event_description" cols="27"></textarea>
85 </div>
86 <div class="">
87 <input type="checkbox" id="event_all_day" class="event_all_day" ` + checked + `>
88 <label for="event_all_day">Journée entière</label>
89 </div>
90 <div class="">
91 <label for="event_start">Début</label>
92 <input type="` + input + `" id="event_start" value="` + start_value + `">
93 </div>
94 <div class="">
95 <label for="event_end">Fin</label>
96 <input type="` + input + `" id="event_end" value="` + end_value + `">
97 </div>
98 <div class="">
99 <label for="event_color">Couleur</label>
100 <input type="color" id="event_color" value="#3788D8">
101 </div>
102 <button class="submit_new_event">Enregistrer</button>
103 <button class="event_close_button">Annuler</button>
104 </div>`;
105 aside.innerHTML = aside_content;
106 calendar.updateSize();
107 },
108 //unselect: function(event, view){},
109 eventClick: function(info){
110 const aside = document.querySelector('aside');
111 const checked = info.event.allDay ? 'checked' : '';
112 const input = info.event.allDay ? 'date' : 'datetime-local';
113
114 // change des objets Date en chaînes compatibles avec les input
115 function formatDate(date){
116 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'));
118 }
119 function getEndMinusOneDay(date){
120 date.setDate(date.getDate() - 1); // jour de fin modifié pour ne pas faire bizarre pour l'utilisateur
121 return date;
122 }
123
124 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);
126
127 const aside_content = `<div class="form_event">
128 <div class="event_title_box">
129 <h2>Modifier un évènement</h2>
130 </div>
131 <div class="">
132 <label for="event_title">Nom</label>
133 <input type="text" id="event_title" value="` + info.event.title + `">
134 <input type="hidden" id="event_id" value="` + info.event.id + `">
135 </div>
136 <div class="">
137 <label for="event_description">Description</label>
138 <textarea id="event_description" cols="27">` + info.event.extendedProps.description + `</textarea>
139 </div>
140 <div class="">
141 <input type="checkbox" id="event_all_day" class="event_all_day" ` + checked + `>
142 <label for="event_all_day">Journée entière</label>
143 </div>
144 <div class="">
145 <label for="event_start">Début</label>
146 <input type="` + input + `" id="event_start" value="` + formated_start + `">
147 </div>
148 <div class="">
149 <label for="event_end">Fin</label>
150 <input type="` + input + `" id="event_end" value="` + formated_end + `">
151 </div>
152 <div class="">
153 <label for="event_color">Couleur</label>
154 <input type="color" id="event_color" value="` + info.event.backgroundColor + `">
155 </div>
156 <button class="submit_update_event">Modifier</button>
157 <button class="event_close_button">Annuler</button>
158 </div>`;
159 aside.innerHTML = aside_content;
160 calendar.updateSize();
161 },
162 viewDidMount: function(info){ // déclenché lorsque qu'une nouvelle vue est chargée (mois, semaine...)
163 if(selected_start_string){
164 calendar.gotoDate(new Date(selected_start_string));
165 }
166 },
167 //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 });
170
171 function hideModal(){
172 const aside = document.querySelector('aside');
173 aside.innerHTML = '';
174 calendar.updateSize();
175 }
176
177 function submitEvent(new_event){
178 const event_title = document.getElementById('event_title').value;
179 const event_description = document.getElementById('event_description').value;
180 const event_all_day = document.getElementById('event_all_day').checked;
181 let event_start = document.getElementById('event_start').value;
182 let event_end = document.getElementById('event_end').value;
183 const event_color = document.getElementById('event_color').value; // #3788d8 par défaut
184 const event_id = new_event ? '' : document.getElementById('event_id').value;
185
186 if(event_title.length !== 0 && event_start.length !== 0 && event_end.length !== 0 && event_color.length !== 0
187 && (new_event || event_id.length !== 0))
188 {
189 if(event_all_day){
190 // on remet le jour de fin exclu
191 const tmp_object = new Date(event_end);
192 tmp_object.setDate(tmp_object.getDate() + 1);
193 event_end = tmp_object.toISOString().split('T')[0];
194 }
195 else{
196 event_start = new Date(event_start).toISOString();
197 event_end = new Date(event_end).toISOString();
198 }
199 console.log(event_end);
200
201 if(event_start > event_end || (!event_all_day && event_start == event_end)){
202 return;
203 }
204
205 // création
206 if(new_event){
207 const event = {
208 title: event_title,
209 description: event_description,
210 allDay: event_all_day,
211 start: event_start,
212 end: event_end,
213 color: event_color
214 };
215
216 fetch('../src/post-ajax.php', {
217 method: 'POST',
218 headers: {
219 'Content-Type': 'application/json',
220 },
221 body: JSON.stringify(event),
222 })
223 .then(response => response.json())
224 .then(data => {
225 if(data.success){
226 event.id = data.id;
227 calendar.addEvent(event);
228 hideModal();
229 }
230 })
231 .catch((error) => {
232 console.error('Error:', error);
233 });
234
235 }
236 // modification
237 else{
238 const event = calendar.getEventById(event_id);
239
240 if(event){
241 const event_copy = {
242 id: parseInt(event.id),
243 description: event_description,
244 title: event_title,
245 allDay: event_all_day,
246 start: event_start,
247 end: event_end,
248 color: event_color
249 };
250
251 fetch('../src/post-ajax.php', {
252 method: 'POST',
253 headers: {
254 'Content-Type': 'application/json',
255 },
256 body: JSON.stringify(event_copy),
257 })
258 .then(response => response.json())
259 .then(data => {
260 if(data.success){
261 event.setProp('title', event_title);
262 event.setExtendedProp('description', event_description);
263 event.setAllDay(event_all_day);
264 event.setStart(event_start);
265 event.setEnd(event_end);
266 event.setProp('color', event_color);
267 hideModal();
268 }
269 })
270 .catch((error) => {
271 console.error('Error:', error);
272 });
273 }
274 else{
275 console.log("Événement non trouvé !");
276 }
277 }
278 }
279 else{
280 // notif input vide
281 console.log('erreur: input vide');
282 }
283 }
284
285 function checkAllDay(){
286 const event_start_input = document.getElementById('event_start');
287 const event_end_input = document.getElementById('event_end');
288
289 const start = event_start_input.value;
290 const end = event_end_input.value;
291
292 if(document.getElementById('event_all_day').checked){
293 event_start_input.type = 'date';
294 event_end_input.type = 'date';
295
296 event_start_input.value = start.split('T')[0];
297 event_end_input.value = end.split('T')[0];
298 }
299 else{
300 event_start_input.type = 'datetime-local';
301 event_end_input.type = 'datetime-local';
302
303 event_start_input.value = start + 'T10:00';
304 event_end_input.value = end + 'T11:00';
305 }
306 }
307
308 document.addEventListener('keydown', function(event){
309 if(event.key === 'Escape') {
310 hideModal();
311 }
312 });
313
314 // technique de la délégation d'événements pour utiliser un bouton ajouté dynamiquement
315 document.addEventListener('click', function(event){
316 if(event.target.classList.contains('event_close_button')){
317 hideModal();
318 }
319 else if(event.target.classList.contains('event_all_day')){
320 checkAllDay();
321 }
322 else if(event.target.classList.contains('submit_new_event')){
323 submitEvent(true);
324 }
325 else if(event.target.classList.contains('submit_update_event')){
326 submitEvent(false);
327 }
328 });
329
330 calendar.render();
331}); \ No newline at end of file