nodebb-plugin-calendar-onekite 11.1.3 → 11.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/library.js +12 -12
- package/package.json +1 -1
- package/public/admin.js +27 -24
- package/public/client.js +149 -126
- package/templates/admin/plugins/calendar-onekite.tpl +1 -1
- package/templates/calendar-onekite/calendar.tpl +1 -1
package/library.js
CHANGED
|
@@ -27,31 +27,31 @@ Plugin.init = async function (params) {
|
|
|
27
27
|
routeHelpers.setupPageRoute(router, '/calendar', mw([buildHeader]), controllers.renderCalendar);
|
|
28
28
|
|
|
29
29
|
// Public API (read-only)
|
|
30
|
-
//
|
|
31
|
-
routeHelpers.setupApiRoute(router, 'get', '/
|
|
32
|
-
routeHelpers.setupApiRoute(router, 'get', '/
|
|
30
|
+
// Use a plugin namespace under /api/v3/plugins/... (client-side api module expects this)
|
|
31
|
+
routeHelpers.setupApiRoute(router, 'get', '/plugins/calendar-onekite/events', [], api.getEvents);
|
|
32
|
+
routeHelpers.setupApiRoute(router, 'get', '/plugins/calendar-onekite/items', [], api.getItems);
|
|
33
33
|
|
|
34
34
|
// Authenticated API
|
|
35
35
|
// req.uid is provided by exposeUid; ensureLoggedIn blocks unauthenticated users.
|
|
36
|
-
routeHelpers.setupApiRoute(router, 'post', '/
|
|
36
|
+
routeHelpers.setupApiRoute(router, 'post', '/plugins/calendar-onekite/reservations', mw([exposeUid, ensureLoggedIn]), api.createReservation);
|
|
37
37
|
|
|
38
38
|
// Admin ACP page
|
|
39
39
|
routeHelpers.setupAdminPageRoute(router, '/admin/plugins/calendar-onekite', [], admin.renderAdmin);
|
|
40
40
|
|
|
41
|
-
// Admin API (under /api
|
|
41
|
+
// Admin API (under /api)
|
|
42
42
|
const adminMiddlewares = mw([
|
|
43
43
|
exposeUid,
|
|
44
44
|
ensureLoggedIn,
|
|
45
45
|
middleware.admin && middleware.admin.checkPrivileges,
|
|
46
46
|
]);
|
|
47
47
|
|
|
48
|
-
//
|
|
49
|
-
routeHelpers.setupApiRoute(router, 'get', '/
|
|
50
|
-
routeHelpers.setupApiRoute(router, 'post', '/
|
|
51
|
-
routeHelpers.setupApiRoute(router, 'get', '/
|
|
52
|
-
routeHelpers.setupApiRoute(router, 'put', '/
|
|
53
|
-
routeHelpers.setupApiRoute(router, 'put', '/
|
|
54
|
-
routeHelpers.setupApiRoute(router, 'post', '/
|
|
48
|
+
// Admin API (client uses /api/v3/admin/... via api module)
|
|
49
|
+
routeHelpers.setupApiRoute(router, 'get', '/admin/plugins/calendar-onekite', adminMiddlewares, admin.getSettings);
|
|
50
|
+
routeHelpers.setupApiRoute(router, 'post', '/admin/plugins/calendar-onekite', adminMiddlewares, admin.saveSettings);
|
|
51
|
+
routeHelpers.setupApiRoute(router, 'get', '/admin/plugins/calendar-onekite/pending', adminMiddlewares, admin.listPending);
|
|
52
|
+
routeHelpers.setupApiRoute(router, 'put', '/admin/plugins/calendar-onekite/reservations/:rid/approve', adminMiddlewares, admin.approveReservation);
|
|
53
|
+
routeHelpers.setupApiRoute(router, 'put', '/admin/plugins/calendar-onekite/reservations/:rid/refuse', adminMiddlewares, admin.refuseReservation);
|
|
54
|
+
routeHelpers.setupApiRoute(router, 'post', '/admin/plugins/calendar-onekite/purge', adminMiddlewares, admin.purgeByYear);
|
|
55
55
|
|
|
56
56
|
// Background cleanup for expired "pending" reservations
|
|
57
57
|
scheduler.start();
|
package/package.json
CHANGED
package/public/admin.js
CHANGED
|
@@ -1,9 +1,19 @@
|
|
|
1
|
-
/* global define, app, $ */
|
|
2
1
|
'use strict';
|
|
3
2
|
|
|
4
|
-
|
|
3
|
+
/* globals define */
|
|
4
|
+
|
|
5
|
+
define('admin/plugins/calendar-onekite', ['api', 'alerts', 'jquery'], function (api, alerts, $) {
|
|
6
|
+
function alertSuccess(msg) {
|
|
7
|
+
if (alerts && typeof alerts.success === 'function') return alerts.success(msg);
|
|
8
|
+
if (alerts && typeof alerts.alert === 'function') return alerts.alert({ type: 'success', message: msg });
|
|
9
|
+
}
|
|
10
|
+
function alertError(msg) {
|
|
11
|
+
if (alerts && typeof alerts.error === 'function') return alerts.error(msg);
|
|
12
|
+
if (alerts && typeof alerts.alert === 'function') return alerts.alert({ type: 'danger', message: msg });
|
|
13
|
+
}
|
|
14
|
+
|
|
5
15
|
function fillForm($form, settings) {
|
|
6
|
-
Object.keys(settings).forEach((k) => {
|
|
16
|
+
Object.keys(settings || {}).forEach((k) => {
|
|
7
17
|
const $el = $form.find(`[name="${k}"]`);
|
|
8
18
|
if (!$el.length) return;
|
|
9
19
|
|
|
@@ -20,7 +30,7 @@ define('calendar-onekite-admin', function () {
|
|
|
20
30
|
return '<div class="text-muted">Aucune demande en attente.</div>';
|
|
21
31
|
}
|
|
22
32
|
|
|
23
|
-
return list.map(r => {
|
|
33
|
+
return list.map((r) => {
|
|
24
34
|
const start = new Date(Number(r.start)).toLocaleString();
|
|
25
35
|
const end = new Date(Number(r.end)).toLocaleString();
|
|
26
36
|
const expires = new Date(Number(r.expiresAt)).toLocaleString();
|
|
@@ -50,7 +60,7 @@ define('calendar-onekite-admin', function () {
|
|
|
50
60
|
}
|
|
51
61
|
|
|
52
62
|
async function loadPending() {
|
|
53
|
-
const resp = await
|
|
63
|
+
const resp = await api.get('/admin/plugins/calendar-onekite/pending', {});
|
|
54
64
|
return resp.pending || [];
|
|
55
65
|
}
|
|
56
66
|
|
|
@@ -61,14 +71,11 @@ define('calendar-onekite-admin', function () {
|
|
|
61
71
|
$container.on('click', '.js-approve', async function () {
|
|
62
72
|
const rid = $(this).closest('[data-rid]').data('rid');
|
|
63
73
|
try {
|
|
64
|
-
await
|
|
65
|
-
|
|
66
|
-
method: 'PUT',
|
|
67
|
-
});
|
|
68
|
-
app.alertSuccess('Réservation validée, email de paiement envoyé.');
|
|
74
|
+
await api.put(`/admin/plugins/calendar-onekite/reservations/${rid}/approve`, {});
|
|
75
|
+
alertSuccess('Réservation validée, email de paiement envoyé.');
|
|
69
76
|
await refreshPending();
|
|
70
77
|
} catch (e) {
|
|
71
|
-
|
|
78
|
+
alertError((e && e.message) || 'Erreur');
|
|
72
79
|
}
|
|
73
80
|
});
|
|
74
81
|
|
|
@@ -76,15 +83,11 @@ define('calendar-onekite-admin', function () {
|
|
|
76
83
|
const rid = $(this).closest('[data-rid]').data('rid');
|
|
77
84
|
const note = prompt('Motif (optionnel) :') || '';
|
|
78
85
|
try {
|
|
79
|
-
await
|
|
80
|
-
|
|
81
|
-
method: 'PUT',
|
|
82
|
-
data: { note },
|
|
83
|
-
});
|
|
84
|
-
app.alertSuccess('Réservation refusée, email envoyé.');
|
|
86
|
+
await api.put(`/admin/plugins/calendar-onekite/reservations/${rid}/refuse`, { note });
|
|
87
|
+
alertSuccess('Réservation refusée, email envoyé.');
|
|
85
88
|
await refreshPending();
|
|
86
89
|
} catch (e) {
|
|
87
|
-
|
|
90
|
+
alertError((e && e.message) || 'Erreur');
|
|
88
91
|
}
|
|
89
92
|
});
|
|
90
93
|
}
|
|
@@ -100,7 +103,7 @@ define('calendar-onekite-admin', function () {
|
|
|
100
103
|
const $form = $('#calendar-onekite-settings');
|
|
101
104
|
const $saved = $('#calendar-onekite-saved');
|
|
102
105
|
|
|
103
|
-
const resp = await
|
|
106
|
+
const resp = await api.get('/admin/plugins/calendar-onekite', {});
|
|
104
107
|
fillForm($form, resp.settings || {});
|
|
105
108
|
|
|
106
109
|
$form.on('submit', async function (e) {
|
|
@@ -110,12 +113,12 @@ define('calendar-onekite-admin', function () {
|
|
|
110
113
|
data.showUsernamesOnCalendar = $('#showUsernamesOnCalendar').prop('checked');
|
|
111
114
|
|
|
112
115
|
try {
|
|
113
|
-
await
|
|
116
|
+
await api.post('/admin/plugins/calendar-onekite', data);
|
|
114
117
|
$saved.removeClass('d-none');
|
|
115
118
|
setTimeout(() => $saved.addClass('d-none'), 2000);
|
|
116
|
-
|
|
119
|
+
alertSuccess('Paramètres enregistrés.');
|
|
117
120
|
} catch (err) {
|
|
118
|
-
|
|
121
|
+
alertError((err && err.message) || 'Erreur lors de la sauvegarde');
|
|
119
122
|
}
|
|
120
123
|
});
|
|
121
124
|
|
|
@@ -123,10 +126,10 @@ define('calendar-onekite-admin', function () {
|
|
|
123
126
|
e.preventDefault();
|
|
124
127
|
const year = $(this).find('[name="year"]').val();
|
|
125
128
|
try {
|
|
126
|
-
const resp2 = await
|
|
129
|
+
const resp2 = await api.post('/admin/plugins/calendar-onekite/purge', { year });
|
|
127
130
|
$('#calendar-onekite-purge-result').html(`<div class="alert alert-success">Année ${resp2.result.year}: ${resp2.result.purged} réservation(s) supprimée(s).</div>`);
|
|
128
131
|
} catch (err) {
|
|
129
|
-
$('#calendar-onekite-purge-result').html(`<div class="alert alert-danger">${(err
|
|
132
|
+
$('#calendar-onekite-purge-result').html(`<div class="alert alert-danger">${(err && err.message) || 'Erreur'}</div>`);
|
|
130
133
|
}
|
|
131
134
|
});
|
|
132
135
|
|
package/public/client.js
CHANGED
|
@@ -1,145 +1,168 @@
|
|
|
1
|
-
/* global define, app, bootbox, ajaxify, FullCalendar, $ */
|
|
2
1
|
'use strict';
|
|
3
2
|
|
|
4
|
-
define
|
|
5
|
-
|
|
3
|
+
/* globals define, ajaxify, FullCalendar */
|
|
4
|
+
|
|
5
|
+
define('calendar-onekite', ['api', 'alerts', 'bootbox'], function (api, alerts, bootbox) {
|
|
6
|
+
function toLocale(dt) {
|
|
6
7
|
try { return new Date(dt).toLocaleString(); } catch (e) { return String(dt); }
|
|
7
8
|
}
|
|
8
9
|
|
|
10
|
+
function alertSuccess(msg) {
|
|
11
|
+
if (alerts && typeof alerts.success === 'function') {
|
|
12
|
+
return alerts.success(msg);
|
|
13
|
+
}
|
|
14
|
+
if (alerts && typeof alerts.alert === 'function') {
|
|
15
|
+
return alerts.alert({ type: 'success', message: msg });
|
|
16
|
+
}
|
|
17
|
+
console.log(msg);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function alertError(msg) {
|
|
21
|
+
if (alerts && typeof alerts.error === 'function') {
|
|
22
|
+
return alerts.error(msg);
|
|
23
|
+
}
|
|
24
|
+
if (alerts && typeof alerts.alert === 'function') {
|
|
25
|
+
return alerts.alert({ type: 'danger', message: msg });
|
|
26
|
+
}
|
|
27
|
+
console.error(msg);
|
|
28
|
+
}
|
|
29
|
+
|
|
9
30
|
async function fetchItems() {
|
|
10
|
-
|
|
31
|
+
// NodeBB client api module automatically prefixes /api/v3
|
|
32
|
+
return await api.get('/plugins/calendar-onekite/items', {});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function waitForFullCalendar(cb) {
|
|
36
|
+
if (typeof FullCalendar !== 'undefined' && FullCalendar && FullCalendar.Calendar) {
|
|
37
|
+
return cb();
|
|
38
|
+
}
|
|
39
|
+
setTimeout(function () { waitForFullCalendar(cb); }, 50);
|
|
11
40
|
}
|
|
12
41
|
|
|
13
42
|
function init(opts) {
|
|
14
|
-
const el = document.querySelector(opts.el);
|
|
43
|
+
const el = document.querySelector((opts && opts.el) || '#onekite-calendar');
|
|
15
44
|
if (!el) return;
|
|
16
45
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
46
|
+
waitForFullCalendar(function () {
|
|
47
|
+
const calendar = new FullCalendar.Calendar(el, {
|
|
48
|
+
initialView: 'dayGridMonth',
|
|
49
|
+
selectable: true,
|
|
50
|
+
locale: (opts && opts.locale) || 'fr',
|
|
51
|
+
headerToolbar: {
|
|
52
|
+
left: 'prev,next today',
|
|
53
|
+
center: 'title',
|
|
54
|
+
right: 'dayGridMonth,timeGridWeek,timeGridDay',
|
|
55
|
+
},
|
|
56
|
+
events: async function (info, success, failure) {
|
|
57
|
+
try {
|
|
58
|
+
const resp = await api.get('/plugins/calendar-onekite/events', { start: info.startStr, end: info.endStr });
|
|
59
|
+
success(resp.events || resp);
|
|
60
|
+
} catch (err) {
|
|
61
|
+
failure(err);
|
|
62
|
+
alertError((err && err.message) || 'Erreur lors du chargement du calendrier');
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
select: function (selectionInfo) {
|
|
66
|
+
if (!ajaxify || !ajaxify.data || !ajaxify.data.loggedIn) {
|
|
67
|
+
return bootbox.alert('Vous devez être connecté pour faire une demande.');
|
|
68
|
+
}
|
|
69
|
+
openReservationDialog(selectionInfo.startStr, selectionInfo.endStr);
|
|
70
|
+
},
|
|
71
|
+
dateClick: function (info) {
|
|
72
|
+
if (!ajaxify || !ajaxify.data || !ajaxify.data.loggedIn) {
|
|
73
|
+
return bootbox.alert('Vous devez être connecté pour faire une demande.');
|
|
74
|
+
}
|
|
75
|
+
const start = info.dateStr;
|
|
76
|
+
const end = new Date(info.date.getTime() + 60 * 60 * 1000).toISOString();
|
|
77
|
+
openReservationDialog(start, end);
|
|
78
|
+
},
|
|
79
|
+
eventClick: function (clickInfo) {
|
|
80
|
+
const ev = clickInfo.event;
|
|
81
|
+
const st = ev.extendedProps && ev.extendedProps.status;
|
|
82
|
+
const pay = ev.extendedProps && ev.extendedProps.paymentUrl;
|
|
83
|
+
let msg = `<p><strong>${ev.title}</strong></p>
|
|
84
|
+
<p>Du ${toLocale(ev.start)}<br/>Au ${toLocale(ev.end)}</p>
|
|
85
|
+
<p>Statut: <strong>${st}</strong></p>`;
|
|
86
|
+
if (pay) {
|
|
87
|
+
msg += `<p><a class="btn btn-primary" href="${pay}" target="_blank" rel="noopener">Payer sur HelloAsso</a></p>`;
|
|
88
|
+
}
|
|
89
|
+
bootbox.alert({ title: 'Réservation', message: msg });
|
|
90
|
+
},
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
calendar.render();
|
|
94
|
+
|
|
95
|
+
async function openReservationDialog(startStr, endStr) {
|
|
96
|
+
let itemsResp;
|
|
97
|
+
try {
|
|
98
|
+
itemsResp = await fetchItems();
|
|
99
|
+
} catch (e) {
|
|
100
|
+
return bootbox.alert('Impossible de récupérer la liste du matériel (HelloAsso). Vérifiez les paramètres dans l’ACP.');
|
|
57
101
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
});
|
|
102
|
+
const items = (itemsResp && itemsResp.items) || [];
|
|
103
|
+
if (!items.length) return bootbox.alert('Aucun matériel disponible (liste HelloAsso vide).');
|
|
61
104
|
|
|
62
|
-
|
|
105
|
+
const optionsHtml = items.map(function (it) {
|
|
106
|
+
const price = (it.price !== '' && it.price !== null && it.price !== undefined) ? ` — ${it.price}` : '';
|
|
107
|
+
const safeName = String(it.name || '').replace(/"/g, '"');
|
|
108
|
+
return `<option value="${String(it.id)}" data-name="${safeName}" data-price="${String(it.price ?? '')}">${safeName}${price}</option>`;
|
|
109
|
+
}).join('');
|
|
63
110
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
const payload = {
|
|
115
|
-
itemId,
|
|
116
|
-
itemName,
|
|
117
|
-
itemPrice,
|
|
118
|
-
start: $f.find('[name="start"]').val(),
|
|
119
|
-
end: $f.find('[name="end"]').val(),
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
return $.ajax({
|
|
123
|
-
url: '/api/v3/calendar-onekite/reservations',
|
|
124
|
-
method: 'POST',
|
|
125
|
-
data: payload,
|
|
126
|
-
}).done(function () {
|
|
127
|
-
app.alertSuccess('Demande envoyée (en attente de validation).');
|
|
128
|
-
calendar.refetchEvents();
|
|
129
|
-
}).fail(function (xhr) {
|
|
130
|
-
const err = (xhr.responseJSON && xhr.responseJSON.error) || 'Erreur';
|
|
131
|
-
app.alertError(err);
|
|
132
|
-
});
|
|
111
|
+
const formHtml = `
|
|
112
|
+
<form id="onekite-reserve-form">
|
|
113
|
+
<div class="mb-2">
|
|
114
|
+
<label class="form-label">Matériel</label>
|
|
115
|
+
<select class="form-select" name="itemId">${optionsHtml}</select>
|
|
116
|
+
</div>
|
|
117
|
+
<div class="mb-2">
|
|
118
|
+
<label class="form-label">Début</label>
|
|
119
|
+
<input class="form-control" name="start" value="${startStr}" />
|
|
120
|
+
</div>
|
|
121
|
+
<div class="mb-2">
|
|
122
|
+
<label class="form-label">Fin</label>
|
|
123
|
+
<input class="form-control" name="end" value="${endStr}" />
|
|
124
|
+
</div>
|
|
125
|
+
<div class="alert alert-warning mt-3">
|
|
126
|
+
Votre demande sera <strong>en attente</strong> jusqu’à validation, et le matériel sera bloqué pendant la durée définie.
|
|
127
|
+
</div>
|
|
128
|
+
</form>
|
|
129
|
+
`;
|
|
130
|
+
|
|
131
|
+
bootbox.dialog({
|
|
132
|
+
title: 'Nouvelle demande de réservation',
|
|
133
|
+
message: formHtml,
|
|
134
|
+
buttons: {
|
|
135
|
+
cancel: { label: 'Annuler', className: 'btn-secondary' },
|
|
136
|
+
ok: {
|
|
137
|
+
label: 'Envoyer la demande',
|
|
138
|
+
className: 'btn-primary',
|
|
139
|
+
callback: async function () {
|
|
140
|
+
const form = document.getElementById('onekite-reserve-form');
|
|
141
|
+
const itemSelect = form.querySelector('[name="itemId"]');
|
|
142
|
+
const selected = itemSelect.options[itemSelect.selectedIndex];
|
|
143
|
+
|
|
144
|
+
const payload = {
|
|
145
|
+
itemId: itemSelect.value,
|
|
146
|
+
itemName: selected.getAttribute('data-name'),
|
|
147
|
+
itemPrice: selected.getAttribute('data-price'),
|
|
148
|
+
start: form.querySelector('[name="start"]').value,
|
|
149
|
+
end: form.querySelector('[name="end"]').value,
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
try {
|
|
153
|
+
await api.post('/plugins/calendar-onekite/reservations', payload);
|
|
154
|
+
alertSuccess('Demande envoyée (en attente de validation).');
|
|
155
|
+
calendar.refetchEvents();
|
|
156
|
+
} catch (err) {
|
|
157
|
+
alertError((err && err.message) || 'Erreur');
|
|
158
|
+
}
|
|
159
|
+
return false;
|
|
160
|
+
},
|
|
133
161
|
},
|
|
134
162
|
},
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
dlg.init(function () {
|
|
139
|
-
// focus select
|
|
140
|
-
$('#onekite-reserve-form select').focus();
|
|
141
|
-
});
|
|
142
|
-
}
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
});
|
|
143
166
|
}
|
|
144
167
|
|
|
145
168
|
return { init };
|
|
@@ -106,7 +106,7 @@
|
|
|
106
106
|
(function () {
|
|
107
107
|
function boot() {
|
|
108
108
|
if (!window.require) return setTimeout(boot, 50);
|
|
109
|
-
window.require(['calendar-onekite
|
|
109
|
+
window.require(['admin/plugins/calendar-onekite'], function (Admin) {
|
|
110
110
|
Admin.init();
|
|
111
111
|
});
|
|
112
112
|
}
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/fullcalendar@6.1.11/index.global.min.css" />
|
|
12
12
|
|
|
13
13
|
<script defer src="https://cdn.jsdelivr.net/npm/fullcalendar@6.1.11/index.global.min.js"></script>
|
|
14
|
-
<script defer src="https://cdn.jsdelivr.net/npm/
|
|
14
|
+
<script defer src="https://cdn.jsdelivr.net/npm/@fullcalendar/core@6.1.11/locales-all.global.min.js"></script>
|
|
15
15
|
|
|
16
16
|
<script>
|
|
17
17
|
(function () {
|