nodebb-plugin-equipment-calendar 0.1.0 → 0.2.2
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/README.md +6 -0
- package/library.js +24 -4
- package/package.json +1 -1
- package/plugin.json +18 -10
- package/public/templates/admin/plugins/equipment-calendar.tpl +4 -4
- package/public/templates/equipment-calendar/approvals.tpl +3 -3
- package/public/templates/equipment-calendar/calendar.tpl +7 -4
- package/public/templates/equipment-calendar/payment-return.tpl +1 -1
package/README.md
CHANGED
|
@@ -35,3 +35,9 @@ Le plugin vérifie la signature si `webhookSecret` est renseigné (exemple basiq
|
|
|
35
35
|
- Ce plugin est un squelette complet mais générique : adapte la logique de paiement HelloAsso selon ton besoin exact
|
|
36
36
|
(type de checkout, itemization, montant, etc.).
|
|
37
37
|
- Pour un contrôle d'overlap strict : le plugin empêche les réservations qui chevauchent (même item) pour les statuts bloquants.
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
## URLs
|
|
41
|
+
- Calendrier: `/equipment/calendar` (alias `/calendar`)
|
|
42
|
+
- Validations: `/equipment/approvals`
|
|
43
|
+
- ACP: `/admin/plugins/equipment-calendar`
|
package/library.js
CHANGED
|
@@ -306,7 +306,18 @@ function clampRange(startStr, endStr, tz) {
|
|
|
306
306
|
|
|
307
307
|
// --- Routes ---
|
|
308
308
|
plugin.init = async function (params) {
|
|
309
|
-
|
|
309
|
+
const { router } = params;
|
|
310
|
+
const mid = params.middleware;
|
|
311
|
+
|
|
312
|
+
// Admin (ACP) routes
|
|
313
|
+
if (mid && mid.admin) {
|
|
314
|
+
router.get('/admin/plugins/equipment-calendar', mid.admin.buildHeader, renderAdminPage);
|
|
315
|
+
router.get('/api/admin/plugins/equipment-calendar', renderAdminPage);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// Convenience alias (optional): /calendar -> /equipment/calendar
|
|
319
|
+
router.get('/calendar', (req, res) => res.redirect('/equipment/calendar'));
|
|
320
|
+
|
|
310
321
|
|
|
311
322
|
// To verify webhook signature we need raw body; add a rawBody collector for this route only
|
|
312
323
|
router.post('/equipment/webhook/helloasso',
|
|
@@ -350,6 +361,7 @@ plugin.init = async function (params) {
|
|
|
350
361
|
title: 'Paiement',
|
|
351
362
|
rid: req.query.rid || '',
|
|
352
363
|
status: req.query.status || 'ok',
|
|
364
|
+
statusError: String(req.query.status || '') === 'error',
|
|
353
365
|
});
|
|
354
366
|
});
|
|
355
367
|
|
|
@@ -389,6 +401,10 @@ async function renderAdminPage(req, res) {
|
|
|
389
401
|
res.render('admin/plugins/equipment-calendar', {
|
|
390
402
|
title: 'Equipment Calendar',
|
|
391
403
|
settings,
|
|
404
|
+
view_dayGridMonth: (settings.defaultView || 'dayGridMonth') === 'dayGridMonth',
|
|
405
|
+
view_timeGridWeek: (settings.defaultView || '') === 'timeGridWeek',
|
|
406
|
+
view_timeGridDay: (settings.defaultView || '') === 'timeGridDay',
|
|
407
|
+
view_showRequesterToAll: String(settings.showRequesterToAll || '0') === '1',
|
|
392
408
|
});
|
|
393
409
|
}
|
|
394
410
|
|
|
@@ -397,10 +413,13 @@ async function renderCalendarPage(req, res) {
|
|
|
397
413
|
const settings = await getSettings();
|
|
398
414
|
const items = parseItems(settings.itemsJson).filter(i => i.active);
|
|
399
415
|
|
|
416
|
+
const rawItems = items;
|
|
417
|
+
|
|
400
418
|
const tz = settings.timezone || 'Europe/Paris';
|
|
401
419
|
|
|
402
|
-
const itemId = String(req.query.itemId || (
|
|
403
|
-
const chosenItem =
|
|
420
|
+
const itemId = String(req.query.itemId || (rawItems[0]?.id || '')).trim();
|
|
421
|
+
const chosenItem = rawItems.find(i => i.id === itemId) || rawItems[0] || null;
|
|
422
|
+
const itemsView = rawItems.map(it => ({ ...it, selected: chosenItem ? it.id === chosenItem.id : false }));
|
|
404
423
|
|
|
405
424
|
// Determine range to render
|
|
406
425
|
const now = DateTime.now().setZone(tz);
|
|
@@ -437,7 +456,7 @@ async function renderCalendarPage(req, res) {
|
|
|
437
456
|
|
|
438
457
|
res.render('equipment-calendar/calendar', {
|
|
439
458
|
title: 'Réservation de matériel',
|
|
440
|
-
items,
|
|
459
|
+
items: itemsView,
|
|
441
460
|
chosenItemId: chosenItem ? chosenItem.id : '',
|
|
442
461
|
chosenItemName: chosenItem ? chosenItem.name : '',
|
|
443
462
|
chosenItemPriceCents: chosenItem ? chosenItem.priceCents : 0,
|
|
@@ -499,6 +518,7 @@ async function renderApprovalsPage(req, res) {
|
|
|
499
518
|
res.render('equipment-calendar/approvals', {
|
|
500
519
|
title: 'Validation des réservations',
|
|
501
520
|
rows,
|
|
521
|
+
hasRows: Array.isArray(rows) && rows.length > 0,
|
|
502
522
|
csrf: req.csrfToken,
|
|
503
523
|
});
|
|
504
524
|
}
|
package/package.json
CHANGED
package/plugin.json
CHANGED
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
{
|
|
2
|
-
"id": "nodebb-plugin-
|
|
3
|
-
"name": "
|
|
4
|
-
"description": "
|
|
5
|
-
"url": "
|
|
2
|
+
"id": "nodebb-plugin-calendar-onekite",
|
|
3
|
+
"name": "Calendar Onekite",
|
|
4
|
+
"description": "Calendrier + réservation matériel + validation admin + paiement HelloAsso pour NodeBB v4",
|
|
5
|
+
"url": "",
|
|
6
|
+
"version": "0.2.2",
|
|
6
7
|
"library": "./library.js",
|
|
8
|
+
"staticDirs": {
|
|
9
|
+
"static": "static"
|
|
10
|
+
},
|
|
11
|
+
"acpScripts": [
|
|
12
|
+
"static/js/admin.js"
|
|
13
|
+
],
|
|
7
14
|
"hooks": [
|
|
8
15
|
{
|
|
9
16
|
"hook": "static:app.load",
|
|
@@ -14,12 +21,13 @@
|
|
|
14
21
|
"method": "addAdminNavigation"
|
|
15
22
|
},
|
|
16
23
|
{
|
|
17
|
-
"hook": "filter:
|
|
18
|
-
"method": "
|
|
24
|
+
"hook": "filter:widgets.getWidgets",
|
|
25
|
+
"method": "defineWidgets"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"hook": "filter:widget.render:calendarUpcoming",
|
|
29
|
+
"method": "renderUpcomingWidget"
|
|
19
30
|
}
|
|
20
31
|
],
|
|
21
|
-
"
|
|
22
|
-
"public": "./public"
|
|
23
|
-
},
|
|
24
|
-
"templates": "./public/templates"
|
|
32
|
+
"templates": "templates"
|
|
25
33
|
}
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
<input id="notifyGroup" class="form-control" value="{{settings.notifyGroup}}">
|
|
27
27
|
</div>
|
|
28
28
|
<div class="form-check">
|
|
29
|
-
<input class="form-check-input" type="checkbox" id="showRequesterToAll" {{#if
|
|
29
|
+
<input class="form-check-input" type="checkbox" id="showRequesterToAll" {{#if view_showRequesterToAll}}checked{{/if}}>
|
|
30
30
|
<label class="form-check-label" for="showRequesterToAll">Afficher le demandeur à tout le monde</label>
|
|
31
31
|
</div>
|
|
32
32
|
</div>
|
|
@@ -49,9 +49,9 @@
|
|
|
49
49
|
<h5>Calendrier</h5>
|
|
50
50
|
<div class="mb-3"><label class="form-label">Vue par défaut</label>
|
|
51
51
|
<select id="defaultView" class="form-select">
|
|
52
|
-
<option value="dayGridMonth" {{#if
|
|
53
|
-
<option value="timeGridWeek" {{#if
|
|
54
|
-
<option value="timeGridDay" {{#if
|
|
52
|
+
<option value="dayGridMonth" {{#if view_dayGridMonth}}selected{{/if}}>Mois</option>
|
|
53
|
+
<option value="timeGridWeek" {{#if view_timeGridWeek}}selected{{/if}}>Semaine</option>
|
|
54
|
+
<option value="timeGridDay" {{#if view_timeGridDay}}selected{{/if}}>Jour</option>
|
|
55
55
|
</select>
|
|
56
56
|
</div>
|
|
57
57
|
<div class="mb-3"><label class="form-label">Timezone</label><input id="timezone" class="form-control" value="{{settings.timezone}}"></div>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<div class="equipment-approvals-page">
|
|
2
2
|
<h1>Validation des réservations</h1>
|
|
3
3
|
|
|
4
|
-
{{#if
|
|
4
|
+
{{#if hasRows}}
|
|
5
5
|
<div class="table-responsive">
|
|
6
6
|
<table class="table table-striped align-middle">
|
|
7
7
|
<thead>
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
</tr>
|
|
17
17
|
</thead>
|
|
18
18
|
<tbody>
|
|
19
|
-
{{#rows}}
|
|
19
|
+
{{#each rows}}
|
|
20
20
|
<tr>
|
|
21
21
|
<td>{{itemName}}</td>
|
|
22
22
|
<td>{{requester}}</td>
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
</form>
|
|
42
42
|
</td>
|
|
43
43
|
</tr>
|
|
44
|
-
{{/
|
|
44
|
+
{{/each}}
|
|
45
45
|
</tbody>
|
|
46
46
|
</table>
|
|
47
47
|
</div>
|
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
<div>
|
|
7
7
|
<label class="form-label">Matériel</label>
|
|
8
8
|
<select name="itemId" class="form-select" onchange="this.form.submit()">
|
|
9
|
-
{{#items}}
|
|
10
|
-
<option value="{{id}}" {{#if
|
|
11
|
-
{{/
|
|
9
|
+
{{#each items}}
|
|
10
|
+
<option value="{{id}}" {{#if selected}}selected{{/if}}>{{name}} — {{location}}</option>
|
|
11
|
+
{{/each}}
|
|
12
12
|
</select>
|
|
13
13
|
</div>
|
|
14
14
|
<div class="text-muted small">
|
|
@@ -55,13 +55,16 @@
|
|
|
55
55
|
<script src="/plugins/nodebb-plugin-equipment-calendar/lib/client.js"></script>
|
|
56
56
|
|
|
57
57
|
<script>
|
|
58
|
-
|
|
58
|
+
// eventsJson is already JSON, we output it unescaped by using data attribute trick
|
|
59
|
+
window.EC_EVENTS = JSON.parse(document.getElementById('ec-events-json').textContent);
|
|
59
60
|
window.EC_INITIAL_DATE = "{{initialDateISO}}";
|
|
60
61
|
window.EC_INITIAL_VIEW = "{{view}}";
|
|
61
62
|
window.EC_TZ = "{{tz}}";
|
|
62
63
|
window.EC_CAN_CREATE = {{#if canCreate}}true{{else}}false{{/if}};
|
|
63
64
|
</script>
|
|
64
65
|
|
|
66
|
+
<script type="application/json" id="ec-events-json">{{{eventsJson}}}</script>
|
|
67
|
+
|
|
65
68
|
<style>
|
|
66
69
|
.ec-status-pending .fc-event-title { font-weight: 600; }
|
|
67
70
|
.ec-status-valid .fc-event-title { font-weight: 700; }
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<div class="equipment-payment-return">
|
|
2
2
|
<h1>Paiement</h1>
|
|
3
|
-
{{#if
|
|
3
|
+
{{#if statusError}}
|
|
4
4
|
<div class="alert alert-danger">Le paiement semble avoir échoué. Référence réservation: <code>{{rid}}</code></div>
|
|
5
5
|
{{else}}
|
|
6
6
|
<div class="alert alert-info">Merci. Si le paiement est confirmé, la réservation passera en "validée". Référence: <code>{{rid}}</code></div>
|