nodebb-plugin-equipment-calendar 2.2.2 → 3.0.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
@@ -17,6 +17,19 @@ const nconf = require.main.require('nconf');
17
17
 
18
18
  const winston = require.main.require('winston');
19
19
 
20
+ function decorateCalendarEvents(events) {
21
+ return (events || []).map((ev) => {
22
+ const status = ev.status || (ev.extendedProps && ev.extendedProps.status) || 'pending';
23
+ const icon = status === 'paid' ? '✅' : (status === 'approved' ? '✔️' : (status === 'pending' ? '⏳' : (status === 'cancelled' ? '❌' : '⚠️')));
24
+ const title = ev.title || '';
25
+ return Object.assign({}, ev, {
26
+ title: `${icon} ${title}`,
27
+ classNames: ['ec-status-' + status],
28
+ extendedProps: Object.assign({}, ev.extendedProps || {}, { status }),
29
+ });
30
+ });
31
+ }
32
+
20
33
  function generateId() {
21
34
  try {
22
35
  // Node 14+ / modern: crypto.randomUUID
@@ -35,6 +48,11 @@ const crypto = require('crypto');
35
48
  const plugin = {};
36
49
  const SETTINGS_KEY = 'equipmentCalendar';
37
50
 
51
+ const EC_KEYS = {
52
+ reservationsZset: 'ec:reservations',
53
+ reservationKey: (id) => `ec:reservation:${id}`,
54
+ };
55
+
38
56
  const DEFAULT_SETTINGS = {
39
57
  creatorGroups: 'registered-users',
40
58
  approverGroup: 'administrators',
@@ -681,90 +699,26 @@ plugin.addAdminRoutes = async function (params) {
681
699
  };
682
700
 
683
701
 
684
- async function renderAdminReservationsPage(req, res) {
685
- if (!(await ensureIsAdmin(req, res))) return;
686
-
687
- const settings = await getSettings();
688
- const items = await getActiveItems(settings);
689
- const itemById = {};
690
- items.forEach(it => { itemById[it.id] = it; });
691
-
692
- const status = String(req.query.status || ''); // optional filter
693
- const itemId = String(req.query.itemId || ''); // optional filter
694
- const q = String(req.query.q || '').trim(); // search rid/user/notes
695
-
696
- const page = Math.max(1, parseInt(req.query.page, 10) || 1);
697
- const perPage = Math.min(100, Math.max(10, parseInt(req.query.perPage, 10) || 50));
698
-
699
- const allRids = await db.getSortedSetRevRange('equipmentCalendar:reservations', 0, -1);
700
- const totalAll = allRids.length;
701
-
702
- const rows = [];
703
- for (const rid of allRids) {
704
- // eslint-disable-next-line no-await-in-loop
705
- const r = await db.getObject(`equipmentCalendar:reservation:${rid}`);
706
- if (!r || !r.rid) continue;
707
-
708
- if (status && String(r.status) !== status) continue;
709
- if (itemId && String(r.itemId) !== itemId) continue;
710
-
711
- const notes = String(r.notesUser || '');
712
- const ridStr = String(r.rid || rid);
713
- if (q) {
714
- const hay = (ridStr + ' ' + String(r.uid || '') + ' ' + notes).toLowerCase();
715
- if (!hay.includes(q.toLowerCase())) continue;
716
- }
717
702
 
703
+ async function renderAdminReservationsPage(req, res) {
704
+ const isAdmin = await user.isAdminOrGlobalMod(req.uid);
705
+ if (!isAdmin) return res.status(403).render('403', {});
706
+ const ids = await db.getSortedSetRevRange(EC_KEYS.reservationsZset, 0, 199);
707
+ const rowsRaw = await db.getObjects(ids.map(id => EC_KEYS.reservationKey(id)));
708
+ const rows = (rowsRaw || []).filter(Boolean).map((r) => {
718
709
  const startMs = parseInt(r.startMs, 10) || 0;
719
710
  const endMs = parseInt(r.endMs, 10) || 0;
720
- const createdAt = parseInt(r.createdAt, 10) || 0;
721
-
722
- rows.push({
723
- rid: ridStr,
724
- itemId: String(r.itemId || ''),
725
- itemName: (itemById[String(r.itemId || '')] && itemById[String(r.itemId || '')].name) || String(r.itemId || ''),
726
- uid: String(r.uid || ''),
727
- status: String(r.status || ''),
728
- start: startMs ? new Date(startMs).toISOString() : '',
729
- end: endMs ? new Date(endMs).toISOString() : '',
730
- createdAt: createdAt ? new Date(createdAt).toISOString() : '',
731
- notesUser: notes,
732
- });
733
- }
734
-
735
- const total = rows.length;
736
- const totalPages = Math.max(1, Math.ceil(total / perPage));
737
- const safePage = Math.min(page, totalPages);
738
- const startIndex = (safePage - 1) * perPage;
739
- const pageRows = rows.slice(startIndex, startIndex + perPage);
740
-
741
- const itemOptions = [{ id: '', name: 'Tous' }].concat(items.map(i => ({ id: i.id, name: i.name })));
742
- const statusOptions = [
743
- { id: '', name: 'Tous' },
744
- { id: 'pending', name: 'pending' },
745
- { id: 'approved', name: 'approved' },
746
- { id: 'paid', name: 'paid' },
747
- { id: 'rejected', name: 'rejected' },
748
- { id: 'cancelled', name: 'cancelled' },
749
- ];
750
-
751
- res.render('admin/plugins/equipment-calendar-reservations', {
752
- title: 'Equipment Calendar - Réservations',
753
- settings,
754
- rows: pageRows,
755
- hasRows: pageRows.length > 0,
756
- itemOptions: itemOptions.map(o => ({ ...o, selected: o.id === itemId })),
757
- statusOptions: statusOptions.map(o => ({ ...o, selected: o.id === status })),
758
- q,
759
- page: safePage,
760
- perPage,
761
- total,
762
- totalAll,
763
- totalPages,
764
- prevPage: safePage > 1 ? safePage - 1 : 0,
765
- nextPage: safePage < totalPages ? safePage + 1 : 0,
766
- actionBase: '/admin/plugins/equipment-calendar/reservations',
711
+ return {
712
+ id: r.id,
713
+ itemName: r.itemName || r.itemId,
714
+ startIso: startMs ? new Date(startMs).toISOString().slice(0,10) : '',
715
+ endIso: endMs ? new Date(endMs - 24*60*60*1000).toISOString().slice(0,10) : '',
716
+ days: parseInt(r.days, 10) || 1,
717
+ status: r.status || 'pending',
718
+ total: r.total || '0',
719
+ };
767
720
  });
721
+ res.render('admin/plugins/equipment-calendar-reservations', { title: 'Réservations', rows, hasRows: rows.length>0 });
768
722
  }
769
723
 
770
724
  async function handleAdminApprove(req, res) {
@@ -989,6 +943,8 @@ async function renderCalendarPage(req, res) {
989
943
  }));
990
944
 
991
945
  res.render('equipment-calendar/calendar', {
946
+ defaultView: settings.defaultView,
947
+
992
948
  title: 'Réservation de matériel',
993
949
  items,
994
950
  view,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodebb-plugin-equipment-calendar",
3
- "version": "2.2.2",
3
+ "version": "3.0.0",
4
4
  "description": "Equipment reservation calendar for NodeBB (FullCalendar, approvals, HelloAsso payments)",
5
5
  "main": "library.js",
6
6
  "scripts": {
package/plugin.json CHANGED
@@ -25,6 +25,6 @@
25
25
  "scripts": [
26
26
  "public/js/client.js"
27
27
  ],
28
- "version": "0.7.4",
28
+ "version": "0.7.5",
29
29
  "minver": "4.7.1"
30
30
  }
@@ -93,10 +93,12 @@ require(['jquery', 'bootstrap'], function ($, bootstrap) {
93
93
  const events = Array.isArray(window.EC_EVENTS) ? window.EC_EVENTS : [];
94
94
 
95
95
  const calendar = new window.FullCalendar.Calendar(calendarEl, {
96
- initialView: 'dayGridMonth',
96
+ headerToolbar:{left:'prev,next today',center:'title',right:'dayGridMonth,listMonth,listWeek'},
97
+ initialView: (window.EC_DEFAULT_VIEW || 'dayGridMonth'),
97
98
  selectable: canCreate,
98
99
  selectMirror: true,
99
100
  events,
101
+ eventDidMount:function(info){try{const s=info.event.extendedProps&&info.event.extendedProps.status;info.el.title=(s?('Statut: '+s+' — '):'')+info.event.title;}catch(e){}},
100
102
  dateClick: function (info) {
101
103
  if (!canCreate) return;
102
104
  const startMs = Date.UTC(info.date.getUTCFullYear(), info.date.getUTCMonth(), info.date.getUTCDate());
@@ -35,7 +35,20 @@
35
35
  </div>
36
36
  </div>
37
37
 
38
+
38
39
  <div class="card card-body mb-3">
40
+ <h5>Affichage calendrier</h5>
41
+ <div class="mb-0">
42
+ <label class="form-label">Vue par défaut</label>
43
+ <select class="form-select" name="defaultView">
44
+ <option value="dayGridMonth" {{{ if view_dayGridMonth }}}selected{{{ end }}}>Mois</option>
45
+ <option value="listMonth" {{{ if view_listMonth }}}selected{{{ end }}}>Liste (mois)</option>
46
+ <option value="listWeek" {{{ if view_listWeek }}}selected{{{ end }}}>Liste (semaine)</option>
47
+ </select>
48
+ </div>
49
+ </div>
50
+
51
+ <div class="card card-body mb-3">
39
52
  <h5>HelloAsso</h5>
40
53
  <div class="mb-3">
41
54
  <label class="form-label">API Base URL (prod/sandbox)</label>
@@ -74,6 +74,7 @@
74
74
  </div>
75
75
 
76
76
  <script src="https://cdn.jsdelivr.net/npm/fullcalendar@6.1.19/index.global.min.js"></script>
77
+ <script>window.EC_DEFAULT_VIEW = '{defaultView}';</script>
77
78
  <script src="{relative_path}/plugins/nodebb-plugin-equipment-calendar/js/client.js"></script>
78
79
 
79
80
  <script>
@@ -82,3 +83,5 @@
82
83
  window.EC_CAN_CREATE = {canCreateJs};
83
84
  window.EC_TZ = '{tz}';
84
85
  </script>
86
+
87
+ <style>.ec-status-pending{opacity:.85}.ec-status-paid{font-weight:600}.ec-status-cancelled,.ec-status-expired{opacity:.6;text-decoration:line-through}</style>