nodebb-plugin-calendar-onekite 11.2.7 → 11.2.9

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 CHANGED
@@ -387,7 +387,7 @@ api.getItems = async function (req, res) {
387
387
  clientSecret: settings.helloassoClientSecret,
388
388
  });
389
389
  if (!token) {
390
- console.warn('[calendar-onekite] HelloAsso access token not obtained (approve API)', { rid });
390
+ console.warn('[calendar-onekite] HelloAsso access token not obtained (items API)', { uid: req.uid || 0 });
391
391
  }
392
392
 
393
393
  if (!token) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodebb-plugin-calendar-onekite",
3
- "version": "11.2.7",
3
+ "version": "11.2.9",
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
@@ -64,8 +64,17 @@ define('forum/calendar-onekite', ['alerts', 'bootbox', 'hooks'], function (alert
64
64
  return `${pad2(d.getHours())}:${pad2(d.getMinutes())}`;
65
65
  }
66
66
 
67
- function buildLocalDatetime(dateStr, timeStr) {
68
- return `${dateStr}T${timeStr}`;
67
+ // Build an ISO datetime *with timezone* from local date/time inputs.
68
+ // This avoids ambiguity when the server runs in UTC and prevents
69
+ // "single-day" events (07:00→23:59 local) from spilling into the next day
70
+ // when rendered back to the browser.
71
+ function buildLocalIso(dateStr, timeStr) {
72
+ try {
73
+ const d = new Date(`${dateStr}T${timeStr}`);
74
+ return d.toISOString();
75
+ } catch (e) {
76
+ return `${dateStr}T${timeStr}`;
77
+ }
69
78
  }
70
79
 
71
80
  function seTimeOptions(selected, include2359) {
@@ -124,22 +133,22 @@ define('forum/calendar-onekite', ['alerts', 'bootbox', 'hooks'], function (alert
124
133
  <div class="row g-2">
125
134
  <div class="col-12 col-md-6">
126
135
  <label class="form-label">Début</label>
127
- <div class="row g-2">
128
- <div class="col-7">
136
+ <div class="row g-2 align-items-end">
137
+ <div class="col-12 col-sm-8">
129
138
  <input type="date" class="form-control" id="onekite-se-start-date" value="${escapeHtml(toDateInputValue(seStart))}" />
130
139
  </div>
131
- <div class="col-5">
140
+ <div class="col-12 col-sm-4">
132
141
  <select class="form-select onekite-time-select" id="onekite-se-start-time">${seTimeOptions(seStartTime, false)}</select>
133
142
  </div>
134
143
  </div>
135
144
  </div>
136
145
  <div class="col-12 col-md-6">
137
146
  <label class="form-label">Fin</label>
138
- <div class="row g-2">
139
- <div class="col-7">
147
+ <div class="row g-2 align-items-end">
148
+ <div class="col-12 col-sm-8">
140
149
  <input type="date" class="form-control" id="onekite-se-end-date" value="${escapeHtml(toDateInputValue(seEnd))}" />
141
150
  </div>
142
- <div class="col-5">
151
+ <div class="col-12 col-sm-4">
143
152
  <select class="form-select onekite-time-select" id="onekite-se-end-time">${seTimeOptions(seEndTime, true)}</select>
144
153
  </div>
145
154
  </div>
@@ -180,8 +189,8 @@ define('forum/calendar-onekite', ['alerts', 'bootbox', 'hooks'], function (alert
180
189
  const st = (document.getElementById('onekite-se-start-time')?.value || '').trim();
181
190
  const ed = (document.getElementById('onekite-se-end-date')?.value || '').trim();
182
191
  const et = (document.getElementById('onekite-se-end-time')?.value || '').trim();
183
- const startVal = (sd && st) ? buildLocalDatetime(sd, st) : '';
184
- const endVal = (ed && et) ? buildLocalDatetime(ed, et) : '';
192
+ const startVal = (sd && st) ? buildLocalIso(sd, st) : '';
193
+ const endVal = (ed && et) ? buildLocalIso(ed, et) : '';
185
194
  const address = (document.getElementById('onekite-se-address')?.value || '').trim();
186
195
  const notes = (document.getElementById('onekite-se-notes')?.value || '').trim();
187
196
  const lat = (document.getElementById('onekite-se-lat')?.value || '').trim();
@@ -778,13 +787,25 @@ function toDatetimeLocalValue(date) {
778
787
  const btn = document.querySelector('#onekite-calendar .fc-newSpecial-button');
779
788
  if (!btn) return;
780
789
  const isSpecial = mode === 'special';
781
- btn.textContent = isSpecial ? 'Évènement ✓' : 'Évènement';
790
+ const label = isSpecial ? 'Évènement ✓' : 'Évènement';
791
+ // FullCalendar wraps text in a span.fc-button-text. Replacing the entire
792
+ // button textContent can confuse its rerendering and lead to duplicated
793
+ // labels ("EvenementEvenement").
794
+ const span = btn.querySelector('.fc-button-text');
795
+ if (span) {
796
+ span.textContent = label;
797
+ } else {
798
+ btn.textContent = label;
799
+ }
782
800
  btn.classList.toggle('onekite-active', isSpecial);
783
801
  } catch (e) {}
784
802
  }
785
803
 
786
804
  function setMode(next) {
787
805
  mode = next;
806
+ if (mode === 'special') {
807
+ showAlert('info', 'Mode évènement : sélectionnez une date ou une plage');
808
+ }
788
809
  refreshDesktopModeButton();
789
810
  try {
790
811
  const mb = document.querySelector('#onekite-mobile-controls .onekite-mode-btn');
@@ -899,6 +920,21 @@ function toDatetimeLocalValue(date) {
899
920
  failureCallback(e);
900
921
  }
901
922
  },
923
+ eventDidMount: function (arg) {
924
+ try {
925
+ const ev = arg && arg.event;
926
+ if (!ev) return;
927
+ if (ev.extendedProps && ev.extendedProps.type === 'special') {
928
+ // Some themes override FC event colors. Force ours for special events.
929
+ const el2 = arg.el;
930
+ if (el2 && el2.style) {
931
+ el2.style.backgroundColor = '#8e44ad';
932
+ el2.style.borderColor = '#8e44ad';
933
+ el2.style.color = '#ffffff';
934
+ }
935
+ }
936
+ } catch (e) {}
937
+ },
902
938
  select: async function (info) {
903
939
  if (isDialogOpen) {
904
940
  return;
@@ -997,9 +1033,12 @@ function toDatetimeLocalValue(date) {
997
1033
  }
998
1034
  isDialogOpen = true;
999
1035
  try {
1000
- // Default to a same-day event: 00:00 -> 23:59
1001
- const end = new Date(start.getTime() + (23 * 60 + 59) * 60 * 1000);
1002
- const payload = await openSpecialEventDialog({ start, end, allDay: true });
1036
+ // Default to a same-day event in local time: 07:00 -> 23:59
1037
+ const start2 = new Date(start);
1038
+ start2.setHours(7, 0, 0, 0);
1039
+ const end2 = new Date(start);
1040
+ end2.setHours(23, 59, 0, 0);
1041
+ const payload = await openSpecialEventDialog({ start: start2, end: end2, allDay: false });
1003
1042
  setMode('reservation');
1004
1043
  if (!payload) {
1005
1044
  isDialogOpen = false;