PageSourceSearch

https://www.vico.corsica/assets/agenda/agenda_controller-97bc96808689a34d964c9b9021b3706f.js

js vico.corsica collected 2026-10-02 16:59:53 UTC 7,599 bytes, 214 lines download raw bytes

1import { Controller } from "@hotwired/stimulus";
2import moment from 'moment-timezone';
3import { Calendar, dayGridPlugin, timeGridPlugin, listPlugin, interactionPlugin, allLocales } from 'fullcalendar-adapter';
4import { trans } from 'translator';
5import Swal from 'sweetalert2';
6
7export default class extends Controller {
8    static values = {
9        localeUser: String,
10        timezoneUser: String,
11        stationSlug: String,
12    }
13    static targets = ['calendar', "checkboxCategorie", "checkboxAll"];
14    
15    #isCreatingEvent = false;
16
17    connect() {
18        document.addEventListener('calendar:refresh', this.refreshCalendar.bind(this));
19
20        this.calendar = new Calendar(this.calendarTarget, {
21            /*nextDayThreshold: "00:00:00",*/
22            /*now: getNowWithOffset(offset),*/
23            plugins: [dayGridPlugin, listPlugin, timeGridPlugin, interactionPlugin],
24            initialView: 'dayGridMonth',
25            locales: allLocales,
26            locale: this.localeUserValue,
27            timeZone: this.timezoneUserValue,
28            editable: true,
29            selectable: true,
30            lazyFetching: true,
31            eventSources: this.getSources(),
32            eventConstraint: this.getCalendarDateConstraints(),
33            selectConstraint: this.getCalendarDateConstraints(),
34            headerToolbar: {
35                left: 'prev,next today',
36                center: 'title',
37                right: 'dayGridMonth,timeGridWeek,timeGridDay,listAllYears'
38            },
39            views: {
40                listAllYears: {
41                    type: 'listYear',
42                    duration: { years: 2 },
43                    buttonText: trans('agenda.index.liste_evenement_button'),
44                },
45                timeGridWeek: {
46                    displayEventEnd: true,
47                },
48            },
49            select: this.newEvenement.bind(this),
50            eventClick: this.handleEventClick.bind(this),
51            eventDrop: this.updateEvenement.bind(this),
52            eventResize: this.updateEvenement.bind(this),
53            eventContent: this.affichageMonth.bind(this),
54
55            eventDataTransform: (rawEventData) => {
56                // On travaille uniquement sur les événements allDay avec start/end définis
57                if (!rawEventData.allDay || !rawEventData.start || !rawEventData.end) {
58                    return rawEventData;
59                }
60
61                const tz = this.timezoneUserValue;
62                const start = moment.tz(rawEventData.start, tz);
63                const end = moment.tz(rawEventData.end, tz);
64
65                // multi-jours si au moins 1 jour d'écart entre start et end (en jours calendaires)
66                const isMultiDay = end.clone().startOf('day').diff(start.clone().startOf('day'), 'days') >= 1;
67                const isEndOfDay = end.hour() === 23 && end.minute() === 59;
68
69                if (isMultiDay && isEndOfDay) {
70                    // On décale la fin à minuit du jour suivant pour que FullCalendar
71                    // couvre bien tous les jours concernés
72                    const displayEnd = end.clone().add(1, 'minute');
73
74                    return {
75                        ...rawEventData,
76                        end: displayEnd.toISOString(),
77                    };
78                }
79
80                return rawEventData;
81            },
82        });
83
84        this.calendar.render();
85    }
86
87    filterEvents(event) {
88        const categorieCode = event.target.value;
89        const isChecked = event.target.checked;
90        const events = this.calendar.getEvents();
91
92        events.forEach((calendarEvent) => {
93            if (calendarEvent.extendedProps.categorie.code === categorieCode || categorieCode === 'all') {
94                calendarEvent.setProp('display', isChecked ? 'auto' : 'none');
95            }
96        });
97    }
98
99    filterAllEvents(event) {
100        const isChecked = event.target.checked;
101
102        this.checkboxCategorieTargets.forEach((checkbox) => {
103            checkbox.checked = isChecked;
104        });
105
106        const events = this.calendar.getEvents();
107        events.forEach((calendarEvent) => {
108            calendarEvent.setProp('display', isChecked ? 'auto' : 'none');
109        });
110    }
111
112    handleEventClick(info) {
113        if (this.#isCreatingEvent) {
114            return;
115        }
116        this.#isCreatingEvent = true;
117
118        const event = info.event;
119        const url = Routing.generate('app_agenda_edit_flow', {
120            '_locale': this.localeUserValue,
121            slug: event.extendedProps.slug || event.slug,
122            station_slug: this.stationSlugValue
123        });
124        window.location.href = url;
125    }
126
127    newEvenement(info = null) {
128
129        if (this.#isCreatingEvent) {
130            return;
131        }
132        
133        this.#isCreatingEvent = true;
134
135        let params = {
136            '_locale': this.localeUserValue,
137            'station_slug': this.stationSlugValue
138        };
139
140        if (info) {
141            const tz = this.timezoneUserValue;
142            if (info.view.type === 'dayGridMonth') {
143                const start = moment.tz(info.startStr, tz)
144                    .set({ hour: moment().hour(), minute: moment().minute(), second: 0, millisecond: 0 });
145                const end = start.clone().add(1, 'hour');
146                params['start'] = start.format();
147                params['end'] = end.format();
148            } else {
149                // Pour les autres vues, on garde le comportement natif
150                params['start'] = info.startStr;
151                params['end'] = info.endStr;
152            }
153            params['timezone'] = tz;
154
155        }
156
157        const url = Routing.generate('app_agenda_create_flow', params);
158        window.location.href = url;
159    }
160
161    updateEvenement(info) {
162        const event = info.event;
163        const timezone = this.timezoneUserValue;
164        const start = moment.tz(event.start, timezone).format('YYYY-MM-DDTHH:mm:ss');
165        const end = moment.tz(event.end, timezone).format('YYYY-MM-DDTHH:mm:ss');
166
167        const url = Routing.generate('app_agenda_update', {
168            id: event.id,
169            station_slug: this.stationSlugValue
170        });
171
172        fetch(url, {
173            method: 'POST',
174            body: JSON.stringify({ start, end, timezone }),
175        })
176            .then(() => Swal.fire('Événement mis à jour avec succès', '', 'success'))
177            .catch(err => Swal.fire('Erreur', 'Impossible de mettre à jour l\'événement', 'error'));
178    }
179
180    getSources() {
181        const evenement_url = Routing.generate('app_agenda_list', { station_slug: this.stationSlugValue });
182        return [
183            {
184                url: evenement_url,
185                color: '#a2c617',
186                textColor: 'white',
187                editable: true,
188            },
189        ];
190    }
191
192    affichageMonth(info) {
193        const evenement = info.event;
194        let spanAffichage = document.createElement('span');
195
196        if (info.view.type === "dayGridMonth" && evenement.allDay) {
197            spanAffichage.innerHTML = evenement.title;
198            return { domNodes: [spanAffichage] };
199        }
200
201        return true;
202    }
203
204    getCalendarDateConstraints() {
205        return {
206            start: moment().tz(this.timezoneUserValue).startOf('day').format('YYYY-MM-DDTHH:mm:ss'),  // Début aujourd'hui à minuit
207            end: moment().tz(this.timezoneUserValue).add(100, 'years').startOf('day').format('YYYY-MM-DDTHH:mm:ss')  // Fin dans 100 ans
208        };
209    }
210
211    refreshCalendar() {
212        this.calendar.refetchEvents();
213    }
214}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.