nodebb-plugin-calendar-onekite 1.3.8 → 1.4.0

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/library.js CHANGED
@@ -1,5 +1,27 @@
1
1
  'use strict';
2
2
 
3
+ /**
4
+ * NodeBB v4 plugin: Calendar OneKite
5
+ * - Events + booking items (with pickupLocation per item)
6
+ * - Multi-day reservations
7
+ * - Admin validation -> HelloAsso checkout link -> payment webhook -> mark paid
8
+ * - Admin planning view + "My reservations" page
9
+ * - Settings:
10
+ * - Settings key: "calendar-onekite"
11
+ * - Admin page: /admin/plugins/calendar-onekite
12
+ * - Admin API: /api/admin/plugins/calendar-onekite
13
+ *
14
+ * Templates expected:
15
+ * - templates/calendar.tpl
16
+ * - templates/calendar-my-reservations.tpl
17
+ * - templates/admin/plugins/calendar-onekite.tpl
18
+ * - templates/admin/calendar-planning.tpl
19
+ * - templates/widgets/calendar-upcoming.tpl
20
+ * - templates/emails/calendar-reservation-created.tpl
21
+ * - templates/emails/calendar-reservation-approved.tpl
22
+ * - templates/emails/calendar-payment-confirmed.tpl
23
+ */
24
+
3
25
  const db = require.main.require('./src/database');
4
26
  const user = require.main.require('./src/user');
5
27
  const meta = require.main.require('./src/meta');
@@ -11,6 +33,7 @@ const helloAsso = require('./helloasso');
11
33
  const Plugin = {};
12
34
  let appRef = null;
13
35
 
36
+ const SETTINGS_KEY = 'calendar-onekite';
14
37
  const EVENTS_SET_KEY = 'calendar:events:start';
15
38
  const EVENT_KEY_PREFIX = 'calendar:event:';
16
39
 
@@ -23,18 +46,21 @@ async function nextReservationId() {
23
46
  return String(rid);
24
47
  }
25
48
 
26
- // nombre de jours entre deux dates (inclus)
49
+ // number of days between two yyyy-mm-dd dates (inclusive)
27
50
  function daysBetween(start, end) {
28
51
  const d1 = new Date(start);
29
52
  const d2 = new Date(end);
30
53
  if (isNaN(d1.getTime()) || isNaN(d2.getTime())) return 1;
54
+
31
55
  const t1 = Date.UTC(d1.getFullYear(), d1.getMonth(), d1.getDate());
32
56
  const t2 = Date.UTC(d2.getFullYear(), d2.getMonth(), d2.getDate());
33
57
  const diff = Math.round((t2 - t1) / (1000 * 60 * 60 * 24));
34
58
  return Math.max(1, diff + 1);
35
59
  }
36
60
 
37
- /* ********* EVENTS ********* */
61
+ /* -----------------------------
62
+ * Events
63
+ * ---------------------------- */
38
64
 
39
65
  async function createEvent(data, uid) {
40
66
  const eid = await db.incrObjectField('global', 'nextCalendarEid');
@@ -99,8 +125,7 @@ async function updateEvent(eid, data) {
99
125
  allDay: data.allDay !== undefined ? (data.allDay ? 1 : 0) : existing.allDay,
100
126
  location: data.location !== undefined ? String(data.location) : existing.location,
101
127
  visibility: data.visibility !== undefined ? String(data.visibility) : existing.visibility,
102
- bookingEnabled:
103
- data.bookingEnabled !== undefined ? (data.bookingEnabled ? 1 : 0) : (existing.bookingEnabled || 0),
128
+ bookingEnabled: data.bookingEnabled !== undefined ? (data.bookingEnabled ? 1 : 0) : (existing.bookingEnabled || 0),
104
129
  bookingItems: JSON.stringify(bookingItems),
105
130
  updatedAt: String(Date.now()),
106
131
  };
@@ -120,8 +145,10 @@ async function getEventsBetween(start, end) {
120
145
  const endTs = new Date(end).getTime();
121
146
  const eids = await db.getSortedSetRangeByScore(EVENTS_SET_KEY, 0, -1, startTs, endTs);
122
147
  if (!eids || !eids.length) return [];
148
+
123
149
  const keys = eids.map(eid => getEventKey(eid));
124
150
  const events = await db.getObjects(keys);
151
+
125
152
  return (events || []).filter(Boolean).map(ev => ({
126
153
  ...ev,
127
154
  bookingItems: JSON.parse(ev.bookingItems || '[]'),
@@ -132,16 +159,20 @@ async function getUpcomingEvents(limit = 5) {
132
159
  const now = Date.now();
133
160
  const eids = await db.getSortedSetRangeByScore(EVENTS_SET_KEY, 0, limit - 1, now, '+inf');
134
161
  if (!eids || !eids.length) return [];
162
+
135
163
  const keys = eids.map(eid => getEventKey(eid));
136
164
  const events = await db.getObjects(keys);
137
165
  return (events || []).filter(Boolean);
138
166
  }
139
167
 
140
- /* ********* PERMISSIONS ********* */
168
+ /* -----------------------------
169
+ * Permissions
170
+ * ---------------------------- */
141
171
 
142
172
  async function userCanCreate(uid) {
143
173
  if (!uid || uid === 0) return false;
144
- const settings = await Settings.get('calendar-onekite');
174
+
175
+ const settings = await Settings.get(SETTINGS_KEY);
145
176
  if (!settings || !settings.allowedGroups) return false;
146
177
 
147
178
  const allowedSet = new Set(
@@ -153,15 +184,16 @@ async function userCanCreate(uid) {
153
184
  if (!allowedSet.size) return false;
154
185
 
155
186
  const userGroupsArr = await user.getUserGroups([uid]);
156
- const groups = (userGroupsArr[0] || []).map(g => g.name.toLowerCase());
187
+ const groups = (userGroupsArr[0] || []).map(g => (g.name || '').toLowerCase());
188
+
157
189
  return groups.some(g => allowedSet.has(g));
158
190
  }
159
191
 
160
192
  async function userCanBook(uid) {
161
193
  if (!uid || uid === 0) return false;
162
194
 
163
- const settings = await Settings.get('calendar-onekite');
164
- // Si pas configuré, on permet à tout le monde connecté
195
+ const settings = await Settings.get(SETTINGS_KEY);
196
+ // if not configured -> allow any logged-in user
165
197
  if (!settings || !settings.allowedBookingGroups) return true;
166
198
 
167
199
  const allowedSet = new Set(
@@ -173,11 +205,14 @@ async function userCanBook(uid) {
173
205
  if (!allowedSet.size) return true;
174
206
 
175
207
  const userGroupsArr = await user.getUserGroups([uid]);
176
- const groups = (userGroupsArr[0] || []).map(g => g.name.toLowerCase());
208
+ const groups = (userGroupsArr[0] || []).map(g => (g.name || '').toLowerCase());
209
+
177
210
  return groups.some(g => allowedSet.has(g));
178
211
  }
179
212
 
180
- /* ********* RSVP ********* */
213
+ /* -----------------------------
214
+ * RSVP
215
+ * ---------------------------- */
181
216
 
182
217
  async function setRsvp(eid, uid, status) {
183
218
  const key = getEventKey(eid);
@@ -213,7 +248,9 @@ async function setRsvp(eid, uid, status) {
213
248
  return updated;
214
249
  }
215
250
 
216
- /* ********* PRIX ********* */
251
+ /* -----------------------------
252
+ * Pricing
253
+ * ---------------------------- */
217
254
 
218
255
  function computePrice(event, reservation) {
219
256
  const items = JSON.parse(event.bookingItems || '[]');
@@ -221,15 +258,29 @@ function computePrice(event, reservation) {
221
258
  if (!item) return 0;
222
259
 
223
260
  const unit = Number(item.price || 0);
224
- const days = reservation.days || 1;
225
- return unit * reservation.quantity * days;
261
+ const days = Number(reservation.days || 1);
262
+ return unit * Number(reservation.quantity || 0) * days;
226
263
  }
227
264
 
228
- /* ********* ADMIN PAGE (AJOUTÉ) ********* */
265
+ /* -----------------------------
266
+ * Renderers (pages)
267
+ * ---------------------------- */
268
+
269
+ function renderCalendarPage(req, res) {
270
+ res.render('calendar', { title: 'Calendrier' });
271
+ }
272
+
273
+ function renderMyReservationsPage(req, res) {
274
+ res.render('calendar-my-reservations', { title: 'Mes réservations' });
275
+ }
276
+
277
+ function renderPlanningPage(req, res) {
278
+ res.render('admin/calendar-planning', { title: 'Planning des réservations' });
279
+ }
229
280
 
230
281
  async function renderAdminPage(req, res) {
231
282
  try {
232
- const settings = (await Settings.get('calendar-onekite')) || {};
283
+ const settings = (await Settings.get(SETTINGS_KEY)) || {};
233
284
  res.render('admin/plugins/calendar-onekite', {
234
285
  title: 'Calendar OneKite',
235
286
  settings,
@@ -242,25 +293,28 @@ async function renderAdminPage(req, res) {
242
293
  }
243
294
  }
244
295
 
245
- /* ********* INIT ********* */
296
+ /* -----------------------------
297
+ * Plugin init
298
+ * ---------------------------- */
246
299
 
247
300
  Plugin.init = async function (params) {
248
301
  const { router, middleware } = params;
249
302
  appRef = params.app;
250
303
 
251
- /* PAGES */
252
-
304
+ // Public pages
253
305
  router.get('/calendar', middleware.buildHeader, renderCalendarPage);
254
306
  router.get('/api/calendar', renderCalendarPage);
255
307
 
256
308
  router.get('/calendar/my-reservations', middleware.buildHeader, renderMyReservationsPage);
257
309
  router.get('/api/calendar/my-reservations/page', renderMyReservationsPage);
258
310
 
259
- // admin planning
311
+ // Admin planning page
260
312
  router.get('/admin/calendar/planning', middleware.admin.buildHeader, renderPlanningPage);
261
313
  router.get('/api/admin/calendar/planning/page', renderPlanningPage);
262
314
 
263
- /* API EVENTS */
315
+ /* -----------------------------
316
+ * Events API
317
+ * ---------------------------- */
264
318
 
265
319
  router.get('/api/calendar/events', async (req, res) => {
266
320
  try {
@@ -321,7 +375,9 @@ Plugin.init = async function (params) {
321
375
  }
322
376
  });
323
377
 
324
- /* RSVP */
378
+ /* -----------------------------
379
+ * RSVP
380
+ * ---------------------------- */
325
381
 
326
382
  router.post('/api/calendar/event/:eid/rsvp', middleware.ensureLoggedIn, async (req, res) => {
327
383
  try {
@@ -335,7 +391,9 @@ Plugin.init = async function (params) {
335
391
  }
336
392
  });
337
393
 
338
- /* PERMISSIONS CLIENT */
394
+ /* -----------------------------
395
+ * Client permissions endpoints
396
+ * ---------------------------- */
339
397
 
340
398
  router.get('/api/calendar/permissions/create', async (req, res) => {
341
399
  const uid = req.user?.uid || 0;
@@ -349,7 +407,9 @@ Plugin.init = async function (params) {
349
407
  res.json({ allow });
350
408
  });
351
409
 
352
- /* RÉSERVATION MULTI-JOURS */
410
+ /* -----------------------------
411
+ * Multi-day reservation request
412
+ * ---------------------------- */
353
413
 
354
414
  router.post('/api/calendar/event/:eid/book', middleware.ensureLoggedIn, async (req, res) => {
355
415
  try {
@@ -360,10 +420,12 @@ Plugin.init = async function (params) {
360
420
  if (!await userCanBook(uid)) {
361
421
  return res.status(403).json({ error: 'Vous n’êtes pas autorisé à réserver du matériel.' });
362
422
  }
363
-
364
423
  if (!dateStart || !dateEnd) {
365
424
  return res.status(400).json({ error: 'Dates de début et de fin obligatoires.' });
366
425
  }
426
+ if (String(dateEnd) < String(dateStart)) {
427
+ return res.status(400).json({ error: 'La date de fin doit être ≥ la date de début.' });
428
+ }
367
429
 
368
430
  const event = await getEvent(eid);
369
431
  if (!event) return res.status(404).json({ error: 'Événement introuvable' });
@@ -378,18 +440,21 @@ Plugin.init = async function (params) {
378
440
 
379
441
  const allRes = JSON.parse(event.reservations || '[]');
380
442
 
443
+ // Stock check: sum overlapping reservations (pending_admin/awaiting_payment/paid) for the same item
381
444
  const overlapping = allRes.filter(r => {
382
445
  if (r.itemId !== itemId) return false;
383
446
  if (r.status === 'cancelled') return false;
447
+
384
448
  const startR = new Date(r.dateStart);
385
449
  const endR = new Date(r.dateEnd);
386
450
  const startN = new Date(dateStart);
387
451
  const endN = new Date(dateEnd);
452
+
388
453
  return !(endN < startR || startN > endR);
389
454
  });
390
455
 
391
- const used = overlapping.reduce((sum, r) => sum + r.quantity, 0);
392
- const available = item.total - used;
456
+ const used = overlapping.reduce((sum, r) => sum + Number(r.quantity || 0), 0);
457
+ const available = Number(item.total || 0) - used;
393
458
  if (q > available) return res.status(400).json({ error: 'Matériel indisponible pour ces dates.' });
394
459
 
395
460
  const rid = await nextReservationId();
@@ -408,7 +473,7 @@ Plugin.init = async function (params) {
408
473
  status: 'pending_admin',
409
474
  helloAssoOrderId: null,
410
475
  createdAt: now,
411
- pickupLocation: item.pickupLocation || '',
476
+ pickupLocation: String(item.pickupLocation || ''),
412
477
  };
413
478
 
414
479
  allRes.push(reservation);
@@ -417,6 +482,7 @@ Plugin.init = async function (params) {
417
482
 
418
483
  await db.setObject(getEventKey(eid), event);
419
484
 
485
+ // Email: request created
420
486
  try {
421
487
  await emailer.send('calendar-reservation-created', uid, {
422
488
  subject: 'Votre demande de réservation a été envoyée',
@@ -443,37 +509,46 @@ Plugin.init = async function (params) {
443
509
  }
444
510
  });
445
511
 
446
- /* ADMIN : LISTE PENDING */
512
+ /* -----------------------------
513
+ * Admin: pending list
514
+ * ---------------------------- */
447
515
 
448
516
  router.get('/api/admin/calendar/pending', middleware.admin.checkPrivileges, async (req, res) => {
449
517
  try {
450
518
  const result = [];
451
519
  const eids = await db.getSortedSetRange(EVENTS_SET_KEY, 0, -1);
520
+
452
521
  for (const eid of eids) {
453
522
  const event = await getEvent(eid);
454
523
  if (!event) continue;
524
+
455
525
  const reservations = JSON.parse(event.reservations || '[]');
456
526
  const pending = reservations.filter(r => r.status === 'pending_admin');
457
527
  if (pending.length) result.push({ event, reservations: pending });
458
528
  }
529
+
459
530
  res.json(result);
460
531
  } catch (err) {
461
532
  res.status(500).json({ error: err.message });
462
533
  }
463
534
  });
464
535
 
465
- /* ADMIN : VALIDATION */
536
+ /* -----------------------------
537
+ * Admin: validate reservation -> awaiting_payment + HelloAsso checkout
538
+ * ---------------------------- */
466
539
 
467
540
  router.post('/api/admin/calendar/reservation/:rid/validate', middleware.admin.checkPrivileges, async (req, res) => {
468
541
  try {
469
542
  const rid = req.params.rid;
470
543
  const eids = await db.getSortedSetRange(EVENTS_SET_KEY, 0, -1);
544
+
471
545
  let targetEvent = null;
472
546
  let reservation = null;
473
547
 
474
548
  for (const eid of eids) {
475
549
  const event = await getEvent(eid);
476
550
  if (!event) continue;
551
+
477
552
  const reservations = JSON.parse(event.reservations || '[]');
478
553
  const r = reservations.find(rr => rr.rid === rid);
479
554
  if (r) {
@@ -487,6 +562,7 @@ Plugin.init = async function (params) {
487
562
  if (reservation.status !== 'pending_admin') return res.status(400).json({ error: 'Réservation déjà traitée' });
488
563
 
489
564
  reservation.status = 'awaiting_payment';
565
+
490
566
  const resList = JSON.parse(targetEvent.reservations || '[]');
491
567
  const idx = resList.findIndex(r => r.rid === rid);
492
568
  resList[idx] = reservation;
@@ -526,49 +602,57 @@ Plugin.init = async function (params) {
526
602
  }
527
603
  });
528
604
 
529
- /* ADMIN : ANNULATION */
605
+ /* -----------------------------
606
+ * Admin: cancel reservation
607
+ * ---------------------------- */
530
608
 
531
609
  router.post('/api/admin/calendar/reservation/:rid/cancel', middleware.admin.checkPrivileges, async (req, res) => {
532
610
  try {
533
611
  const rid = req.params.rid;
534
612
  const eids = await db.getSortedSetRange(EVENTS_SET_KEY, 0, -1);
535
- let reservation = null;
613
+
614
+ let found = false;
536
615
 
537
616
  for (const eid of eids) {
538
617
  const event = await getEvent(eid);
539
618
  if (!event) continue;
619
+
540
620
  const reservations = JSON.parse(event.reservations || '[]');
541
621
  const rIndex = reservations.findIndex(rr => rr.rid === rid);
542
622
  if (rIndex !== -1) {
543
- reservation = reservations[rIndex];
544
- reservation.status = 'cancelled';
545
- reservations[rIndex] = reservation;
623
+ reservations[rIndex].status = 'cancelled';
546
624
  event.reservations = JSON.stringify(reservations);
547
625
  await db.setObject(getEventKey(event.eid), event);
626
+ found = true;
548
627
  break;
549
628
  }
550
629
  }
551
630
 
552
- if (!reservation) return res.status(404).json({ error: 'Réservation introuvable' });
631
+ if (!found) return res.status(404).json({ error: 'Réservation introuvable' });
553
632
  res.json({ success: true });
554
633
  } catch (err) {
555
634
  res.status(500).json({ error: err.message });
556
635
  }
557
636
  });
558
637
 
559
- /* WEBHOOK HELLOASSO */
638
+ /* -----------------------------
639
+ * HelloAsso webhook (marks reservation paid)
640
+ * ---------------------------- */
560
641
 
561
642
  router.post('/api/calendar/helloasso/webhook', async (req, res) => {
562
643
  try {
563
644
  const payload = req.body;
564
645
  const order = payload.order || payload;
565
- const orderId = String(order.id);
646
+
647
+ const orderId = String(order.id || '');
566
648
  const state = order.state || order.status || '';
567
649
  if (state !== 'Paid') return res.json({ ignored: true });
568
650
 
569
651
  const custom = order.customFields || {};
570
- const eid = String(custom.eid);
571
- const rid = String(custom.rid);
652
+ const eid = String(custom.eid || '');
653
+ const rid = String(custom.rid || '');
654
+
655
+ if (!eid || !rid) return res.status(400).json({ error: 'Missing eid/rid in customFields' });
572
656
 
573
657
  const event = await getEvent(eid);
574
658
  if (!event) throw new Error('Event not found');
@@ -580,12 +664,13 @@ Plugin.init = async function (params) {
580
664
  const reservation = reservations[rIndex];
581
665
  if (reservation.status === 'paid') return res.json({ ok: true });
582
666
 
667
+ // Update optional aggregate reserved count
583
668
  const items = JSON.parse(event.bookingItems || '[]');
584
669
  const itemIndex = items.findIndex(i => i.id === reservation.itemId);
585
670
  if (itemIndex !== -1) {
586
- const item = items[itemIndex];
587
- item.reserved = (item.reserved || 0) + reservation.quantity;
588
- items[itemIndex] = item;
671
+ const it = items[itemIndex];
672
+ it.reserved = (Number(it.reserved || 0) + Number(reservation.quantity || 0));
673
+ items[itemIndex] = it;
589
674
  }
590
675
 
591
676
  reservation.status = 'paid';
@@ -619,21 +704,26 @@ Plugin.init = async function (params) {
619
704
  }
620
705
  });
621
706
 
622
- /* PAGE ADMIN PLUGIN (CORRIGÉ) */
707
+ /* -----------------------------
708
+ * Admin plugin page + settings API
709
+ * IMPORTANT: keep these routes EXACTLY aligned with your admin.js
710
+ * ---------------------------- */
623
711
 
624
712
  router.get('/admin/plugins/calendar-onekite', middleware.admin.buildHeader, renderAdminPage);
625
713
  router.get('/api/admin/plugins/calendar-onekite', renderAdminPage);
626
714
 
627
715
  router.put('/api/admin/plugins/calendar-onekite', middleware.admin.checkPrivileges, async (req, res) => {
628
716
  try {
629
- await Settings.set('calendar-onekite', req.body);
717
+ await Settings.set(SETTINGS_KEY, req.body);
630
718
  res.json({ status: 'ok' });
631
719
  } catch (err) {
632
720
  res.status(500).json({ error: err.message });
633
721
  }
634
722
  });
635
723
 
636
- /* PAGE "MES RÉSERVATIONS" */
724
+ /* -----------------------------
725
+ * My reservations API
726
+ * ---------------------------- */
637
727
 
638
728
  router.get('/api/calendar/my-reservations', middleware.ensureLoggedIn, async (req, res) => {
639
729
  try {
@@ -646,10 +736,12 @@ Plugin.init = async function (params) {
646
736
  for (const eid of eids) {
647
737
  const event = await getEvent(eid);
648
738
  if (!event) continue;
739
+
649
740
  const items = JSON.parse(event.bookingItems || '[]');
650
741
  const reservations = JSON.parse(event.reservations || '[]');
742
+
651
743
  reservations
652
- .filter(r => r.uid === uid)
744
+ .filter(r => String(r.uid) === uid)
653
745
  .forEach(r => {
654
746
  const item = items.find(i => i.id === r.itemId);
655
747
  result.push({
@@ -663,14 +755,15 @@ Plugin.init = async function (params) {
663
755
  }
664
756
 
665
757
  result.sort((a, b) => new Date(a.dateStart) - new Date(b.dateStart));
666
-
667
758
  res.json(result);
668
759
  } catch (err) {
669
760
  res.status(500).json({ error: err.message });
670
761
  }
671
762
  });
672
763
 
673
- /* PAGE ADMIN PLANNING : liste toutes les résas futures */
764
+ /* -----------------------------
765
+ * Admin planning API (future reservations)
766
+ * ---------------------------- */
674
767
 
675
768
  router.get('/api/admin/calendar/planning', middleware.admin.checkPrivileges, async (req, res) => {
676
769
  try {
@@ -681,8 +774,10 @@ Plugin.init = async function (params) {
681
774
  for (const eid of eids) {
682
775
  const event = await getEvent(eid);
683
776
  if (!event) continue;
777
+
684
778
  const items = JSON.parse(event.bookingItems || '[]');
685
779
  const reservations = JSON.parse(event.reservations || '[]');
780
+
686
781
  reservations
687
782
  .filter(r => r.status === 'pending_admin' || r.status === 'awaiting_payment' || r.status === 'paid')
688
783
  .forEach(r => {
@@ -711,20 +806,13 @@ Plugin.init = async function (params) {
711
806
  });
712
807
  };
713
808
 
714
- function renderCalendarPage(req, res) {
715
- res.render('calendar', { title: 'Calendrier' });
716
- }
717
-
718
- function renderMyReservationsPage(req, res) {
719
- res.render('calendar-my-reservations', { title: 'Mes réservations' });
720
- }
721
-
722
- function renderPlanningPage(req, res) {
723
- res.render('admin/calendar-planning', { title: 'Planning des réservations' });
724
- }
809
+ /* -----------------------------
810
+ * ACP nav entry
811
+ * ---------------------------- */
725
812
 
726
813
  Plugin.addAdminNavigation = function (header) {
727
814
  header.plugins.push({
815
+ // NodeBB ACP will resolve this under /admin
728
816
  route: '/plugins/calendar-onekite',
729
817
  icon: 'fa fa-calendar',
730
818
  name: 'Calendar OneKite',
@@ -732,6 +820,10 @@ Plugin.addAdminNavigation = function (header) {
732
820
  return header;
733
821
  };
734
822
 
823
+ /* -----------------------------
824
+ * Widget
825
+ * ---------------------------- */
826
+
735
827
  Plugin.defineWidgets = async function (widgets) {
736
828
  widgets.push({
737
829
  widget: 'calendarUpcoming',
@@ -744,7 +836,7 @@ Plugin.defineWidgets = async function (widgets) {
744
836
 
745
837
  Plugin.renderUpcomingWidget = async function (widget, callback) {
746
838
  try {
747
- const settings = (await Settings.get('calendar-onekite')) || {};
839
+ const settings = (await Settings.get(SETTINGS_KEY)) || {};
748
840
  const limit = Number(widget.data.limit || settings.limit || 5);
749
841
  const events = await getUpcomingEvents(limit);
750
842
  const html = await appRef.renderAsync('widgets/calendar-upcoming', { events });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodebb-plugin-calendar-onekite",
3
- "version": "1.3.8",
3
+ "version": "1.4.0",
4
4
  "description": "",
5
5
  "repository": {
6
6
  "type": "git",
package/plugin.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "name": "Calendar Onekite",
4
4
  "description": "Calendrier + réservation de matériel + validation admin + paiement HelloAsso pour NodeBB v4",
5
5
  "url": "",
6
- "version": "1.3.8",
6
+ "version": "1.4.0",
7
7
  "library": "./library.js",
8
8
  "staticDirs": {
9
9
  "static": "static"
@@ -1,5 +1,5 @@
1
1
  <div id="calendar-onekite-admin">
2
- <h2>Calendar Lite – Administration</h2>
2
+ <h2>Calendar Onekite – Administration</h2>
3
3
 
4
4
  <h3>Réservations en attente</h3>
5
5
  <div id="pending-reservations"></div>
@@ -2,8 +2,8 @@
2
2
  <h2>Planning des réservations de matériel</h2>
3
3
 
4
4
  <p>
5
- <a href="/admin/plugins/calendar-lite" class="btn btn-default btn-sm">
6
- ← Retour au plugin Calendar Lite
5
+ <a href="/admin/plugins/calendar-onekite" class="btn btn-default btn-sm">
6
+ ← Retour au plugin Calendar Onekite
7
7
  </a>
8
8
  </p>
9
9
 
@@ -27,4 +27,4 @@
27
27
  </table>
28
28
  </div>
29
29
 
30
- <script src="/plugins/nodebb-plugin-calendar-lite/static/js/admin.js"></script>
30
+ <script src="/plugins/nodebb-plugin-calendar-onekite/static/js/admin.js"></script>