nodebb-plugin-calendar-onekite 11.2.2 → 11.2.3

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/public/client.js +101 -5
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodebb-plugin-calendar-onekite",
3
- "version": "11.2.2",
3
+ "version": "11.2.3",
4
4
  "description": "FullCalendar-based equipment reservation workflow with admin approval & HelloAsso payment for NodeBB",
5
5
  "main": "library.js",
6
6
  "license": "MIT",
package/public/client.js CHANGED
@@ -16,9 +16,78 @@ define('forum/calendar-onekite', ['alerts', 'bootbox', 'hooks'], function (alert
16
16
  .replace(/'/g, ''');
17
17
  }
18
18
 
19
+ function pad2(n) { return String(n).padStart(2, '0'); }
20
+
21
+ function toDateInputValue(d) {
22
+ const dt = (d instanceof Date) ? d : new Date(d);
23
+ return `${dt.getFullYear()}-${pad2(dt.getMonth() + 1)}-${pad2(dt.getDate())}`;
24
+ }
25
+
26
+ function roundTo5Minutes(dt) {
27
+ const d = new Date(dt);
28
+ const m = d.getMinutes();
29
+ const rounded = Math.round(m / 5) * 5;
30
+ d.setMinutes(rounded, 0, 0);
31
+ return d;
32
+ }
33
+
34
+ function timeString(dt) {
35
+ const d = (dt instanceof Date) ? dt : new Date(dt);
36
+ return `${pad2(d.getHours())}:${pad2(d.getMinutes())}`;
37
+ }
38
+
39
+ function buildLocalDatetime(dateStr, timeStr) {
40
+ return `${dateStr}T${timeStr}`;
41
+ }
42
+
43
+ function seTimeOptions(selected, include2359) {
44
+ const opts = [];
45
+ for (let h = 7; h < 24; h++) {
46
+ for (let m = 0; m < 60; m += 5) {
47
+ const t = `${pad2(h)}:${pad2(m)}`;
48
+ opts.push(`<option value="${t}" ${t === selected ? 'selected' : ''}>${t}</option>`);
49
+ }
50
+ }
51
+ if (include2359) {
52
+ const t = '23:59';
53
+ opts.push(`<option value="${t}" ${t === selected ? 'selected' : ''}>${t}</option>`);
54
+ }
55
+ return opts.join('');
56
+ }
57
+
19
58
  async function openSpecialEventDialog(selectionInfo) {
20
59
  const start = selectionInfo.start;
21
60
  const end = selectionInfo.end;
61
+
62
+ // Prefer event times starting at 07:00 for day-clicks (all-day selections).
63
+ let seStart = new Date(start);
64
+ let seEnd = new Date(end);
65
+
66
+ const isAllDayClick = selectionInfo && (selectionInfo.allDay || (
67
+ seStart.getHours() === 0 && seStart.getMinutes() === 0 &&
68
+ seEnd.getHours() === 0 && seEnd.getMinutes() === 0 &&
69
+ Math.abs(seEnd.getTime() - seStart.getTime() - 24 * 60 * 60 * 1000) < 1000
70
+ ));
71
+
72
+ if (isAllDayClick) {
73
+ // Keep same day; default start at 07:00 and end at 23:59
74
+ seStart.setHours(7, 0, 0, 0);
75
+ seEnd = new Date(seStart);
76
+ seEnd.setHours(23, 59, 0, 0);
77
+ } else {
78
+ seStart = roundTo5Minutes(seStart);
79
+ seEnd = roundTo5Minutes(seEnd);
80
+ if (seStart.getHours() < 7) {
81
+ seStart.setHours(7, 0, 0, 0);
82
+ if (seEnd <= seStart) {
83
+ seEnd = new Date(seStart);
84
+ seEnd.setHours(8, 0, 0, 0);
85
+ }
86
+ }
87
+ }
88
+
89
+ const seStartTime = timeString(seStart);
90
+ const seEndTime = timeString(seEnd);
22
91
  const html = `
23
92
  <div class="mb-3">
24
93
  <label class="form-label">Titre</label>
@@ -27,11 +96,25 @@ define('forum/calendar-onekite', ['alerts', 'bootbox', 'hooks'], function (alert
27
96
  <div class="row g-2">
28
97
  <div class="col-12 col-md-6">
29
98
  <label class="form-label">Début</label>
30
- <input type="datetime-local" class="form-control" id="onekite-se-start" value="${escapeHtml(toDatetimeLocalValue(start))}" />
99
+ <div class="row g-2">
100
+ <div class="col-7">
101
+ <input type="date" class="form-control" id="onekite-se-start-date" value="${escapeHtml(toDateInputValue(seStart))}" />
102
+ </div>
103
+ <div class="col-5">
104
+ <select class="form-select" id="onekite-se-start-time">${seTimeOptions(seStartTime, false)}</select>
105
+ </div>
106
+ </div>
31
107
  </div>
32
108
  <div class="col-12 col-md-6">
33
109
  <label class="form-label">Fin</label>
34
- <input type="datetime-local" class="form-control" id="onekite-se-end" value="${escapeHtml(toDatetimeLocalValue(end))}" />
110
+ <div class="row g-2">
111
+ <div class="col-7">
112
+ <input type="date" class="form-control" id="onekite-se-end-date" value="${escapeHtml(toDateInputValue(seEnd))}" />
113
+ </div>
114
+ <div class="col-5">
115
+ <select class="form-select" id="onekite-se-end-time">${seTimeOptions(seEndTime, true)}</select>
116
+ </div>
117
+ </div>
35
118
  </div>
36
119
  </div>
37
120
  <div class="mt-3">
@@ -65,8 +148,12 @@ define('forum/calendar-onekite', ['alerts', 'bootbox', 'hooks'], function (alert
65
148
  className: 'btn-primary',
66
149
  callback: () => {
67
150
  const title = (document.getElementById('onekite-se-title')?.value || '').trim();
68
- const startVal = (document.getElementById('onekite-se-start')?.value || '').trim();
69
- const endVal = (document.getElementById('onekite-se-end')?.value || '').trim();
151
+ const sd = (document.getElementById('onekite-se-start-date')?.value || '').trim();
152
+ const st = (document.getElementById('onekite-se-start-time')?.value || '').trim();
153
+ const ed = (document.getElementById('onekite-se-end-date')?.value || '').trim();
154
+ const et = (document.getElementById('onekite-se-end-time')?.value || '').trim();
155
+ const startVal = (sd && st) ? buildLocalDatetime(sd, st) : '';
156
+ const endVal = (ed && et) ? buildLocalDatetime(ed, et) : '';
70
157
  const address = (document.getElementById('onekite-se-address')?.value || '').trim();
71
158
  const notes = (document.getElementById('onekite-se-notes')?.value || '').trim();
72
159
  const lat = (document.getElementById('onekite-se-lat')?.value || '').trim();
@@ -848,7 +935,16 @@ function toDatetimeLocalValue(date) {
848
935
  } else if (code === '409') {
849
936
  showAlert('error', 'Impossible : au moins un matériel est déjà réservé ou en attente sur cette période.');
850
937
  } else {
851
- const msg = payload && (payload.message || payload.error || payload.msg) ? String(payload.message || payload.error || payload.msg) : '';
938
+ const msgRaw = payload && (payload.message || payload.error || payload.msg)
939
+ ? String(payload.message || payload.error || payload.msg)
940
+ : '';
941
+
942
+ // NodeBB can return a plain "not-logged-in" string when the user is not authenticated.
943
+ // We want a user-friendly message consistent with the membership requirement.
944
+ const msg = (/\bnot-logged-in\b/i.test(msgRaw) || /\[\[error:not-logged-in\]\]/i.test(msgRaw))
945
+ ? 'Vous devez être adhérent Onekite'
946
+ : msgRaw;
947
+
852
948
  showAlert('error', msg || ((e && (e.status === 401 || e.status === 403)) ? 'Vous devez être adhérent Onekite' : 'Erreur lors de la création de la demande.'));
853
949
  }
854
950
  calendar.unselect();