nodebb-plugin-calendar-onekite 11.1.21 → 11.1.23
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/lib/admin.js +63 -209
- package/lib/api.js +125 -131
- package/lib/db.js +36 -22
- package/lib/helloasso.js +80 -106
- package/library.js +67 -69
- package/package.json +4 -5
- package/plugin.json +16 -12
- package/public/admin.js +99 -196
- package/public/client.js +186 -123
- package/templates/admin/plugins/calendar-onekite.tpl +74 -77
- package/templates/calendar-onekite.tpl +5 -15
package/public/client.js
CHANGED
|
@@ -1,161 +1,224 @@
|
|
|
1
|
-
/*
|
|
1
|
+
/* globals define, require */
|
|
2
|
+
'use strict';
|
|
2
3
|
|
|
3
|
-
define('forum/calendar-onekite', ['
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
try {
|
|
8
|
-
if (alerts && typeof alerts[type] === 'function') {
|
|
9
|
-
alerts[type](msg);
|
|
10
|
-
return;
|
|
11
|
-
}
|
|
12
|
-
} catch (e) {}
|
|
13
|
-
alert(msg);
|
|
4
|
+
define('forum/calendar-onekite', ['api', 'bootbox', 'alerts', 'hooks'], function (api, bootbox, alerts, hooks) {
|
|
5
|
+
function fmtEuros(cents) {
|
|
6
|
+
const v = (Number(cents || 0) / 100);
|
|
7
|
+
return v.toLocaleString(undefined, { style: 'currency', currency: 'EUR' });
|
|
14
8
|
}
|
|
15
9
|
|
|
16
|
-
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
});
|
|
22
|
-
if (!res.ok) {
|
|
23
|
-
throw new Error(`${res.status}`);
|
|
24
|
-
}
|
|
25
|
-
return await res.json();
|
|
10
|
+
function daysBetween(startStr, endStr) {
|
|
11
|
+
const s = new Date(startStr + 'T00:00:00Z');
|
|
12
|
+
const e = new Date(endStr + 'T00:00:00Z');
|
|
13
|
+
const d = Math.max(1, Math.round((e - s) / 86400000));
|
|
14
|
+
return d;
|
|
26
15
|
}
|
|
27
16
|
|
|
28
|
-
async function
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
17
|
+
async function loadCatalog() {
|
|
18
|
+
const res = await fetch('/api/v3/plugins/calendar-onekite/catalog', { credentials: 'same-origin' })
|
|
19
|
+
.catch(() => null);
|
|
20
|
+
if (res && res.ok) return (await res.json()).items || [];
|
|
21
|
+
const res2 = await fetch('/api/plugins/calendar-onekite/catalog', { credentials: 'same-origin' });
|
|
22
|
+
const j = await res2.json();
|
|
23
|
+
return j.items || [];
|
|
34
24
|
}
|
|
35
25
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
26
|
+
function showReservationModal(selection, catalog, onSubmit) {
|
|
27
|
+
const start = selection.startStr; // YYYY-MM-DD
|
|
28
|
+
const end = selection.endStr; // YYYY-MM-DD (exclusive)
|
|
29
|
+
const days = daysBetween(start, end);
|
|
30
|
+
|
|
31
|
+
const rows = catalog.map((it) => {
|
|
32
|
+
const price = fmtEuros(it.priceCents || 0);
|
|
33
|
+
return `<div class="onekite-item-row d-flex justify-content-between align-items-center py-1" data-id="${it.id}" data-price="${it.priceCents || 0}">
|
|
34
|
+
<label class="mb-0 d-flex align-items-center gap-2" style="cursor:pointer;">
|
|
35
|
+
<input type="checkbox" class="onekite-item-check" data-id="${it.id}">
|
|
36
|
+
<span>${it.name}</span>
|
|
37
|
+
</label>
|
|
38
|
+
<span class="text-muted">${price}</span>
|
|
39
|
+
</div>`;
|
|
40
|
+
}).join('');
|
|
41
|
+
|
|
42
|
+
const html = `
|
|
43
|
+
<div>
|
|
44
|
+
<div class="mb-2"><strong>Dates :</strong> ${start} → ${end} (${days} jour(s))</div>
|
|
45
|
+
<div class="mb-2 text-muted small">Sélectionne un ou plusieurs matériels. (Tu peux aussi Ctrl+clic sur une ligne.)</div>
|
|
46
|
+
<div class="border rounded p-2" style="max-height: 240px; overflow:auto;" id="onekite-item-list">${rows || '<div class="text-muted">Aucun matériel</div>'}</div>
|
|
47
|
+
<div class="mt-3 d-flex justify-content-between align-items-center">
|
|
48
|
+
<div><strong>Total estimé :</strong> <span id="onekite-total">0 €</span></div>
|
|
49
|
+
<div class="text-muted small">prix/jour × ${days}</div>
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
`;
|
|
53
|
+
|
|
54
|
+
const dialog = bootbox.dialog({
|
|
55
|
+
title: 'Demande de réservation',
|
|
56
|
+
message: html,
|
|
57
|
+
buttons: {
|
|
58
|
+
cancel: { label: 'Annuler', className: 'btn-secondary' },
|
|
59
|
+
ok: {
|
|
60
|
+
label: 'Envoyer',
|
|
61
|
+
className: 'btn-primary',
|
|
62
|
+
callback: function () {
|
|
63
|
+
const checks = dialog.find('.onekite-item-check:checked');
|
|
64
|
+
const ids = Array.from(checks).map(c => c.getAttribute('data-id'));
|
|
65
|
+
if (!ids.length) {
|
|
66
|
+
alerts.error('Sélectionne au moins un matériel.');
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
onSubmit({ start, end, itemIds: ids, days });
|
|
70
|
+
return false; // keep open; we'll close manually on success
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
},
|
|
40
74
|
});
|
|
41
|
-
}
|
|
42
75
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
76
|
+
function computeTotal() {
|
|
77
|
+
const selected = dialog.find('.onekite-item-check:checked');
|
|
78
|
+
let sum = 0;
|
|
79
|
+
selected.each(function () {
|
|
80
|
+
const id = this.getAttribute('data-id');
|
|
81
|
+
const row = dialog.find(`.onekite-item-row[data-id="${id}"]`);
|
|
82
|
+
sum += Number(row.attr('data-price') || 0);
|
|
83
|
+
});
|
|
84
|
+
const total = sum * days;
|
|
85
|
+
dialog.find('#onekite-total').text(fmtEuros(total));
|
|
48
86
|
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
async function openReservationDialog(selectionInfo, items) {
|
|
52
|
-
const start = selectionInfo.start;
|
|
53
|
-
const end = selectionInfo.end;
|
|
54
87
|
|
|
55
|
-
|
|
88
|
+
dialog.on('change', '.onekite-item-check', computeTotal);
|
|
56
89
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
<select class="form-select" id="onekite-item">${optionsHtml}</select>
|
|
65
|
-
</div>
|
|
66
|
-
`,
|
|
67
|
-
buttons: {
|
|
68
|
-
cancel: {
|
|
69
|
-
label: 'Annuler',
|
|
70
|
-
className: 'btn-secondary',
|
|
71
|
-
},
|
|
72
|
-
ok: {
|
|
73
|
-
label: 'Envoyer',
|
|
74
|
-
className: 'btn-primary',
|
|
75
|
-
callback: function () {
|
|
76
|
-
const el = document.getElementById('onekite-item');
|
|
77
|
-
const itemId = el ? el.value : '';
|
|
78
|
-
const item = items.find(i => String(i.id) === String(itemId));
|
|
79
|
-
resolve({ itemId, itemName: item ? item.name : itemId });
|
|
80
|
-
},
|
|
81
|
-
},
|
|
82
|
-
},
|
|
83
|
-
});
|
|
90
|
+
// Ctrl+click on row toggles
|
|
91
|
+
dialog.on('click', '.onekite-item-row', function (ev) {
|
|
92
|
+
if (ev.ctrlKey || ev.metaKey) {
|
|
93
|
+
const chk = this.querySelector('.onekite-item-check');
|
|
94
|
+
chk.checked = !chk.checked;
|
|
95
|
+
computeTotal();
|
|
96
|
+
}
|
|
84
97
|
});
|
|
85
|
-
}
|
|
86
98
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
return;
|
|
91
|
-
}
|
|
99
|
+
computeTotal();
|
|
100
|
+
return dialog;
|
|
101
|
+
}
|
|
92
102
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
}
|
|
103
|
+
async function initCalendar() {
|
|
104
|
+
const el = document.getElementById('onekite-calendar');
|
|
105
|
+
if (!el || !window.FullCalendar) return;
|
|
97
106
|
|
|
98
|
-
const
|
|
107
|
+
const catalog = await loadCatalog();
|
|
99
108
|
|
|
100
|
-
const calendar = new FullCalendar.Calendar(el, {
|
|
109
|
+
const calendar = new window.FullCalendar.Calendar(el, {
|
|
101
110
|
initialView: 'dayGridMonth',
|
|
102
|
-
locale: 'fr',
|
|
103
111
|
selectable: true,
|
|
104
112
|
selectMirror: true,
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
} catch (e) {
|
|
111
|
-
failureCallback(e);
|
|
112
|
-
}
|
|
113
|
-
},
|
|
114
|
-
select: async function (info) {
|
|
113
|
+
locale: 'fr',
|
|
114
|
+
height: 'auto',
|
|
115
|
+
displayEventTime: false,
|
|
116
|
+
dayMaxEvents: true,
|
|
117
|
+
events: async function (info, success, failure) {
|
|
115
118
|
try {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
+
const url = new URL('/api/v3/plugins/calendar-onekite/events', window.location.origin);
|
|
120
|
+
url.searchParams.set('start', info.startStr);
|
|
121
|
+
url.searchParams.set('end', info.endStr);
|
|
122
|
+
let r = await fetch(url.toString(), { credentials: 'same-origin' });
|
|
123
|
+
if (!r.ok) {
|
|
124
|
+
// fallback
|
|
125
|
+
const url2 = new URL('/api/plugins/calendar-onekite/events', window.location.origin);
|
|
126
|
+
url2.searchParams.set('start', info.startStr);
|
|
127
|
+
url2.searchParams.set('end', info.endStr);
|
|
128
|
+
r = await fetch(url2.toString(), { credentials: 'same-origin' });
|
|
119
129
|
}
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
130
|
+
const j = await r.json();
|
|
131
|
+
success(j);
|
|
132
|
+
} catch (e) { failure(e); }
|
|
133
|
+
},
|
|
134
|
+
select: function (selection) {
|
|
135
|
+
if (!catalog.length) {
|
|
136
|
+
alerts.error('Aucun matériel disponible (catalogue HelloAsso non chargé).');
|
|
137
|
+
calendar.unselect();
|
|
138
|
+
return;
|
|
127
139
|
}
|
|
140
|
+
|
|
141
|
+
const dialog = showReservationModal(selection, catalog, async function (payload) {
|
|
142
|
+
try {
|
|
143
|
+
let r = await fetch('/api/v3/plugins/calendar-onekite/reservations', {
|
|
144
|
+
method: 'POST',
|
|
145
|
+
headers: { 'Content-Type': 'application/json' },
|
|
146
|
+
credentials: 'same-origin',
|
|
147
|
+
body: JSON.stringify(payload),
|
|
148
|
+
});
|
|
149
|
+
if (!r.ok) {
|
|
150
|
+
r = await fetch('/api/plugins/calendar-onekite/reservations', {
|
|
151
|
+
method: 'POST',
|
|
152
|
+
headers: { 'Content-Type': 'application/json' },
|
|
153
|
+
credentials: 'same-origin',
|
|
154
|
+
body: JSON.stringify(payload),
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
const j = await r.json();
|
|
158
|
+
if (!j.ok) throw new Error(j?.status?.message || 'Erreur');
|
|
159
|
+
alerts.success('Demande envoyée.');
|
|
160
|
+
dialog.modal('hide');
|
|
161
|
+
calendar.unselect();
|
|
162
|
+
calendar.refetchEvents();
|
|
163
|
+
} catch (e) {
|
|
164
|
+
alerts.error(e.message || 'Erreur');
|
|
165
|
+
}
|
|
166
|
+
});
|
|
128
167
|
},
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
const
|
|
132
|
-
const
|
|
133
|
-
|
|
168
|
+
eventClick: function (info) {
|
|
169
|
+
const p = info.event.extendedProps || {};
|
|
170
|
+
const items = (p.items || []).map(it => `• ${it.name}`).join('\n');
|
|
171
|
+
const total = p.totalCents ? fmtEuros(p.totalCents) : '';
|
|
172
|
+
bootbox.alert({
|
|
173
|
+
title: info.event.title,
|
|
174
|
+
message: `<pre style="white-space: pre-wrap;">${items || ''}\n\nJours: ${p.days || ''}\nTotal estimé: ${total}</pre>`,
|
|
175
|
+
});
|
|
134
176
|
},
|
|
135
177
|
});
|
|
136
178
|
|
|
137
179
|
calendar.render();
|
|
138
180
|
}
|
|
139
181
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
182
|
+
function loadAssetsOnce() {
|
|
183
|
+
// Load FullCalendar from CDN only once
|
|
184
|
+
if (window.FullCalendar) return Promise.resolve();
|
|
185
|
+
const css = document.createElement('link');
|
|
186
|
+
css.rel = 'stylesheet';
|
|
187
|
+
css.href = 'https://cdn.jsdelivr.net/npm/fullcalendar@6.1.11/index.global.min.css';
|
|
188
|
+
document.head.appendChild(css);
|
|
189
|
+
|
|
190
|
+
const js1 = document.createElement('script');
|
|
191
|
+
js1.src = 'https://cdn.jsdelivr.net/npm/fullcalendar@6.1.11/index.global.min.js';
|
|
192
|
+
js1.async = true;
|
|
193
|
+
|
|
194
|
+
const js2 = document.createElement('script');
|
|
195
|
+
js2.src = 'https://cdn.jsdelivr.net/npm/@fullcalendar/core@6.1.11/locales-all.global.min.js';
|
|
196
|
+
js2.async = true;
|
|
197
|
+
|
|
198
|
+
return new Promise((resolve) => {
|
|
199
|
+
js1.onload = () => {
|
|
200
|
+
document.head.appendChild(js2);
|
|
201
|
+
js2.onload = () => resolve();
|
|
202
|
+
};
|
|
203
|
+
document.head.appendChild(js1);
|
|
204
|
+
});
|
|
148
205
|
}
|
|
149
206
|
|
|
150
|
-
|
|
151
|
-
|
|
207
|
+
function onAjaxifyEnd(data) {
|
|
208
|
+
if (ajaxify?.data?.template?.name !== 'calendar-onekite') return;
|
|
209
|
+
loadAssetsOnce().then(initCalendar);
|
|
152
210
|
}
|
|
153
211
|
|
|
154
|
-
|
|
155
|
-
// call once after current tick.
|
|
156
|
-
setTimeout(() => autoInit({ template: (ajaxify && ajaxify.data && ajaxify.data.template) || { name: '' } }), 0);
|
|
212
|
+
hooks.on('action:ajaxify.end', onAjaxifyEnd);
|
|
157
213
|
|
|
158
214
|
return {
|
|
159
|
-
init
|
|
215
|
+
init: function () {
|
|
216
|
+
// If landing directly
|
|
217
|
+
if (ajaxify?.data?.template?.name === 'calendar-onekite') {
|
|
218
|
+
loadAssetsOnce().then(initCalendar);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
160
221
|
};
|
|
161
222
|
});
|
|
223
|
+
|
|
224
|
+
require(['forum/calendar-onekite'], function (m) { m.init(); });
|
|
@@ -1,91 +1,88 @@
|
|
|
1
1
|
<!-- IMPORT admin/partials/settings/header.tpl -->
|
|
2
2
|
|
|
3
3
|
<div class="row">
|
|
4
|
-
<div class="col-lg-
|
|
5
|
-
<
|
|
6
|
-
|
|
7
|
-
<
|
|
8
|
-
<
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
<
|
|
4
|
+
<div class="col-lg-12">
|
|
5
|
+
<h2>Calendar OneKite</h2>
|
|
6
|
+
|
|
7
|
+
<ul class="nav nav-tabs mb-3" role="tablist">
|
|
8
|
+
<li class="nav-item"><a class="nav-link active" data-bs-toggle="tab" href="#onekite-settings" role="tab">Paramètres</a></li>
|
|
9
|
+
<li class="nav-item"><a class="nav-link" data-bs-toggle="tab" href="#onekite-pending" role="tab">Demandes en attente</a></li>
|
|
10
|
+
<li class="nav-item"><a class="nav-link" data-bs-toggle="tab" href="#onekite-debug" role="tab">Debug</a></li>
|
|
11
|
+
</ul>
|
|
12
|
+
|
|
13
|
+
<div class="tab-content">
|
|
14
|
+
<div class="tab-pane fade show active" id="onekite-settings" role="tabpanel">
|
|
15
|
+
<div class="card mb-3">
|
|
16
|
+
<div class="card-body">
|
|
17
|
+
<div class="mb-3">
|
|
18
|
+
<label class="form-label">Environnement HelloAsso</label>
|
|
19
|
+
<select class="form-select" id="helloassoEnv">
|
|
20
|
+
<option value="sandbox">Sandbox</option>
|
|
21
|
+
<option value="prod">Production</option>
|
|
22
|
+
</select>
|
|
23
|
+
</div>
|
|
24
|
+
|
|
25
|
+
<div class="row">
|
|
26
|
+
<div class="col-md-6 mb-3">
|
|
27
|
+
<label class="form-label">Client ID</label>
|
|
28
|
+
<input class="form-control" id="helloassoClientId" />
|
|
29
|
+
</div>
|
|
30
|
+
<div class="col-md-6 mb-3">
|
|
31
|
+
<label class="form-label">Client Secret</label>
|
|
32
|
+
<input class="form-control" id="helloassoClientSecret" placeholder="*** pour conserver" />
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
|
|
36
|
+
<div class="row">
|
|
37
|
+
<div class="col-md-4 mb-3">
|
|
38
|
+
<label class="form-label">Organization Slug</label>
|
|
39
|
+
<input class="form-control" id="helloassoOrganizationSlug" />
|
|
40
|
+
</div>
|
|
41
|
+
<div class="col-md-4 mb-3">
|
|
42
|
+
<label class="form-label">Form Type</label>
|
|
43
|
+
<input class="form-control" id="helloassoFormType" placeholder="shop" />
|
|
44
|
+
</div>
|
|
45
|
+
<div class="col-md-4 mb-3">
|
|
46
|
+
<label class="form-label">Form Slug</label>
|
|
47
|
+
<input class="form-control" id="helloassoFormSlug" />
|
|
48
|
+
</div>
|
|
49
|
+
</div>
|
|
50
|
+
|
|
51
|
+
<button class="btn btn-primary" id="onekite-save">Enregistrer</button>
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
|
|
55
|
+
<div class="card">
|
|
56
|
+
<div class="card-body">
|
|
57
|
+
<h5>Purge calendrier</h5>
|
|
58
|
+
<div class="input-group" style="max-width: 300px;">
|
|
59
|
+
<input class="form-control" id="onekite-purge-year" placeholder="YYYY" />
|
|
60
|
+
<button class="btn btn-danger" id="onekite-purge">Purger</button>
|
|
61
|
+
</div>
|
|
62
|
+
<div class="text-muted small mt-2">Supprime toutes les réservations dont le début est dans l'année donnée.</div>
|
|
63
|
+
</div>
|
|
64
|
+
</div>
|
|
16
65
|
</div>
|
|
17
66
|
|
|
18
|
-
<div class="
|
|
19
|
-
<
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
<div class="mb-3">
|
|
26
|
-
<label class="form-label">Environnement</label>
|
|
27
|
-
<select class="form-select" name="helloassoEnv">
|
|
28
|
-
<option value="prod">Production</option>
|
|
29
|
-
<option value="sandbox">Sandbox</option>
|
|
30
|
-
</select>
|
|
67
|
+
<div class="tab-pane fade" id="onekite-pending" role="tabpanel">
|
|
68
|
+
<div class="card">
|
|
69
|
+
<div class="card-body">
|
|
70
|
+
<div id="onekite-pending-list" class="small text-muted">Chargement…</div>
|
|
71
|
+
</div>
|
|
72
|
+
</div>
|
|
31
73
|
</div>
|
|
32
74
|
|
|
33
|
-
<div class="
|
|
34
|
-
<
|
|
35
|
-
|
|
75
|
+
<div class="tab-pane fade" id="onekite-debug" role="tabpanel">
|
|
76
|
+
<div class="card">
|
|
77
|
+
<div class="card-body">
|
|
78
|
+
<button class="btn btn-secondary" id="onekite-debug-run">Tester le chargement du matériel (HelloAsso)</button>
|
|
79
|
+
<pre class="mt-3" id="onekite-debug-output" style="white-space: pre-wrap;"></pre>
|
|
80
|
+
</div>
|
|
81
|
+
</div>
|
|
36
82
|
</div>
|
|
37
83
|
|
|
38
|
-
<div class="mb-3">
|
|
39
|
-
<label class="form-label">Client Secret</label>
|
|
40
|
-
<input class="form-control" name="helloassoClientSecret" type="password">
|
|
41
|
-
</div>
|
|
42
|
-
|
|
43
|
-
<div class="mb-3">
|
|
44
|
-
<label class="form-label">Organization Slug</label>
|
|
45
|
-
<input class="form-control" name="helloassoOrganizationSlug">
|
|
46
|
-
</div>
|
|
47
|
-
|
|
48
|
-
<div class="mb-3">
|
|
49
|
-
<label class="form-label">Form Type</label>
|
|
50
|
-
<input class="form-control" name="helloassoFormType" placeholder="membership | crowdfunding | donation | ticketing | ...">
|
|
51
|
-
</div>
|
|
52
|
-
|
|
53
|
-
<div class="mb-3">
|
|
54
|
-
<label class="form-label">Form Slug</label>
|
|
55
|
-
<input class="form-control" name="helloassoFormSlug">
|
|
56
|
-
</div>
|
|
57
|
-
|
|
58
|
-
<button type="button" class="btn btn-primary" id="onekite-save">Enregistrer</button>
|
|
59
|
-
</form>
|
|
60
|
-
|
|
61
|
-
<hr class="my-4" />
|
|
62
|
-
|
|
63
|
-
<h4>Demandes en attente</h4>
|
|
64
|
-
<div id="onekite-pending" class="list-group"></div>
|
|
65
|
-
|
|
66
|
-
<hr class="my-4" />
|
|
67
|
-
|
|
68
|
-
<h4>Purge</h4>
|
|
69
|
-
<div class="d-flex gap-2 align-items-center">
|
|
70
|
-
<input class="form-control" style="max-width: 160px;" id="onekite-purge-year" placeholder="YYYY">
|
|
71
|
-
<button type="button" class="btn btn-outline-danger" id="onekite-purge">Purger</button>
|
|
72
84
|
</div>
|
|
73
|
-
|
|
74
|
-
<hr class="my-4" />
|
|
75
|
-
|
|
76
|
-
<h4>Debug HelloAsso</h4>
|
|
77
|
-
<p class="text-muted">Teste la récupération du token et la liste du matériel (items).</p>
|
|
78
|
-
<button type="button" class="btn btn-secondary" id="onekite-debug-run">Tester le chargement du matériel</button>
|
|
79
|
-
<pre id="onekite-debug-output" class="mt-3 p-3 bg-light" style="max-height: 360px; overflow: auto;"></pre>
|
|
80
85
|
</div>
|
|
81
86
|
</div>
|
|
82
87
|
|
|
83
|
-
<script>
|
|
84
|
-
require(['admin/plugins/calendar-onekite'], function (mod) {
|
|
85
|
-
if (mod && mod.init) {
|
|
86
|
-
mod.init();
|
|
87
|
-
}
|
|
88
|
-
});
|
|
89
|
-
</script>
|
|
90
|
-
|
|
91
88
|
<!-- IMPORT admin/partials/settings/footer.tpl -->
|
|
@@ -1,17 +1,7 @@
|
|
|
1
|
-
<div class="
|
|
2
|
-
<div class="
|
|
3
|
-
<
|
|
4
|
-
|
|
5
|
-
<div id="onekite-calendar" style="margin-top: 1rem;"></div>
|
|
6
|
-
</div>
|
|
1
|
+
<div class="calendar-onekite-page">
|
|
2
|
+
<div class="d-flex align-items-center justify-content-between mb-3">
|
|
3
|
+
<h2 class="mb-0">Calendrier</h2>
|
|
4
|
+
<div class="text-muted small">Réservations (jours uniquement)</div>
|
|
7
5
|
</div>
|
|
6
|
+
<div id="onekite-calendar"></div>
|
|
8
7
|
</div>
|
|
9
|
-
|
|
10
|
-
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/fullcalendar@6.1.11/index.global.min.css" />
|
|
11
|
-
<script src="https://cdn.jsdelivr.net/npm/fullcalendar@6.1.11/index.global.min.js"></script>
|
|
12
|
-
<script src="https://cdn.jsdelivr.net/npm/@fullcalendar/core@6.1.11/locales-all.global.min.js"></script>
|
|
13
|
-
|
|
14
|
-
<!--
|
|
15
|
-
No inline require() here.
|
|
16
|
-
The plugin's forum script auto-initialises on the calendar page via ajaxify.
|
|
17
|
-
-->
|