nodebb-plugin-equipment-calendar 9.0.2 → 9.0.4
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 +1 -1
- package/plugin.json +1 -1
- package/public/js/client.js +59 -9
- package/public/templates/admin/plugins/equipment-calendar.tpl +46 -47
- package/templates/admin/plugins/equipment-calendar-helloasso-test.tpl +54 -0
- package/templates/admin/plugins/equipment-calendar-reservations.tpl +99 -0
- package/templates/admin/plugins/equipment-calendar.tpl +83 -0
- package/templates/equipment-calendar/approvals.tpl +51 -0
- package/templates/equipment-calendar/calendar.tpl +89 -0
- package/templates/equipment-calendar/payment-return.tpl +15 -0
package/package.json
CHANGED
package/plugin.json
CHANGED
package/public/js/client.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
require(['jquery', 'bootstrap'], function ($, bootstrap) {
|
|
1
|
+
require(['jquery', 'bootstrap', 'bootbox'], function ($, bootstrap, bootbox) {
|
|
2
2
|
'use strict';
|
|
3
3
|
/* global window, document, FullCalendar */
|
|
4
4
|
|
|
@@ -54,6 +54,38 @@ require(['jquery', 'bootstrap'], function ($, bootstrap) {
|
|
|
54
54
|
} catch (e) { return iso; }
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
+
|
|
58
|
+
function escapeHtml(str) {
|
|
59
|
+
return String(str || '').replace(/[&<>"']/g, function (c) {
|
|
60
|
+
return ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' })[c];
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function ecDoAction(rid, action, eventObj) {
|
|
65
|
+
try {
|
|
66
|
+
const csrf = window.EC_CSRF || (window.config && window.config.csrf_token) || '';
|
|
67
|
+
return fetch((config.relative_path || '') + '/api/equipment/reservations/' + encodeURIComponent(rid) + '/action', {
|
|
68
|
+
method: 'POST',
|
|
69
|
+
headers: { 'Content-Type': 'application/json', 'x-csrf-token': csrf },
|
|
70
|
+
body: JSON.stringify({ action: action })
|
|
71
|
+
}).then(function (r) {
|
|
72
|
+
return r.json().catch(function () { return {}; }).then(function (j) { return { ok: r.ok, json: j }; });
|
|
73
|
+
}).then(function (res) {
|
|
74
|
+
if (!res.ok || (res.json && res.json.error)) {
|
|
75
|
+
if (window.app && app.alertError) app.alertError((res.json && res.json.error) ? res.json.error : 'Erreur');
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
if (action === 'delete') {
|
|
79
|
+
try { eventObj.remove(); } catch (e) {}
|
|
80
|
+
} else {
|
|
81
|
+
try { eventObj.setExtendedProp('status', action === 'approve' ? 'approved' : 'rejected'); } catch (e) {}
|
|
82
|
+
}
|
|
83
|
+
if (window.app && app.alertSuccess) app.alertSuccess('OK');
|
|
84
|
+
return true;
|
|
85
|
+
});
|
|
86
|
+
} catch (e) { return false; }
|
|
87
|
+
}
|
|
88
|
+
|
|
57
89
|
function updateTotalPrice() {
|
|
58
90
|
try {
|
|
59
91
|
const sel = document.getElementById('ec-item-ids');
|
|
@@ -140,7 +172,6 @@ function updateTotalPrice() {
|
|
|
140
172
|
dayMaxEventRows: true,
|
|
141
173
|
displayEventTime: false,
|
|
142
174
|
timeZone: 'UTC',
|
|
143
|
-
locale: 'fr',
|
|
144
175
|
events: events,
|
|
145
176
|
select: function (info) {
|
|
146
177
|
if (!window.EC_CAN_CREATE) return;
|
|
@@ -151,15 +182,34 @@ function updateTotalPrice() {
|
|
|
151
182
|
},
|
|
152
183
|
dateClick: function (info) {
|
|
153
184
|
if (!window.EC_CAN_CREATE) return;
|
|
154
|
-
openCreateModal(info.dateStr, info.dateStr);
|
|
185
|
+
openCreateModal(String(info.dateStr||'').slice(0,10), String(info.dateStr||'').slice(0,10));
|
|
155
186
|
},
|
|
156
187
|
eventClick: function (info) {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
188
|
+
try {
|
|
189
|
+
info.jsEvent && info.jsEvent.preventDefault && info.jsEvent.preventDefault();
|
|
190
|
+
const ep = info.event.extendedProps || {};
|
|
191
|
+
const rid = ep.rid || info.event.id;
|
|
192
|
+
const status = ep.status || 'pending';
|
|
193
|
+
const title = info.event.title || 'Reservation';
|
|
194
|
+
const start = String(info.event.startStr || '').slice(0,10);
|
|
195
|
+
const end = String(info.event.endStr || '').slice(0,10);
|
|
196
|
+
const total = ep.total || 0;
|
|
197
|
+
|
|
198
|
+
let msg = '<div><strong>' + escapeHtml(title) + '</strong></div>';
|
|
199
|
+
msg += '<div>Periode: ' + escapeHtml(start) + ' -> ' + escapeHtml(end) + '</div>';
|
|
200
|
+
msg += '<div>Statut: ' + escapeHtml(status) + '</div>';
|
|
201
|
+
if (total) msg += '<div>Total: ' + escapeHtml(String(total)) + ' EUR</div>';
|
|
202
|
+
|
|
203
|
+
const buttons = { close: { label: 'Fermer', className: 'btn-secondary' } };
|
|
204
|
+
|
|
205
|
+
if (window.EC_IS_APPROVER) {
|
|
206
|
+
buttons.approve = { label: 'Valider', className: 'btn-success', callback: function () { return ecDoAction(rid, 'approve', info.event); } };
|
|
207
|
+
buttons.reject = { label: 'Rejeter', className: 'btn-warning', callback: function () { return ecDoAction(rid, 'reject', info.event); } };
|
|
208
|
+
buttons.delete = { label: 'Supprimer', className: 'btn-danger', callback: function () { return ecDoAction(rid, 'delete', info.event); } };
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
bootbox.dialog({ title: 'Reservation', message: msg, buttons: buttons });
|
|
212
|
+
} catch (e) {}
|
|
163
213
|
},
|
|
164
214
|
});
|
|
165
215
|
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
|
|
2
1
|
<div class="acp-page-container">
|
|
3
2
|
<div class="d-flex align-items-center justify-content-between flex-wrap gap-2">
|
|
4
3
|
<h1 class="mb-0">Equipment Calendar</h1>
|
|
@@ -9,76 +8,76 @@
|
|
|
9
8
|
</div>
|
|
10
9
|
</div>
|
|
11
10
|
|
|
12
|
-
<
|
|
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
|
+
|
|
13
14
|
<div class="card card-body mb-3">
|
|
14
15
|
<h5>Permissions</h5>
|
|
15
16
|
<div class="mb-3">
|
|
16
17
|
<label class="form-label">Groupes autorisés à créer une demande (creatorGroups)</label>
|
|
17
|
-
<input class="form-control" type="text" name="creatorGroups"
|
|
18
|
+
<input class="form-control" type="text" name="creatorGroups" value="{settings.creatorGroups}">
|
|
18
19
|
</div>
|
|
19
20
|
<div class="mb-3">
|
|
20
21
|
<label class="form-label">Groupe valideur (approverGroup)</label>
|
|
21
|
-
<input class="form-control" type="text" name="approverGroup"
|
|
22
|
+
<input class="form-control" type="text" name="approverGroup" value="{settings.approverGroup}">
|
|
22
23
|
</div>
|
|
23
24
|
<div class="mb-0">
|
|
24
25
|
<label class="form-label">Groupe notifié (notifyGroup)</label>
|
|
25
|
-
<input class="form-control" type="text" name="notifyGroup"
|
|
26
|
+
<input class="form-control" type="text" name="notifyGroup" value="{settings.notifyGroup}">
|
|
26
27
|
</div>
|
|
27
28
|
</div>
|
|
28
29
|
|
|
29
30
|
<div class="card card-body mb-3">
|
|
30
|
-
<h5>
|
|
31
|
-
<div class="
|
|
32
|
-
<
|
|
33
|
-
|
|
34
|
-
<input class="form-control" type="text" name="ha_clientId" data-field="ha_clientId" value="{settings.ha_clientId}">
|
|
35
|
-
</div>
|
|
36
|
-
<div class="col-md-6">
|
|
37
|
-
<label class="form-label">Client Secret</label>
|
|
38
|
-
<input class="form-control" type="password" name="ha_clientSecret" data-field="ha_clientSecret" value="{settings.ha_clientSecret}">
|
|
39
|
-
</div>
|
|
40
|
-
<div class="col-md-6">
|
|
41
|
-
<label class="form-label">Organization slug</label>
|
|
42
|
-
<input class="form-control" type="text" name="ha_organizationSlug" data-field="ha_organizationSlug" value="{settings.ha_organizationSlug}">
|
|
43
|
-
</div>
|
|
44
|
-
<div class="col-md-6">
|
|
45
|
-
<label class="form-label">Return URL base (callback)</label>
|
|
46
|
-
<input class="form-control" type="text" name="ha_returnUrl" data-field="ha_returnUrl" value="{settings.ha_returnUrl}" placeholder="https://www.onekite.com">
|
|
47
|
-
<div class="form-text">Le plugin ajoutera automatiquement le chemin de callback.</div>
|
|
48
|
-
</div>
|
|
49
|
-
<div class="col-md-6">
|
|
50
|
-
<label class="form-label">Form Type</label>
|
|
51
|
-
<input class="form-control" type="text" name="ha_itemsFormType" data-field="ha_itemsFormType" value="{settings.ha_itemsFormType}" placeholder="shop">
|
|
52
|
-
</div>
|
|
53
|
-
<div class="col-md-6">
|
|
54
|
-
<label class="form-label">Form Slug</label>
|
|
55
|
-
<input class="form-control" type="text" name="ha_itemsFormSlug" data-field="ha_itemsFormSlug" value="{settings.ha_itemsFormSlug}" placeholder="locations-materiel-2026">
|
|
56
|
-
</div>
|
|
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}">
|
|
57
35
|
</div>
|
|
58
36
|
</div>
|
|
59
37
|
|
|
60
38
|
<div class="card card-body mb-3">
|
|
61
|
-
<h5>
|
|
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>
|
|
62
57
|
<div class="row g-3">
|
|
63
58
|
<div class="col-md-6">
|
|
64
|
-
<label class="form-label">
|
|
65
|
-
<
|
|
66
|
-
<option value="dayGridMonth">Mois</option>
|
|
67
|
-
<option value="timeGridWeek">Semaine</option>
|
|
68
|
-
<option value="listWeek">Liste</option>
|
|
69
|
-
</select>
|
|
70
|
-
</div>
|
|
71
|
-
<div class="col-md-6">
|
|
72
|
-
<label class="form-label">Timezone</label>
|
|
73
|
-
<input class="form-control" type="text" name="timezone" data-field="timezone" value="{settings.timezone}">
|
|
59
|
+
<label class="form-label">Form Type</label>
|
|
60
|
+
<input class="form-control" type="text" name="ha_itemsFormType" value="{settings.ha_itemsFormType}" placeholder="shop">
|
|
74
61
|
</div>
|
|
75
62
|
<div class="col-md-6">
|
|
76
|
-
<label class="form-label">
|
|
77
|
-
<input class="form-control" type="
|
|
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">
|
|
78
65
|
</div>
|
|
79
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>
|
|
80
71
|
</div>
|
|
81
72
|
|
|
82
|
-
<button
|
|
83
|
-
</
|
|
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 }}}
|
|
84
83
|
</div>
|
|
@@ -0,0 +1,54 @@
|
|
|
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>
|
|
@@ -0,0 +1,99 @@
|
|
|
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>
|
|
@@ -0,0 +1,83 @@
|
|
|
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="{config.relative_path}/admin/plugins/equipment-calendar">Paramètres</a>
|
|
6
|
+
<a class="btn btn-outline-secondary" href="{config.relative_path}/admin/plugins/equipment-calendar/reservations">Réservations</a>
|
|
7
|
+
</div>
|
|
8
|
+
</div>
|
|
9
|
+
|
|
10
|
+
<form class="equipment-calendar-settings mt-3" method="post" action="{config.relative_path}/admin/plugins/equipment-calendar/save">
|
|
11
|
+
<input type="hidden" name="_csrf" value="{csrf_token}">
|
|
12
|
+
<div class="card card-body mb-3">
|
|
13
|
+
<h5>Permissions</h5>
|
|
14
|
+
<div class="mb-3">
|
|
15
|
+
<label class="form-label">creatorGroups (séparés par virgule)</label>
|
|
16
|
+
<input class="form-control" type="text" name="creatorGroups" value="{settings.creatorGroups}">
|
|
17
|
+
</div>
|
|
18
|
+
<div class="mb-3">
|
|
19
|
+
<label class="form-label">approverGroup</label>
|
|
20
|
+
<input class="form-control" type="text" name="approverGroup" value="{settings.approverGroup}">
|
|
21
|
+
</div>
|
|
22
|
+
<div class="mb-0">
|
|
23
|
+
<label class="form-label">notifyGroup</label>
|
|
24
|
+
<input class="form-control" type="text" name="notifyGroup" value="{settings.notifyGroup}">
|
|
25
|
+
</div>
|
|
26
|
+
</div>
|
|
27
|
+
|
|
28
|
+
<div class="card card-body mb-3">
|
|
29
|
+
<h5>HelloAsso</h5>
|
|
30
|
+
<div class="row g-3">
|
|
31
|
+
<div class="col-md-6">
|
|
32
|
+
<label class="form-label">API base URL</label>
|
|
33
|
+
<input class="form-control" type="text" name="ha_apiBaseUrl" value="{settings.ha_apiBaseUrl}" placeholder="https://api.helloasso.com ou https://api.helloasso-sandbox.com">
|
|
34
|
+
</div>
|
|
35
|
+
<div class="col-md-6">
|
|
36
|
+
<label class="form-label">Organization slug</label>
|
|
37
|
+
<input class="form-control" type="text" name="ha_organizationSlug" value="{settings.ha_organizationSlug}">
|
|
38
|
+
</div>
|
|
39
|
+
<div class="col-md-6">
|
|
40
|
+
<label class="form-label">Client ID</label>
|
|
41
|
+
<input class="form-control" type="text" name="ha_clientId" value="{settings.ha_clientId}">
|
|
42
|
+
</div>
|
|
43
|
+
<div class="col-md-6">
|
|
44
|
+
<label class="form-label">Client Secret</label>
|
|
45
|
+
<input class="form-control" type="password" name="ha_clientSecret" value="{settings.ha_clientSecret}">
|
|
46
|
+
</div>
|
|
47
|
+
<div class="col-md-6">
|
|
48
|
+
<label class="form-label">Form Type</label>
|
|
49
|
+
<input class="form-control" type="text" name="ha_itemsFormType" value="{settings.ha_itemsFormType}" placeholder="shop">
|
|
50
|
+
</div>
|
|
51
|
+
<div class="col-md-6">
|
|
52
|
+
<label class="form-label">Form Slug</label>
|
|
53
|
+
<input class="form-control" type="text" name="ha_itemsFormSlug" value="{settings.ha_itemsFormSlug}" placeholder="locations-materiel-2026">
|
|
54
|
+
</div>
|
|
55
|
+
<div class="col-12">
|
|
56
|
+
<label class="form-label">Return URL base (callback)</label>
|
|
57
|
+
<input class="form-control" type="text" name="ha_returnUrl" value="{settings.ha_returnUrl}" placeholder="https://www.onekite.com">
|
|
58
|
+
</div>
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
|
|
62
|
+
<div class="card card-body mb-3">
|
|
63
|
+
<h5>Paiement</h5>
|
|
64
|
+
<div class="row g-3">
|
|
65
|
+
<div class="col-md-6">
|
|
66
|
+
<label class="form-label">paymentTimeoutMinutes</label>
|
|
67
|
+
<input class="form-control" type="number" min="1" name="paymentTimeoutMinutes" value="{settings.paymentTimeoutMinutes}">
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
71
|
+
|
|
72
|
+
<button class="btn btn-primary" type="submit">Enregistrer</button>
|
|
73
|
+
</form>
|
|
74
|
+
|
|
75
|
+
<script>
|
|
76
|
+
(function () {
|
|
77
|
+
var params = new URLSearchParams(window.location.search || '');
|
|
78
|
+
if (params.get('saved') === '1') {
|
|
79
|
+
require(['alerts'], function (alerts) { alerts.success('Paramètres enregistrés'); });
|
|
80
|
+
}
|
|
81
|
+
})();
|
|
82
|
+
</script>
|
|
83
|
+
</div>
|
|
@@ -0,0 +1,51 @@
|
|
|
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>
|
|
@@ -0,0 +1,89 @@
|
|
|
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>
|
|
@@ -0,0 +1,15 @@
|
|
|
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>
|