nodebb-plugin-equipment-calendar 9.1.4 → 9.1.6
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 +13 -4
- package/package.json +2 -9
- package/plugin.json +1 -2
- package/public/js/acp.js +15 -11
- package/public/js/admin.js +20 -11
- package/public/js/client.js +8 -3
- package/templates/equipment-calendar/calendar.tpl +4 -7
- package/README.md +0 -43
- package/public/css/style.css +0 -2
- package/public/lib/admin.js +0 -32
- package/public/lib/client.js +0 -50
- package/public/styles.css +0 -1
- package/public/templates/admin/plugins/equipment-calendar-helloasso-test.tpl +0 -54
- package/public/templates/admin/plugins/equipment-calendar-reservations.tpl +0 -99
- package/public/templates/admin/plugins/equipment-calendar.tpl +0 -83
- package/public/templates/equipment-calendar/approvals.tpl +0 -51
- package/public/templates/equipment-calendar/calendar.tpl +0 -89
- package/public/templates/equipment-calendar/payment-return.tpl +0 -15
- package/scripts/postinstall.js +0 -25
- package/templates/admin/plugins/equipment-calendar-helloasso-test.tpl +0 -54
- package/templates/admin/plugins/equipment-calendar-reservations.tpl +0 -99
- package/templates/equipment-calendar/approvals.tpl +0 -51
- package/templates/equipment-calendar/payment-return.tpl +0 -15
package/library.js
CHANGED
|
@@ -75,21 +75,30 @@ function toEvent(r) {
|
|
|
75
75
|
plugin.init = async function (params) {
|
|
76
76
|
const { router, middleware } = params;
|
|
77
77
|
|
|
78
|
+
// ACP
|
|
78
79
|
router.get('/admin/plugins/equipment-calendar', middleware.admin.buildHeader, async (req, res) => {
|
|
79
80
|
res.render('admin/plugins/equipment-calendar', {});
|
|
80
81
|
});
|
|
81
82
|
router.get('/api/admin/plugins/equipment-calendar', async (req, res) => res.json({}));
|
|
82
83
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
84
|
+
// Calendar page + alias /calendar
|
|
85
|
+
async function renderCal(req, res) {
|
|
86
|
+
const settings = await getSettings();
|
|
87
|
+
res.render('equipment-calendar/calendar', { defaultView: settings.defaultView });
|
|
88
|
+
}
|
|
89
|
+
router.get('/equipment/calendar', middleware.buildHeader, renderCal);
|
|
90
|
+
router.get('/calendar', middleware.buildHeader, renderCal);
|
|
91
|
+
|
|
86
92
|
router.get('/api/equipment/calendar', async (req, res) => res.json({}));
|
|
93
|
+
router.get('/api/calendar', async (req, res) => res.json({}));
|
|
87
94
|
|
|
95
|
+
// Events
|
|
88
96
|
router.get('/api/plugins/equipment-calendar/events', async (req, res) => {
|
|
89
97
|
const list = await listReservations(1000);
|
|
90
98
|
res.json({ events: list.map(toEvent) });
|
|
91
99
|
});
|
|
92
100
|
|
|
101
|
+
// Create reservation
|
|
93
102
|
router.post('/api/plugins/equipment-calendar/reservations', middleware.applyCSRF, async (req, res) => {
|
|
94
103
|
const uid = req.uid;
|
|
95
104
|
if (!uid) return res.status(403).json({ message: 'forbidden' });
|
|
@@ -109,7 +118,7 @@ plugin.init = async function (params) {
|
|
|
109
118
|
res.json({ ok: true, rid: r.rid });
|
|
110
119
|
});
|
|
111
120
|
|
|
112
|
-
winston.info('[equipment-calendar] loaded (official4x
|
|
121
|
+
winston.info('[equipment-calendar] loaded (official4x CDN fix)');
|
|
113
122
|
};
|
|
114
123
|
|
|
115
124
|
plugin.addAdminMenu = async function (header) {
|
package/package.json
CHANGED
|
@@ -1,13 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nodebb-plugin-equipment-calendar",
|
|
3
|
-
"version": "9.1.
|
|
4
|
-
"description": "Equipment reservation calendar (NodeBB 4.x compatible, official ACP settings, FullCalendar standard bundle)",
|
|
3
|
+
"version": "9.1.6",
|
|
5
4
|
"main": "library.js",
|
|
6
|
-
"license": "MIT"
|
|
7
|
-
"dependencies": {
|
|
8
|
-
"fullcalendar": "^6.1.20"
|
|
9
|
-
},
|
|
10
|
-
"scripts": {
|
|
11
|
-
"postinstall": "node ./scripts/postinstall.js"
|
|
12
|
-
}
|
|
5
|
+
"license": "MIT"
|
|
13
6
|
}
|
package/plugin.json
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "nodebb-plugin-equipment-calendar",
|
|
3
3
|
"name": "Equipment Calendar",
|
|
4
|
-
"description": "Equipment reservation calendar (NodeBB 4.x)",
|
|
5
4
|
"library": "./library.js",
|
|
6
5
|
"hooks": [
|
|
7
6
|
{
|
|
@@ -14,7 +13,7 @@
|
|
|
14
13
|
}
|
|
15
14
|
],
|
|
16
15
|
"acpScripts": [
|
|
17
|
-
"public/js/
|
|
16
|
+
"public/js/admin.js"
|
|
18
17
|
],
|
|
19
18
|
"staticDirs": {
|
|
20
19
|
"public": "public"
|
package/public/js/acp.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
'use strict';
|
|
2
|
-
/* globals $, ajaxify */
|
|
3
|
-
define('admin/plugins/equipment-calendar', ['settings', 'alerts'], function (Settings, alerts) {
|
|
4
|
-
const Admin = {};
|
|
2
|
+
/* globals $, ajaxify, require */
|
|
5
3
|
|
|
6
|
-
|
|
4
|
+
(function () {
|
|
5
|
+
function onEnd(Settings, alerts) {
|
|
7
6
|
const url = (ajaxify && ajaxify.data && ajaxify.data.url) ? ajaxify.data.url : (window.location.pathname || '');
|
|
8
7
|
if (!url.startsWith('admin/plugins/equipment-calendar')) return;
|
|
9
8
|
|
|
10
9
|
const container = $('.equipment-calendar-settings');
|
|
11
10
|
if (!container.length) return;
|
|
12
11
|
|
|
12
|
+
// load
|
|
13
13
|
try {
|
|
14
14
|
if (typeof Settings.sync === 'function') {
|
|
15
15
|
Settings.sync('equipmentCalendar', container);
|
|
@@ -18,7 +18,7 @@ define('admin/plugins/equipment-calendar', ['settings', 'alerts'], function (Set
|
|
|
18
18
|
}
|
|
19
19
|
} catch (e) {}
|
|
20
20
|
|
|
21
|
-
$('#save').off('click.ec').on('click.ec', function (e) {
|
|
21
|
+
$('#ec-save').off('click.ec').on('click.ec', function (e) {
|
|
22
22
|
e.preventDefault();
|
|
23
23
|
const done = function () {
|
|
24
24
|
alerts.success('[[admin/settings:settings-saved]]');
|
|
@@ -35,10 +35,14 @@ define('admin/plugins/equipment-calendar', ['settings', 'alerts'], function (Set
|
|
|
35
35
|
});
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
$(window).off('action:ajaxify.end.ec').on('action:ajaxify.end.ec', function () {
|
|
39
|
+
require(['settings', 'alerts'], function (Settings, alerts) {
|
|
40
|
+
onEnd(Settings, alerts);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
42
43
|
|
|
43
|
-
|
|
44
|
-
|
|
44
|
+
// run once
|
|
45
|
+
require(['settings', 'alerts'], function (Settings, alerts) {
|
|
46
|
+
onEnd(Settings, alerts);
|
|
47
|
+
});
|
|
48
|
+
})();
|
package/public/js/admin.js
CHANGED
|
@@ -1,28 +1,37 @@
|
|
|
1
|
-
|
|
2
1
|
'use strict';
|
|
3
2
|
/* globals $, ajaxify */
|
|
4
3
|
define('admin/plugins/equipment-calendar', ['settings', 'alerts'], function (Settings, alerts) {
|
|
5
4
|
const Admin = {};
|
|
6
5
|
|
|
7
|
-
function
|
|
6
|
+
function onPage() {
|
|
8
7
|
const url = (ajaxify && ajaxify.data && ajaxify.data.url) ? ajaxify.data.url : (window.location.pathname || '');
|
|
9
|
-
if (!url
|
|
10
|
-
return;
|
|
11
|
-
}
|
|
8
|
+
if (!url.startsWith('admin/plugins/equipment-calendar')) return;
|
|
12
9
|
|
|
13
|
-
|
|
10
|
+
const container = $('.equipment-calendar-settings');
|
|
11
|
+
if (!container.length) return;
|
|
12
|
+
|
|
13
|
+
if (typeof Settings.sync === 'function') {
|
|
14
|
+
Settings.sync('equipmentCalendar', container);
|
|
15
|
+
} else {
|
|
16
|
+
Settings.load('equipmentCalendar', container);
|
|
17
|
+
}
|
|
14
18
|
|
|
15
19
|
$('#save').off('click.ec').on('click.ec', function (e) {
|
|
16
|
-
|
|
17
|
-
|
|
20
|
+
e.preventDefault();
|
|
21
|
+
const done = function () {
|
|
18
22
|
alerts.success('[[admin/settings:settings-saved]]');
|
|
19
|
-
}
|
|
23
|
+
};
|
|
24
|
+
if (typeof Settings.persist === 'function') {
|
|
25
|
+
Settings.persist('equipmentCalendar', container, done, true);
|
|
26
|
+
} else {
|
|
27
|
+
Settings.save('equipmentCalendar', container, done);
|
|
28
|
+
}
|
|
20
29
|
});
|
|
21
30
|
}
|
|
22
31
|
|
|
23
32
|
Admin.init = function () {
|
|
24
|
-
$(window).off('action:ajaxify.end.ec').on('action:ajaxify.end.ec',
|
|
25
|
-
|
|
33
|
+
$(window).off('action:ajaxify.end.ec').on('action:ajaxify.end.ec', onPage);
|
|
34
|
+
onPage();
|
|
26
35
|
};
|
|
27
36
|
|
|
28
37
|
return Admin;
|
package/public/js/client.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
'use strict';
|
|
2
|
-
/* globals $, ajaxify
|
|
2
|
+
/* globals $, ajaxify */
|
|
3
3
|
define('equipment-calendar/client', ['api', 'alerts'], function (api, alerts) {
|
|
4
4
|
const Client = {};
|
|
5
5
|
|
|
6
6
|
function pageMatch() {
|
|
7
7
|
const url = (ajaxify && ajaxify.data && ajaxify.data.url) ? ajaxify.data.url : '';
|
|
8
|
-
return url === 'equipment/calendar';
|
|
8
|
+
return url === 'equipment/calendar' || url === 'calendar';
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
function isoDate(s) { return String(s || '').slice(0, 10); }
|
|
@@ -29,7 +29,12 @@ define('equipment-calendar/client', ['api', 'alerts'], function (api, alerts) {
|
|
|
29
29
|
|
|
30
30
|
async function initCalendar() {
|
|
31
31
|
const el = document.getElementById('ec-calendar');
|
|
32
|
-
if (!el
|
|
32
|
+
if (!el) return;
|
|
33
|
+
|
|
34
|
+
if (!window.FullCalendar || !window.FullCalendar.Calendar) {
|
|
35
|
+
el.innerHTML = '<div class="alert alert-danger">FullCalendar non chargé (CDN). Vérifie la CSP (Content-Security-Policy) et l’accès à cdn.jsdelivr.net.</div>';
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
33
38
|
|
|
34
39
|
const events = await loadEvents();
|
|
35
40
|
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
<div class="container-fluid px-3 equipment-calendar-page">
|
|
2
|
-
<
|
|
3
|
-
<h1 class="mb-0">Calendrier des réservations</h1>
|
|
4
|
-
</div>
|
|
5
|
-
|
|
2
|
+
<h1 class="mb-3">Calendrier des réservations</h1>
|
|
6
3
|
<div id="ec-calendar"></div>
|
|
7
4
|
|
|
8
5
|
<div class="modal fade" id="ecModal" tabindex="-1" aria-hidden="true">
|
|
@@ -24,7 +21,7 @@
|
|
|
24
21
|
<input class="form-control" type="date" name="endIso" id="ec-end" required>
|
|
25
22
|
</div>
|
|
26
23
|
<div class="mb-3">
|
|
27
|
-
<label class="form-label">Matériel (IDs, séparés par virgule)
|
|
24
|
+
<label class="form-label">Matériel (IDs, séparés par virgule)</label>
|
|
28
25
|
<input class="form-control" type="text" name="itemIds" id="ec-items" placeholder="ex: item123,item456" required>
|
|
29
26
|
</div>
|
|
30
27
|
</div>
|
|
@@ -38,8 +35,8 @@
|
|
|
38
35
|
</div>
|
|
39
36
|
</div>
|
|
40
37
|
|
|
41
|
-
<link rel="stylesheet" href="
|
|
42
|
-
<script src="
|
|
38
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/fullcalendar/index.global.min.css">
|
|
39
|
+
<script src="https://cdn.jsdelivr.net/npm/fullcalendar/index.global.min.js"></script>
|
|
43
40
|
|
|
44
41
|
<script>
|
|
45
42
|
require(['equipment-calendar/client'], function (mod) { mod.init(); });
|
package/README.md
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
# NodeBB Equipment Calendar (v0.1.0)
|
|
2
|
-
|
|
3
|
-
Plugin NodeBB (testé pour NodeBB v4.7.x) pour gérer des réservations de matériel via un calendrier (FullCalendar),
|
|
4
|
-
avec workflow : demande -> validation par un groupe -> lien de paiement HelloAsso -> statut payé/validé.
|
|
5
|
-
|
|
6
|
-
## Fonctionnement (sans "AJAX applicatif")
|
|
7
|
-
- La page calendrier est rendue côté serveur avec les évènements de la période demandée.
|
|
8
|
-
- Les actions (création, validation, refus) sont des POST classiques, suivis d'une redirection.
|
|
9
|
-
- FullCalendar est utilisé en mode "events inline" (pas de feed JSON automatique).
|
|
10
|
-
|
|
11
|
-
## Installation
|
|
12
|
-
```bash
|
|
13
|
-
cd /path/to/nodebb
|
|
14
|
-
npm install /path/to/nodebb-plugin-equipment-calendar
|
|
15
|
-
./nodebb build
|
|
16
|
-
./nodebb restart
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
## Configuration
|
|
20
|
-
Dans l'ACP : Plugins -> Equipment Calendar
|
|
21
|
-
|
|
22
|
-
- Groupes autorisés à créer une demande
|
|
23
|
-
- Groupe validateur
|
|
24
|
-
- Groupe notifié
|
|
25
|
-
- Matériel (JSON)
|
|
26
|
-
- Paramètres HelloAsso (clientId, clientSecret, organizationSlug, returnUrl, webhookSecret, etc.)
|
|
27
|
-
|
|
28
|
-
## Webhook HelloAsso
|
|
29
|
-
Déclare l'URL :
|
|
30
|
-
`https://<ton-forum>/equipment/webhook/helloasso`
|
|
31
|
-
|
|
32
|
-
Le plugin vérifie la signature si `webhookSecret` est renseigné (exemple basique).
|
|
33
|
-
|
|
34
|
-
## Remarques
|
|
35
|
-
- Ce plugin est un squelette complet mais générique : adapte la logique de paiement HelloAsso selon ton besoin exact
|
|
36
|
-
(type de checkout, itemization, montant, etc.).
|
|
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/public/css/style.css
DELETED
package/public/lib/admin.js
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
/* global $, app */
|
|
3
|
-
|
|
4
|
-
define('admin/plugins/equipment-calendar', function () {
|
|
5
|
-
const EquipmentCalendar = {};
|
|
6
|
-
|
|
7
|
-
EquipmentCalendar.init = function () {
|
|
8
|
-
$('#save').on('click', function () {
|
|
9
|
-
const payload = {
|
|
10
|
-
creatorGroups: $('#creatorGroups').val(),
|
|
11
|
-
approverGroup: $('#approverGroup').val(),
|
|
12
|
-
notifyGroup: $('#notifyGroup').val(),
|
|
13
|
-
itemsJson: $('#itemsJson').val(),
|
|
14
|
-
ha_clientId: $('#ha_clientId').val(),
|
|
15
|
-
ha_clientSecret: $('#ha_clientSecret').val(),
|
|
16
|
-
ha_organizationSlug: $('#ha_organizationSlug').val(),
|
|
17
|
-
ha_returnUrl: $('#ha_returnUrl').val(),
|
|
18
|
-
ha_webhookSecret: $('#ha_webhookSecret').val(),
|
|
19
|
-
defaultView: $('#defaultView').val(),
|
|
20
|
-
timezone: $('#timezone').val(),
|
|
21
|
-
showRequesterToAll: $('#showRequesterToAll').is(':checked') ? '1' : '0',
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
socket.emit('admin.settings.save', { hash: 'equipmentCalendar', values: payload }, function (err) {
|
|
25
|
-
if (err) return app.alertError(err.message || err);
|
|
26
|
-
app.alertSuccess('Sauvegardé');
|
|
27
|
-
});
|
|
28
|
-
});
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
return EquipmentCalendar;
|
|
32
|
-
});
|
package/public/lib/client.js
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
/* global $, window, document, FullCalendar */
|
|
3
|
-
|
|
4
|
-
(function () {
|
|
5
|
-
function submitCreate(startISO, endISO) {
|
|
6
|
-
const form = document.getElementById('ec-create-form');
|
|
7
|
-
if (!form) return;
|
|
8
|
-
form.querySelector('input[name="start"]').value = startISO;
|
|
9
|
-
form.querySelector('input[name="end"]').value = endISO;
|
|
10
|
-
form.submit();
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
$(document).ready(function () {
|
|
14
|
-
const el = document.getElementById('equipment-calendar');
|
|
15
|
-
if (!el) return;
|
|
16
|
-
|
|
17
|
-
const events = window.EC_EVENTS || [];
|
|
18
|
-
const initialDate = window.EC_INITIAL_DATE;
|
|
19
|
-
const initialView = window.EC_INITIAL_VIEW || 'dayGridMonth';
|
|
20
|
-
|
|
21
|
-
const calendar = new FullCalendar.Calendar(el, {
|
|
22
|
-
initialView: initialView,
|
|
23
|
-
initialDate: initialDate,
|
|
24
|
-
timeZone: window.EC_TZ || 'local',
|
|
25
|
-
selectable: window.EC_CAN_CREATE === true,
|
|
26
|
-
selectMirror: true,
|
|
27
|
-
events: events,
|
|
28
|
-
select: function (info) {
|
|
29
|
-
// FullCalendar provides end exclusive for all-day; for timed selections it's fine.
|
|
30
|
-
submitCreate(info.startStr, info.endStr);
|
|
31
|
-
},
|
|
32
|
-
dateClick: function (info) {
|
|
33
|
-
// Create 1-hour slot by default
|
|
34
|
-
const start = info.date;
|
|
35
|
-
const end = new Date(start.getTime() + 60 * 60 * 1000);
|
|
36
|
-
submitCreate(start.toISOString(), end.toISOString());
|
|
37
|
-
},
|
|
38
|
-
eventClick: function (info) {
|
|
39
|
-
// optionally show details
|
|
40
|
-
},
|
|
41
|
-
headerToolbar: {
|
|
42
|
-
left: 'prev,next today',
|
|
43
|
-
center: 'title',
|
|
44
|
-
right: 'dayGridMonth,timeGridWeek,timeGridDay'
|
|
45
|
-
},
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
calendar.render();
|
|
49
|
-
});
|
|
50
|
-
}());
|
package/public/styles.css
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
.equipment-calendar-page { }
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
<div class="acp-page-container">
|
|
2
|
-
<div class="d-flex align-items-center justify-content-between flex-wrap gap-2">
|
|
3
|
-
<h1 class="mb-0">Equipment Calendar</h1>
|
|
4
|
-
<div class="btn-group">
|
|
5
|
-
<a class="btn btn-outline-secondary" href="/admin/plugins/equipment-calendar">Paramètres</a>
|
|
6
|
-
<a class="btn btn-outline-secondary" href="/admin/plugins/equipment-calendar/reservations">Réservations</a>
|
|
7
|
-
<a class="btn btn-secondary active" href="/admin/plugins/equipment-calendar/helloasso-test">Test HelloAsso</a>
|
|
8
|
-
</div>
|
|
9
|
-
</div>
|
|
10
|
-
|
|
11
|
-
<div class="card card-body mt-3">
|
|
12
|
-
<div class="d-flex flex-wrap gap-2 mb-2">
|
|
13
|
-
<a class="btn btn-outline-primary" href="/admin/plugins/equipment-calendar/helloasso-test">Test (cache OK)</a>
|
|
14
|
-
<a class="btn btn-outline-primary" href="/admin/plugins/equipment-calendar/helloasso-test?force=1">Test (forcer nouveau token)</a>
|
|
15
|
-
<a class="btn btn-outline-danger" href="/admin/plugins/equipment-calendar/helloasso-test?force=1&clear=1" onclick="return confirm('Supprimer le token stocké et retester ?');">Vider token + retester</a>
|
|
16
|
-
</div>
|
|
17
|
-
{{{ if ok }}}
|
|
18
|
-
<div class="alert alert-success">{message}</div>
|
|
19
|
-
{{{ else }}}
|
|
20
|
-
<div class="alert alert-danger">Échec : {message}</div>
|
|
21
|
-
{{{ end }}}
|
|
22
|
-
|
|
23
|
-
<div class="small text-muted">
|
|
24
|
-
Form: <code>{settings.ha_itemsFormType}</code> / <code>{settings.ha_itemsFormSlug}</code> — Orga: <code>{settings.ha_organizationSlug}</code>
|
|
25
|
-
</div>
|
|
26
|
-
{{{ if hasSampleItems }}}
|
|
27
|
-
<div class="card card-body mt-3">
|
|
28
|
-
<div class="d-flex flex-wrap gap-2 mb-2">
|
|
29
|
-
<a class="btn btn-outline-primary" href="/admin/plugins/equipment-calendar/helloasso-test">Test (cache OK)</a>
|
|
30
|
-
<a class="btn btn-outline-primary" href="/admin/plugins/equipment-calendar/helloasso-test?force=1">Test (forcer nouveau token)</a>
|
|
31
|
-
<a class="btn btn-outline-danger" href="/admin/plugins/equipment-calendar/helloasso-test?force=1&clear=1" onclick="return confirm('Supprimer le token stocké et retester ?');">Vider token + retester</a>
|
|
32
|
-
</div>
|
|
33
|
-
<h5 class="mb-2">Aperçu (10 premiers articles)</h5>
|
|
34
|
-
<div class="table-responsive">
|
|
35
|
-
<table class="table table-striped align-middle">
|
|
36
|
-
<thead>
|
|
37
|
-
<tr>
|
|
38
|
-
<th>ID</th>
|
|
39
|
-
<th>Nom</th>
|
|
40
|
-
</tr>
|
|
41
|
-
</thead>
|
|
42
|
-
<tbody>
|
|
43
|
-
{{{ each sampleItems }}}
|
|
44
|
-
<tr>
|
|
45
|
-
<td><code>{sampleItems.id}</code></td>
|
|
46
|
-
<td>{sampleItems.rawName}</td>
|
|
47
|
-
</tr>
|
|
48
|
-
{{{ end }}}
|
|
49
|
-
</tbody>
|
|
50
|
-
</table>
|
|
51
|
-
</div>
|
|
52
|
-
</div>
|
|
53
|
-
{{{ end }}}
|
|
54
|
-
</div>
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
<div class="acp-page-container">
|
|
2
|
-
<div class="d-flex align-items-center justify-content-between flex-wrap gap-2">
|
|
3
|
-
<h1 class="mb-0">Equipment Calendar</h1>
|
|
4
|
-
<div class="btn-group">
|
|
5
|
-
<a class="btn btn-outline-secondary" href="/admin/plugins/equipment-calendar">Paramètres</a>
|
|
6
|
-
<a class="btn btn-secondary active" href="/admin/plugins/equipment-calendar/reservations">Réservations</a>
|
|
7
|
-
</div>
|
|
8
|
-
</div>
|
|
9
|
-
|
|
10
|
-
<form method="get" action="/admin/plugins/equipment-calendar/reservations" class="card card-body mt-3 mb-3">
|
|
11
|
-
<div class="row g-2 align-items-end">
|
|
12
|
-
<div class="col-md-3">
|
|
13
|
-
<label class="form-label">Statut</label>
|
|
14
|
-
<select class="form-select" name="status">
|
|
15
|
-
{{{ each statusOptions }}}
|
|
16
|
-
<option value="{statusOptions.id}" {{{ if statusOptions.selected }}}selected{{{ end }}}>{statusOptions.name}</option>
|
|
17
|
-
{{{ end }}}
|
|
18
|
-
</select>
|
|
19
|
-
</div>
|
|
20
|
-
<div class="col-md-4">
|
|
21
|
-
<label class="form-label">Matériel</label>
|
|
22
|
-
<select class="form-select" name="itemId">
|
|
23
|
-
{{{ each itemOptions }}}
|
|
24
|
-
<option value="{itemOptions.id}" {{{ if itemOptions.selected }}}selected{{{ end }}}>{itemOptions.name}</option>
|
|
25
|
-
{{{ end }}}
|
|
26
|
-
</select>
|
|
27
|
-
</div>
|
|
28
|
-
<div class="col-md-3">
|
|
29
|
-
<label class="form-label">Recherche</label>
|
|
30
|
-
<input class="form-control" name="q" value="{q}" placeholder="rid, uid, note">
|
|
31
|
-
</div>
|
|
32
|
-
<div class="col-md-2">
|
|
33
|
-
<button class="btn btn-primary w-100" type="submit">Filtrer</button>
|
|
34
|
-
</div>
|
|
35
|
-
</div>
|
|
36
|
-
</form>
|
|
37
|
-
|
|
38
|
-
<div class="text-muted small mb-2">
|
|
39
|
-
Total (filtré) : <strong>{total}</strong> - Total (global) : <strong>{totalAll}</strong>
|
|
40
|
-
</div>
|
|
41
|
-
|
|
42
|
-
{{{ if hasRows }}}
|
|
43
|
-
<div class="table-responsive">
|
|
44
|
-
<table class="table table-striped align-middle">
|
|
45
|
-
<thead><tr>
|
|
46
|
-
<th>Matériel</th>
|
|
47
|
-
<th>Période</th>
|
|
48
|
-
<th>Créée le</th>
|
|
49
|
-
<th>Statut</th>
|
|
50
|
-
<th>Total</th>
|
|
51
|
-
<th class="text-end">Actions</th>
|
|
52
|
-
</tr></thead>
|
|
53
|
-
<tbody>
|
|
54
|
-
{{{ each rows }}}
|
|
55
|
-
<tr>
|
|
56
|
-
<td><code>{rows.rid}</code></td>
|
|
57
|
-
<td>{rows.itemName}</td>
|
|
58
|
-
<td>{rows.uid}</td>
|
|
59
|
-
<td><small>{rows.start}</small></td>
|
|
60
|
-
<td><small>{rows.end}</small></td>
|
|
61
|
-
<td><code>{rows.status}</code></td>
|
|
62
|
-
<td><small>{rows.createdAt}</small></td>
|
|
63
|
-
<td><small>{rows.notesUser}</small></td>
|
|
64
|
-
<td class="text-nowrap">
|
|
65
|
-
<form method="post" action="/admin/plugins/equipment-calendar/reservations/{rows.rid}/approve" class="d-inline">
|
|
66
|
-
<input type="hidden" name="_csrf" value="{config.csrf_token}">
|
|
67
|
-
<button class="btn btn-sm btn-success" type="submit">Approve</button>
|
|
68
|
-
</form>
|
|
69
|
-
<form method="post" action="/admin/plugins/equipment-calendar/reservations/{rows.rid}/reject" class="d-inline ms-1">
|
|
70
|
-
<input type="hidden" name="_csrf" value="{config.csrf_token}">
|
|
71
|
-
<button class="btn btn-sm btn-warning" type="submit">Reject</button>
|
|
72
|
-
</form>
|
|
73
|
-
<form method="post" action="/admin/plugins/equipment-calendar/reservations/{rows.rid}/delete" class="d-inline ms-1" onsubmit="return confirm('Supprimer définitivement ?');">
|
|
74
|
-
<input type="hidden" name="_csrf" value="{config.csrf_token}">
|
|
75
|
-
<button class="btn btn-sm btn-danger" type="submit">Delete</button>
|
|
76
|
-
</form>
|
|
77
|
-
</td>
|
|
78
|
-
</tr>
|
|
79
|
-
{{{ end }}}
|
|
80
|
-
</tbody>
|
|
81
|
-
</table>
|
|
82
|
-
</div>
|
|
83
|
-
|
|
84
|
-
<div class="d-flex justify-content-between align-items-center mt-3">
|
|
85
|
-
<div>Page {page} / {totalPages}</div>
|
|
86
|
-
<div class="btn-group">
|
|
87
|
-
{{{ if prevPage }}}
|
|
88
|
-
<a class="btn btn-outline-secondary" href="/admin/plugins/equipment-calendar/reservations?page={prevPage}&perPage={perPage}">Précédent</a>
|
|
89
|
-
{{{ end }}}
|
|
90
|
-
{{{ if nextPage }}}
|
|
91
|
-
<a class="btn btn-outline-secondary" href="/admin/plugins/equipment-calendar/reservations?page={nextPage}&perPage={perPage}">Suivant</a>
|
|
92
|
-
{{{ end }}}
|
|
93
|
-
</div>
|
|
94
|
-
</div>
|
|
95
|
-
|
|
96
|
-
{{{ else }}}
|
|
97
|
-
<div class="alert alert-info">Aucune réservation trouvée.</div>
|
|
98
|
-
{{{ end }}}
|
|
99
|
-
</div>
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
<div class="acp-page-container">
|
|
2
|
-
<div class="d-flex align-items-center justify-content-between flex-wrap gap-2">
|
|
3
|
-
<h1 class="mb-0">Equipment Calendar</h1>
|
|
4
|
-
<div class="btn-group">
|
|
5
|
-
<a class="btn btn-secondary active" href="/admin/plugins/equipment-calendar">Paramètres</a>
|
|
6
|
-
<a class="btn btn-outline-secondary" href="/admin/plugins/equipment-calendar/reservations">Réservations</a>
|
|
7
|
-
<a class="btn btn-outline-secondary" href="/admin/plugins/equipment-calendar/helloasso-test">Test HelloAsso</a>
|
|
8
|
-
</div>
|
|
9
|
-
</div>
|
|
10
|
-
|
|
11
|
-
<form id="ec-admin-form" method="post" action="/admin/plugins/equipment-calendar/save" class="mb-3 mt-3">
|
|
12
|
-
<input type="hidden" name="_csrf" value="{config.csrf_token}">
|
|
13
|
-
|
|
14
|
-
<div class="card card-body mb-3">
|
|
15
|
-
<h5>Permissions</h5>
|
|
16
|
-
<div class="mb-3">
|
|
17
|
-
<label class="form-label">Groupes autorisés à créer une demande (creatorGroups)</label>
|
|
18
|
-
<input class="form-control" type="text" name="creatorGroups" value="{settings.creatorGroups}">
|
|
19
|
-
</div>
|
|
20
|
-
<div class="mb-3">
|
|
21
|
-
<label class="form-label">Groupe valideur (approverGroup)</label>
|
|
22
|
-
<input class="form-control" type="text" name="approverGroup" value="{settings.approverGroup}">
|
|
23
|
-
</div>
|
|
24
|
-
<div class="mb-0">
|
|
25
|
-
<label class="form-label">Groupe notifié (notifyGroup)</label>
|
|
26
|
-
<input class="form-control" type="text" name="notifyGroup" value="{settings.notifyGroup}">
|
|
27
|
-
</div>
|
|
28
|
-
</div>
|
|
29
|
-
|
|
30
|
-
<div class="card card-body mb-3">
|
|
31
|
-
<h5>Paiement</h5>
|
|
32
|
-
<div class="mb-0">
|
|
33
|
-
<label class="form-label">Timeout paiement (minutes)</label>
|
|
34
|
-
<input class="form-control" type="number" min="1" name="paymentTimeoutMinutes" value="{settings.paymentTimeoutMinutes}">
|
|
35
|
-
</div>
|
|
36
|
-
</div>
|
|
37
|
-
|
|
38
|
-
<div class="card card-body mb-3">
|
|
39
|
-
<h5>HelloAsso</h5>
|
|
40
|
-
<div class="mb-3">
|
|
41
|
-
<label class="form-label">API Base URL (prod/sandbox)</label>
|
|
42
|
-
<input class="form-control" type="text" name="ha_apiBaseUrl" value="{settings.ha_apiBaseUrl}">
|
|
43
|
-
<div class="form-text">Prod: <code>https://api.helloasso.com</code> — Sandbox: <code>https://api.helloasso-sandbox.com</code></div>
|
|
44
|
-
</div>
|
|
45
|
-
<div class="mb-3">
|
|
46
|
-
<label class="form-label">Organization slug</label>
|
|
47
|
-
<input class="form-control" type="text" name="ha_organizationSlug" value="{settings.ha_organizationSlug}">
|
|
48
|
-
</div>
|
|
49
|
-
<div class="mb-3">
|
|
50
|
-
<label class="form-label">Client ID</label>
|
|
51
|
-
<input class="form-control" type="text" name="ha_clientId" value="{settings.ha_clientId}">
|
|
52
|
-
</div>
|
|
53
|
-
<div class="mb-3">
|
|
54
|
-
<label class="form-label">Client Secret</label>
|
|
55
|
-
<input class="form-control" type="password" name="ha_clientSecret" value="{settings.ha_clientSecret}">
|
|
56
|
-
</div>
|
|
57
|
-
<div class="row g-3">
|
|
58
|
-
<div class="col-md-6">
|
|
59
|
-
<label class="form-label">Form Type</label>
|
|
60
|
-
<input class="form-control" type="text" name="ha_itemsFormType" value="{settings.ha_itemsFormType}" placeholder="shop">
|
|
61
|
-
</div>
|
|
62
|
-
<div class="col-md-6">
|
|
63
|
-
<label class="form-label">Form Slug</label>
|
|
64
|
-
<input class="form-control" type="text" name="ha_itemsFormSlug" value="{settings.ha_itemsFormSlug}" placeholder="locations-materiel-2026">
|
|
65
|
-
</div>
|
|
66
|
-
</div>
|
|
67
|
-
<div class="mb-0 mt-3">
|
|
68
|
-
<label class="form-label">Préfixe itemName (checkout)</label>
|
|
69
|
-
<input class="form-control" type="text" name="ha_calendarItemNamePrefix" value="{settings.ha_calendarItemNamePrefix}">
|
|
70
|
-
</div>
|
|
71
|
-
</div>
|
|
72
|
-
|
|
73
|
-
<button class="btn btn-primary" type="submit">Enregistrer</button>
|
|
74
|
-
</form>
|
|
75
|
-
|
|
76
|
-
{{{ if saved }}}
|
|
77
|
-
<script>
|
|
78
|
-
if (window.app && typeof window.app.alertSuccess === 'function') {
|
|
79
|
-
window.app.alertSuccess('Paramètres enregistrés');
|
|
80
|
-
}
|
|
81
|
-
</script>
|
|
82
|
-
{{{ end }}}
|
|
83
|
-
</div>
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
<div class="equipment-approvals-page">
|
|
2
|
-
<h1>Validation des réservations</h1>
|
|
3
|
-
|
|
4
|
-
{{{ if hasRows }}}
|
|
5
|
-
<div class="table-responsive">
|
|
6
|
-
<table class="table table-striped align-middle">
|
|
7
|
-
<thead>
|
|
8
|
-
<tr>
|
|
9
|
-
<th>Matériel</th>
|
|
10
|
-
<th>Demandeur</th>
|
|
11
|
-
<th>Début</th>
|
|
12
|
-
<th>Fin</th>
|
|
13
|
-
<th>Statut</th>
|
|
14
|
-
<th>Paiement</th>
|
|
15
|
-
<th>Actions</th>
|
|
16
|
-
</tr>
|
|
17
|
-
</thead>
|
|
18
|
-
<tbody>
|
|
19
|
-
{{{ each rows }}}
|
|
20
|
-
<tr>
|
|
21
|
-
<td>{rows.itemName}</td>
|
|
22
|
-
<td>{rows.requester}</td>
|
|
23
|
-
<td>{rows.start}</td>
|
|
24
|
-
<td>{rows.end}</td>
|
|
25
|
-
<td><code>{rows.status}</code></td>
|
|
26
|
-
<td>
|
|
27
|
-
{{{ if rows.paymentUrl }}}
|
|
28
|
-
<a href="{rows.paymentUrl}" target="_blank" rel="noreferrer">Lien</a>
|
|
29
|
-
{{{ else }}}
|
|
30
|
-
-
|
|
31
|
-
{{{ end }}}
|
|
32
|
-
</td>
|
|
33
|
-
<td>
|
|
34
|
-
<form method="post" action="/equipment/reservations/{rows.id}/approve" class="d-inline">
|
|
35
|
-
<input type="hidden" name="_csrf" value="{rows.csrf}">
|
|
36
|
-
<button class="btn btn-sm btn-success" type="submit">Approuver</button>
|
|
37
|
-
</form>
|
|
38
|
-
<form method="post" action="/equipment/reservations/{rows.id}/reject" class="d-inline ms-1">
|
|
39
|
-
<input type="hidden" name="_csrf" value="{rows.csrf}">
|
|
40
|
-
<button class="btn btn-sm btn-danger" type="submit">Refuser</button>
|
|
41
|
-
</form>
|
|
42
|
-
</td>
|
|
43
|
-
</tr>
|
|
44
|
-
{{{ end }}}
|
|
45
|
-
</tbody>
|
|
46
|
-
</table>
|
|
47
|
-
</div>
|
|
48
|
-
{{{ else }}}
|
|
49
|
-
<div class="alert alert-success">Aucune demande en attente 🎉</div>
|
|
50
|
-
{{{ end }}}
|
|
51
|
-
</div>
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
<div class="equipment-calendar-page">
|
|
2
|
-
<h1>Réservation de matériel</h1>
|
|
3
|
-
|
|
4
|
-
{{{ if canCreate }}}
|
|
5
|
-
<div class="alert alert-info">
|
|
6
|
-
Clique sur une date ou sélectionne une plage sur le calendrier pour faire une demande.
|
|
7
|
-
</div>
|
|
8
|
-
{{{ else }}}
|
|
9
|
-
<div class="alert alert-info">Tu peux consulter le calendrier, mais tu n’as pas les droits pour créer une demande.</div>
|
|
10
|
-
{{{ end }}}
|
|
11
|
-
|
|
12
|
-
<div class="card card-body mb-3">
|
|
13
|
-
<div id="ec-calendar"></div>
|
|
14
|
-
</div>
|
|
15
|
-
|
|
16
|
-
<!-- Modal de demande -->
|
|
17
|
-
<div class="modal fade" id="ec-create-modal" tabindex="-1" aria-hidden="true">
|
|
18
|
-
<div class="modal-dialog modal-dialog-centered">
|
|
19
|
-
<div class="modal-content">
|
|
20
|
-
<form id="ec-create-form" method="post" action="{relative_path}/equipment/reservations/create">
|
|
21
|
-
<div class="modal-header">
|
|
22
|
-
<h5 class="modal-title">Demande de réservation</h5>
|
|
23
|
-
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
24
|
-
</div>
|
|
25
|
-
|
|
26
|
-
<div class="modal-body">
|
|
27
|
-
<input type="hidden" name="_csrf" value="{config.csrf_token}">
|
|
28
|
-
<input type="hidden" id="ec-start-iso" name="start" value="">
|
|
29
|
-
<input type="hidden" id="ec-end-iso" name="end" value="">
|
|
30
|
-
|
|
31
|
-
<div class="row g-3 mb-3">
|
|
32
|
-
<div class="col-6">
|
|
33
|
-
<label class="form-label">Début</label>
|
|
34
|
-
<input class="form-control" type="date" id="ec-start-date" required>
|
|
35
|
-
</div>
|
|
36
|
-
<div class="col-6">
|
|
37
|
-
<label class="form-label">Fin</label>
|
|
38
|
-
<input class="form-control" type="date" id="ec-end-date" required>
|
|
39
|
-
</div>
|
|
40
|
-
</div>
|
|
41
|
-
|
|
42
|
-
<div class="mb-3">
|
|
43
|
-
<label class="form-label">Matériel</label>
|
|
44
|
-
<select class="form-select" id="ec-item-ids" name="itemIds" multiple required>
|
|
45
|
-
<!-- BEGIN items -->
|
|
46
|
-
<option value="{items.id}" data-price="{items.price}">{items.name}</option>
|
|
47
|
-
<!-- END items -->
|
|
48
|
-
</select>
|
|
49
|
-
<div class="form-text">Tu peux sélectionner plusieurs matériels.</div>
|
|
50
|
-
</div>
|
|
51
|
-
|
|
52
|
-
<div class="mb-3">
|
|
53
|
-
<label class="form-label">Notes</label>
|
|
54
|
-
<textarea class="form-control" name="notesUser" rows="3" placeholder="Infos utiles..."></textarea>
|
|
55
|
-
</div>
|
|
56
|
-
|
|
57
|
-
<div class="mb-0">
|
|
58
|
-
<div class="fw-semibold">Durée</div>
|
|
59
|
-
<div id="ec-total-days">1 jour</div>
|
|
60
|
-
<hr class="my-2">
|
|
61
|
-
<div class="fw-semibold">Total estimé</div>
|
|
62
|
-
<div id="ec-total-price" class="fs-5">0 EUR</div>
|
|
63
|
-
</div>
|
|
64
|
-
</div>
|
|
65
|
-
|
|
66
|
-
<div class="modal-footer">
|
|
67
|
-
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Annuler</button>
|
|
68
|
-
<button type="submit" class="btn btn-primary">Envoyer la demande</button>
|
|
69
|
-
</div>
|
|
70
|
-
</form>
|
|
71
|
-
</div>
|
|
72
|
-
</div>
|
|
73
|
-
</div>
|
|
74
|
-
</div>
|
|
75
|
-
|
|
76
|
-
<script src="https://cdn.jsdelivr.net/npm/fullcalendar@6.1.19/index.global.min.js"></script>
|
|
77
|
-
<script src="{relative_path}/plugins/nodebb-plugin-equipment-calendar/js/client.js"></script>
|
|
78
|
-
|
|
79
|
-
<script>
|
|
80
|
-
window.EC_EVENTS = JSON.parse(atob('{eventsB64}'));
|
|
81
|
-
window.EC_BLOCKS = JSON.parse(atob('{blocksB64}'));
|
|
82
|
-
window.EC_CAN_CREATE = {canCreateJs};
|
|
83
|
-
window.EC_TZ = '{tz}';
|
|
84
|
-
</script>
|
|
85
|
-
|
|
86
|
-
<script>
|
|
87
|
-
window.EC_CSRF = '{csrf_token}';
|
|
88
|
-
window.EC_IS_APPROVER = {isApprover};
|
|
89
|
-
</script>
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
<div class="card card-body">
|
|
2
|
-
<h4>Retour paiement</h4>
|
|
3
|
-
|
|
4
|
-
{{{ if statusPaid }}}
|
|
5
|
-
<div class="alert alert-success">✅ Paiement confirmé.</div>
|
|
6
|
-
{{{ end }}}
|
|
7
|
-
|
|
8
|
-
{{{ if statusError }}}
|
|
9
|
-
<div class="alert alert-danger">❌ Paiement non confirmé.</div>
|
|
10
|
-
{{{ end }}}
|
|
11
|
-
|
|
12
|
-
<p>{message}</p>
|
|
13
|
-
|
|
14
|
-
<a class="btn btn-primary" href="/equipment/calendar">Retour au calendrier</a>
|
|
15
|
-
</div>
|
package/scripts/postinstall.js
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const fs = require('fs');
|
|
4
|
-
const path = require('path');
|
|
5
|
-
|
|
6
|
-
function copyFile(src, dst) {
|
|
7
|
-
fs.mkdirSync(path.dirname(dst), { recursive: true });
|
|
8
|
-
fs.copyFileSync(src, dst);
|
|
9
|
-
}
|
|
10
|
-
function exists(p) { try { fs.accessSync(p); return true; } catch { return false; } }
|
|
11
|
-
|
|
12
|
-
(function main() {
|
|
13
|
-
const root = path.join(__dirname, '..');
|
|
14
|
-
const outDir = path.join(root, 'public', 'vendor', 'fullcalendar');
|
|
15
|
-
fs.mkdirSync(outDir, { recursive: true });
|
|
16
|
-
|
|
17
|
-
const js = path.join(root, 'node_modules', 'fullcalendar', 'index.global.min.js');
|
|
18
|
-
const css = path.join(root, 'node_modules', 'fullcalendar', 'index.global.min.css');
|
|
19
|
-
|
|
20
|
-
const copied = [];
|
|
21
|
-
if (exists(js)) { copyFile(js, path.join(outDir, 'index.global.min.js')); copied.push(js); }
|
|
22
|
-
if (exists(css)) { copyFile(css, path.join(outDir, 'index.global.min.css')); copied.push(css); }
|
|
23
|
-
|
|
24
|
-
fs.writeFileSync(path.join(outDir, 'manifest.json'), JSON.stringify({ copied }, null, 2), 'utf-8');
|
|
25
|
-
})();
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
<div class="acp-page-container">
|
|
2
|
-
<div class="d-flex align-items-center justify-content-between flex-wrap gap-2">
|
|
3
|
-
<h1 class="mb-0">Equipment Calendar</h1>
|
|
4
|
-
<div class="btn-group">
|
|
5
|
-
<a class="btn btn-outline-secondary" href="/admin/plugins/equipment-calendar">Paramètres</a>
|
|
6
|
-
<a class="btn btn-outline-secondary" href="/admin/plugins/equipment-calendar/reservations">Réservations</a>
|
|
7
|
-
<a class="btn btn-secondary active" href="/admin/plugins/equipment-calendar/helloasso-test">Test HelloAsso</a>
|
|
8
|
-
</div>
|
|
9
|
-
</div>
|
|
10
|
-
|
|
11
|
-
<div class="card card-body mt-3">
|
|
12
|
-
<div class="d-flex flex-wrap gap-2 mb-2">
|
|
13
|
-
<a class="btn btn-outline-primary" href="/admin/plugins/equipment-calendar/helloasso-test">Test (cache OK)</a>
|
|
14
|
-
<a class="btn btn-outline-primary" href="/admin/plugins/equipment-calendar/helloasso-test?force=1">Test (forcer nouveau token)</a>
|
|
15
|
-
<a class="btn btn-outline-danger" href="/admin/plugins/equipment-calendar/helloasso-test?force=1&clear=1" onclick="return confirm('Supprimer le token stocké et retester ?');">Vider token + retester</a>
|
|
16
|
-
</div>
|
|
17
|
-
{{{ if ok }}}
|
|
18
|
-
<div class="alert alert-success">{message}</div>
|
|
19
|
-
{{{ else }}}
|
|
20
|
-
<div class="alert alert-danger">Échec : {message}</div>
|
|
21
|
-
{{{ end }}}
|
|
22
|
-
|
|
23
|
-
<div class="small text-muted">
|
|
24
|
-
Form: <code>{settings.ha_itemsFormType}</code> / <code>{settings.ha_itemsFormSlug}</code> — Orga: <code>{settings.ha_organizationSlug}</code>
|
|
25
|
-
</div>
|
|
26
|
-
{{{ if hasSampleItems }}}
|
|
27
|
-
<div class="card card-body mt-3">
|
|
28
|
-
<div class="d-flex flex-wrap gap-2 mb-2">
|
|
29
|
-
<a class="btn btn-outline-primary" href="/admin/plugins/equipment-calendar/helloasso-test">Test (cache OK)</a>
|
|
30
|
-
<a class="btn btn-outline-primary" href="/admin/plugins/equipment-calendar/helloasso-test?force=1">Test (forcer nouveau token)</a>
|
|
31
|
-
<a class="btn btn-outline-danger" href="/admin/plugins/equipment-calendar/helloasso-test?force=1&clear=1" onclick="return confirm('Supprimer le token stocké et retester ?');">Vider token + retester</a>
|
|
32
|
-
</div>
|
|
33
|
-
<h5 class="mb-2">Aperçu (10 premiers articles)</h5>
|
|
34
|
-
<div class="table-responsive">
|
|
35
|
-
<table class="table table-striped align-middle">
|
|
36
|
-
<thead>
|
|
37
|
-
<tr>
|
|
38
|
-
<th>ID</th>
|
|
39
|
-
<th>Nom</th>
|
|
40
|
-
</tr>
|
|
41
|
-
</thead>
|
|
42
|
-
<tbody>
|
|
43
|
-
{{{ each sampleItems }}}
|
|
44
|
-
<tr>
|
|
45
|
-
<td><code>{sampleItems.id}</code></td>
|
|
46
|
-
<td>{sampleItems.rawName}</td>
|
|
47
|
-
</tr>
|
|
48
|
-
{{{ end }}}
|
|
49
|
-
</tbody>
|
|
50
|
-
</table>
|
|
51
|
-
</div>
|
|
52
|
-
</div>
|
|
53
|
-
{{{ end }}}
|
|
54
|
-
</div>
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
<div class="acp-page-container">
|
|
2
|
-
<div class="d-flex align-items-center justify-content-between flex-wrap gap-2">
|
|
3
|
-
<h1 class="mb-0">Equipment Calendar</h1>
|
|
4
|
-
<div class="btn-group">
|
|
5
|
-
<a class="btn btn-outline-secondary" href="/admin/plugins/equipment-calendar">Paramètres</a>
|
|
6
|
-
<a class="btn btn-secondary active" href="/admin/plugins/equipment-calendar/reservations">Réservations</a>
|
|
7
|
-
</div>
|
|
8
|
-
</div>
|
|
9
|
-
|
|
10
|
-
<form method="get" action="/admin/plugins/equipment-calendar/reservations" class="card card-body mt-3 mb-3">
|
|
11
|
-
<div class="row g-2 align-items-end">
|
|
12
|
-
<div class="col-md-3">
|
|
13
|
-
<label class="form-label">Statut</label>
|
|
14
|
-
<select class="form-select" name="status">
|
|
15
|
-
{{{ each statusOptions }}}
|
|
16
|
-
<option value="{statusOptions.id}" {{{ if statusOptions.selected }}}selected{{{ end }}}>{statusOptions.name}</option>
|
|
17
|
-
{{{ end }}}
|
|
18
|
-
</select>
|
|
19
|
-
</div>
|
|
20
|
-
<div class="col-md-4">
|
|
21
|
-
<label class="form-label">Matériel</label>
|
|
22
|
-
<select class="form-select" name="itemId">
|
|
23
|
-
{{{ each itemOptions }}}
|
|
24
|
-
<option value="{itemOptions.id}" {{{ if itemOptions.selected }}}selected{{{ end }}}>{itemOptions.name}</option>
|
|
25
|
-
{{{ end }}}
|
|
26
|
-
</select>
|
|
27
|
-
</div>
|
|
28
|
-
<div class="col-md-3">
|
|
29
|
-
<label class="form-label">Recherche</label>
|
|
30
|
-
<input class="form-control" name="q" value="{q}" placeholder="rid, uid, note">
|
|
31
|
-
</div>
|
|
32
|
-
<div class="col-md-2">
|
|
33
|
-
<button class="btn btn-primary w-100" type="submit">Filtrer</button>
|
|
34
|
-
</div>
|
|
35
|
-
</div>
|
|
36
|
-
</form>
|
|
37
|
-
|
|
38
|
-
<div class="text-muted small mb-2">
|
|
39
|
-
Total (filtré) : <strong>{total}</strong> - Total (global) : <strong>{totalAll}</strong>
|
|
40
|
-
</div>
|
|
41
|
-
|
|
42
|
-
{{{ if hasRows }}}
|
|
43
|
-
<div class="table-responsive">
|
|
44
|
-
<table class="table table-striped align-middle">
|
|
45
|
-
<thead><tr>
|
|
46
|
-
<th>Matériel</th>
|
|
47
|
-
<th>Période</th>
|
|
48
|
-
<th>Créée le</th>
|
|
49
|
-
<th>Statut</th>
|
|
50
|
-
<th>Total</th>
|
|
51
|
-
<th class="text-end">Actions</th>
|
|
52
|
-
</tr></thead>
|
|
53
|
-
<tbody>
|
|
54
|
-
{{{ each rows }}}
|
|
55
|
-
<tr>
|
|
56
|
-
<td><code>{rows.rid}</code></td>
|
|
57
|
-
<td>{rows.itemName}</td>
|
|
58
|
-
<td>{rows.uid}</td>
|
|
59
|
-
<td><small>{rows.start}</small></td>
|
|
60
|
-
<td><small>{rows.end}</small></td>
|
|
61
|
-
<td><code>{rows.status}</code></td>
|
|
62
|
-
<td><small>{rows.createdAt}</small></td>
|
|
63
|
-
<td><small>{rows.notesUser}</small></td>
|
|
64
|
-
<td class="text-nowrap">
|
|
65
|
-
<form method="post" action="/admin/plugins/equipment-calendar/reservations/{rows.rid}/approve" class="d-inline">
|
|
66
|
-
<input type="hidden" name="_csrf" value="{config.csrf_token}">
|
|
67
|
-
<button class="btn btn-sm btn-success" type="submit">Approve</button>
|
|
68
|
-
</form>
|
|
69
|
-
<form method="post" action="/admin/plugins/equipment-calendar/reservations/{rows.rid}/reject" class="d-inline ms-1">
|
|
70
|
-
<input type="hidden" name="_csrf" value="{config.csrf_token}">
|
|
71
|
-
<button class="btn btn-sm btn-warning" type="submit">Reject</button>
|
|
72
|
-
</form>
|
|
73
|
-
<form method="post" action="/admin/plugins/equipment-calendar/reservations/{rows.rid}/delete" class="d-inline ms-1" onsubmit="return confirm('Supprimer définitivement ?');">
|
|
74
|
-
<input type="hidden" name="_csrf" value="{config.csrf_token}">
|
|
75
|
-
<button class="btn btn-sm btn-danger" type="submit">Delete</button>
|
|
76
|
-
</form>
|
|
77
|
-
</td>
|
|
78
|
-
</tr>
|
|
79
|
-
{{{ end }}}
|
|
80
|
-
</tbody>
|
|
81
|
-
</table>
|
|
82
|
-
</div>
|
|
83
|
-
|
|
84
|
-
<div class="d-flex justify-content-between align-items-center mt-3">
|
|
85
|
-
<div>Page {page} / {totalPages}</div>
|
|
86
|
-
<div class="btn-group">
|
|
87
|
-
{{{ if prevPage }}}
|
|
88
|
-
<a class="btn btn-outline-secondary" href="/admin/plugins/equipment-calendar/reservations?page={prevPage}&perPage={perPage}">Précédent</a>
|
|
89
|
-
{{{ end }}}
|
|
90
|
-
{{{ if nextPage }}}
|
|
91
|
-
<a class="btn btn-outline-secondary" href="/admin/plugins/equipment-calendar/reservations?page={nextPage}&perPage={perPage}">Suivant</a>
|
|
92
|
-
{{{ end }}}
|
|
93
|
-
</div>
|
|
94
|
-
</div>
|
|
95
|
-
|
|
96
|
-
{{{ else }}}
|
|
97
|
-
<div class="alert alert-info">Aucune réservation trouvée.</div>
|
|
98
|
-
{{{ end }}}
|
|
99
|
-
</div>
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
<div class="equipment-approvals-page">
|
|
2
|
-
<h1>Validation des réservations</h1>
|
|
3
|
-
|
|
4
|
-
{{{ if hasRows }}}
|
|
5
|
-
<div class="table-responsive">
|
|
6
|
-
<table class="table table-striped align-middle">
|
|
7
|
-
<thead>
|
|
8
|
-
<tr>
|
|
9
|
-
<th>Matériel</th>
|
|
10
|
-
<th>Demandeur</th>
|
|
11
|
-
<th>Début</th>
|
|
12
|
-
<th>Fin</th>
|
|
13
|
-
<th>Statut</th>
|
|
14
|
-
<th>Paiement</th>
|
|
15
|
-
<th>Actions</th>
|
|
16
|
-
</tr>
|
|
17
|
-
</thead>
|
|
18
|
-
<tbody>
|
|
19
|
-
{{{ each rows }}}
|
|
20
|
-
<tr>
|
|
21
|
-
<td>{rows.itemName}</td>
|
|
22
|
-
<td>{rows.requester}</td>
|
|
23
|
-
<td>{rows.start}</td>
|
|
24
|
-
<td>{rows.end}</td>
|
|
25
|
-
<td><code>{rows.status}</code></td>
|
|
26
|
-
<td>
|
|
27
|
-
{{{ if rows.paymentUrl }}}
|
|
28
|
-
<a href="{rows.paymentUrl}" target="_blank" rel="noreferrer">Lien</a>
|
|
29
|
-
{{{ else }}}
|
|
30
|
-
-
|
|
31
|
-
{{{ end }}}
|
|
32
|
-
</td>
|
|
33
|
-
<td>
|
|
34
|
-
<form method="post" action="/equipment/reservations/{rows.id}/approve" class="d-inline">
|
|
35
|
-
<input type="hidden" name="_csrf" value="{rows.csrf}">
|
|
36
|
-
<button class="btn btn-sm btn-success" type="submit">Approuver</button>
|
|
37
|
-
</form>
|
|
38
|
-
<form method="post" action="/equipment/reservations/{rows.id}/reject" class="d-inline ms-1">
|
|
39
|
-
<input type="hidden" name="_csrf" value="{rows.csrf}">
|
|
40
|
-
<button class="btn btn-sm btn-danger" type="submit">Refuser</button>
|
|
41
|
-
</form>
|
|
42
|
-
</td>
|
|
43
|
-
</tr>
|
|
44
|
-
{{{ end }}}
|
|
45
|
-
</tbody>
|
|
46
|
-
</table>
|
|
47
|
-
</div>
|
|
48
|
-
{{{ else }}}
|
|
49
|
-
<div class="alert alert-success">Aucune demande en attente 🎉</div>
|
|
50
|
-
{{{ end }}}
|
|
51
|
-
</div>
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
<div class="card card-body">
|
|
2
|
-
<h4>Retour paiement</h4>
|
|
3
|
-
|
|
4
|
-
{{{ if statusPaid }}}
|
|
5
|
-
<div class="alert alert-success">✅ Paiement confirmé.</div>
|
|
6
|
-
{{{ end }}}
|
|
7
|
-
|
|
8
|
-
{{{ if statusError }}}
|
|
9
|
-
<div class="alert alert-danger">❌ Paiement non confirmé.</div>
|
|
10
|
-
{{{ end }}}
|
|
11
|
-
|
|
12
|
-
<p>{message}</p>
|
|
13
|
-
|
|
14
|
-
<a class="btn btn-primary" href="/equipment/calendar">Retour au calendrier</a>
|
|
15
|
-
</div>
|