nodebb-plugin-calendar-onekite 11.2.27 → 11.2.29

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/CHANGELOG.md CHANGED
@@ -1,13 +1,18 @@
1
+ ## 1.0.4
2
+ - Fix: prevent `$ is not defined` by providing an immediate no-op global `$`/`jQuery` stub on calendar page and replacing it with real jQuery when available.
3
+ - Fix: provide a compatibility shim for `app.alert(...)` to reduce console errors on some themes.
4
+
5
+
1
6
  # Changelog
2
7
 
3
- ## 1.0.2
4
- - Calendrier : compatibilité thèmes/legacy expose `window.$` / `window.jQuery` si absents (dependency AMD `jquery`) pour éviter l’erreur **`$ is not defined`**.
5
- - Calendrier : compatibilité NodeBB shim minimal pour `app.alert(...)` (redirige vers le module `alerts`) afin d’éviter l’erreur **`app.alert is not a function`**.
6
- - Calendrier : évite la double initialisation (hook `action:ajaxify.end` + tick initial) qui pouvait provoquer un clignotement au chargement.
7
- - ACP : suppression du `require(...)` inline dans le template ACP (l’ACP script est déjà chargé via `acpScripts`) et auto-init via `ajaxify`.
8
+ ## 1.0.3
9
+ - ACP : correction du multi-binding sur le bouton "Enregistrer" (plus de 4 popups vertes).
10
+ - ACP : blocage de la soumission native du formulaire (plus de reload avec query params, et plus d'erreur `require is not defined`).
11
+ - Calendrier : ajout d'un shim `$` plus robuste (attend `require`), afin d'éviter `Uncaught ReferenceError: $ is not defined` sur les retours paiement / thèmes legacy.
12
+ - Calendrier : garde-fou anti double-initialisation pour supprimer le clignotement (double refresh) au chargement et après certaines actions.
8
13
 
9
14
  ## 1.0.1
10
- - Correctif : après validation dune réservation depuis l’ACP, le lien HelloAsso (`paymentUrl`) est maintenant bien enregistré et transmis au calendrier (bouton **Payer maintenant** visible dans la modale).
15
+ - Correctif : après validation d'une réservation depuis l’ACP, le lien HelloAsso (paymentUrl) est maintenant bien enregistré et transmis au calendrier (bouton **Payer maintenant** visible dans la modale).
11
16
 
12
17
  ## 1.0.0
13
18
 
@@ -20,7 +25,7 @@
20
25
  - Évènements : alignement de l’horloge identique aux icônes des réservations (rendu sans colonne de temps FullCalendar).
21
26
  - Création d’évènement : clic sur un jour = valeur par défaut **07:00 → 07:00** (gestion robuste de l’`end` exclusif FullCalendar).
22
27
  - Modales : correction d’un cas où, après annulation (ESC/clic extérieur), les modales ne réapparaissaient plus.
23
- - Nouveau : conservation du dernier mode sélectionné (**Location** / **Évènement**) même après création/annulation/refetch (persisté par utilisateur via `localStorage`).
28
+ - **Nouveau** : conservation du dernier mode sélectionné (**Location** / **Évènement**) même après création/annulation/refetch (persisté par utilisateur via `localStorage`).
24
29
 
25
30
  ### Emails
26
31
  - Template relance : ajout du même bloc de paiement que dans pending (bouton + lien de secours).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodebb-plugin-calendar-onekite",
3
- "version": "11.2.27",
3
+ "version": "11.2.29",
4
4
  "description": "FullCalendar-based equipment reservation workflow with admin approval & HelloAsso payment for NodeBB",
5
5
  "main": "library.js",
6
6
  "license": "MIT",
@@ -11,4 +11,4 @@
11
11
  "nbbpm": {
12
12
  "compatibility": "^4.0.0"
13
13
  }
14
- }
14
+ }
package/plugin.json CHANGED
@@ -31,5 +31,5 @@
31
31
  "acpScripts": [
32
32
  "public/admin.js"
33
33
  ],
34
- "version": "1.0.2"
35
- }
34
+ "version": "1.0.4"
35
+ }
package/public/admin.js CHANGED
@@ -1,5 +1,5 @@
1
1
 
2
- define('admin/plugins/calendar-onekite', ['alerts', 'bootbox', 'hooks'], function (alerts, bootbox, hooks) {
2
+ define('admin/plugins/calendar-onekite', ['alerts', 'bootbox'], function (alerts, bootbox) {
3
3
  'use strict';
4
4
 
5
5
  // Cache of pending reservations keyed by rid so delegated click handlers
@@ -396,16 +396,45 @@ define('admin/plugins/calendar-onekite', ['alerts', 'bootbox', 'hooks'], functio
396
396
  }
397
397
  }
398
398
 
399
+ // Expose the current save handler so the ONE global click listener can
400
+ // always call the latest page's `doSave` (ajaxify rebuilds the DOM).
401
+ try {
402
+ window.__oneKiteAdminDoSave = doSave;
403
+ } catch (e) {}
404
+
399
405
  // Save buttons (NodeBB header/footer "Enregistrer" + floppy icon)
400
406
  // Use ONE delegated listener to avoid double submissions.
401
407
  const SAVE_SELECTOR = '#save, .save, [data-action="save"], .settings-save, .floating-save, .btn[data-action="save"]';
402
- document.addEventListener('click', (ev) => {
403
- const btn = ev.target && ev.target.closest && ev.target.closest(SAVE_SELECTOR);
404
- if (!btn) return;
405
- // Only handle clicks while we're on this plugin page
406
- if (!document.getElementById('onekite-settings-form')) return;
407
- doSave(ev);
408
- });
408
+ if (!window.__oneKiteAdminSaveBound) {
409
+ window.__oneKiteAdminSaveBound = true;
410
+ document.addEventListener('click', (ev) => {
411
+ const btn = ev.target && ev.target.closest && ev.target.closest(SAVE_SELECTOR);
412
+ if (!btn) return;
413
+ // Only handle clicks while we're on this plugin page
414
+ if (!document.getElementById('onekite-settings-form')) return;
415
+ try {
416
+ ev.preventDefault();
417
+ ev.stopPropagation();
418
+ } catch (e) {}
419
+ // Call the latest save handler (set during init)
420
+ if (typeof window.__oneKiteAdminDoSave === 'function') {
421
+ window.__oneKiteAdminDoSave(ev);
422
+ }
423
+ }, true);
424
+ }
425
+
426
+ // Also prevent native form submission (which can cause a full page reload
427
+ // with query params, and multiple "success" toasts).
428
+ if (form && form.dataset && form.dataset.oneKiteSubmitBound !== '1') {
429
+ form.dataset.oneKiteSubmitBound = '1';
430
+ form.addEventListener('submit', (ev) => {
431
+ try {
432
+ ev.preventDefault();
433
+ ev.stopPropagation();
434
+ } catch (e) {}
435
+ doSave(ev);
436
+ });
437
+ }
409
438
 
410
439
  // Approve/refuse buttons
411
440
  const pendingWrap = document.getElementById('onekite-pending');
@@ -782,23 +811,5 @@ define('admin/plugins/calendar-onekite', ['alerts', 'bootbox', 'hooks'], functio
782
811
  }
783
812
  }
784
813
 
785
- // Auto-init when the ACP page is rendered (ajaxify navigation).
786
- function autoInit(data) {
787
- try {
788
- const tpl = data && data.template ? data.template.name : (window.ajaxify && window.ajaxify.data && window.ajaxify.data.template ? window.ajaxify.data.template.name : '');
789
- if (tpl === 'admin/plugins/calendar-onekite') {
790
- init();
791
- }
792
- } catch (e) {}
793
- }
794
-
795
- try {
796
- if (hooks && typeof hooks.on === 'function') {
797
- hooks.on('action:ajaxify.end', autoInit);
798
- }
799
- // Initial load fallback
800
- setTimeout(() => autoInit({ template: (window.ajaxify && window.ajaxify.data && window.ajaxify.data.template) || { name: '' } }), 0);
801
- } catch (e) {}
802
-
803
814
  return { init };
804
815
  });
package/public/client.js CHANGED
@@ -1,34 +1,8 @@
1
1
  /* global FullCalendar, ajaxify */
2
2
 
3
- define('forum/calendar-onekite', ['jquery', 'alerts', 'bootbox', 'hooks'], function ($, alerts, bootbox, hooks) {
3
+ define('forum/calendar-onekite', ['alerts', 'bootbox', 'hooks'], function (alerts, bootbox, hooks) {
4
4
  'use strict';
5
5
 
6
- // Some themes or legacy scripts still expect a global jQuery ($).
7
- // Provide a safe global alias when missing.
8
- try {
9
- if (typeof window.$ === 'undefined' && $) window.$ = $;
10
- if (typeof window.jQuery === 'undefined' && $) window.jQuery = $;
11
- } catch (e) {}
12
-
13
- // NodeBB core evolved from app.alert(...) to the alerts module.
14
- // Provide a tiny compatibility shim to prevent console errors from legacy code.
15
- try {
16
- if (typeof window.app !== 'undefined' && window.app && typeof window.app.alert !== 'function') {
17
- window.app.alert = function (data) {
18
- try {
19
- const type = (data && data.type) ? String(data.type) : 'info';
20
- const msg = (data && (data.message || data.text)) ? String(data.message || data.text) : '';
21
- if (!msg) return;
22
- if (alerts && typeof alerts[type] === 'function') {
23
- alerts[type](msg);
24
- } else if (alerts && typeof alerts.alert === 'function') {
25
- alerts.alert({ type, message: msg });
26
- }
27
- } catch (e) {}
28
- };
29
- }
30
- } catch (e) {}
31
-
32
6
  // Ensure small UI tweaks are applied even when themes override bootstrap defaults.
33
7
  (function ensureOneKiteStyles() {
34
8
  try {
@@ -854,22 +828,20 @@ function toDatetimeLocalValue(date) {
854
828
  return;
855
829
  }
856
830
 
857
- // Prevent double initialisation (ajaxify.end + initial-load tick) which can
858
- // cause a visible "double refresh" flicker.
859
- try {
860
- if (el.dataset && el.dataset.onekiteInit === '1') {
861
- return;
862
- }
863
- if (el.dataset) {
864
- el.dataset.onekiteInit = '1';
865
- }
866
- } catch (e) {}
867
-
868
831
  if (typeof FullCalendar === 'undefined') {
869
832
  showAlert('error', 'FullCalendar non chargé');
870
833
  return;
871
834
  }
872
835
 
836
+ // Guard against double initialisation (ajaxify.end + initial tick)
837
+ // which can cause a visible "double refresh"/blink.
838
+ if (el.dataset && el.dataset.oneKiteCalendarInited === '1') {
839
+ return;
840
+ }
841
+ if (el.dataset) {
842
+ el.dataset.oneKiteCalendarInited = '1';
843
+ }
844
+
873
845
  const items = await loadItems();
874
846
  const caps = await loadCapabilities().catch(() => ({}));
875
847
  const canCreateSpecial = !!caps.canCreateSpecial;
@@ -1670,19 +1642,10 @@ function toDatetimeLocalValue(date) {
1670
1642
  }
1671
1643
 
1672
1644
  // Auto-init on /calendar when ajaxify finishes rendering.
1673
- // Prevent double initialisation (ajaxify.end + initial tick) which causes a visible flicker.
1674
- let didInit = false;
1675
-
1676
1645
  function autoInit(data) {
1677
1646
  try {
1678
1647
  const tpl = data && data.template ? data.template.name : (ajaxify && ajaxify.data && ajaxify.data.template ? ajaxify.data.template.name : '');
1679
1648
  if (tpl === 'calendar-onekite') {
1680
- const el = document.querySelector('#onekite-calendar');
1681
- if (!el) return;
1682
- if (el.dataset && el.dataset.onekiteInit === '1') return;
1683
- if (didInit) return;
1684
- didInit = true;
1685
- try { if (el.dataset) el.dataset.onekiteInit = '1'; } catch (e) {}
1686
1649
  init('#onekite-calendar');
1687
1650
  }
1688
1651
  } catch (e) {}
@@ -179,10 +179,12 @@
179
179
  </div>
180
180
  </div>
181
181
 
182
- <!--
183
- No inline require() here.
184
- NodeBB loads this plugin's ACP script via plugin.json (acpScripts).
185
- The admin module auto-initialises on ajaxify.
186
- -->
182
+ <script>
183
+ require(['admin/plugins/calendar-onekite'], function (mod) {
184
+ if (mod && mod.init) {
185
+ mod.init();
186
+ }
187
+ });
188
+ </script>
187
189
 
188
190
  <!-- IMPORT admin/partials/settings/footer.tpl -->
@@ -7,6 +7,65 @@
7
7
  </div>
8
8
  </div>
9
9
 
10
+ <!--
11
+ Some themes/plugins still rely on a global `$` (jQuery), especially on
12
+ redirect/return flows (payment providers). NodeBB uses AMD modules, so
13
+ jQuery is not guaranteed to be on `window`.
14
+ We install a tiny shim that waits until `require` exists, then exposes
15
+ `window.$` and `window.jQuery`.
16
+ -->
17
+ <script>
18
+ (function () {
19
+ try {
20
+ // Provide an immediate global `$`/`jQuery` to avoid legacy scripts crashing
21
+ // before RequireJS is available. This stub is replaced with real jQuery when possible.
22
+ if (typeof window.$ === 'undefined') {
23
+ var noop = function () { return noop; };
24
+ noop.length = 0;
25
+ noop.ready = function (fn) { try { if (typeof fn === 'function') fn(); } catch (e) {} return noop; };
26
+ noop.on = noop.off = noop.click = noop.append = noop.prepend = noop.remove =
27
+ noop.addClass = noop.removeClass = noop.toggleClass = noop.attr = noop.css =
28
+ noop.text = noop.html = noop.val = function () { return noop; };
29
+ noop.ajax = function () { try { return Promise.resolve(); } catch (e) { return {}; } };
30
+ window.jQuery = window.$ = noop;
31
+ }
32
+
33
+ // NodeBB compatibility: some themes/plugins still call app.alert({...})
34
+ try {
35
+ if (window.app && typeof window.app.alert !== 'function') {
36
+ window.app.alert = function (opts) {
37
+ try {
38
+ if (window.alerts && typeof window.alerts.alert === 'function') {
39
+ return window.alerts.alert(opts);
40
+ }
41
+ var msg = (opts && (opts.message || opts.msg)) || '';
42
+ var type = (opts && (opts.type || opts.alertType)) || '';
43
+ if (type === 'success' && window.app && typeof window.app.alertSuccess === 'function') return window.app.alertSuccess(msg);
44
+ if (window.app && typeof window.app.alertError === 'function') return window.app.alertError(msg);
45
+ } catch (e) {}
46
+ };
47
+ }
48
+ } catch (e) {}
49
+
50
+ var tries = 0;
51
+ function attempt() {
52
+ tries += 1;
53
+ // If real jQuery is already present, keep it.
54
+ if (typeof window.$ === 'function' && window.$.fn && window.$.fn.jquery) return;
55
+
56
+ if (typeof require === 'function') {
57
+ require(['jquery'], function (jq) {
58
+ try { window.jQuery = window.$ = jq; } catch (e) {}
59
+ });
60
+ return;
61
+ }
62
+ if (tries < 200) setTimeout(attempt, 25);
63
+ }
64
+ attempt();
65
+ } catch (e) {}
66
+ })();
67
+ </script>
68
+
10
69
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/fullcalendar@6.1.11/index.global.min.css" />
11
70
  <script src="https://cdn.jsdelivr.net/npm/fullcalendar@6.1.11/index.global.min.js"></script>
12
71
  <script src="https://cdn.jsdelivr.net/npm/@fullcalendar/core@6.1.11/locales-all.global.min.js"></script>