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 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodebb-plugin-onekite-calendar",
3
- "version": "1.0.29",
3
+ "version": "1.0.31",
4
4
  "description": "FullCalendar-based equipment reservation workflow with admin approval & HelloAsso payment for NodeBB",
5
5
  "main": "library.js",
6
6
  "license": "MIT",
package/plugin.json CHANGED
@@ -39,5 +39,5 @@
39
39
  "acpScripts": [
40
40
  "public/admin.js"
41
41
  ],
42
- "version": "1.0.29"
42
+ "version": "1.0.31"
43
43
  }
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 -->