nodebb-plugin-onekite-calendar 1.0.29 → 1.0.31
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/lib/api.js +35 -0
- package/package.json +1 -1
- package/plugin.json +1 -1
- package/public/client.js +17 -0
- package/templates/emails/calendar-onekite_cancelled.tpl +15 -0
package/lib/api.js
CHANGED
|
@@ -726,6 +726,22 @@ api.createReservation = async function (req, res) {
|
|
|
726
726
|
return res.status(400).json({ error: "bad-dates" });
|
|
727
727
|
}
|
|
728
728
|
|
|
729
|
+
// Business rule: a reservation cannot start on the current day or in the past.
|
|
730
|
+
// We compare against server-local midnight. (Front-end also prevents it.)
|
|
731
|
+
try {
|
|
732
|
+
const today0 = new Date();
|
|
733
|
+
today0.setHours(0, 0, 0, 0);
|
|
734
|
+
const tomorrow0 = today0.getTime() + 24 * 60 * 60 * 1000;
|
|
735
|
+
if (start < tomorrow0) {
|
|
736
|
+
return res.status(400).json({
|
|
737
|
+
error: 'date-too-soon',
|
|
738
|
+
message: "Impossible de réserver pour aujourd’hui ou une date passée.",
|
|
739
|
+
});
|
|
740
|
+
}
|
|
741
|
+
} catch (e) {
|
|
742
|
+
// If anything goes wrong, fail safe by allowing the normal flow.
|
|
743
|
+
}
|
|
744
|
+
|
|
729
745
|
// Support both legacy single itemId and new itemIds[] payload
|
|
730
746
|
const itemIds = Array.isArray(req.body.itemIds) ? req.body.itemIds.map(String) : ((req.body.itemId ? [String(req.body.itemId)] : []));
|
|
731
747
|
const itemNames = Array.isArray(req.body.itemNames) ? req.body.itemNames.map(String) : (req.body.itemName ? [String(req.body.itemName)] : []);
|
|
@@ -1045,6 +1061,25 @@ api.cancelReservation = async function (req, res) {
|
|
|
1045
1061
|
});
|
|
1046
1062
|
} catch (e) {}
|
|
1047
1063
|
|
|
1064
|
+
|
|
1065
|
+
// Email requester
|
|
1066
|
+
try {
|
|
1067
|
+
const requesterUid = parseInt(r.uid, 10);
|
|
1068
|
+
const requester = await user.getUserFields(requesterUid, ['username']);
|
|
1069
|
+
const canceller = await user.getUserFields(uid, ['username']);
|
|
1070
|
+
if (requesterUid) {
|
|
1071
|
+
await sendEmail('calendar-onekite_cancelled', requesterUid, 'Location matériel - Réservation annulée', {
|
|
1072
|
+
uid: requesterUid,
|
|
1073
|
+
username: requester && requester.username ? requester.username : '',
|
|
1074
|
+
itemName: (Array.isArray(r.itemNames) ? r.itemNames.join(', ') : (r.itemName || '')),
|
|
1075
|
+
itemNames: (Array.isArray(r.itemNames) ? r.itemNames : (r.itemName ? [r.itemName] : [])),
|
|
1076
|
+
dateRange: `Du ${formatFR(r.start)} au ${formatFR(r.end)}`,
|
|
1077
|
+
cancelledBy: canceller && canceller.username ? canceller.username : '',
|
|
1078
|
+
cancelledByUrl: (canceller && canceller.username) ? `${normalizeBaseUrl(meta)}/user/${encodeURIComponent(String(canceller.username))}` : '',
|
|
1079
|
+
});
|
|
1080
|
+
}
|
|
1081
|
+
} catch (e) {}
|
|
1082
|
+
|
|
1048
1083
|
return res.json({ ok: true, status: 'cancelled' });
|
|
1049
1084
|
};
|
|
1050
1085
|
|
package/package.json
CHANGED
package/plugin.json
CHANGED
package/public/client.js
CHANGED
|
@@ -1187,6 +1187,21 @@ function toDatetimeLocalValue(date) {
|
|
|
1187
1187
|
return;
|
|
1188
1188
|
}
|
|
1189
1189
|
|
|
1190
|
+
// Business rule: reservations cannot start today or in the past.
|
|
1191
|
+
// (We validate again on the server, but this gives immediate feedback.)
|
|
1192
|
+
try {
|
|
1193
|
+
const startDateCheck = toLocalYmd(info.start);
|
|
1194
|
+
const todayCheck = toLocalYmd(new Date());
|
|
1195
|
+
if (startDateCheck <= todayCheck) {
|
|
1196
|
+
showAlert('error', "Impossible de réserver pour aujourd’hui ou une date passée.");
|
|
1197
|
+
calendar.unselect();
|
|
1198
|
+
isDialogOpen = false;
|
|
1199
|
+
return;
|
|
1200
|
+
}
|
|
1201
|
+
} catch (e) {
|
|
1202
|
+
// ignore
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1190
1205
|
if (!items || !items.length) {
|
|
1191
1206
|
showAlert('error', 'Aucun matériel disponible (items HelloAsso non chargés).');
|
|
1192
1207
|
calendar.unselect();
|
|
@@ -1227,6 +1242,8 @@ function toDatetimeLocalValue(date) {
|
|
|
1227
1242
|
}
|
|
1228
1243
|
} else if (code === '409') {
|
|
1229
1244
|
showAlert('error', 'Impossible : au moins un matériel est déjà réservé ou en attente sur cette période.');
|
|
1245
|
+
} else if (code === '400' && payload && (payload.error === 'date-too-soon' || payload.code === 'date-too-soon')) {
|
|
1246
|
+
showAlert('error', String(payload.message || "Impossible de réserver pour aujourd’hui ou une date passée."));
|
|
1230
1247
|
} else {
|
|
1231
1248
|
const msgRaw = payload && (payload.message || payload.error || payload.msg)
|
|
1232
1249
|
? String(payload.message || payload.error || payload.msg)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<p>Bonjour {username},</p>
|
|
2
|
+
<p>Votre réservation de location de matériel a été annulée.</p>
|
|
3
|
+
|
|
4
|
+
<p><strong>Matériel réservé</strong></p>
|
|
5
|
+
<ul>
|
|
6
|
+
<!-- BEGIN itemNames -->
|
|
7
|
+
<li>{itemNames}</li>
|
|
8
|
+
<!-- END itemNames -->
|
|
9
|
+
</ul>
|
|
10
|
+
|
|
11
|
+
<p>{dateRange}</p>
|
|
12
|
+
|
|
13
|
+
<!-- IF cancelledBy -->
|
|
14
|
+
<p><strong>Annulée par :</strong> {cancelledBy}</p>
|
|
15
|
+
<!-- ENDIF cancelledBy -->
|