nodebb-plugin-onekite-calendar 2.0.31 → 2.0.33

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 (33) hide show
  1. package/CHANGELOG.md +0 -5
  2. package/lib/api.js +0 -60
  3. package/lib/scheduler.js +6 -41
  4. package/lib/widgets.js +1 -23
  5. package/package.json +1 -1
  6. package/plugin.json +1 -1
  7. package/public/client.js +1 -28
  8. package/templates/emails/calendar-onekite_pending.tpl +2 -0
  9. package/templates/emails/calendar-onekite_validator_expired.tpl +0 -2
  10. package/pkg/package/CHANGELOG.md +0 -161
  11. package/pkg/package/lib/admin.js +0 -698
  12. package/pkg/package/lib/api.js +0 -1468
  13. package/pkg/package/lib/controllers.js +0 -11
  14. package/pkg/package/lib/db.js +0 -224
  15. package/pkg/package/lib/discord.js +0 -190
  16. package/pkg/package/lib/helloasso.js +0 -352
  17. package/pkg/package/lib/helloassoWebhook.js +0 -389
  18. package/pkg/package/lib/scheduler.js +0 -201
  19. package/pkg/package/lib/widgets.js +0 -468
  20. package/pkg/package/library.js +0 -164
  21. package/pkg/package/package.json +0 -14
  22. package/pkg/package/plugin.json +0 -43
  23. package/pkg/package/public/admin.js +0 -1493
  24. package/pkg/package/public/client.js +0 -2406
  25. package/pkg/package/templates/admin/plugins/calendar-onekite.tpl +0 -307
  26. package/pkg/package/templates/calendar-onekite.tpl +0 -51
  27. package/pkg/package/templates/emails/calendar-onekite_approved.tpl +0 -40
  28. package/pkg/package/templates/emails/calendar-onekite_cancelled.tpl +0 -15
  29. package/pkg/package/templates/emails/calendar-onekite_expired.tpl +0 -11
  30. package/pkg/package/templates/emails/calendar-onekite_paid.tpl +0 -15
  31. package/pkg/package/templates/emails/calendar-onekite_pending.tpl +0 -15
  32. package/pkg/package/templates/emails/calendar-onekite_refused.tpl +0 -15
  33. package/pkg/package/templates/emails/calendar-onekite_reminder.tpl +0 -20
package/CHANGELOG.md CHANGED
@@ -1,10 +1,5 @@
1
1
  # Changelog – calendar-onekite
2
2
 
3
- ## 1.3.28
4
- - Perf (temps réel) : debounce global des `refetchEvents()` (calendrier + widget) et **skip** quand l’onglet est masqué (refetch unique au retour de visibilité).
5
- - Perf/Robustesse (multi-instance) : ajout d’un **lock distribué Redis** sur le tick scheduler (un seul runner exécute le cycle à un instant donné).
6
- - Perf (API events) : cache Redis très court (**TTL 2s**) par utilisateur/permissions sur l’endpoint events pour absorber les rafales.
7
-
8
3
  ## 1.3.27
9
4
  - Refactor : ajout d'un module `lib/utils.js` (format dates FR, lecture settings ACP, helpers listes/UIDs) pour supprimer les duplications.
10
5
  - Cleanup : suppression d'un doublon de fonctions dans `helloassoWebhook.js` (formatFR + getReservationIdFromPayload).
package/lib/api.js CHANGED
@@ -9,8 +9,6 @@ const user = require.main.require('./src/user');
9
9
  const groups = require.main.require('./src/groups');
10
10
  const db = require.main.require('./src/database');
11
11
  const logger = require.main.require('./src/logger');
12
- let cache = null;
13
- try { cache = require.main.require('./src/cache'); } catch (e) { cache = null; }
14
12
 
15
13
  const dbLayer = require('./db');
16
14
  const { formatFR } = require('./utils');
@@ -479,42 +477,6 @@ function computeEtag(payload) {
479
477
  return `W/"${hash}"`;
480
478
  }
481
479
 
482
- async function cacheGet(key) {
483
- try {
484
- if (!cache) return null;
485
- if (typeof cache.get === 'function') {
486
- if (cache.get.length >= 2) {
487
- return await new Promise((resolve) => {
488
- cache.get(key, (err, data) => resolve(err ? null : data));
489
- });
490
- }
491
- return await cache.get(key);
492
- }
493
- } catch (e) {}
494
- return null;
495
- }
496
-
497
- async function cacheSet(key, value, ttlSeconds) {
498
- try {
499
- if (!cache) return;
500
- const ttl = Math.max(1, parseInt(ttlSeconds, 10) || 2);
501
- if (typeof cache.set === 'function') {
502
- if (cache.set.length >= 4) {
503
- await new Promise((resolve) => {
504
- cache.set(key, value, ttl, (err) => resolve(!err));
505
- });
506
- return;
507
- }
508
- // Some NodeBB versions accept an options object.
509
- try {
510
- await cache.set(key, value, { ttl });
511
- return;
512
- } catch (e) {}
513
- await cache.set(key, value, ttl);
514
- }
515
- } catch (e) {}
516
- }
517
-
518
480
  api.getEvents = async function (req, res) {
519
481
  const qStartRaw = (req && req.query && req.query.start !== undefined) ? String(req.query.start).trim() : '';
520
482
  const qEndRaw = (req && req.query && req.query.end !== undefined) ? String(req.query.end).trim() : '';
@@ -534,22 +496,6 @@ api.getEvents = async function (req, res) {
534
496
  const canSpecialCreate = req.uid ? await canCreateSpecial(req.uid, settings) : false;
535
497
  const canSpecialDelete = req.uid ? await canDeleteSpecial(req.uid, settings) : false;
536
498
 
537
- // Ultra-short Redis-backed cache (2s) to absorb bursts and reduce redundant renders.
538
- // Keyed per uid + permissions because payload can include private fields.
539
- const uidKey = req.uid ? String(req.uid) : '0';
540
- const cacheKey = `calendar-onekite:events:${uidKey}:${canMod ? 'm' : 'u'}:${widgetMode ? 'w' : 'p'}:${qStartYmd || startTs}:${qEndYmd || endTs}`;
541
- try {
542
- const cached = await cacheGet(cacheKey);
543
- if (cached && cached.payload && cached.etag) {
544
- res.setHeader('ETag', cached.etag);
545
- res.setHeader('Cache-Control', 'private, max-age=0, must-revalidate');
546
- if (String(req.headers['if-none-match'] || '') === String(cached.etag)) {
547
- return res.status(304).end();
548
- }
549
- return res.json(cached.payload);
550
- }
551
- } catch (e) {}
552
-
553
499
  // Fetch a wider window because an event can start before the query range
554
500
  // and still overlap.
555
501
  const wideStart = Math.max(0, startTs - 366 * 24 * 3600 * 1000);
@@ -688,12 +634,6 @@ api.getEvents = async function (req, res) {
688
634
  });
689
635
 
690
636
  const etag = computeEtag(out);
691
-
692
- // Store in cache for a very short period (burst absorption).
693
- try {
694
- await cacheSet(cacheKey, { etag, payload: out }, 2);
695
- } catch (e) {}
696
-
697
637
  res.setHeader('ETag', etag);
698
638
  res.setHeader('Cache-Control', 'private, max-age=0, must-revalidate');
699
639
  if (String(req.headers['if-none-match'] || '') === etag) {
package/lib/scheduler.js CHANGED
@@ -8,43 +8,9 @@ const realtime = require('./realtime');
8
8
  const nconf = require.main.require('nconf');
9
9
  const groups = require.main.require('./src/groups');
10
10
  const utils = require('./utils');
11
- let redis = null;
12
- try { redis = require.main.require('./src/redis'); } catch (e) { redis = null; }
13
11
 
14
12
  let timer = null;
15
13
 
16
- // Distributed lock (Redis) to avoid running the scheduler on multiple NodeBB instances.
17
- // Safe no-op if Redis is not available.
18
- async function acquireSchedulerLock(ttlMs) {
19
- const key = 'calendar-onekite:scheduler:lock';
20
- const value = `${process.pid}:${Date.now()}`;
21
- const ttl = Math.max(1000, parseInt(ttlMs, 10) || 55000);
22
-
23
- // Prefer Redis SET NX PX.
24
- try {
25
- const client = redis && (redis.client || redis);
26
- if (client && typeof client.set === 'function') {
27
- const res = await new Promise((resolve, reject) => {
28
- // node_redis style
29
- client.set(key, value, 'NX', 'PX', ttl, (err, out) => {
30
- if (err) return reject(err);
31
- resolve(out);
32
- });
33
- });
34
- return String(res || '').toUpperCase() === 'OK';
35
- }
36
- } catch (e) {
37
- // ignore and fall back
38
- }
39
-
40
- // Fallback: in-process lock only.
41
- if (global.__onekiteSchedulerInProcessLockUntil && Date.now() < global.__onekiteSchedulerInProcessLockUntil) {
42
- return false;
43
- }
44
- global.__onekiteSchedulerInProcessLockUntil = Date.now() + ttl;
45
- return true;
46
- }
47
-
48
14
  // Some NodeBB database adapters don't expose setAdd/setRemove helpers.
49
15
  // Use a safe "add once" guard to avoid crashing the scheduler.
50
16
  async function addOnce(key, value) {
@@ -434,14 +400,13 @@ function start() {
434
400
  // eslint-disable-next-line no-console
435
401
  console.info('[calendar-onekite] Scheduler enabled');
436
402
  timer = setInterval(() => {
437
- (async () => {
438
- const locked = await acquireSchedulerLock(55 * 1000);
439
- if (!locked) return;
440
- await expirePending();
441
- await processAwaitingPayment();
442
- })().catch((err) => {
403
+ expirePending().catch((err) => {
404
+ // eslint-disable-next-line no-console
405
+ console.warn('[calendar-onekite] Scheduler error in expirePending', err && err.message ? err.message : err);
406
+ });
407
+ processAwaitingPayment().catch((err) => {
443
408
  // eslint-disable-next-line no-console
444
- console.warn('[calendar-onekite] Scheduler tick error', err && err.message ? err.message : err);
409
+ console.warn('[calendar-onekite] Scheduler error in processAwaitingPayment', err && err.message ? err.message : err);
445
410
  });
446
411
  }, 60 * 1000);
447
412
  }
package/lib/widgets.js CHANGED
@@ -376,20 +376,13 @@ dateClick: function() { window.location.href = calUrl; },
376
376
  let tRefetch = null;
377
377
  const refetch = function () {
378
378
  try {
379
- // Skip work while tab is hidden; refetch once when visible again.
380
- try {
381
- if (typeof document !== 'undefined' && document && document.hidden) {
382
- window.__oneKiteWidgetNeedsRefetch = true;
383
- return;
384
- }
385
- } catch (e) {}
386
379
  if (tRefetch) return;
387
380
  tRefetch = setTimeout(() => {
388
381
  tRefetch = null;
389
382
  try {
390
383
  calendar.refetchEvents();
391
384
  } catch (e) {}
392
- }, 450);
385
+ }, 200);
393
386
  } catch (e) {}
394
387
  };
395
388
 
@@ -409,21 +402,6 @@ dateClick: function() { window.location.href = calUrl; },
409
402
  };
410
403
  socket.on('event:calendar-onekite.calendarUpdated', triggerAll);
411
404
  socket.on('event:calendar-onekite.reservationUpdated', triggerAll);
412
-
413
- // If updates occurred while hidden, refetch once when visible again.
414
- try {
415
- if (!window.__oneKiteWidgetVisibilityBound && typeof document !== 'undefined' && document && typeof document.addEventListener === 'function') {
416
- window.__oneKiteWidgetVisibilityBound = true;
417
- document.addEventListener('visibilitychange', () => {
418
- try {
419
- if (!document.hidden && window.__oneKiteWidgetNeedsRefetch) {
420
- window.__oneKiteWidgetNeedsRefetch = false;
421
- triggerAll();
422
- }
423
- } catch (e) {}
424
- }, { passive: true });
425
- }
426
- } catch (e) {}
427
405
  }
428
406
  } catch (e) {}
429
407
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodebb-plugin-onekite-calendar",
3
- "version": "2.0.31",
3
+ "version": "2.0.33",
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/plugin.json CHANGED
@@ -39,5 +39,5 @@
39
39
  "acpScripts": [
40
40
  "public/admin.js"
41
41
  ],
42
- "version": "2.0.31"
42
+ "version": "2.0.33"
43
43
  }
package/public/client.js CHANGED
@@ -543,40 +543,13 @@ define('forum/calendar-onekite', ['alerts', 'bootbox', 'hooks'], function (alert
543
543
  function scheduleRefetch(cal) {
544
544
  try {
545
545
  if (!cal || typeof cal.refetchEvents !== 'function') return;
546
-
547
- // Avoid refetching while the tab is hidden; do it once when visible again.
548
- try {
549
- if (typeof document !== 'undefined' && document && document.hidden) {
550
- window.__onekiteNeedsRefetch = true;
551
- return;
552
- }
553
- } catch (e) {}
554
-
555
546
  clearTimeout(window.__onekiteRefetchTimer);
556
547
  window.__onekiteRefetchTimer = setTimeout(() => {
557
548
  try { cal.refetchEvents(); } catch (e) {}
558
- }, 450);
549
+ }, 150);
559
550
  } catch (e) {}
560
551
  }
561
552
 
562
- // If updates occurred while hidden, refetch once when visible again.
563
- try {
564
- if (!window.__onekiteVisibilityBound && typeof document !== 'undefined' && document && typeof document.addEventListener === 'function') {
565
- window.__onekiteVisibilityBound = true;
566
- document.addEventListener('visibilitychange', () => {
567
- try {
568
- if (!document.hidden && window.__onekiteNeedsRefetch) {
569
- window.__onekiteNeedsRefetch = false;
570
- const cal = window.oneKiteCalendar;
571
- if (cal && typeof cal.refetchEvents === 'function') {
572
- scheduleRefetch(cal);
573
- }
574
- }
575
- } catch (e) {}
576
- }, { passive: true });
577
- }
578
- } catch (e) {}
579
-
580
553
  async function fetchJsonCached(url, opts) {
581
554
  const cached = jsonCache.get(url);
582
555
  const headers = Object.assign({}, (opts && opts.headers) || {});
@@ -13,3 +13,5 @@
13
13
  <p>{dateRange}</p>
14
14
 
15
15
  <p><strong>Total estimé :</strong> {total} €</p>
16
+
17
+ <p><a href="{adminUrl}">Ouvrir l'ACP</a></p>
@@ -13,5 +13,3 @@
13
13
  </ul>
14
14
 
15
15
  <p>{dateRange}</p>
16
-
17
- <p><a href="{adminUrl}">Ouvrir l'ACP</a></p>
@@ -1,161 +0,0 @@
1
- # Changelog – calendar-onekite
2
-
3
- ## 1.3.21
4
- - Comptabilisation : les **sorties gratuites annulées** ne sont plus comptées (elles sont exclues si `cancelledAt` est présent, même si le statut est resté `paid`).
5
-
6
- ## 1.3.20
7
- - Temps réel : le **widget** (Calendrier 2 semaines) se met à jour dynamiquement comme la page calendrier (créations / changements de statut / annulations…), sans rechargement.
8
-
9
- ## 1.3.19
10
- - Annulation : un validateur/gestionnaire peut désormais annuler **même sa propre** réservation après paiement (la restriction `cannot-cancel-paid` ne s'applique plus aux validateurs).
11
-
12
- ## 1.3.18
13
- - Fix HelloAsso webhook : suppression d'une double déclaration `realtime` qui empêchait le plugin de se charger (SyntaxError).
14
-
15
- ## 1.3.17
16
- - Temps réel : les **créations** (locations / évènements), **changements de statut** (validation, refus, annulation, paiement reçu) et **expirations** se répercutent automatiquement sur le calendrier pour **tous les utilisateurs connectés**, sans rechargement de page.
17
-
18
- ## 1.3.16
19
- - Calendrier : le **jour actuel** n’est plus affiché comme indisponible (gris + panneau sens interdit). Seules les **dates passées** restent désactivées visuellement.
20
-
21
- ## 1.3.15
22
- - Permissions : les **modérateurs globaux / gestionnaires** (groupe NodeBB « Global Moderators », selon le slug) sont désormais considérés comme validateurs pour les actions de modération (dont l’**annulation**).
23
-
24
- ## 1.3.14
25
- - Mobile FAB : la modale de sélection de dates autorise désormais la réservation **le jour même** (seules les dates passées sont désactivées).
26
-
27
- ## 1.3.13
28
- - Réservations : il est désormais possible de réserver pour le jour même (seules les dates passées sont refusées).
29
- - Annulation : les validateurs peuvent annuler une réservation déjà payée.
30
-
31
- ## 1.3.12
32
- - Expiration automatique : envoi d’un email au demandeur avec la raison :
33
- - « Demande non prise en charge dans le temps imparti » (demande en attente)
34
- - « Paiement non reçu dans le temps imparti » (paiement en attente)
35
- - ACP : ajout de 2 paramètres pour envoyer un rappel email aux validateurs sur les demandes en attente / paiement en attente.
36
- - Emails validateurs : nouveaux templates (rappel + expiration) avec lien vers l’ACP.
37
-
38
- ## 1.3.11
39
- - ACP (Demandes en attente) : affichage du pseudo du demandeur avec lien vers sa fiche utilisateur.
40
-
41
- ## 1.3.10
42
- - Mobile FAB : surlignage de la sélection de dates rendu lisible en light mode (contraste renforcé, start/end en primary).
43
-
44
- ## 1.3.9
45
- - Mobile FAB : correction de la sélection de plage (tous les jours sélectionnés sont désormais bien inclus dans la surbrillance).
46
- - Mobile FAB : amélioration du rendu en mode sombre (bordures/fonds adaptés, contraste renforcé).
47
-
48
- ## 1.3.8
49
- - ACP Comptabilisation : ajout d’un **grand total** (montant total des locations payées sur la période) + compteurs (payées / sorties gratuites).
50
-
51
- ## 1.3.7
52
- - Mobile FAB : remplacement des 2 champs date par un **sélecteur unique** (calendrier) permettant de choisir directement une plage (clic début puis fin, fin incluse), avec dates passées/aujourd'hui désactivées.
53
-
54
- ## 1.3.6
55
- - ACP Comptabilisation : ajout d’un tableau séparé « Détails des sorties gratuites » (avec le nom du matériel).
56
- - Mobile FAB : la modale utilise maintenant des champs date avec calendrier (type="date") et empêche toute réservation pour le jour même ou dans le passé.
57
-
58
- ## 1.3.5
59
- - ACP : ajout d’un paramètre « Location longue durée (jours) pour validateurs ». Si une réservation faite par un validateur dépasse ce nombre de jours, elle redevient payante et suit le workflow normal (demande → validation → paiement HelloAsso). Mets 0 pour conserver le comportement « toujours gratuit ».
60
-
61
- ## 1.3.4
62
- - Réservations gratuites (validateurs) : ne sont plus comptées comme chiffre d’affaires dans la comptabilisation. Elles apparaissent désormais sur une ligne séparée « Sorties gratuites » (et sont marquées (gratuit) dans le détail).
63
-
64
- ## 1.3.3
65
- - Comptabilité : les réservations « auto-checkées » (réservations faites par un validateur) recalculent désormais le total côté serveur (catalogue HelloAsso × nb jours calendaires) afin d’être comptabilisées correctement.
66
-
67
- ## 1.3.2
68
- - Les membres validateurs (ceux qui peuvent valider/supprimer une demande) voient leurs propres réservations passer directement en statut payé/checked (pas de workflow paiement).
69
- - UI : message de succès adapté (réservation confirmée).
70
-
71
- ## 1.3.1
72
- - ACP : ajout de 2 actions rapides dans l’onglet Maintenance : « Tout mettre en maintenance » et « Tout enlever de maintenance » (avec audit).
73
-
74
- ## 1.3.0
75
- - Maintenance (sans dates) : blocage manuel ON/OFF par matériel (ACP + API). Les matériels en maintenance sont grisés et affichés comme : 🔧 Nom (en maintenance).
76
- - Audit : journal des actions (demande, validation, refus, annulation, maintenance) avec consultation et purge par année (ACP + API).
77
-
78
- ## 1.2.18
79
- - API events + anti double booking : les tests de chevauchement utilisent désormais en priorité startDate/endDate (YYYY-MM-DD) quand disponibles (logique calendaire pure, endDate exclusive). Cela supprime définitivement les faux chevauchements liés aux timestamps/fuseaux/DST, notamment sur mobile et « Durée rapide ».
80
-
81
- ## 1.2.17
82
- - Modale réservation : la requête de disponibilité initiale utilise aussi des dates calendaires (YYYY-MM-DD) au lieu de startStr/endStr/toISOString(), ce qui corrige le grisé erroné (mobile + durée rapide).
83
-
84
- ## 1.2.16
85
- - Modale réservation (Durée rapide) : correction du grisé erroné des matériels réservés la veille. Les requêtes de disponibilité utilisent désormais des dates calendaires (YYYY-MM-DD) au lieu de toISOString() (UTC), pour éviter tout faux chevauchement lié au fuseau/DST.
86
-
87
- ## 1.2.15
88
- - Réservations (Durée rapide) : génération des évènements all-day basée sur startDate/endDate (YYYY-MM-DD) quand disponibles, pour éviter tout décalage lié à toISOString() (UTC) et empêcher un grisé « non disponible » le jour suivant.
89
-
90
- ## 1.2.14
91
- - Réservations : correction d’un faux chevauchement (problème 1h) quand FullCalendar envoie des bornes all-day à minuit UTC (Z / +00:00) — le lendemain n’est plus marqué « non disponible ».
92
-
93
- ## 1.2.13
94
- - ACP (mode sombre) : correction de la visibilité de la liste d’autocomplete d’adresse (couleurs via variables Bootstrap)
95
-
96
- ## 1.2.12
97
- - Modales (calendrier + ACP) : autocomplete adresse rendu identique et compatible Bootstrap input-group (plus de wrapper qui casse l’affichage)
98
-
99
- ## 1.2.11
100
- - ACP : ajout de la recherche automatique d’adresse (autocomplete Nominatim) dans la modale de validation (unitaire + batch), comme sur le calendrier
101
-
102
- ## 1.2.10
103
- - HelloAsso : calcul des jours 100% fiable (différence en jours calendaires Y/M/J, sans dépendance aux heures/au fuseau/DST)
104
- - FullCalendar : endDate traitée comme exclusive partout (UI + checkout)
105
- - HelloAsso : montant du checkout recalculé côté serveur à partir du catalogue (prix/jour × nbJours)
106
-
107
- ## 1.2.9
108
- - Modale réservation : retour du grisé des matériels indisponibles (API events expose à nouveau itemIds)
109
-
110
- ## 1.2.8
111
- - Popup réservation : correction « Durée rapide » (la période envoyée correspond bien à la durée sélectionnée)
112
-
113
- ## 1.2.7
114
- - UI : suppression du bouton flottant mobile « + Réserver »
115
- - Client : nettoyage du code associé (suppression du bloc FAB)
116
-
117
- ## 1.2.6
118
- - ACP : anti double action (verrou UI + boutons désactivés/spinner) sur valider/refuser (unitaire + batch)
119
-
120
- ## 1.2.5
121
- - Mobile : bouton flottant « + Réserver »
122
- - Création : anti double-tap/click (verrou actions) + invalidation cache events + refetch léger
123
- - Calendrier : jours passés / aujourd’hui affichés comme non-sélectionnables (règle visuelle)
124
- - Popup réservation : raccourcis de durée (1j/2j/3j/7j) + recalcul total + mise à jour des matériels bloqués
125
- - ACP : actions en batch (valider/refuser une sélection) + compteur de sélection
126
- - API : idempotence sur valider/refuser + audit enrichi (refusedBy, cancelledByUsername)
127
-
128
- ## 1.0.3
129
- - Suppression du texte d’archivage dans le toast de purge (plus de « 0 archivés »)
130
- - Renommage du plugin : nodebb-plugin-onekite-calendar
131
- - Discord : notification « ❌ Réservation annulée » (annulation manuelle + annulation automatique) + option ACP
132
-
133
- ## 1.0.2
134
- - Purge calendrier : suppression réelle des réservations (aucune logique d’archivage)
135
- - Compta conservée séparément (la purge n’y touche jamais)
136
-
137
- ## 1.0.1
138
- - ACP : modales Valider / Refuser OK (plus de retour au premier onglet)
139
- - Scheduler : optimisation (batch DB, scans réduits)
140
- - API events : pagination interne + ETag optimisé
141
- - HelloAsso : token mis en cache + protection rate-limit (429 / Cloudflare 1015), sans logs supplémentaires
142
- - Discord : notifications en embeds + icônes ⏳ (demande) et 💳 (paiement)
143
- - Widget : suppression de “2 semaines” dans le titre affiché
144
- - Correctifs divers de stabilité/performance (dont crash au démarrage)
145
-
146
- ## 1.0.0
147
- - Première version stable du plugin calendar-onekite
148
- - Gestion des réservations de matériel avec contrôle de disponibilité
149
- - Calendrier FullCalendar via CDN
150
- - Validation / refus des demandes depuis l’ACP
151
- - Notifications Discord
152
- - Intégration paiements HelloAsso
153
-
154
- ## 1.2.19
155
- - Mobile: ajout d’un bouton flottant (FAB) sur la page calendrier uniquement.
156
- - Le FAB ouvre une mini-modale de sélection de dates (dd/mm/yyyy) puis ouvre la modale standard de réservation.
157
- - Le FAB est automatiquement retiré quand on navigue hors de la page calendrier.
158
-
159
- ## 1.3.10
160
- - Widget : ne pas afficher le statut « Payée » pour les sorties gratuites (validateurs).
161
- - Modale réservation : affichage de la période corrigé (fin affichée inclusive, fin stockée exclusive).