nodebb-plugin-calendar-onekite 11.2.19 → 11.2.21

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 +68 -15
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodebb-plugin-calendar-onekite",
3
- "version": "11.2.19",
3
+ "version": "11.2.21",
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
@@ -95,11 +95,15 @@ define('forum/calendar-onekite', ['alerts', 'bootbox', 'hooks'], function (alert
95
95
 
96
96
  async function openSpecialEventDialog(selectionInfo) {
97
97
  const start = selectionInfo.start;
98
+ // FullCalendar can omit `end` for certain interactions. Also, for all-day
99
+ // selections, `end` is exclusive (next day at 00:00). We normalise below
100
+ // so a single-day click always defaults to a single-day range.
98
101
  const end = selectionInfo.end;
99
102
 
100
103
  // Prefer event times starting at 07:00 for day-clicks (all-day selections).
101
104
  let seStart = new Date(start);
102
- let seEnd = new Date(end);
105
+ // If `end` is missing, start with the same day.
106
+ let seEnd = end ? new Date(end) : new Date(seStart);
103
107
 
104
108
  const msSpan = seEnd.getTime() - seStart.getTime();
105
109
  const isAllDaySelection = selectionInfo && (selectionInfo.allDay || (
@@ -107,19 +111,25 @@ define('forum/calendar-onekite', ['alerts', 'bootbox', 'hooks'], function (alert
107
111
  seEnd.getHours() === 0 && seEnd.getMinutes() === 0
108
112
  ));
109
113
 
110
- const isSingleDayAllDay = isAllDaySelection && msSpan <= (24 * 60 * 60 * 1000 + 1500);
114
+ // For all-day selections, the simplest robust check is:
115
+ // does (end - 1ms) fall on the same calendar day as start?
116
+ const endMinus1 = isAllDaySelection ? new Date(seEnd.getTime() - 1) : null;
117
+ const isSingleDayAllDay = isAllDaySelection && (!end || (endMinus1 && toDateInputValue(seStart) === toDateInputValue(endMinus1)));
111
118
 
112
119
  if (isSingleDayAllDay) {
113
- // Keep same day; default start at 07:00 and end at 23:59
120
+ // Keep same day; default to a "one day" block (07:00 07:00).
121
+ // We keep end date equal to start date in the modal. On submit,
122
+ // if end <= start we automatically roll end by +1 day.
114
123
  seStart.setHours(7, 0, 0, 0);
115
124
  seEnd = new Date(seStart);
116
- seEnd.setHours(23, 59, 0, 0);
125
+ seEnd.setHours(7, 0, 0, 0);
117
126
  } else if (isAllDaySelection) {
118
- // Multi-day all-day selection: start at 07:00 on first day, end at 23:59 on last day
127
+ // Multi-day all-day selection: start at 07:00 on first day, end at 07:00 on last day
128
+ // (end date shown is the last selected day; submit logic will roll if needed).
119
129
  seStart.setHours(7, 0, 0, 0);
120
- const lastDay = new Date(seEnd.getTime() - 24 * 60 * 60 * 1000);
130
+ const lastDay = endMinus1 ? new Date(endMinus1) : new Date(seEnd);
121
131
  seEnd = new Date(lastDay);
122
- seEnd.setHours(23, 59, 0, 0);
132
+ seEnd.setHours(7, 0, 0, 0);
123
133
  } else {
124
134
  seStart = roundTo5Minutes(seStart);
125
135
  seEnd = roundTo5Minutes(seEnd);
@@ -180,6 +190,7 @@ define('forum/calendar-onekite', ['alerts', 'bootbox', 'hooks'], function (alert
180
190
  `;
181
191
 
182
192
  return await new Promise((resolve) => {
193
+ let resolved = false;
183
194
  const dialog = bootbox.dialog({
184
195
  title: 'Créer un évènement',
185
196
  message: html,
@@ -187,7 +198,10 @@ define('forum/calendar-onekite', ['alerts', 'bootbox', 'hooks'], function (alert
187
198
  cancel: {
188
199
  label: 'Annuler',
189
200
  className: 'btn-secondary',
190
- callback: () => resolve(null),
201
+ callback: () => {
202
+ resolved = true;
203
+ resolve(null);
204
+ },
191
205
  },
192
206
  ok: {
193
207
  label: 'Créer',
@@ -198,12 +212,35 @@ define('forum/calendar-onekite', ['alerts', 'bootbox', 'hooks'], function (alert
198
212
  const st = (document.getElementById('onekite-se-start-time')?.value || '').trim();
199
213
  const ed = (document.getElementById('onekite-se-end-date')?.value || '').trim();
200
214
  const et = (document.getElementById('onekite-se-end-time')?.value || '').trim();
201
- const startVal = (sd && st) ? buildLocalIso(sd, st) : '';
202
- const endVal = (ed && et) ? buildLocalIso(ed, et) : '';
215
+ // If the user keeps the same day but chooses an end time earlier than
216
+ // (or equal to) the start time (default 07:00 → 07:00), interpret
217
+ // it as an overnight/24h range and roll the end date by +1 day.
218
+ let startVal = '';
219
+ let endVal = '';
220
+ try {
221
+ if (sd && st) {
222
+ const startDt = new Date(`${sd}T${st}`);
223
+ startVal = startDt.toISOString();
224
+ if (ed && et) {
225
+ let endDt = new Date(`${ed}T${et}`);
226
+ if (!isNaN(startDt) && !isNaN(endDt) && endDt.getTime() <= startDt.getTime()) {
227
+ // Only auto-roll when user kept the same end date.
228
+ if (ed === sd) {
229
+ endDt = new Date(endDt.getTime() + 24 * 60 * 60 * 1000);
230
+ }
231
+ }
232
+ endVal = endDt.toISOString();
233
+ }
234
+ }
235
+ } catch (e) {
236
+ startVal = (sd && st) ? buildLocalIso(sd, st) : '';
237
+ endVal = (ed && et) ? buildLocalIso(ed, et) : '';
238
+ }
203
239
  const address = (document.getElementById('onekite-se-address')?.value || '').trim();
204
240
  const notes = (document.getElementById('onekite-se-notes')?.value || '').trim();
205
241
  const lat = (document.getElementById('onekite-se-lat')?.value || '').trim();
206
242
  const lon = (document.getElementById('onekite-se-lon')?.value || '').trim();
243
+ resolved = true;
207
244
  resolve({ title, start: startVal, end: endVal, address, notes, lat, lon });
208
245
  return true;
209
246
  },
@@ -211,6 +248,19 @@ define('forum/calendar-onekite', ['alerts', 'bootbox', 'hooks'], function (alert
211
248
  },
212
249
  });
213
250
 
251
+ // If the modal is closed via ESC, backdrop click, or the "X" button,
252
+ // Bootbox does not trigger our cancel callback. Ensure we always resolve
253
+ // and release the global lock.
254
+ try {
255
+ dialog.on('hidden.bs.modal', () => {
256
+ if (resolved) return;
257
+ resolved = true;
258
+ resolve(null);
259
+ });
260
+ } catch (e) {
261
+ // ignore
262
+ }
263
+
214
264
  // init leaflet
215
265
  dialog.on('shown.bs.modal', () => {
216
266
  setTimeout(async () => {
@@ -222,7 +272,6 @@ define('forum/calendar-onekite', ['alerts', 'bootbox', 'hooks'], function (alert
222
272
  const map = L.map(mapEl).setView([46.5, 2.5], 5);
223
273
  L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { maxZoom: 19, attribution: '&copy; OpenStreetMap' }).addTo(map);
224
274
  setTimeout(() => { try { map.invalidateSize(); } catch (e) {} }, 100);
225
- setTimeout(() => { try { map.invalidateSize(); } catch (e) {} }, 100);
226
275
  let marker = null;
227
276
  function setMarker(lat, lon) {
228
277
  if (marker) map.removeLayer(marker);
@@ -937,6 +986,10 @@ function toDatetimeLocalValue(date) {
937
986
  const mapped = (Array.isArray(data) ? data : []).map((ev) => {
938
987
  try {
939
988
  if (ev && ev.extendedProps && ev.extendedProps.type === 'special' && ev.start && ev.end) {
989
+ // Force the same visual layout as reservations in month view
990
+ // (avoid the "dot" layout which introduces a leading gap).
991
+ ev.display = 'block';
992
+
940
993
  const s = new Date(ev.start);
941
994
  const e = new Date(ev.end);
942
995
  const ts = `${pad2(s.getHours())}:${pad2(s.getMinutes())}`;
@@ -980,7 +1033,6 @@ function toDatetimeLocalValue(date) {
980
1033
  try {
981
1034
  if (mode === 'special' && canCreateSpecial) {
982
1035
  const payload = await openSpecialEventDialog(info);
983
- setMode('reservation');
984
1036
  if (!payload) {
985
1037
  calendar.unselect();
986
1038
  isDialogOpen = false;
@@ -996,6 +1048,7 @@ function toDatetimeLocalValue(date) {
996
1048
  });
997
1049
  });
998
1050
  showAlert('success', 'Évènement créé.');
1051
+ setMode('reservation');
999
1052
  calendar.refetchEvents();
1000
1053
  calendar.unselect();
1001
1054
  isDialogOpen = false;
@@ -1070,13 +1123,12 @@ function toDatetimeLocalValue(date) {
1070
1123
  }
1071
1124
  isDialogOpen = true;
1072
1125
  try {
1073
- // Default to a same-day event in local time: 07:00 -> 23:59
1126
+ // Default to a one-day block: 07:00 -> 07:00 (end rolled to +1 day on submit).
1074
1127
  const start2 = new Date(start);
1075
1128
  start2.setHours(7, 0, 0, 0);
1076
1129
  const end2 = new Date(start);
1077
- end2.setHours(23, 59, 0, 0);
1130
+ end2.setHours(7, 0, 0, 0);
1078
1131
  const payload = await openSpecialEventDialog({ start: start2, end: end2, allDay: false });
1079
- setMode('reservation');
1080
1132
  if (!payload) {
1081
1133
  isDialogOpen = false;
1082
1134
  return;
@@ -1090,6 +1142,7 @@ function toDatetimeLocalValue(date) {
1090
1142
  body: JSON.stringify(payload),
1091
1143
  });
1092
1144
  });
1145
+ setMode('reservation');
1093
1146
  showAlert('success', 'Évènement créé.');
1094
1147
  calendar.refetchEvents();
1095
1148
  } finally {