@routebot/constants 0.3.66 → 0.3.68

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.
@@ -10,6 +10,11 @@ const USER_TYPES = {
10
10
  SCHOOL_PARENT: "2",
11
11
  PERSONEL: "10",
12
12
  ADMIN: "1000",
13
+ // POI (Point of Interest) — NOT a real person/app user. A lightweight reusable "place"
14
+ // (hotel, park, venue) used as an ordered stop inside a Tour service. Has no phone/
15
+ // relations and is excluded from all normal user/personel/parent listings.
16
+ // See: backend/modules/tour-builder/.
17
+ POI: "POI",
13
18
  };
14
19
 
15
20
  const CLIENT_ROLES = {
@@ -0,0 +1,32 @@
1
+ // Personal mobile login codes (SMS-less countries, e.g. US).
2
+ // Alphabet is intentionally restricted for easy typing across keyboards/locales:
3
+ // - No I, L, O, 0, 1 (ambiguous when read aloud / transcribed)
4
+ // - No Q, W (missing or awkward on many non-English layouts)
5
+ const LOGIN_CODE_ALPHABET = "ABCDEFGHJKMNPRSTUVYZ23456789";
6
+ const LOGIN_CODE_LENGTH = 8;
7
+
8
+ const LOGIN_CODE_PATTERN = new RegExp(`^[${LOGIN_CODE_ALPHABET}]{${LOGIN_CODE_LENGTH}}$`);
9
+
10
+ const normalizeLoginCode = (input) => {
11
+ const upper = (input || "").toString().toUpperCase();
12
+ let out = "";
13
+ for (const ch of upper) {
14
+ if (LOGIN_CODE_ALPHABET.includes(ch)) {
15
+ out += ch;
16
+ }
17
+ }
18
+ return out;
19
+ };
20
+
21
+ const isValidLoginCode = (code) => {
22
+ if (code == null || code === "") return true;
23
+ return LOGIN_CODE_PATTERN.test(code);
24
+ };
25
+
26
+ module.exports = {
27
+ LOGIN_CODE_ALPHABET,
28
+ LOGIN_CODE_LENGTH,
29
+ LOGIN_CODE_PATTERN,
30
+ normalizeLoginCode,
31
+ isValidLoginCode,
32
+ };
@@ -6,6 +6,10 @@ const SERVICE_TYPES = {
6
6
  TRANSFER_PERSONEL: "TRANSFER_PERSONEL", // happens 1 time, date required
7
7
  TRANSFER_STUDENT: "TRANSFER_STUDENT", // happens 1 time, date required
8
8
  TRANSFER_RANDOM: "TRANSFER_RANDOM", // happens 1 time, date required
9
+ // TOUR — one-time, place-to-place trip authored via the Tour Builder. Stored as a
10
+ // PersonelService (school = destination/venue, users = ordered POI stops, routeType =
11
+ // PICK_UP). Distinct serviceType keeps tours out of the other personel/transfer tabs.
12
+ TOUR: "TOUR", // happens 1 time, date required
9
13
  };
10
14
 
11
15
  const ROUTE_TYPES = {
@@ -0,0 +1,66 @@
1
+ // Tour Builder — shared constants + pure helpers used across backend, frontend and
2
+ // (implicitly) mobile. Keeping the tour stop-ordering logic here is the SINGLE source of
3
+ // truth so the operator's arrangement (web), the persisted order (backend) and the driver's
4
+ // view (mobile) never diverge.
5
+ //
6
+ // Data model recap (see backend/modules/tour-builder/):
7
+ // A Tour is a standard PersonelService with:
8
+ // - serviceType = SERVICE_TYPES.TOUR
9
+ // - routeType = TOUR_ROUTE_TYPE (PICK_UP): collect guests at each stop, arrive at anchor
10
+ // - school = the anchor / final destination (a venue School record)
11
+ // - users = ordered POI stops (usertype: USER_TYPES.POI)
12
+ // - usersdetails = [{ user, transfertime }] carrying the optional per-stop pickup time
13
+ //
14
+ // Mobile (backend/modules/mobile/driver/driver.mobile.controller.js) sorts a personel
15
+ // service's passengers by `transfertime` ascending (timed first, untimed keep order).
16
+ // `orderTourStops` below replicates that EXACT rule so the web preview matches mobile.
17
+
18
+ // Tours are modeled as PICK_UP so that:
19
+ // 1) the anchor (venue) is the final navigation destination, and
20
+ // 2) per-stop pickup times are shown to the driver (mobile shows transfertime on PICK_UP only).
21
+ const TOUR_ROUTE_TYPE = "PICK_UP";
22
+
23
+ /**
24
+ * Validates an "HH:mm" (24h) time string. Empty/invalid → false.
25
+ * @param {string} value
26
+ * @returns {boolean}
27
+ */
28
+ function isValidTourStopTime(value) {
29
+ if (typeof value !== "string") return false;
30
+ return /^([01]\d|2[0-3]):([0-5]\d)$/.test(value.trim());
31
+ }
32
+
33
+ /**
34
+ * Orders tour stops the same way mobile does: stops WITH a valid transfertime come first
35
+ * (ascending by time), stops WITHOUT a time keep their original relative order and go last.
36
+ * The sort is STABLE (insertion index tie-breaker). Pure + side-effect free.
37
+ *
38
+ * @template T
39
+ * @param {T[]} stops - objects exposing a `transfertime` string
40
+ * @returns {T[]} a new, ordered array (input is not mutated)
41
+ */
42
+ function orderTourStops(stops) {
43
+ if (!Array.isArray(stops)) return [];
44
+
45
+ return stops
46
+ .map((stop, index) => ({ stop, index }))
47
+ .sort((a, b) => {
48
+ const validA = isValidTourStopTime(a.stop && a.stop.transfertime);
49
+ const validB = isValidTourStopTime(b.stop && b.stop.transfertime);
50
+
51
+ if (validA && validB) {
52
+ const compared = a.stop.transfertime.localeCompare(b.stop.transfertime);
53
+ return compared !== 0 ? compared : a.index - b.index;
54
+ }
55
+ if (validA && !validB) return -1;
56
+ if (!validA && validB) return 1;
57
+ return a.index - b.index;
58
+ })
59
+ .map((entry) => entry.stop);
60
+ }
61
+
62
+ module.exports = {
63
+ TOUR_ROUTE_TYPE,
64
+ isValidTourStopTime,
65
+ orderTourStops,
66
+ };
package/index.js CHANGED
@@ -5,6 +5,7 @@ module.exports = {
5
5
  ...require("./constants/company.constants.js"),
6
6
  ...require("./constants/process.constants.js"),
7
7
  ...require("./constants/service.constants.js"),
8
+ ...require("./constants/tour.constants.js"),
8
9
  ...require("./constants/sms.constants.js"),
9
10
  ...require("./constants/stock.constants.js"),
10
11
  ...require("./constants/maps.constants.js"),
@@ -13,4 +14,5 @@ module.exports = {
13
14
  ...require("./constants/access-control.constants.js"),
14
15
  ...require("./constants/plan.constants.js"),
15
16
  ...require("./constants/telemetry.constants.js"),
17
+ ...require("./constants/login-code.constants.js"),
16
18
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@routebot/constants",
3
- "version": "0.3.66",
3
+ "version": "0.3.68",
4
4
  "description": "Shared constants for RouteBot FE/BE",
5
5
  "main": "index.js",
6
6
  "files": [