nodebb-plugin-onekite-calendar 2.0.68 → 2.0.70
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/api.js +67 -1
- package/lib/discord.js +4 -4
- package/lib/shared.js +1 -0
- package/lib/widgets.js +1 -2
- package/library.js +2 -0
- package/package.json +1 -1
- package/plugin.json +1 -1
- package/public/client.js +116 -11
package/lib/api.js
CHANGED
|
@@ -647,6 +647,7 @@ api.getSpecialEventDetails = async function (req, res) {
|
|
|
647
647
|
const settings = await getSettings();
|
|
648
648
|
const canMod = uid ? await canValidate(uid, settings) : false;
|
|
649
649
|
const canSpecialDelete = uid ? await canDeleteSpecial(uid, settings) : false;
|
|
650
|
+
const canSpecialCreate = uid ? await canCreateSpecial(uid, settings) : false;
|
|
650
651
|
|
|
651
652
|
const eid = String(req.params.eid || '').trim();
|
|
652
653
|
if (!eid) return res.status(400).json({ error: 'missing-eid' });
|
|
@@ -680,6 +681,7 @@ api.getSpecialEventDetails = async function (req, res) {
|
|
|
680
681
|
notes: ev.notes || '',
|
|
681
682
|
participants,
|
|
682
683
|
participantsUsernames: await usernamesByUids(participants),
|
|
684
|
+
canEditSpecial: canSpecialCreate,
|
|
683
685
|
canDeleteSpecial: canSpecialDelete,
|
|
684
686
|
canJoin: uid ? await canJoinSpecial(uid, settings) : false,
|
|
685
687
|
isParticipant: uid ? participants.includes(String(uid)) : false,
|
|
@@ -871,6 +873,37 @@ api.deleteSpecialEvent = async function (req, res) {
|
|
|
871
873
|
res.json({ ok: true });
|
|
872
874
|
};
|
|
873
875
|
|
|
876
|
+
api.updateSpecialEvent = async function (req, res) {
|
|
877
|
+
const settings = await getSettings();
|
|
878
|
+
if (!req.uid) return res.status(401).json({ error: 'not-logged-in' });
|
|
879
|
+
const ok = await canCreateSpecial(req.uid, settings);
|
|
880
|
+
if (!ok) return res.status(403).json({ error: 'not-allowed' });
|
|
881
|
+
|
|
882
|
+
const eid = String(req.params.eid || '').replace(/^special:/, '').trim();
|
|
883
|
+
if (!eid) return res.status(400).json({ error: 'bad-id' });
|
|
884
|
+
|
|
885
|
+
const ev = await dbLayer.getSpecialEvent(eid);
|
|
886
|
+
if (!ev) return res.status(404).json({ error: 'not-found' });
|
|
887
|
+
|
|
888
|
+
const startTs = toTs(req.body && req.body.start);
|
|
889
|
+
const endTs = toTs(req.body && req.body.end);
|
|
890
|
+
if (!Number.isFinite(startTs) || !Number.isFinite(endTs) || !(startTs < endTs)) {
|
|
891
|
+
return res.status(400).json({ error: 'bad-dates' });
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
ev.title = String((req.body && req.body.title) || '').trim() || ev.title || 'Évènement';
|
|
895
|
+
ev.start = String(startTs);
|
|
896
|
+
ev.end = String(endTs);
|
|
897
|
+
ev.address = String((req.body && req.body.address) || '').trim();
|
|
898
|
+
ev.lat = String((req.body && req.body.lat) || '').trim();
|
|
899
|
+
ev.lon = String((req.body && req.body.lon) || '').trim();
|
|
900
|
+
ev.notes = String((req.body && req.body.notes) || '').trim();
|
|
901
|
+
|
|
902
|
+
await dbLayer.saveSpecialEvent(ev);
|
|
903
|
+
realtime.emitCalendarUpdated({ kind: 'specialEvent', action: 'updated', eid });
|
|
904
|
+
return res.json({ ok: true });
|
|
905
|
+
};
|
|
906
|
+
|
|
874
907
|
/**
|
|
875
908
|
* Get detailed information about an outing (prévision de sortie).
|
|
876
909
|
*
|
|
@@ -911,6 +944,7 @@ api.getOutingDetails = async function (req, res) {
|
|
|
911
944
|
});
|
|
912
945
|
|
|
913
946
|
const participants = normalizeUidList(o.participants);
|
|
947
|
+
const canEditOuting = uid ? await canRequest(uid, settings, Date.now()) : false;
|
|
914
948
|
const out = {
|
|
915
949
|
oid: o.oid,
|
|
916
950
|
title: o.title || '',
|
|
@@ -922,7 +956,8 @@ api.getOutingDetails = async function (req, res) {
|
|
|
922
956
|
notes: o.notes || '',
|
|
923
957
|
participants,
|
|
924
958
|
participantsUsernames: await usernamesByUids(participants),
|
|
925
|
-
|
|
959
|
+
canEditOuting,
|
|
960
|
+
canJoin: canEditOuting,
|
|
926
961
|
isParticipant: uid ? participants.includes(String(uid)) : false,
|
|
927
962
|
canDeleteOuting: canMod || (uid && String(uid) === String(o.uid)),
|
|
928
963
|
icsUrl: links.icsUrl,
|
|
@@ -1090,6 +1125,37 @@ api.deleteOuting = async function (req, res) {
|
|
|
1090
1125
|
return res.json({ ok: true });
|
|
1091
1126
|
};
|
|
1092
1127
|
|
|
1128
|
+
api.updateOuting = async function (req, res) {
|
|
1129
|
+
const settings = await getSettings();
|
|
1130
|
+
if (!req.uid) return res.status(401).json({ error: 'not-logged-in' });
|
|
1131
|
+
const ok = await canRequest(req.uid, settings, Date.now());
|
|
1132
|
+
if (!ok) return res.status(403).json({ error: 'not-allowed' });
|
|
1133
|
+
|
|
1134
|
+
const oid = String(req.params.oid || '').replace(/^outing:/, '').trim();
|
|
1135
|
+
if (!oid) return res.status(400).json({ error: 'bad-id' });
|
|
1136
|
+
|
|
1137
|
+
const o = await dbLayer.getOuting(oid);
|
|
1138
|
+
if (!o) return res.status(404).json({ error: 'not-found' });
|
|
1139
|
+
|
|
1140
|
+
const startTs = toTs(req.body && req.body.start);
|
|
1141
|
+
const endTs = toTs(req.body && req.body.end);
|
|
1142
|
+
if (!Number.isFinite(startTs) || !Number.isFinite(endTs) || !(startTs < endTs)) {
|
|
1143
|
+
return res.status(400).json({ error: 'bad-dates' });
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
o.title = String((req.body && req.body.title) || '').trim() || o.title || 'Sortie';
|
|
1147
|
+
o.start = String(startTs);
|
|
1148
|
+
o.end = String(endTs);
|
|
1149
|
+
o.address = String((req.body && req.body.address) || '').trim();
|
|
1150
|
+
o.lat = String((req.body && req.body.lat) || '').trim();
|
|
1151
|
+
o.lon = String((req.body && req.body.lon) || '').trim();
|
|
1152
|
+
o.notes = String((req.body && req.body.notes) || '').trim();
|
|
1153
|
+
|
|
1154
|
+
await dbLayer.saveOuting(o);
|
|
1155
|
+
realtime.emitCalendarUpdated({ kind: 'outing', action: 'updated', oid });
|
|
1156
|
+
return res.json({ ok: true });
|
|
1157
|
+
};
|
|
1158
|
+
|
|
1093
1159
|
api.getItems = async function (req, res) {
|
|
1094
1160
|
const settings = await getSettings();
|
|
1095
1161
|
|
package/lib/discord.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const https = require('https');
|
|
4
4
|
const { URL } = require('url');
|
|
5
|
-
const { formatFRShort } = require('./shared');
|
|
5
|
+
const { formatFRShort, forumBaseUrl } = require('./shared');
|
|
6
6
|
|
|
7
7
|
function isEnabled(v, defaultValue) {
|
|
8
8
|
if (v === undefined || v === null || v === '') return defaultValue !== false;
|
|
@@ -53,7 +53,7 @@ function postWebhook(webhookUrl, payload) {
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
function buildReservationMessage(kind, reservation) {
|
|
56
|
-
const calUrl = '
|
|
56
|
+
const calUrl = forumBaseUrl() + '/calendar';
|
|
57
57
|
const username = reservation && reservation.username ? String(reservation.username) : '';
|
|
58
58
|
const items = (reservation && Array.isArray(reservation.itemNames) && reservation.itemNames.length)
|
|
59
59
|
? reservation.itemNames.map(String)
|
|
@@ -87,7 +87,7 @@ function buildWebhookPayload(kind, reservation) {
|
|
|
87
87
|
? 'Onekite • Paiement'
|
|
88
88
|
: (kind === 'cancelled' ? 'Onekite • Annulation' : 'Onekite • Réservation');
|
|
89
89
|
|
|
90
|
-
const calUrl = '
|
|
90
|
+
const calUrl = forumBaseUrl() + '/calendar';
|
|
91
91
|
const username = reservation && reservation.username ? String(reservation.username) : '';
|
|
92
92
|
const items = (reservation && Array.isArray(reservation.itemNames) && reservation.itemNames.length)
|
|
93
93
|
? reservation.itemNames.map(String)
|
|
@@ -186,7 +186,7 @@ async function notifyReservationCancelled(settings, reservation) {
|
|
|
186
186
|
|
|
187
187
|
function buildSimpleCalendarPayload(kind, label, entity, opts) {
|
|
188
188
|
const options = opts || {};
|
|
189
|
-
const calUrl = options.calUrl || '
|
|
189
|
+
const calUrl = options.calUrl || forumBaseUrl() + '/calendar';
|
|
190
190
|
const webhookUsername = options.webhookUsername || `Onekite • ${label}`;
|
|
191
191
|
|
|
192
192
|
const title = kind === 'deleted' ? '❌ ' + label + ' annulé(e)' : label + ' créé(e)';
|
package/lib/shared.js
CHANGED
package/lib/widgets.js
CHANGED
package/library.js
CHANGED
|
@@ -82,6 +82,7 @@ Plugin.init = async function (params) {
|
|
|
82
82
|
router.post('/api/v3/plugins/calendar-onekite/special-events', ...publicExpose, api.createSpecialEvent);
|
|
83
83
|
router.get('/api/v3/plugins/calendar-onekite/special-events/:eid', ...publicExpose, api.getSpecialEventDetails);
|
|
84
84
|
router.delete('/api/v3/plugins/calendar-onekite/special-events/:eid', ...publicExpose, api.deleteSpecialEvent);
|
|
85
|
+
router.put('/api/v3/plugins/calendar-onekite/special-events/:eid', ...publicExpose, api.updateSpecialEvent);
|
|
85
86
|
// Participants (self-join) for special events
|
|
86
87
|
router.post('/api/v3/plugins/calendar-onekite/special-events/:eid/participants', ...publicExpose, api.joinSpecialEvent);
|
|
87
88
|
router.delete('/api/v3/plugins/calendar-onekite/special-events/:eid/participants', ...publicExpose, api.leaveSpecialEvent);
|
|
@@ -90,6 +91,7 @@ Plugin.init = async function (params) {
|
|
|
90
91
|
router.post('/api/v3/plugins/calendar-onekite/outings', ...publicExpose, api.createOuting);
|
|
91
92
|
router.get('/api/v3/plugins/calendar-onekite/outings/:oid', ...publicExpose, api.getOutingDetails);
|
|
92
93
|
router.delete('/api/v3/plugins/calendar-onekite/outings/:oid', ...publicExpose, api.deleteOuting);
|
|
94
|
+
router.put('/api/v3/plugins/calendar-onekite/outings/:oid', ...publicExpose, api.updateOuting);
|
|
93
95
|
// Participants (self-join) for outings
|
|
94
96
|
router.post('/api/v3/plugins/calendar-onekite/outings/:oid/participants', ...publicExpose, api.joinOuting);
|
|
95
97
|
router.delete('/api/v3/plugins/calendar-onekite/outings/:oid/participants', ...publicExpose, api.leaveOuting);
|
package/package.json
CHANGED
package/plugin.json
CHANGED
package/public/client.js
CHANGED
|
@@ -239,8 +239,32 @@ define('forum/calendar-onekite', ['alerts', 'bootbox', 'hooks'], function (alert
|
|
|
239
239
|
return opts.join('');
|
|
240
240
|
}
|
|
241
241
|
|
|
242
|
+
function buildInitialValuesFromEvent(details) {
|
|
243
|
+
function toLocal(ts) {
|
|
244
|
+
const d = new Date(parseInt(ts, 10));
|
|
245
|
+
return {
|
|
246
|
+
ymd: `${d.getFullYear()}-${pad2(d.getMonth() + 1)}-${pad2(d.getDate())}`,
|
|
247
|
+
time: `${pad2(d.getHours())}:${pad2(d.getMinutes())}`,
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
const s = toLocal(details.start);
|
|
251
|
+
const e = toLocal(details.end);
|
|
252
|
+
return {
|
|
253
|
+
title: details.title || '',
|
|
254
|
+
startYmd: s.ymd,
|
|
255
|
+
startTime: s.time,
|
|
256
|
+
endYmd: e.ymd,
|
|
257
|
+
endTime: e.time,
|
|
258
|
+
address: details.address || details.pickupAddress || '',
|
|
259
|
+
lat: details.lat || details.pickupLat || '',
|
|
260
|
+
lon: details.lon || details.pickupLon || '',
|
|
261
|
+
notes: details.notes || '',
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
|
|
242
265
|
async function openSpecialEventDialog(selectionInfo, opts) {
|
|
243
266
|
const kind = (opts && opts.kind) ? String(opts.kind) : 'special';
|
|
267
|
+
const iv = opts && opts.initialValues;
|
|
244
268
|
const start = selectionInfo.start;
|
|
245
269
|
// FullCalendar can omit `end` for certain interactions. Also, for all-day
|
|
246
270
|
// selections, `end` is exclusive (next day at 00:00). We normalise below
|
|
@@ -289,12 +313,20 @@ define('forum/calendar-onekite', ['alerts', 'bootbox', 'hooks'], function (alert
|
|
|
289
313
|
}
|
|
290
314
|
}
|
|
291
315
|
|
|
316
|
+
// Edit mode: override computed dates with existing event values.
|
|
317
|
+
if (iv) {
|
|
318
|
+
if (iv.startYmd && iv.startTime) seStart = new Date(`${iv.startYmd}T${iv.startTime}`);
|
|
319
|
+
if (iv.endYmd && iv.endTime) seEnd = new Date(`${iv.endYmd}T${iv.endTime}`);
|
|
320
|
+
}
|
|
321
|
+
|
|
292
322
|
const seStartTime = timeString(seStart);
|
|
293
323
|
const seEndTime = timeString(seEnd);
|
|
294
|
-
// Same UX for all types: an example placeholder, but never pre-fill the value.
|
|
295
|
-
// If left empty, the server can still apply its own default label.
|
|
296
324
|
const defaultTitlePlaceholder = 'Ex: ...';
|
|
297
|
-
const defaultTitleValue = '';
|
|
325
|
+
const defaultTitleValue = iv ? (iv.title || '') : '';
|
|
326
|
+
const ivAddress = iv ? (iv.address || '') : '';
|
|
327
|
+
const ivLat = iv ? (iv.lat || '') : '';
|
|
328
|
+
const ivLon = iv ? (iv.lon || '') : '';
|
|
329
|
+
const ivNotes = iv ? (iv.notes || '') : '';
|
|
298
330
|
|
|
299
331
|
const html = `
|
|
300
332
|
<div class="mb-3">
|
|
@@ -328,16 +360,16 @@ define('forum/calendar-onekite', ['alerts', 'bootbox', 'hooks'], function (alert
|
|
|
328
360
|
<div class="mt-3">
|
|
329
361
|
<label class="form-label">Adresse</label>
|
|
330
362
|
<div class="input-group">
|
|
331
|
-
<input type="text" class="form-control" id="onekite-se-address" placeholder="Adresse complète" />
|
|
363
|
+
<input type="text" class="form-control" id="onekite-se-address" placeholder="Adresse complète" value="${escapeHtml(ivAddress)}" />
|
|
332
364
|
<button class="btn btn-outline-secondary" type="button" id="onekite-se-geocode">Rechercher</button>
|
|
333
365
|
</div>
|
|
334
366
|
<div id="onekite-se-map" style="height:220px; border:1px solid #ddd; border-radius:6px; margin-top:0.5rem;"></div>
|
|
335
|
-
<input type="hidden" id="onekite-se-lat" />
|
|
336
|
-
<input type="hidden" id="onekite-se-lon" />
|
|
367
|
+
<input type="hidden" id="onekite-se-lat" value="${escapeHtml(ivLat)}" />
|
|
368
|
+
<input type="hidden" id="onekite-se-lon" value="${escapeHtml(ivLon)}" />
|
|
337
369
|
</div>
|
|
338
370
|
<div class="mt-3">
|
|
339
371
|
<label class="form-label">Notes (facultatif)</label>
|
|
340
|
-
<textarea class="form-control" id="onekite-se-notes" rows="3" placeholder="..."
|
|
372
|
+
<textarea class="form-control" id="onekite-se-notes" rows="3" placeholder="...">${escapeHtml(ivNotes)}</textarea>
|
|
341
373
|
</div>
|
|
342
374
|
${(kind !== 'outing' && opts && opts.withContent) ? `
|
|
343
375
|
<div class="mt-3">
|
|
@@ -349,8 +381,11 @@ define('forum/calendar-onekite', ['alerts', 'bootbox', 'hooks'], function (alert
|
|
|
349
381
|
|
|
350
382
|
return await new Promise((resolve) => {
|
|
351
383
|
let resolved = false;
|
|
384
|
+
const isEditMode = !!iv;
|
|
352
385
|
const dialog = bootbox.dialog({
|
|
353
|
-
title:
|
|
386
|
+
title: isEditMode
|
|
387
|
+
? (kind === 'outing' ? 'Modifier la sortie' : "Modifier l'évènement")
|
|
388
|
+
: (kind === 'outing' ? 'Créer une prévision de sortie' : 'Créer un évènement'),
|
|
354
389
|
message: html,
|
|
355
390
|
buttons: {
|
|
356
391
|
cancel: {
|
|
@@ -362,7 +397,7 @@ define('forum/calendar-onekite', ['alerts', 'bootbox', 'hooks'], function (alert
|
|
|
362
397
|
},
|
|
363
398
|
},
|
|
364
399
|
ok: {
|
|
365
|
-
label: 'Créer',
|
|
400
|
+
label: isEditMode ? 'Enregistrer' : 'Créer',
|
|
366
401
|
className: 'btn-primary',
|
|
367
402
|
callback: () => {
|
|
368
403
|
const title = (document.getElementById('onekite-se-title')?.value || '').trim();
|
|
@@ -446,6 +481,14 @@ define('forum/calendar-onekite', ['alerts', 'bootbox', 'hooks'], function (alert
|
|
|
446
481
|
document.getElementById('onekite-se-lon').value = String(lon);
|
|
447
482
|
map.setView([lat, lon], 14);
|
|
448
483
|
}
|
|
484
|
+
// Pre-fill marker if editing an event with existing coordinates.
|
|
485
|
+
if (ivLat && ivLon) {
|
|
486
|
+
const initLat = Number(ivLat);
|
|
487
|
+
const initLon = Number(ivLon);
|
|
488
|
+
if (Number.isFinite(initLat) && Number.isFinite(initLon)) {
|
|
489
|
+
setMarker(initLat, initLon);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
449
492
|
const geocodeBtn = document.getElementById('onekite-se-geocode');
|
|
450
493
|
const addrInput = document.getElementById('onekite-se-address');
|
|
451
494
|
try {
|
|
@@ -1223,8 +1266,14 @@ function toDatetimeLocalValue(date) {
|
|
|
1223
1266
|
}
|
|
1224
1267
|
|
|
1225
1268
|
if (typeof FullCalendar === 'undefined') {
|
|
1226
|
-
|
|
1227
|
-
|
|
1269
|
+
const loaded = await new Promise((resolve) => {
|
|
1270
|
+
const start = Date.now();
|
|
1271
|
+
const iv = setInterval(() => {
|
|
1272
|
+
if (typeof FullCalendar !== 'undefined') { clearInterval(iv); resolve(true); }
|
|
1273
|
+
else if (Date.now() - start > 5000) { clearInterval(iv); resolve(false); }
|
|
1274
|
+
}, 50);
|
|
1275
|
+
});
|
|
1276
|
+
if (!loaded) { showAlert('error', 'FullCalendar non chargé'); return; }
|
|
1228
1277
|
}
|
|
1229
1278
|
|
|
1230
1279
|
const items = await loadItems();
|
|
@@ -1763,11 +1812,39 @@ function toDatetimeLocalValue(date) {
|
|
|
1763
1812
|
${notes ? `<div class="mb-2"><strong>Notes</strong><br>${escapeHtml(notes).replace(/\n/g,'<br>')}</div>` : ''}
|
|
1764
1813
|
`;
|
|
1765
1814
|
const canDel = !!(p.canDeleteSpecial || canDeleteSpecial);
|
|
1815
|
+
const canEdit = !!p.canEditSpecial;
|
|
1766
1816
|
const dlg = bootbox.dialog({
|
|
1767
1817
|
title: 'Évènement',
|
|
1768
1818
|
message: html,
|
|
1769
1819
|
buttons: {
|
|
1770
1820
|
close: { label: 'Fermer', className: 'btn-secondary' },
|
|
1821
|
+
...(canEdit ? {
|
|
1822
|
+
edit: {
|
|
1823
|
+
label: 'Éditer',
|
|
1824
|
+
className: 'btn-default',
|
|
1825
|
+
callback: () => {
|
|
1826
|
+
(async () => {
|
|
1827
|
+
try { dlg.modal('hide'); } catch (e) { try { bootbox.hideAll(); } catch (e2) {} }
|
|
1828
|
+
const eid = String(p.eid || ev.id).replace(/^special:/, '');
|
|
1829
|
+
const initialValues = buildInitialValuesFromEvent(p);
|
|
1830
|
+
const payload = await openSpecialEventDialog({}, { kind: 'special', initialValues });
|
|
1831
|
+
if (!payload) return;
|
|
1832
|
+
try {
|
|
1833
|
+
await fetchJson(`/api/v3/plugins/calendar-onekite/special-events/${encodeURIComponent(eid)}`, {
|
|
1834
|
+
method: 'PUT',
|
|
1835
|
+
body: JSON.stringify(payload),
|
|
1836
|
+
});
|
|
1837
|
+
showAlert('success', 'Évènement mis à jour.');
|
|
1838
|
+
invalidateEventsCache();
|
|
1839
|
+
scheduleRefetch(calendar);
|
|
1840
|
+
} catch (e) {
|
|
1841
|
+
showAlert('error', 'Mise à jour impossible.');
|
|
1842
|
+
}
|
|
1843
|
+
})();
|
|
1844
|
+
return false;
|
|
1845
|
+
},
|
|
1846
|
+
},
|
|
1847
|
+
} : {}),
|
|
1771
1848
|
...(canDel ? {
|
|
1772
1849
|
del: {
|
|
1773
1850
|
label: 'Supprimer',
|
|
@@ -1913,11 +1990,39 @@ function toDatetimeLocalValue(date) {
|
|
|
1913
1990
|
${notes ? `<div class="mb-2"><strong>Notes</strong><br>${escapeHtml(notes).replace(/\n/g,'<br>')}</div>` : ''}
|
|
1914
1991
|
`;
|
|
1915
1992
|
const canDel = !!(p.canDeleteOuting);
|
|
1993
|
+
const canEditOuting = !!p.canEditOuting;
|
|
1916
1994
|
const dlg = bootbox.dialog({
|
|
1917
1995
|
title: 'Sortie',
|
|
1918
1996
|
message: html,
|
|
1919
1997
|
buttons: {
|
|
1920
1998
|
close: { label: 'Fermer', className: 'btn-secondary' },
|
|
1999
|
+
...(canEditOuting ? {
|
|
2000
|
+
edit: {
|
|
2001
|
+
label: 'Éditer',
|
|
2002
|
+
className: 'btn-default',
|
|
2003
|
+
callback: () => {
|
|
2004
|
+
(async () => {
|
|
2005
|
+
try { dlg.modal('hide'); } catch (e) { try { bootbox.hideAll(); } catch (e2) {} }
|
|
2006
|
+
const oid = String(p.oid || ev.id).replace(/^outing:/, '');
|
|
2007
|
+
const initialValues = buildInitialValuesFromEvent(p);
|
|
2008
|
+
const payload = await openSpecialEventDialog({}, { kind: 'outing', initialValues });
|
|
2009
|
+
if (!payload) return;
|
|
2010
|
+
try {
|
|
2011
|
+
await fetchJson(`/api/v3/plugins/calendar-onekite/outings/${encodeURIComponent(oid)}`, {
|
|
2012
|
+
method: 'PUT',
|
|
2013
|
+
body: JSON.stringify(payload),
|
|
2014
|
+
});
|
|
2015
|
+
showAlert('success', 'Sortie mise à jour.');
|
|
2016
|
+
invalidateEventsCache();
|
|
2017
|
+
scheduleRefetch(calendar);
|
|
2018
|
+
} catch (e) {
|
|
2019
|
+
showAlert('error', 'Mise à jour impossible.');
|
|
2020
|
+
}
|
|
2021
|
+
})();
|
|
2022
|
+
return false;
|
|
2023
|
+
},
|
|
2024
|
+
},
|
|
2025
|
+
} : {}),
|
|
1921
2026
|
...(canDel ? {
|
|
1922
2027
|
del: {
|
|
1923
2028
|
label: 'Supprimer',
|