nodebb-plugin-equipment-calendar 3.0.0 → 4.9.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
|
@@ -20,11 +20,8 @@ const winston = require.main.require('winston');
|
|
|
20
20
|
function decorateCalendarEvents(events) {
|
|
21
21
|
return (events || []).map((ev) => {
|
|
22
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
23
|
return Object.assign({}, ev, {
|
|
26
|
-
|
|
27
|
-
classNames: ['ec-status-' + status],
|
|
24
|
+
classNames: (ev.classNames || []).concat(['ec-status-' + status]),
|
|
28
25
|
extendedProps: Object.assign({}, ev.extendedProps || {}, { status }),
|
|
29
26
|
});
|
|
30
27
|
});
|
|
@@ -363,9 +360,13 @@ async function getBookingRids(bookingId) {
|
|
|
363
360
|
return await db.getSetMembers(`equipmentCalendar:booking:${bookingId}:rids`) || [];
|
|
364
361
|
}
|
|
365
362
|
|
|
363
|
+
const GLOBAL_INDEX_KEY = 'ec:reservations';
|
|
364
|
+
|
|
366
365
|
async function saveReservation(res) {
|
|
367
366
|
await db.setObject(resKey(res.id), res);
|
|
368
367
|
await db.sortedSetAdd(itemIndexKey(res.itemId), res.startMs, res.id);
|
|
368
|
+
// Global index for ACP listing
|
|
369
|
+
await db.sortedSetAdd(GLOBAL_INDEX_KEY, res.startMs, res.id);
|
|
369
370
|
}
|
|
370
371
|
|
|
371
372
|
async function getReservation(id) {
|
|
@@ -703,19 +704,22 @@ plugin.addAdminRoutes = async function (params) {
|
|
|
703
704
|
async function renderAdminReservationsPage(req, res) {
|
|
704
705
|
const isAdmin = await user.isAdminOrGlobalMod(req.uid);
|
|
705
706
|
if (!isAdmin) return res.status(403).render('403', {});
|
|
706
|
-
const ids = await db.getSortedSetRevRange(
|
|
707
|
-
const rowsRaw = await db.getObjects(ids.map(id =>
|
|
707
|
+
const ids = await db.getSortedSetRevRange(GLOBAL_INDEX_KEY, 0, 199);
|
|
708
|
+
const rowsRaw = await db.getObjects(ids.map(id => resKey(id)));
|
|
708
709
|
const rows = (rowsRaw || []).filter(Boolean).map((r) => {
|
|
709
710
|
const startMs = parseInt(r.startMs, 10) || 0;
|
|
710
711
|
const endMs = parseInt(r.endMs, 10) || 0;
|
|
711
712
|
return {
|
|
712
713
|
id: r.id,
|
|
714
|
+
bookingId: r.bookingId || '',
|
|
713
715
|
itemName: r.itemName || r.itemId,
|
|
714
716
|
startIso: startMs ? new Date(startMs).toISOString().slice(0,10) : '',
|
|
715
717
|
endIso: endMs ? new Date(endMs - 24*60*60*1000).toISOString().slice(0,10) : '',
|
|
716
|
-
days:
|
|
718
|
+
days: Math.max(1, Math.round((endMs - startMs) / (24*60*60*1000))),
|
|
717
719
|
status: r.status || 'pending',
|
|
718
720
|
total: r.total || '0',
|
|
721
|
+
uid: r.uid,
|
|
722
|
+
createdAt: r.createdAt || 0,
|
|
719
723
|
};
|
|
720
724
|
});
|
|
721
725
|
res.render('admin/plugins/equipment-calendar-reservations', { title: 'Réservations', rows, hasRows: rows.length>0 });
|
|
@@ -1159,7 +1163,7 @@ async function handleCreateReservation(req, res) {
|
|
|
1159
1163
|
const created = [];
|
|
1160
1164
|
for (const itemId of itemIds) {
|
|
1161
1165
|
const rid = await createReservationForItem(req, res, settings, itemId, startMs, endMs, notesUser, bookingId);
|
|
1162
|
-
created.push(rid);
|
|
1166
|
+
created.push(rid.id || rid);
|
|
1163
1167
|
}
|
|
1164
1168
|
|
|
1165
1169
|
// Redirect to calendar with a success flag
|
package/package.json
CHANGED
package/plugin.json
CHANGED
package/public/js/client.js
CHANGED
|
@@ -93,11 +93,25 @@ 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
|
-
headerToolbar:{left:'prev,next today',center:'title',right:'dayGridMonth,listMonth,listWeek'},
|
|
96
|
+
headerToolbar: { left: 'prev,next today', center: 'title', right: 'dayGridMonth,dayGridWeek,timeGridWeek,listMonth,listWeek' },
|
|
97
97
|
initialView: (window.EC_DEFAULT_VIEW || 'dayGridMonth'),
|
|
98
98
|
selectable: canCreate,
|
|
99
99
|
selectMirror: true,
|
|
100
100
|
events,
|
|
101
|
+
eventContent: function(arg) {
|
|
102
|
+
const status = (arg.event.extendedProps && arg.event.extendedProps.status) || 'pending';
|
|
103
|
+
const wrap = document.createElement('div');
|
|
104
|
+
wrap.className = 'ec-event';
|
|
105
|
+
const dot = document.createElement('span');
|
|
106
|
+
dot.className = 'ec-dot';
|
|
107
|
+
const title = document.createElement('span');
|
|
108
|
+
title.className = 'ec-title';
|
|
109
|
+
title.textContent = arg.event.title || '';
|
|
110
|
+
wrap.appendChild(dot);
|
|
111
|
+
wrap.appendChild(title);
|
|
112
|
+
return { domNodes: [wrap] };
|
|
113
|
+
},
|
|
114
|
+
eventClassNames:function(arg){return (arg.event.classNames||[]);},
|
|
101
115
|
eventDidMount:function(info){try{const s=info.event.extendedProps&&info.event.extendedProps.status;info.el.title=(s?('Statut: '+s+' — '):'')+info.event.title;}catch(e){}},
|
|
102
116
|
dateClick: function (info) {
|
|
103
117
|
if (!canCreate) return;
|
|
@@ -42,6 +42,8 @@
|
|
|
42
42
|
<label class="form-label">Vue par défaut</label>
|
|
43
43
|
<select class="form-select" name="defaultView">
|
|
44
44
|
<option value="dayGridMonth" {{{ if view_dayGridMonth }}}selected{{{ end }}}>Mois</option>
|
|
45
|
+
<option value="dayGridWeek" {{{ if view_dayGridWeek }}}selected{{{ end }}}>Semaine</option>
|
|
46
|
+
<option value="timeGridWeek" {{{ if view_timeGridWeek }}}selected{{{ end }}}>Semaine (grille)</option>
|
|
45
47
|
<option value="listMonth" {{{ if view_listMonth }}}selected{{{ end }}}>Liste (mois)</option>
|
|
46
48
|
<option value="listWeek" {{{ if view_listWeek }}}selected{{{ end }}}>Liste (semaine)</option>
|
|
47
49
|
</select>
|
|
@@ -1,3 +1,24 @@
|
|
|
1
|
+
|
|
2
|
+
<style>
|
|
3
|
+
/* Professional status styling */
|
|
4
|
+
.fc .ec-event { display:flex; align-items:center; gap:.4rem; padding:.05rem .35rem; border-radius:.5rem; }
|
|
5
|
+
.fc .ec-dot { width:.55rem; height:.55rem; border-radius:999px; display:inline-block; }
|
|
6
|
+
.fc .ec-title { white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
|
|
7
|
+
|
|
8
|
+
.fc-event.ec-status-pending { border-color: rgba(108,117,125,.35); }
|
|
9
|
+
.fc-event.ec-status-approved { border-color: rgba(13,110,253,.35); }
|
|
10
|
+
.fc-event.ec-status-paid { border-color: rgba(25,135,84,.35); }
|
|
11
|
+
.fc-event.ec-status-cancelled { opacity:.55; text-decoration: line-through; }
|
|
12
|
+
|
|
13
|
+
.fc-event.ec-status-pending .ec-dot { background: #6c757d; }
|
|
14
|
+
.fc-event.ec-status-approved .ec-dot { background: #0d6efd; }
|
|
15
|
+
.fc-event.ec-status-paid .ec-dot { background: #198754; }
|
|
16
|
+
.fc-event.ec-status-cancelled .ec-dot { background: #dc3545; }
|
|
17
|
+
|
|
18
|
+
/* list view rows */
|
|
19
|
+
.fc .fc-list-event.ec-status-cancelled { opacity:.6; text-decoration: line-through; }
|
|
20
|
+
</style>
|
|
21
|
+
|
|
1
22
|
<div class="equipment-calendar-page">
|
|
2
23
|
<h1>Réservation de matériel</h1>
|
|
3
24
|
|
|
@@ -10,7 +31,15 @@
|
|
|
10
31
|
{{{ end }}}
|
|
11
32
|
|
|
12
33
|
<div class="card card-body mb-3">
|
|
13
|
-
|
|
34
|
+
|
|
35
|
+
<div class="d-flex flex-wrap gap-2 align-items-center mb-2" id="ec-legend">
|
|
36
|
+
<span class="badge text-bg-secondary"><i class="fa fa-hourglass-half me-1"></i>En attente</span>
|
|
37
|
+
<span class="badge text-bg-primary"><i class="fa fa-check me-1"></i>Validée</span>
|
|
38
|
+
<span class="badge text-bg-success"><i class="fa fa-check-double me-1"></i>Payée</span>
|
|
39
|
+
<span class="badge text-bg-danger"><i class="fa fa-times me-1"></i>Annulée</span>
|
|
40
|
+
</div>
|
|
41
|
+
|
|
42
|
+
<div id="ec-calendar"></div>
|
|
14
43
|
</div>
|
|
15
44
|
|
|
16
45
|
<!-- Modal de demande -->
|