nodebb-plugin-calendar-onekite 11.2.6 → 11.2.7
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 +6 -1
- package/lib/api.js +2 -0
- package/package.json +1 -1
- package/plugin.json +1 -1
- package/public/admin.js +58 -4
- package/public/client.js +43 -11
- package/templates/admin/plugins/calendar-onekite.tpl +1 -1
- package/templates/emails/calendar-onekite_refused.tpl +5 -1
package/lib/admin.js
CHANGED
|
@@ -228,6 +228,8 @@ admin.refuseReservation = async function (req, res) {
|
|
|
228
228
|
if (!r) return res.status(404).json({ error: 'not-found' });
|
|
229
229
|
|
|
230
230
|
r.status = 'refused';
|
|
231
|
+
r.refusedAt = Date.now();
|
|
232
|
+
r.refusedReason = String((req.body && (req.body.reason || req.body.refusedReason || req.body.refuseReason)) || '').trim();
|
|
231
233
|
await dbLayer.saveReservation(r);
|
|
232
234
|
|
|
233
235
|
try {
|
|
@@ -236,9 +238,12 @@ admin.refuseReservation = async function (req, res) {
|
|
|
236
238
|
await sendEmail('calendar-onekite_refused', requester.email, 'Location matériel - Réservation refusée', {
|
|
237
239
|
uid: parseInt(r.uid, 10),
|
|
238
240
|
username: requester.username,
|
|
239
|
-
itemName: r.itemName,
|
|
241
|
+
itemName: (Array.isArray(r.itemNames) ? r.itemNames.join(', ') : (r.itemName || '')),
|
|
242
|
+
itemNames: (Array.isArray(r.itemNames) ? r.itemNames : (r.itemName ? [r.itemName] : [])),
|
|
243
|
+
dateRange: `Du ${formatFR(r.start)} au ${formatFR(r.end)}`,
|
|
240
244
|
start: formatFR(r.start),
|
|
241
245
|
end: formatFR(r.end),
|
|
246
|
+
refusedReason: r.refusedReason || '',
|
|
242
247
|
});
|
|
243
248
|
}
|
|
244
249
|
} catch (e) {}
|
package/lib/api.js
CHANGED
|
@@ -643,6 +643,7 @@ api.refuseReservation = async function (req, res) {
|
|
|
643
643
|
|
|
644
644
|
r.status = 'refused';
|
|
645
645
|
r.refusedAt = Date.now();
|
|
646
|
+
r.refusedReason = String((req.body && (req.body.reason || req.body.refusedReason || req.body.refuseReason)) || '').trim();
|
|
646
647
|
await dbLayer.saveReservation(r);
|
|
647
648
|
|
|
648
649
|
const requester = await user.getUserFields(r.uid, ['username', 'email']);
|
|
@@ -654,6 +655,7 @@ api.refuseReservation = async function (req, res) {
|
|
|
654
655
|
dateRange: `Du ${formatFR(r.start)} au ${formatFR(r.end)}`,
|
|
655
656
|
start: formatFR(r.start),
|
|
656
657
|
end: formatFR(r.end),
|
|
658
|
+
refusedReason: r.refusedReason || '',
|
|
657
659
|
});
|
|
658
660
|
}
|
|
659
661
|
|
package/package.json
CHANGED
package/plugin.json
CHANGED
package/public/admin.js
CHANGED
|
@@ -408,8 +408,11 @@ define('admin/plugins/calendar-onekite', ['alerts', 'bootbox'], function (alerts
|
|
|
408
408
|
});
|
|
409
409
|
}
|
|
410
410
|
|
|
411
|
-
async function refuse(rid) {
|
|
412
|
-
return await fetchJson(`/api/v3/admin/plugins/calendar-onekite/reservations/${rid}/refuse`, {
|
|
411
|
+
async function refuse(rid, payload) {
|
|
412
|
+
return await fetchJson(`/api/v3/admin/plugins/calendar-onekite/reservations/${rid}/refuse`, {
|
|
413
|
+
method: 'PUT',
|
|
414
|
+
body: JSON.stringify(payload || {}),
|
|
415
|
+
});
|
|
413
416
|
}
|
|
414
417
|
|
|
415
418
|
async function purge(year) {
|
|
@@ -453,6 +456,23 @@ define('admin/plugins/calendar-onekite', ['alerts', 'bootbox'], function (alerts
|
|
|
453
456
|
const form = document.getElementById('onekite-settings-form');
|
|
454
457
|
if (!form) return;
|
|
455
458
|
|
|
459
|
+
// Make the HelloAsso debug output readable in both light and dark ACP themes.
|
|
460
|
+
// NodeBB 4.x uses Bootstrap variables, so we can rely on CSS variables here.
|
|
461
|
+
(function injectAdminCss() {
|
|
462
|
+
const id = 'onekite-admin-css';
|
|
463
|
+
if (document.getElementById(id)) return;
|
|
464
|
+
const style = document.createElement('style');
|
|
465
|
+
style.id = id;
|
|
466
|
+
style.textContent = `
|
|
467
|
+
#onekite-debug-output.onekite-debug-output {
|
|
468
|
+
background: var(--bs-body-bg) !important;
|
|
469
|
+
color: var(--bs-body-color) !important;
|
|
470
|
+
border: 1px solid var(--bs-border-color) !important;
|
|
471
|
+
}
|
|
472
|
+
`;
|
|
473
|
+
document.head.appendChild(style);
|
|
474
|
+
})();
|
|
475
|
+
|
|
456
476
|
// Load settings
|
|
457
477
|
try {
|
|
458
478
|
const s = await loadSettings();
|
|
@@ -528,6 +548,9 @@ define('admin/plugins/calendar-onekite', ['alerts', 'bootbox'], function (alerts
|
|
|
528
548
|
const rid = btn.getAttribute('data-rid');
|
|
529
549
|
if (!rid) return;
|
|
530
550
|
|
|
551
|
+
// Remove the row immediately on success for a snappier UX
|
|
552
|
+
const rowEl = btn.closest('tr') || btn.closest('.onekite-pending-row');
|
|
553
|
+
|
|
531
554
|
try {
|
|
532
555
|
if (action === 'approve') {
|
|
533
556
|
const opts = timeOptions(5).map(t => `<option value="${t}">${t}</option>`).join('');
|
|
@@ -555,6 +578,7 @@ define('admin/plugins/calendar-onekite', ['alerts', 'bootbox'], function (alerts
|
|
|
555
578
|
const adminNote = (document.getElementById('onekite-admin-note')?.value || '').trim();
|
|
556
579
|
const pickupTime = (document.getElementById('onekite-pickup-time')?.value || '').trim();
|
|
557
580
|
await approve(rid, { adminNote, pickupTime });
|
|
581
|
+
if (rowEl && rowEl.parentNode) rowEl.parentNode.removeChild(rowEl);
|
|
558
582
|
bootbox.alert('Réservation validée.');
|
|
559
583
|
resolve(true);
|
|
560
584
|
} catch (e) {
|
|
@@ -568,8 +592,38 @@ define('admin/plugins/calendar-onekite', ['alerts', 'bootbox'], function (alerts
|
|
|
568
592
|
});
|
|
569
593
|
});
|
|
570
594
|
} else if (action === 'refuse') {
|
|
571
|
-
|
|
572
|
-
|
|
595
|
+
const html = `
|
|
596
|
+
<div class="mb-3">
|
|
597
|
+
<label class="form-label">Raison du refus (incluse dans l'email)</label>
|
|
598
|
+
<textarea class="form-control" id="onekite-refuse-reason" rows="3" placeholder="Ex: matériel indisponible, dates impossibles, dossier incomplet..."></textarea>
|
|
599
|
+
</div>
|
|
600
|
+
`;
|
|
601
|
+
await new Promise((resolve) => {
|
|
602
|
+
bootbox.dialog({
|
|
603
|
+
title: 'Refuser la réservation',
|
|
604
|
+
message: html,
|
|
605
|
+
buttons: {
|
|
606
|
+
cancel: { label: 'Annuler', className: 'btn-secondary', callback: () => resolve(false) },
|
|
607
|
+
ok: {
|
|
608
|
+
label: 'Refuser',
|
|
609
|
+
className: 'btn-danger',
|
|
610
|
+
callback: async () => {
|
|
611
|
+
try {
|
|
612
|
+
const reason = (document.getElementById('onekite-refuse-reason')?.value || '').trim();
|
|
613
|
+
await refuse(rid, { reason });
|
|
614
|
+
if (rowEl && rowEl.parentNode) rowEl.parentNode.removeChild(rowEl);
|
|
615
|
+
bootbox.alert('Réservation refusée.');
|
|
616
|
+
resolve(true);
|
|
617
|
+
} catch (e) {
|
|
618
|
+
showAlert('error', 'Refus impossible.');
|
|
619
|
+
resolve(false);
|
|
620
|
+
}
|
|
621
|
+
return false;
|
|
622
|
+
},
|
|
623
|
+
},
|
|
624
|
+
},
|
|
625
|
+
});
|
|
626
|
+
});
|
|
573
627
|
}
|
|
574
628
|
await refreshPending();
|
|
575
629
|
} catch (e) {
|
package/public/client.js
CHANGED
|
@@ -19,8 +19,8 @@ define('forum/calendar-onekite', ['alerts', 'bootbox', 'hooks'], function (alert
|
|
|
19
19
|
width: 100%;
|
|
20
20
|
max-width: 100%;
|
|
21
21
|
box-sizing: border-box;
|
|
22
|
-
padding-right:
|
|
23
|
-
min-width:
|
|
22
|
+
padding-right: 2.25rem; /* leave room for caret */
|
|
23
|
+
min-width: 0;
|
|
24
24
|
text-overflow: clip;
|
|
25
25
|
white-space: nowrap;
|
|
26
26
|
}
|
|
@@ -602,9 +602,10 @@ function attachAddressAutocomplete(inputEl, onPick) {
|
|
|
602
602
|
});
|
|
603
603
|
}
|
|
604
604
|
|
|
605
|
-
async function refuseReservation(rid) {
|
|
605
|
+
async function refuseReservation(rid, payload) {
|
|
606
606
|
return await fetchJson(`/api/v3/plugins/calendar-onekite/reservations/${rid}/refuse`, {
|
|
607
607
|
method: 'PUT',
|
|
608
|
+
body: JSON.stringify(payload || {}),
|
|
608
609
|
});
|
|
609
610
|
}
|
|
610
611
|
|
|
@@ -1123,12 +1124,18 @@ function toDatetimeLocalValue(date) {
|
|
|
1123
1124
|
const canModerate = !!p.canModerate;
|
|
1124
1125
|
const isPending = status === 'pending';
|
|
1125
1126
|
|
|
1127
|
+
const refusedReason = String(p.refusedReason || p.refuseReason || '').trim();
|
|
1128
|
+
const refusedReasonHtml = (status === 'refused' && refusedReason)
|
|
1129
|
+
? `<div class="mb-2"><strong>Raison du refus</strong><br>${escapeHtml(refusedReason)}</div>`
|
|
1130
|
+
: '';
|
|
1131
|
+
|
|
1126
1132
|
const baseHtml = `
|
|
1127
1133
|
${userLine}
|
|
1128
1134
|
<div class="mb-2"><strong>Matériel</strong><br>${itemsHtml}</div>
|
|
1129
1135
|
<div class="mb-2"><strong>Période</strong><br>${period}</div>
|
|
1130
1136
|
${validatedByHtml}
|
|
1131
1137
|
${pickupHtml}
|
|
1138
|
+
${refusedReasonHtml}
|
|
1132
1139
|
<div class="text-muted" style="font-size: 12px;">Statut: ${statusLabel(status)}</div>
|
|
1133
1140
|
`;
|
|
1134
1141
|
|
|
@@ -1171,16 +1178,41 @@ function toDatetimeLocalValue(date) {
|
|
|
1171
1178
|
}
|
|
1172
1179
|
if (showModeration) {
|
|
1173
1180
|
buttons.refuse = {
|
|
1174
|
-
label: '
|
|
1181
|
+
label: 'Refuser',
|
|
1175
1182
|
className: 'btn-outline-danger',
|
|
1176
1183
|
callback: async () => {
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
+
const html = `
|
|
1185
|
+
<div class="mb-3">
|
|
1186
|
+
<label class="form-label">Raison du refus</label>
|
|
1187
|
+
<textarea class="form-control" id="onekite-refuse-reason" rows="3" placeholder="Ex: matériel indisponible, dates impossibles, dossier incomplet..."></textarea>
|
|
1188
|
+
</div>
|
|
1189
|
+
`;
|
|
1190
|
+
return await new Promise((resolve) => {
|
|
1191
|
+
bootbox.dialog({
|
|
1192
|
+
title: 'Refuser la réservation',
|
|
1193
|
+
message: html,
|
|
1194
|
+
buttons: {
|
|
1195
|
+
cancel: { label: 'Annuler', className: 'btn-secondary', callback: () => resolve(false) },
|
|
1196
|
+
ok: {
|
|
1197
|
+
label: 'Refuser',
|
|
1198
|
+
className: 'btn-danger',
|
|
1199
|
+
callback: async () => {
|
|
1200
|
+
try {
|
|
1201
|
+
const reason = (document.getElementById('onekite-refuse-reason')?.value || '').trim();
|
|
1202
|
+
await refuseReservation(rid, { reason });
|
|
1203
|
+
showAlert('success', 'Demande refusée.');
|
|
1204
|
+
calendar.refetchEvents();
|
|
1205
|
+
resolve(true);
|
|
1206
|
+
} catch (e) {
|
|
1207
|
+
showAlert('error', 'Refus impossible.');
|
|
1208
|
+
resolve(false);
|
|
1209
|
+
}
|
|
1210
|
+
return false;
|
|
1211
|
+
},
|
|
1212
|
+
},
|
|
1213
|
+
},
|
|
1214
|
+
});
|
|
1215
|
+
});
|
|
1184
1216
|
},
|
|
1185
1217
|
};
|
|
1186
1218
|
buttons.approve = {
|
|
@@ -138,7 +138,7 @@
|
|
|
138
138
|
<div class="tab-pane fade" id="onekite-tab-debug" role="tabpanel">
|
|
139
139
|
<p class="text-muted">Teste la récupération du token et la liste du matériel (catalogue).</p>
|
|
140
140
|
<button type="button" class="btn btn-secondary me-2" id="onekite-debug-run">Tester le chargement du matériel</button>
|
|
141
|
-
<pre id="onekite-debug-output" class="mt-3 p-3
|
|
141
|
+
<pre id="onekite-debug-output" class="mt-3 p-3 border rounded onekite-debug-output" style="max-height: 360px; overflow: auto;"></pre>
|
|
142
142
|
</div>
|
|
143
143
|
|
|
144
144
|
<div class="tab-pane fade" id="onekite-tab-accounting" role="tabpanel">
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<p>Bonjour {username},</p>
|
|
2
2
|
<p>Votre demande de location de matériel a été refusée.</p>
|
|
3
3
|
|
|
4
|
-
<p><strong>Matériel
|
|
4
|
+
<p><strong>Matériel réservé</strong></p>
|
|
5
5
|
<ul>
|
|
6
6
|
<!-- BEGIN itemNames -->
|
|
7
7
|
<li>{itemNames}</li>
|
|
@@ -9,3 +9,7 @@
|
|
|
9
9
|
</ul>
|
|
10
10
|
|
|
11
11
|
<p>{dateRange}</p>
|
|
12
|
+
|
|
13
|
+
<!-- IF refusedReason -->
|
|
14
|
+
<p><strong>Raison du refus</strong><br>{refusedReason}</p>
|
|
15
|
+
<!-- ENDIF refusedReason -->
|