nodebb-plugin-onekite-calendar 1.0.9 → 1.0.11
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 +15 -7
- package/lib/widgets.js +127 -15
- package/package.json +1 -1
- package/plugin.json +1 -1
package/lib/api.js
CHANGED
|
@@ -134,13 +134,21 @@ function formatFR(tsOrIso) {
|
|
|
134
134
|
}
|
|
135
135
|
|
|
136
136
|
function buildHelloAssoItemName(baseLabel, itemNames, start, end) {
|
|
137
|
-
const base = String(baseLabel || '
|
|
137
|
+
const base = String(baseLabel || '').trim();
|
|
138
138
|
const items = Array.isArray(itemNames) ? itemNames.map((s) => String(s || '').trim()).filter(Boolean) : [];
|
|
139
|
-
const range = (start && end) ?
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
139
|
+
const range = (start && end) ? ('Du ' + formatFR(start) + ' au ' + formatFR(end)) : '';
|
|
140
|
+
|
|
141
|
+
// IMPORTANT:
|
|
142
|
+
// On the public HelloAsso checkout page, line breaks are not always rendered consistently.
|
|
143
|
+
// Build a single-line label using bullet separators.
|
|
144
|
+
let out = '';
|
|
145
|
+
if (base) out += base;
|
|
146
|
+
if (items.length) out += (out ? ' — ' : '') + items.map((it) => '• ' + it).join(' ');
|
|
147
|
+
if (range) out += (out ? ' — ' : '') + range;
|
|
148
|
+
|
|
149
|
+
out = String(out || '').trim();
|
|
150
|
+
if (!out) out = 'Réservation matériel';
|
|
151
|
+
|
|
144
152
|
// HelloAsso constraint: itemName max 250 chars
|
|
145
153
|
if (out.length > 250) {
|
|
146
154
|
out = out.slice(0, 249).trimEnd() + '…';
|
|
@@ -812,7 +820,7 @@ api.approveReservation = async function (req, res) {
|
|
|
812
820
|
// Can be overridden via ACP setting `helloassoCallbackUrl`.
|
|
813
821
|
callbackUrl: normalizeReturnUrl(meta),
|
|
814
822
|
webhookUrl: normalizeCallbackUrl(settings2.helloassoCallbackUrl, meta),
|
|
815
|
-
itemName: buildHelloAssoItemName('
|
|
823
|
+
itemName: buildHelloAssoItemName('', r.itemNames || (r.itemName ? [r.itemName] : []), r.start, r.end),
|
|
816
824
|
containsDonation: false,
|
|
817
825
|
metadata: {
|
|
818
826
|
reservationId: String(rid),
|
package/lib/widgets.js
CHANGED
|
@@ -137,9 +137,19 @@ widgets.renderTwoWeeksWidget = async function (data) {
|
|
|
137
137
|
tip.style.display = 'none';
|
|
138
138
|
}
|
|
139
139
|
|
|
140
|
-
|
|
140
|
+
// Client-side escaping (the server-side helper is not in scope here)
|
|
141
|
+
function escapeHtml(s) {
|
|
142
|
+
return String(s || '')
|
|
143
|
+
.replace(/&/g, '&')
|
|
144
|
+
.replace(/</g, '<')
|
|
145
|
+
.replace(/>/g, '>')
|
|
146
|
+
.replace(/"/g, '"')
|
|
147
|
+
.replace(/'/g, ''');
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
document.addEventListener('pointerdown', (e) => {
|
|
141
151
|
// Close when clicking outside an event
|
|
142
|
-
if (!e.target || !e.target.closest || !e.target.closest('.fc-event')) {
|
|
152
|
+
if (!e.target || !e.target.closest || (!e.target.closest('.fc-event') && !e.target.closest('.onekite-cal-tooltip'))) {
|
|
143
153
|
hideTip();
|
|
144
154
|
}
|
|
145
155
|
}, { passive: true });
|
|
@@ -164,10 +174,51 @@ widgets.renderTwoWeeksWidget = async function (data) {
|
|
|
164
174
|
},
|
|
165
175
|
navLinks: false,
|
|
166
176
|
eventTimeFormat: { hour: '2-digit', minute: '2-digit', hour12: false },
|
|
167
|
-
//
|
|
177
|
+
// Force day number to be numeric only (avoid '1 janvier' style labels)
|
|
178
|
+
dayCellContent: function(arg) {
|
|
179
|
+
try {
|
|
180
|
+
const t = String(arg.dayNumberText || '');
|
|
181
|
+
const m = t.match(/^\d+/);
|
|
182
|
+
return { html: m ? m[0] : t };
|
|
183
|
+
} catch (e) {
|
|
184
|
+
return { html: String(arg.dayNumberText || '') };
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
eventClassNames: function(arg) {
|
|
188
|
+
try {
|
|
189
|
+
const ev = arg && arg.event;
|
|
190
|
+
if (!ev) return ['onekite-singleday'];
|
|
191
|
+
if (ev.extendedProps && (ev.extendedProps.multiDay === true || ev.extendedProps.days > 1)) return ['onekite-multiday'];
|
|
192
|
+
if (ev.start && ev.end) {
|
|
193
|
+
const diff = (new Date(ev.end)).getTime() - (new Date(ev.start)).getTime();
|
|
194
|
+
return [diff > 86400000 ? 'onekite-multiday' : 'onekite-singleday'];
|
|
195
|
+
}
|
|
196
|
+
} catch (e) {}
|
|
197
|
+
return ['onekite-singleday'];
|
|
198
|
+
},
|
|
199
|
+
// Render: dot for single-day, pill for multi-day
|
|
168
200
|
eventContent: function(arg) {
|
|
169
|
-
const
|
|
170
|
-
const
|
|
201
|
+
const ev = arg && arg.event;
|
|
202
|
+
const isMulti = (function(){
|
|
203
|
+
try {
|
|
204
|
+
if (!ev || !ev.start || !ev.end) return false;
|
|
205
|
+
if (ev.extendedProps && (ev.extendedProps.multiDay === true || ev.extendedProps.days > 1)) return true;
|
|
206
|
+
const s = new Date(ev.start);
|
|
207
|
+
const e = new Date(ev.end);
|
|
208
|
+
const diff = e.getTime() - s.getTime();
|
|
209
|
+
return diff > 86400000; // strictly more than 1 day
|
|
210
|
+
} catch (e) { return false; }
|
|
211
|
+
})();
|
|
212
|
+
|
|
213
|
+
if (isMulti) {
|
|
214
|
+
const spacer = document.createElement('span');
|
|
215
|
+
spacer.className = 'onekite-pill-spacer';
|
|
216
|
+
spacer.appendChild(document.createTextNode('\u00A0'));
|
|
217
|
+
return { domNodes: [spacer] };
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const bg = (ev.backgroundColor || (ev.extendedProps && ev.extendedProps.backgroundColor) || '').trim();
|
|
221
|
+
const border = (ev.borderColor || '').trim();
|
|
171
222
|
const color = bg || border || '#3788d8';
|
|
172
223
|
const wrap = document.createElement('span');
|
|
173
224
|
wrap.className = 'onekite-dot-wrap';
|
|
@@ -188,6 +239,21 @@ widgets.renderTwoWeeksWidget = async function (data) {
|
|
|
188
239
|
eventDidMount: function(info) {
|
|
189
240
|
try {
|
|
190
241
|
const ev = info.event;
|
|
242
|
+
// Improve centering for single-day dots by centering the harness,
|
|
243
|
+
// same visual behavior as rentals.
|
|
244
|
+
try {
|
|
245
|
+
const cls = (info.el && info.el.classList) ? info.el.classList : null;
|
|
246
|
+
const isSingle = cls && cls.contains('onekite-singleday');
|
|
247
|
+
const harness = info.el && info.el.closest ? info.el.closest('.fc-daygrid-event-harness') : null;
|
|
248
|
+
if (harness && harness.classList) {
|
|
249
|
+
if (isSingle) {
|
|
250
|
+
harness.classList.add('onekite-harness-dot');
|
|
251
|
+
} else {
|
|
252
|
+
harness.classList.remove('onekite-harness-dot');
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
} catch (e) {}
|
|
256
|
+
|
|
191
257
|
const ep = ev.extendedProps || {};
|
|
192
258
|
const title = (ep.itemNameLine || ep.title || ev.title || '').toString();
|
|
193
259
|
const status = (ep.status || ep.type || '').toString();
|
|
@@ -202,22 +268,36 @@ widgets.renderTwoWeeksWidget = async function (data) {
|
|
|
202
268
|
(status ? ('<div style="opacity:.75; margin-top:2px; font-size:.85em;">' + escapeHtml(status) + '</div>') : '');
|
|
203
269
|
|
|
204
270
|
// Hover (desktop)
|
|
205
|
-
info.el.addEventListener('
|
|
271
|
+
info.el.addEventListener('pointerenter', () => {
|
|
272
|
+
// Only show on devices that actually hover
|
|
273
|
+
if (window.matchMedia && window.matchMedia('(hover: hover)').matches === false) return;
|
|
206
274
|
setTipContent(html);
|
|
207
275
|
const rect = info.el.getBoundingClientRect();
|
|
208
|
-
showTipAt(rect.left + window.scrollX, rect.top + window.scrollY);
|
|
276
|
+
showTipAt(rect.left + rect.width / 2 + window.scrollX, rect.top + window.scrollY);
|
|
209
277
|
}, { passive: true });
|
|
210
|
-
info.el.addEventListener('
|
|
278
|
+
info.el.addEventListener('pointerleave', hideTip, { passive: true });
|
|
211
279
|
|
|
212
|
-
// Tap/click (
|
|
213
|
-
|
|
214
|
-
e.preventDefault();
|
|
215
|
-
e.stopPropagation();
|
|
280
|
+
// Tap (mobile) / click (fallback)
|
|
281
|
+
const openTipFromPoint = (pt) => {
|
|
216
282
|
setTipContent(html);
|
|
217
|
-
const pt = (e.touches && e.touches[0]) ? e.touches[0] : e;
|
|
218
283
|
showTipAt((pt.clientX || 0) + window.scrollX, (pt.clientY || 0) + window.scrollY);
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
info.el.addEventListener('pointerdown', (e) => {
|
|
287
|
+
// On touch devices, FullCalendar can swallow click events during swipe;
|
|
288
|
+
// pointerdown is more reliable.
|
|
289
|
+
if (e && e.preventDefault) e.preventDefault();
|
|
290
|
+
if (e && e.stopPropagation) e.stopPropagation();
|
|
291
|
+
openTipFromPoint(e);
|
|
219
292
|
});
|
|
220
|
-
|
|
293
|
+
|
|
294
|
+
info.el.addEventListener('touchstart', (e) => {
|
|
295
|
+
const t = e.touches && e.touches[0];
|
|
296
|
+
if (!t) return;
|
|
297
|
+
if (e && e.stopPropagation) e.stopPropagation();
|
|
298
|
+
openTipFromPoint(t);
|
|
299
|
+
}, { passive: true });
|
|
300
|
+
} catch (e) {}
|
|
221
301
|
},
|
|
222
302
|
});
|
|
223
303
|
|
|
@@ -261,11 +341,43 @@ widgets.renderTwoWeeksWidget = async function (data) {
|
|
|
261
341
|
.onekite-twoweeks .fc .fc-button { padding: .2rem .35rem; font-size: .75rem; }
|
|
262
342
|
.onekite-twoweeks .fc .fc-daygrid-day-number { font-size: .75rem; padding: 2px; }
|
|
263
343
|
.onekite-twoweeks .fc .fc-col-header-cell-cushion { font-size: .75rem; }
|
|
264
|
-
|
|
344
|
+
|
|
345
|
+
/* Single-day: show only a dot */
|
|
346
|
+
.onekite-twoweeks .fc .fc-event.onekite-singleday { background: transparent !important; border: none !important; }
|
|
347
|
+
.onekite-twoweeks .fc .fc-daygrid-event.onekite-singleday,
|
|
348
|
+
.onekite-twoweeks .fc .fc-daygrid-block-event.onekite-singleday {
|
|
349
|
+
background: transparent !important;
|
|
350
|
+
border: none !important;
|
|
351
|
+
box-shadow: none !important;
|
|
352
|
+
}
|
|
353
|
+
.onekite-twoweeks .fc .fc-daygrid-block-event.onekite-singleday .fc-event-main { padding: 0 !important; }
|
|
354
|
+
|
|
355
|
+
/* Multi-day: keep FullCalendar bar, but hide text and make it a pill */
|
|
356
|
+
.onekite-twoweeks .fc .fc-event.onekite-multiday {
|
|
357
|
+
border-radius: 999px !important;
|
|
358
|
+
overflow: hidden;
|
|
359
|
+
}
|
|
360
|
+
.onekite-twoweeks .fc .fc-daygrid-event.onekite-multiday,
|
|
361
|
+
.onekite-twoweeks .fc .fc-daygrid-block-event.onekite-multiday {
|
|
362
|
+
border-radius: 999px !important;
|
|
363
|
+
box-shadow: none !important;
|
|
364
|
+
}
|
|
365
|
+
.onekite-twoweeks .fc .fc-event.onekite-multiday .fc-event-main { padding: 0 !important; }
|
|
366
|
+
.onekite-twoweeks .fc .fc-event.onekite-multiday .fc-event-title,
|
|
367
|
+
.onekite-twoweeks .fc .fc-event.onekite-multiday .fc-event-time { display:none !important; }
|
|
368
|
+
|
|
369
|
+
.onekite-twoweeks .fc .fc-daygrid-event-harness { margin-top: 2px; }
|
|
370
|
+
/* Center single-day dots inside the day cell (match rentals) */
|
|
371
|
+
.onekite-twoweeks .fc .fc-daygrid-event-harness.onekite-harness-dot {
|
|
372
|
+
display: flex;
|
|
373
|
+
justify-content: center;
|
|
374
|
+
}
|
|
375
|
+
.onekite-twoweeks .fc .fc-daygrid-event { padding: 0 !important; }
|
|
265
376
|
.onekite-twoweeks .fc .fc-event-main { color: inherit; }
|
|
266
377
|
.onekite-twoweeks .fc .fc-event-title { display:none; }
|
|
267
378
|
.onekite-dot-wrap { display:flex; align-items:center; justify-content:center; width: 100%; }
|
|
268
379
|
.onekite-dot { width: 10px; height: 10px; border-radius: 999px; display:inline-block; }
|
|
380
|
+
.onekite-pill-spacer { display:block; height: 10px; line-height: 10px; }
|
|
269
381
|
.onekite-cal-tooltip {
|
|
270
382
|
position: absolute;
|
|
271
383
|
z-index: 99999;
|
package/package.json
CHANGED
package/plugin.json
CHANGED