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