nodebb-plugin-onekite-calendar 2.0.19 → 2.0.21
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/CHANGELOG.md +10 -0
- package/lib/api.js +14 -4
- package/package.json +1 -1
- package/pkg/package/CHANGELOG.md +27 -0
- package/pkg/package/lib/api.js +14 -4
- package/pkg/package/package.json +1 -1
- package/pkg/package/plugin.json +1 -1
- package/pkg/package/public/client.js +11 -5
- package/plugin.json +1 -1
- package/public/client.js +13 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog – calendar-onekite
|
|
2
2
|
|
|
3
|
+
## 1.3.15
|
|
4
|
+
- Permissions : les **modérateurs globaux / gestionnaires** (groupe NodeBB « Global Moderators », selon le slug) sont désormais considérés comme validateurs pour les actions de modération (dont l’**annulation**).
|
|
5
|
+
|
|
6
|
+
## 1.3.14
|
|
7
|
+
- Mobile FAB : la modale de sélection de dates autorise désormais la réservation **le jour même** (seules les dates passées sont désactivées).
|
|
8
|
+
|
|
9
|
+
## 1.3.13
|
|
10
|
+
- Réservations : il est désormais possible de réserver pour le jour même (seules les dates passées sont refusées).
|
|
11
|
+
- Annulation : les validateurs peuvent annuler une réservation déjà payée.
|
|
12
|
+
|
|
3
13
|
## 1.3.12
|
|
4
14
|
- Expiration automatique : envoi d’un email au demandeur avec la raison :
|
|
5
15
|
- « Demande non prise en charge dans le temps imparti » (demande en attente)
|
package/lib/api.js
CHANGED
|
@@ -317,6 +317,16 @@ async function canValidate(uid, settings) {
|
|
|
317
317
|
try {
|
|
318
318
|
const isAdmin = await groups.isMember(uid, 'administrators');
|
|
319
319
|
if (isAdmin) return true;
|
|
320
|
+
|
|
321
|
+
// NodeBB has a built-in "Global Moderators" group (slug can vary by install/version).
|
|
322
|
+
// Allow them as validators as well.
|
|
323
|
+
const globalModeratorCandidates = ['global-moderators', 'Global Moderators', 'global moderators'];
|
|
324
|
+
for (const g of globalModeratorCandidates) {
|
|
325
|
+
try {
|
|
326
|
+
// eslint-disable-next-line no-await-in-loop
|
|
327
|
+
if (await groups.isMember(uid, g)) return true;
|
|
328
|
+
} catch (e) {}
|
|
329
|
+
}
|
|
320
330
|
} catch (e) {}
|
|
321
331
|
|
|
322
332
|
const allowed = normalizeAllowedGroups(settings.validatorGroups || '');
|
|
@@ -866,16 +876,16 @@ api.createReservation = async function (req, res) {
|
|
|
866
876
|
// A validator is "free" only if the rental duration is within the configured threshold.
|
|
867
877
|
const isValidatorFree = !!isValidator && (validatorFreeMaxDays <= 0 || nbDays <= validatorFreeMaxDays);
|
|
868
878
|
|
|
869
|
-
// Business rule: a reservation cannot start
|
|
879
|
+
// Business rule: a reservation cannot start in the past.
|
|
870
880
|
// We compare against server-local midnight. (Front-end also prevents it.)
|
|
871
881
|
try {
|
|
872
882
|
const today0 = new Date();
|
|
873
883
|
today0.setHours(0, 0, 0, 0);
|
|
874
|
-
const
|
|
875
|
-
if (start <
|
|
884
|
+
const today0ts = today0.getTime();
|
|
885
|
+
if (start < today0ts) {
|
|
876
886
|
return res.status(400).json({
|
|
877
887
|
error: 'date-too-soon',
|
|
878
|
-
message: "Impossible de réserver pour
|
|
888
|
+
message: "Impossible de réserver pour une date passée.",
|
|
879
889
|
});
|
|
880
890
|
}
|
|
881
891
|
} catch (e) {
|
package/package.json
CHANGED
package/pkg/package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,32 @@
|
|
|
1
1
|
# Changelog – calendar-onekite
|
|
2
2
|
|
|
3
|
+
## 1.3.15
|
|
4
|
+
- Permissions : les **modérateurs globaux / gestionnaires** (groupe NodeBB « Global Moderators », selon le slug) sont désormais considérés comme validateurs pour les actions de modération (dont l’**annulation**).
|
|
5
|
+
|
|
6
|
+
## 1.3.14
|
|
7
|
+
- Mobile FAB : la modale de sélection de dates autorise désormais la réservation **le jour même** (seules les dates passées sont désactivées).
|
|
8
|
+
|
|
9
|
+
## 1.3.13
|
|
10
|
+
- Réservations : il est désormais possible de réserver pour le jour même (seules les dates passées sont refusées).
|
|
11
|
+
- Annulation : les validateurs peuvent annuler une réservation déjà payée.
|
|
12
|
+
|
|
13
|
+
## 1.3.12
|
|
14
|
+
- Expiration automatique : envoi d’un email au demandeur avec la raison :
|
|
15
|
+
- « Demande non prise en charge dans le temps imparti » (demande en attente)
|
|
16
|
+
- « Paiement non reçu dans le temps imparti » (paiement en attente)
|
|
17
|
+
- ACP : ajout de 2 paramètres pour envoyer un rappel email aux validateurs sur les demandes en attente / paiement en attente.
|
|
18
|
+
- Emails validateurs : nouveaux templates (rappel + expiration) avec lien vers l’ACP.
|
|
19
|
+
|
|
20
|
+
## 1.3.11
|
|
21
|
+
- ACP (Demandes en attente) : affichage du pseudo du demandeur avec lien vers sa fiche utilisateur.
|
|
22
|
+
|
|
23
|
+
## 1.3.10
|
|
24
|
+
- Mobile FAB : surlignage de la sélection de dates rendu lisible en light mode (contraste renforcé, start/end en primary).
|
|
25
|
+
|
|
26
|
+
## 1.3.9
|
|
27
|
+
- Mobile FAB : correction de la sélection de plage (tous les jours sélectionnés sont désormais bien inclus dans la surbrillance).
|
|
28
|
+
- Mobile FAB : amélioration du rendu en mode sombre (bordures/fonds adaptés, contraste renforcé).
|
|
29
|
+
|
|
3
30
|
## 1.3.8
|
|
4
31
|
- ACP Comptabilisation : ajout d’un **grand total** (montant total des locations payées sur la période) + compteurs (payées / sorties gratuites).
|
|
5
32
|
|
package/pkg/package/lib/api.js
CHANGED
|
@@ -317,6 +317,16 @@ async function canValidate(uid, settings) {
|
|
|
317
317
|
try {
|
|
318
318
|
const isAdmin = await groups.isMember(uid, 'administrators');
|
|
319
319
|
if (isAdmin) return true;
|
|
320
|
+
|
|
321
|
+
// NodeBB has a built-in "Global Moderators" group (slug can vary by install/version).
|
|
322
|
+
// Allow them as validators as well.
|
|
323
|
+
const globalModeratorCandidates = ['global-moderators', 'Global Moderators', 'global moderators'];
|
|
324
|
+
for (const g of globalModeratorCandidates) {
|
|
325
|
+
try {
|
|
326
|
+
// eslint-disable-next-line no-await-in-loop
|
|
327
|
+
if (await groups.isMember(uid, g)) return true;
|
|
328
|
+
} catch (e) {}
|
|
329
|
+
}
|
|
320
330
|
} catch (e) {}
|
|
321
331
|
|
|
322
332
|
const allowed = normalizeAllowedGroups(settings.validatorGroups || '');
|
|
@@ -866,16 +876,16 @@ api.createReservation = async function (req, res) {
|
|
|
866
876
|
// A validator is "free" only if the rental duration is within the configured threshold.
|
|
867
877
|
const isValidatorFree = !!isValidator && (validatorFreeMaxDays <= 0 || nbDays <= validatorFreeMaxDays);
|
|
868
878
|
|
|
869
|
-
// Business rule: a reservation cannot start
|
|
879
|
+
// Business rule: a reservation cannot start in the past.
|
|
870
880
|
// We compare against server-local midnight. (Front-end also prevents it.)
|
|
871
881
|
try {
|
|
872
882
|
const today0 = new Date();
|
|
873
883
|
today0.setHours(0, 0, 0, 0);
|
|
874
|
-
const
|
|
875
|
-
if (start <
|
|
884
|
+
const today0ts = today0.getTime();
|
|
885
|
+
if (start < today0ts) {
|
|
876
886
|
return res.status(400).json({
|
|
877
887
|
error: 'date-too-soon',
|
|
878
|
-
message: "Impossible de réserver pour
|
|
888
|
+
message: "Impossible de réserver pour une date passée.",
|
|
879
889
|
});
|
|
880
890
|
}
|
|
881
891
|
} catch (e) {
|
package/pkg/package/package.json
CHANGED
package/pkg/package/plugin.json
CHANGED
|
@@ -1346,14 +1346,14 @@ function toDatetimeLocalValue(date) {
|
|
|
1346
1346
|
return;
|
|
1347
1347
|
}
|
|
1348
1348
|
|
|
1349
|
-
// Business rule: reservations cannot start
|
|
1349
|
+
// Business rule: reservations cannot start in the past.
|
|
1350
1350
|
// (We validate again on the server, but this gives immediate feedback.)
|
|
1351
1351
|
try {
|
|
1352
1352
|
const startDateCheck = toLocalYmd(info.start);
|
|
1353
1353
|
const todayCheck = toLocalYmd(new Date());
|
|
1354
|
-
if (startDateCheck
|
|
1354
|
+
if (startDateCheck < todayCheck) {
|
|
1355
1355
|
lastDateRuleToastAt = Date.now();
|
|
1356
|
-
showAlert('error', "Impossible de réserver pour
|
|
1356
|
+
showAlert('error', "Impossible de réserver pour une date passée.");
|
|
1357
1357
|
calendar.unselect();
|
|
1358
1358
|
isDialogOpen = false;
|
|
1359
1359
|
return;
|
|
@@ -1413,7 +1413,7 @@ function toDatetimeLocalValue(date) {
|
|
|
1413
1413
|
} else if (code === '400' && payload && (payload.error === 'date-too-soon' || payload.code === 'date-too-soon')) {
|
|
1414
1414
|
// If we already showed the client-side toast a moment ago, avoid a duplicate.
|
|
1415
1415
|
if (!lastDateRuleToastAt || (Date.now() - lastDateRuleToastAt) > 1500) {
|
|
1416
|
-
showAlert('error', String(payload.message || "Impossible de réserver pour
|
|
1416
|
+
showAlert('error', String(payload.message || "Impossible de réserver pour une date passée."));
|
|
1417
1417
|
}
|
|
1418
1418
|
} else {
|
|
1419
1419
|
const msgRaw = payload && (payload.message || payload.error || payload.msg)
|
|
@@ -1699,7 +1699,13 @@ function toDatetimeLocalValue(date) {
|
|
|
1699
1699
|
const ownerUid = String(ev.extendedProps && ev.extendedProps.uid ? ev.extendedProps.uid : '');
|
|
1700
1700
|
const isOwner = uidNow && ownerUid && uidNow === ownerUid;
|
|
1701
1701
|
const showModeration = canModerate && isPending;
|
|
1702
|
-
|
|
1702
|
+
// Cancellation rules:
|
|
1703
|
+
// - Owner: can cancel only before payment is completed.
|
|
1704
|
+
// - Validators/admins (canModerate): can cancel even when already paid.
|
|
1705
|
+
const showCancel = (
|
|
1706
|
+
(isOwner && ['pending', 'awaiting_payment'].includes(status)) ||
|
|
1707
|
+
(canModerate && ['pending', 'awaiting_payment', 'approved', 'paid'].includes(status))
|
|
1708
|
+
);
|
|
1703
1709
|
const paymentUrl = String((p && p.paymentUrl) || '');
|
|
1704
1710
|
const showPay = isOwner && status === 'awaiting_payment' && /^https?:\/\//i.test(paymentUrl);
|
|
1705
1711
|
const buttons = {
|
package/plugin.json
CHANGED
package/public/client.js
CHANGED
|
@@ -1356,14 +1356,14 @@ function toDatetimeLocalValue(date) {
|
|
|
1356
1356
|
return;
|
|
1357
1357
|
}
|
|
1358
1358
|
|
|
1359
|
-
// Business rule: reservations cannot start
|
|
1359
|
+
// Business rule: reservations cannot start in the past.
|
|
1360
1360
|
// (We validate again on the server, but this gives immediate feedback.)
|
|
1361
1361
|
try {
|
|
1362
1362
|
const startDateCheck = toLocalYmd(info.start);
|
|
1363
1363
|
const todayCheck = toLocalYmd(new Date());
|
|
1364
|
-
if (startDateCheck
|
|
1364
|
+
if (startDateCheck < todayCheck) {
|
|
1365
1365
|
lastDateRuleToastAt = Date.now();
|
|
1366
|
-
showAlert('error', "Impossible de réserver pour
|
|
1366
|
+
showAlert('error', "Impossible de réserver pour une date passée.");
|
|
1367
1367
|
calendar.unselect();
|
|
1368
1368
|
isDialogOpen = false;
|
|
1369
1369
|
return;
|
|
@@ -1423,7 +1423,7 @@ function toDatetimeLocalValue(date) {
|
|
|
1423
1423
|
} else if (code === '400' && payload && (payload.error === 'date-too-soon' || payload.code === 'date-too-soon')) {
|
|
1424
1424
|
// If we already showed the client-side toast a moment ago, avoid a duplicate.
|
|
1425
1425
|
if (!lastDateRuleToastAt || (Date.now() - lastDateRuleToastAt) > 1500) {
|
|
1426
|
-
showAlert('error', String(payload.message || "Impossible de réserver pour
|
|
1426
|
+
showAlert('error', String(payload.message || "Impossible de réserver pour une date passée."));
|
|
1427
1427
|
}
|
|
1428
1428
|
} else {
|
|
1429
1429
|
const msgRaw = payload && (payload.message || payload.error || payload.msg)
|
|
@@ -1709,7 +1709,13 @@ function toDatetimeLocalValue(date) {
|
|
|
1709
1709
|
const ownerUid = String(ev.extendedProps && ev.extendedProps.uid ? ev.extendedProps.uid : '');
|
|
1710
1710
|
const isOwner = uidNow && ownerUid && uidNow === ownerUid;
|
|
1711
1711
|
const showModeration = canModerate && isPending;
|
|
1712
|
-
|
|
1712
|
+
// Cancellation rules:
|
|
1713
|
+
// - Owner: can cancel only before payment is completed.
|
|
1714
|
+
// - Validators/admins (canModerate): can cancel even when already paid.
|
|
1715
|
+
const showCancel = (
|
|
1716
|
+
(isOwner && ['pending', 'awaiting_payment'].includes(status)) ||
|
|
1717
|
+
(canModerate && ['pending', 'awaiting_payment', 'approved', 'paid'].includes(status))
|
|
1718
|
+
);
|
|
1713
1719
|
const paymentUrl = String((p && p.paymentUrl) || '');
|
|
1714
1720
|
const showPay = isOwner && status === 'awaiting_payment' && /^https?:\/\//i.test(paymentUrl);
|
|
1715
1721
|
const buttons = {
|
|
@@ -2085,11 +2091,10 @@ function toDatetimeLocalValue(date) {
|
|
|
2085
2091
|
async function openFabDatePicker() {
|
|
2086
2092
|
if (!lockAction('fab-date-picker', 700)) return;
|
|
2087
2093
|
|
|
2088
|
-
// Cannot book
|
|
2094
|
+
// Cannot book past dates (mobile FAB). Same-day booking is allowed.
|
|
2089
2095
|
const today = new Date();
|
|
2090
2096
|
today.setHours(0, 0, 0, 0);
|
|
2091
2097
|
const minStart = new Date(today);
|
|
2092
|
-
minStart.setDate(minStart.getDate() + 1);
|
|
2093
2098
|
|
|
2094
2099
|
// Month cursor for the single-calendar range picker
|
|
2095
2100
|
const cursor = new Date(minStart);
|
|
@@ -2114,7 +2119,7 @@ async function openFabDatePicker() {
|
|
|
2114
2119
|
</div>
|
|
2115
2120
|
<div class="onekite-range-grid" id="onekite-range-grid"></div>
|
|
2116
2121
|
<div class="onekite-range-summary" id="onekite-range-summary"></div>
|
|
2117
|
-
<div class="form-text mt-2">Sélectionne une date de début puis une date de fin (incluse). Les dates
|
|
2122
|
+
<div class="form-text mt-2">Sélectionne une date de début puis une date de fin (incluse). Les dates passées sont désactivées.</div>
|
|
2118
2123
|
</div>
|
|
2119
2124
|
`;
|
|
2120
2125
|
|