nodebb-plugin-onekite-calendar 2.0.59 → 2.0.61

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodebb-plugin-onekite-calendar",
3
- "version": "2.0.59",
3
+ "version": "2.0.61",
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.59"
42
+ "version": "2.0.61"
43
43
  }
package/public/client.js CHANGED
@@ -1676,11 +1676,14 @@ function toDatetimeLocalValue(date) {
1676
1676
  const notes = String(p.notes || '').trim();
1677
1677
  const participants = Array.isArray(p.participantsUsernames) ? p.participantsUsernames : [];
1678
1678
  const eidPlain = escapeHtml(String(p.eid || '').replace(/^special:/, ''));
1679
+ // Participants can self-join/leave if they belong to the allowed group.
1680
+ // Use page capabilities as a fallback (details may be minimal in some edge cases).
1681
+ const canJoinHere = !!(p.canJoin || canCreateSpecial);
1679
1682
  // Use real buttons with visible + / - text to avoid relying on icon fonts.
1680
- const joinBtn = (p.canJoin && !p.isParticipant)
1683
+ const joinBtn = (canJoinHere && !p.isParticipant)
1681
1684
  ? `<button type="button" class="btn btn-sm btn-success ms-2 onekite-join-special" data-eid="${eidPlain}" title="S'ajouter" aria-label="S'ajouter">+</button>`
1682
1685
  : '';
1683
- const leaveBtn = (p.canJoin && p.isParticipant)
1686
+ const leaveBtn = (canJoinHere && p.isParticipant)
1684
1687
  ? `<button type="button" class="btn btn-sm btn-danger ms-2 onekite-leave-special" data-eid="${eidPlain}" title="Se retirer" aria-label="Se retirer">−</button>`
1685
1688
  : '';
1686
1689
  const participantsHtml = `<div class="mb-2" id="onekite-participants-special"><strong>Participants</strong>${joinBtn}${leaveBtn}<br>` +
@@ -1738,60 +1741,63 @@ function toDatetimeLocalValue(date) {
1738
1741
  } : {}),
1739
1742
  },
1740
1743
  });
1741
- // Self-join handler
1744
+ // Self-join handler (event delegation so it keeps working after DOM updates)
1742
1745
  try {
1743
- dlg.on('shown.bs.modal', () => {
1744
- dlg.find('.onekite-join-special').off('click').on('click', async (e2) => {
1745
- e2.preventDefault();
1746
- const btn = e2.currentTarget;
1747
- if (!btn) return;
1748
- try {
1749
- btn.classList.add('disabled');
1750
- const eid = String(btn.getAttribute('data-eid') || '').trim();
1751
- if (!eid) return;
1752
- const r = await fetchJson(`/api/v3/plugins/calendar-onekite/special-events/${encodeURIComponent(eid)}/participants`, { method: 'POST' });
1753
- const names = Array.isArray(r && r.participantsUsernames) ? r.participantsUsernames : [];
1754
- const box = dlg.find('#onekite-participants-special');
1755
- if (box && box.length) {
1756
- const links = names.length
1757
- ? names.map((name) => {
1758
- const u = String(name || '').trim();
1759
- if (!u) return '';
1760
- return `<a class="onekite-user-link me-2" href="${window.location.origin}/user/${encodeURIComponent(u)}">${escapeHtml(u)}</a>`;
1761
- }).filter(Boolean).join('')
1762
- : `<span class="text-muted">Aucun</span>`;
1763
- box.html(`<strong>Participants</strong><button type="button" class="btn btn-sm btn-danger ms-2 onekite-leave-special" data-eid="${escapeHtml(eid)}" title="Se retirer" aria-label="Se retirer">−</button><br>${links}`);
1764
- }
1765
- } catch (e3) {
1766
- showAlert('error', "Impossible de s'ajouter.");
1746
+ dlg.off('click.onekiteParticipants');
1747
+ dlg.on('click.onekiteParticipants', '.onekite-join-special', async (e2) => {
1748
+ e2.preventDefault();
1749
+ const btn = e2.currentTarget;
1750
+ if (!btn) return;
1751
+ try {
1752
+ btn.classList.add('disabled');
1753
+ const eid = String(btn.getAttribute('data-eid') || '').trim();
1754
+ if (!eid) return;
1755
+ const r = await fetchJson(`/api/v3/plugins/calendar-onekite/special-events/${encodeURIComponent(eid)}/participants`, { method: 'POST' });
1756
+ const names = Array.isArray(r && r.participantsUsernames) ? r.participantsUsernames : [];
1757
+ const box = dlg.find('#onekite-participants-special');
1758
+ if (box && box.length) {
1759
+ const links = names.length
1760
+ ? names.map((name) => {
1761
+ const u = String(name || '').trim();
1762
+ if (!u) return '';
1763
+ return `<a class="onekite-user-link me-2" href="${window.location.origin}/user/${encodeURIComponent(u)}">${escapeHtml(u)}</a>`;
1764
+ }).filter(Boolean).join('')
1765
+ : `<span class="text-muted">Aucun</span>`;
1766
+ box.html(`<strong>Participants</strong><button type="button" class="btn btn-sm btn-danger ms-2 onekite-leave-special" data-eid="${escapeHtml(eid)}" title="Se retirer" aria-label="Se retirer">−</button><br>${links}`);
1767
1767
  }
1768
- });
1768
+ } catch (e3) {
1769
+ showAlert('error', "Impossible de s'ajouter.");
1770
+ } finally {
1771
+ try { btn.classList.remove('disabled'); } catch (e4) {}
1772
+ }
1773
+ });
1769
1774
 
1770
- dlg.find('.onekite-leave-special').off('click').on('click', async (e2) => {
1771
- e2.preventDefault();
1772
- const btn = e2.currentTarget;
1773
- if (!btn) return;
1774
- try {
1775
- btn.classList.add('disabled');
1776
- const eid = String(btn.getAttribute('data-eid') || '').trim();
1777
- if (!eid) return;
1778
- const r = await fetchJson(`/api/v3/plugins/calendar-onekite/special-events/${encodeURIComponent(eid)}/participants`, { method: 'DELETE' });
1779
- const names = Array.isArray(r && r.participantsUsernames) ? r.participantsUsernames : [];
1780
- const box = dlg.find('#onekite-participants-special');
1781
- if (box && box.length) {
1782
- const links = names.length
1783
- ? names.map((name) => {
1784
- const u = String(name || '').trim();
1785
- if (!u) return '';
1786
- return `<a class="onekite-user-link me-2" href="${window.location.origin}/user/${encodeURIComponent(u)}">${escapeHtml(u)}</a>`;
1787
- }).filter(Boolean).join('')
1788
- : `<span class="text-muted">Aucun</span>`;
1789
- box.html(`<strong>Participants</strong><button type="button" class="btn btn-sm btn-success ms-2 onekite-join-special" data-eid="${escapeHtml(eid)}" title="S'ajouter" aria-label="S'ajouter">+</button><br>${links}`);
1790
- }
1791
- } catch (e3) {
1792
- showAlert('error', "Impossible de se retirer.");
1775
+ dlg.on('click.onekiteParticipants', '.onekite-leave-special', async (e2) => {
1776
+ e2.preventDefault();
1777
+ const btn = e2.currentTarget;
1778
+ if (!btn) return;
1779
+ try {
1780
+ btn.classList.add('disabled');
1781
+ const eid = String(btn.getAttribute('data-eid') || '').trim();
1782
+ if (!eid) return;
1783
+ const r = await fetchJson(`/api/v3/plugins/calendar-onekite/special-events/${encodeURIComponent(eid)}/participants`, { method: 'DELETE' });
1784
+ const names = Array.isArray(r && r.participantsUsernames) ? r.participantsUsernames : [];
1785
+ const box = dlg.find('#onekite-participants-special');
1786
+ if (box && box.length) {
1787
+ const links = names.length
1788
+ ? names.map((name) => {
1789
+ const u = String(name || '').trim();
1790
+ if (!u) return '';
1791
+ return `<a class="onekite-user-link me-2" href="${window.location.origin}/user/${encodeURIComponent(u)}">${escapeHtml(u)}</a>`;
1792
+ }).filter(Boolean).join('')
1793
+ : `<span class="text-muted">Aucun</span>`;
1794
+ box.html(`<strong>Participants</strong><button type="button" class="btn btn-sm btn-success ms-2 onekite-join-special" data-eid="${escapeHtml(eid)}" title="S'ajouter" aria-label="S'ajouter">+</button><br>${links}`);
1793
1795
  }
1794
- });
1796
+ } catch (e3) {
1797
+ showAlert('error', "Impossible de se retirer.");
1798
+ } finally {
1799
+ try { btn.classList.remove('disabled'); } catch (e4) {}
1800
+ }
1795
1801
  });
1796
1802
  } catch (e) {}
1797
1803
  try {
@@ -1816,10 +1822,11 @@ function toDatetimeLocalValue(date) {
1816
1822
  const participants = Array.isArray(p.participantsUsernames) ? p.participantsUsernames : [];
1817
1823
  const oidPlain = escapeHtml(String(p.oid || '').replace(/^outing:/, ''));
1818
1824
  // Use real buttons with visible + / - text to avoid relying on icon fonts.
1819
- const joinBtn = (p.canJoin && !p.isParticipant)
1825
+ const canJoinHere = !!(p.canJoin || canCreateOuting);
1826
+ const joinBtn = (canJoinHere && !p.isParticipant)
1820
1827
  ? `<button type="button" class="btn btn-sm btn-success ms-2 onekite-join-outing" data-oid="${oidPlain}" title="S'ajouter" aria-label="S'ajouter">+</button>`
1821
1828
  : '';
1822
- const leaveBtn = (p.canJoin && p.isParticipant)
1829
+ const leaveBtn = (canJoinHere && p.isParticipant)
1823
1830
  ? `<button type="button" class="btn btn-sm btn-danger ms-2 onekite-leave-outing" data-oid="${oidPlain}" title="Se retirer" aria-label="Se retirer">−</button>`
1824
1831
  : '';
1825
1832
  const participantsHtml = `<div class="mb-2" id="onekite-participants-outing"><strong>Participants</strong>${joinBtn}${leaveBtn}<br>` +
@@ -1877,60 +1884,63 @@ function toDatetimeLocalValue(date) {
1877
1884
  } : {}),
1878
1885
  },
1879
1886
  });
1880
- // Self-join handler
1887
+ // Self-join handler (event delegation so it keeps working after DOM updates)
1881
1888
  try {
1882
- dlg.on('shown.bs.modal', () => {
1883
- dlg.find('.onekite-join-outing').off('click').on('click', async (e2) => {
1884
- e2.preventDefault();
1885
- const btn = e2.currentTarget;
1886
- if (!btn) return;
1887
- try {
1888
- btn.classList.add('disabled');
1889
- const oid = String(btn.getAttribute('data-oid') || '').trim();
1890
- if (!oid) return;
1891
- const r = await fetchJson(`/api/v3/plugins/calendar-onekite/outings/${encodeURIComponent(oid)}/participants`, { method: 'POST' });
1892
- const names = Array.isArray(r && r.participantsUsernames) ? r.participantsUsernames : [];
1893
- const box = dlg.find('#onekite-participants-outing');
1894
- if (box && box.length) {
1895
- const links = names.length
1896
- ? names.map((name) => {
1897
- const u = String(name || '').trim();
1898
- if (!u) return '';
1899
- return `<a class="onekite-user-link me-2" href="${window.location.origin}/user/${encodeURIComponent(u)}">${escapeHtml(u)}</a>`;
1900
- }).filter(Boolean).join('')
1901
- : `<span class="text-muted">Aucun</span>`;
1902
- box.html(`<strong>Participants</strong><button type="button" class="btn btn-sm btn-danger ms-2 onekite-leave-outing" data-oid="${escapeHtml(oid)}" title="Se retirer" aria-label="Se retirer">−</button><br>${links}`);
1903
- }
1904
- } catch (e3) {
1905
- showAlert('error', "Impossible de s'ajouter.");
1889
+ dlg.off('click.onekiteParticipantsOuting');
1890
+ dlg.on('click.onekiteParticipantsOuting', '.onekite-join-outing', async (e2) => {
1891
+ e2.preventDefault();
1892
+ const btn = e2.currentTarget;
1893
+ if (!btn) return;
1894
+ try {
1895
+ btn.classList.add('disabled');
1896
+ const oid = String(btn.getAttribute('data-oid') || '').trim();
1897
+ if (!oid) return;
1898
+ const r = await fetchJson(`/api/v3/plugins/calendar-onekite/outings/${encodeURIComponent(oid)}/participants`, { method: 'POST' });
1899
+ const names = Array.isArray(r && r.participantsUsernames) ? r.participantsUsernames : [];
1900
+ const box = dlg.find('#onekite-participants-outing');
1901
+ if (box && box.length) {
1902
+ const links = names.length
1903
+ ? names.map((name) => {
1904
+ const u = String(name || '').trim();
1905
+ if (!u) return '';
1906
+ return `<a class="onekite-user-link me-2" href="${window.location.origin}/user/${encodeURIComponent(u)}">${escapeHtml(u)}</a>`;
1907
+ }).filter(Boolean).join('')
1908
+ : `<span class="text-muted">Aucun</span>`;
1909
+ box.html(`<strong>Participants</strong><button type="button" class="btn btn-sm btn-danger ms-2 onekite-leave-outing" data-oid="${escapeHtml(oid)}" title="Se retirer" aria-label="Se retirer">−</button><br>${links}`);
1906
1910
  }
1907
- });
1911
+ } catch (e3) {
1912
+ showAlert('error', "Impossible de s'ajouter.");
1913
+ } finally {
1914
+ try { btn.classList.remove('disabled'); } catch (e4) {}
1915
+ }
1916
+ });
1908
1917
 
1909
- dlg.find('.onekite-leave-outing').off('click').on('click', async (e2) => {
1910
- e2.preventDefault();
1911
- const btn = e2.currentTarget;
1912
- if (!btn) return;
1913
- try {
1914
- btn.classList.add('disabled');
1915
- const oid = String(btn.getAttribute('data-oid') || '').trim();
1916
- if (!oid) return;
1917
- const r = await fetchJson(`/api/v3/plugins/calendar-onekite/outings/${encodeURIComponent(oid)}/participants`, { method: 'DELETE' });
1918
- const names = Array.isArray(r && r.participantsUsernames) ? r.participantsUsernames : [];
1919
- const box = dlg.find('#onekite-participants-outing');
1920
- if (box && box.length) {
1921
- const links = names.length
1922
- ? names.map((name) => {
1923
- const u = String(name || '').trim();
1924
- if (!u) return '';
1925
- return `<a class="onekite-user-link me-2" href="${window.location.origin}/user/${encodeURIComponent(u)}">${escapeHtml(u)}</a>`;
1926
- }).filter(Boolean).join('')
1927
- : `<span class="text-muted">Aucun</span>`;
1928
- box.html(`<strong>Participants</strong><button type="button" class="btn btn-sm btn-success ms-2 onekite-join-outing" data-oid="${escapeHtml(oid)}" title="S'ajouter" aria-label="S'ajouter">+</button><br>${links}`);
1929
- }
1930
- } catch (e3) {
1931
- showAlert('error', "Impossible de se retirer.");
1918
+ dlg.on('click.onekiteParticipantsOuting', '.onekite-leave-outing', async (e2) => {
1919
+ e2.preventDefault();
1920
+ const btn = e2.currentTarget;
1921
+ if (!btn) return;
1922
+ try {
1923
+ btn.classList.add('disabled');
1924
+ const oid = String(btn.getAttribute('data-oid') || '').trim();
1925
+ if (!oid) return;
1926
+ const r = await fetchJson(`/api/v3/plugins/calendar-onekite/outings/${encodeURIComponent(oid)}/participants`, { method: 'DELETE' });
1927
+ const names = Array.isArray(r && r.participantsUsernames) ? r.participantsUsernames : [];
1928
+ const box = dlg.find('#onekite-participants-outing');
1929
+ if (box && box.length) {
1930
+ const links = names.length
1931
+ ? names.map((name) => {
1932
+ const u = String(name || '').trim();
1933
+ if (!u) return '';
1934
+ return `<a class="onekite-user-link me-2" href="${window.location.origin}/user/${encodeURIComponent(u)}">${escapeHtml(u)}</a>`;
1935
+ }).filter(Boolean).join('')
1936
+ : `<span class="text-muted">Aucun</span>`;
1937
+ box.html(`<strong>Participants</strong><button type="button" class="btn btn-sm btn-success ms-2 onekite-join-outing" data-oid="${escapeHtml(oid)}" title="S'ajouter" aria-label="S'ajouter">+</button><br>${links}`);
1932
1938
  }
1933
- });
1939
+ } catch (e3) {
1940
+ showAlert('error', "Impossible de se retirer.");
1941
+ } finally {
1942
+ try { btn.classList.remove('disabled'); } catch (e4) {}
1943
+ }
1934
1944
  });
1935
1945
  } catch (e) {}
1936
1946
  try {