@routebot/constants 0.3.73 → 0.3.74
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/constants/access-control.constants.js +26 -0
- package/constants/company.constants.js +4 -0
- package/constants/constants.js +4 -0
- package/constants/customer-portal.constants.js +163 -0
- package/constants/permission.constants.js +1 -0
- package/constants/plan.constants.js +7 -0
- package/index.js +1 -0
- package/package.json +2 -2
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const { PERMISSION_TYPES } = require("./permission.constants");
|
|
2
2
|
const { COMPANY_FEATURES } = require("./company.constants");
|
|
3
|
+
const { CLIENT_ROLES } = require("./constants");
|
|
3
4
|
|
|
4
5
|
const FEATURE_PERMISSION_DEPENDENCIES = {
|
|
5
6
|
[PERMISSION_TYPES.ACCESS_TO_ACCOUNTING]: [COMPANY_FEATURES.ACCOUNTING],
|
|
@@ -11,8 +12,28 @@ const FEATURE_PERMISSION_DEPENDENCIES = {
|
|
|
11
12
|
[PERMISSION_TYPES.ACCESS_TO_TRANSFERS]: [COMPANY_FEATURES.TRANSFERS],
|
|
12
13
|
[PERMISSION_TYPES.ACCESS_TO_SHIFTS]: [COMPANY_FEATURES.SHIFT_MANAGEMENT],
|
|
13
14
|
[PERMISSION_TYPES.ACCESS_TO_BILLING]: [COMPANY_FEATURES.BILLING],
|
|
15
|
+
[PERMISSION_TYPES.ACCESS_TO_CUSTOMER_PORTAL]: [COMPANY_FEATURES.CUSTOMER_PORTAL],
|
|
14
16
|
};
|
|
15
17
|
|
|
18
|
+
// List endpoints (`addAccessFilter`): these roles only see documents in `client.schools`.
|
|
19
|
+
// Unknown roles fall through to company-wide — always add new school-bounded roles here.
|
|
20
|
+
const SCHOOL_SCOPED_CLIENT_ROLES = [
|
|
21
|
+
CLIENT_ROLES.EDITOR,
|
|
22
|
+
CLIENT_ROLES.VIEWER,
|
|
23
|
+
CLIENT_ROLES.DEMO,
|
|
24
|
+
CLIENT_ROLES.CUSTOMER_CONTACT,
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
// Owner/admin ignore school assignment on lists. billing_admin currently falls through
|
|
28
|
+
// to company-wide as well (existing behavior; not listed here on purpose).
|
|
29
|
+
const COMPANY_WIDE_CLIENT_ROLES = [CLIENT_ROLES.OWNER, CLIENT_ROLES.ADMIN];
|
|
30
|
+
|
|
31
|
+
const isSchoolScopedClientRole = (role) => SCHOOL_SCOPED_CLIENT_ROLES.includes(role);
|
|
32
|
+
|
|
33
|
+
const isCompanyWideClientRole = (role) => COMPANY_WIDE_CLIENT_ROLES.includes(role);
|
|
34
|
+
|
|
35
|
+
const isCustomerContactRole = (role) => role === CLIENT_ROLES.CUSTOMER_CONTACT;
|
|
36
|
+
|
|
16
37
|
const isPermissionAvailableForCompanyFeature = (companyFeatures = {}, permissionType) => {
|
|
17
38
|
const featureKeys = FEATURE_PERMISSION_DEPENDENCIES[permissionType];
|
|
18
39
|
|
|
@@ -33,6 +54,11 @@ const isPermissionAvailableForCompany = (company = {}, permissionType) => {
|
|
|
33
54
|
|
|
34
55
|
module.exports = {
|
|
35
56
|
FEATURE_PERMISSION_DEPENDENCIES,
|
|
57
|
+
SCHOOL_SCOPED_CLIENT_ROLES,
|
|
58
|
+
COMPANY_WIDE_CLIENT_ROLES,
|
|
59
|
+
isSchoolScopedClientRole,
|
|
60
|
+
isCompanyWideClientRole,
|
|
61
|
+
isCustomerContactRole,
|
|
36
62
|
isPermissionAvailableForCompanyFeature,
|
|
37
63
|
isTelemetryEnabledForCompany,
|
|
38
64
|
isPermissionAvailableForCompany,
|
|
@@ -20,6 +20,10 @@ const COMPANY_FEATURES = {
|
|
|
20
20
|
// Opt-in "bundled" student routes (students from multiple schools in one route). Default OFF
|
|
21
21
|
// for every plan; enabled per-company via company.features override. See: bundled-route module.
|
|
22
22
|
BUNDLED_STUDENT_ROUTES: "bundledStudentRoutes",
|
|
23
|
+
// Opt-in customer-contact portal (school/site officials file change requests; operators
|
|
24
|
+
// handle them). Default OFF for every plan; enabled per-company via company.features.
|
|
25
|
+
// See: customer-portal.constants.js (backend module lands after this package is released).
|
|
26
|
+
CUSTOMER_PORTAL: "customerPortal",
|
|
23
27
|
};
|
|
24
28
|
|
|
25
29
|
module.exports = {
|
package/constants/constants.js
CHANGED
|
@@ -25,6 +25,10 @@ const CLIENT_ROLES = {
|
|
|
25
25
|
EDITOR: "editor",
|
|
26
26
|
VIEWER: "viewer",
|
|
27
27
|
DEMO: "demo",
|
|
28
|
+
// School/site official at the *transport company's customer* (a School). Invite-only,
|
|
29
|
+
// company-feature gated. Files change requests; never writes students/routes/accounting.
|
|
30
|
+
// NOT a teammate — do not create via invite-teammate. See customer-portal.constants.js.
|
|
31
|
+
CUSTOMER_CONTACT: "customer_contact",
|
|
28
32
|
};
|
|
29
33
|
|
|
30
34
|
const CLIENT_STATUSES = {
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
// Customer portal — shared enums + pure helpers for FE/BE.
|
|
2
|
+
//
|
|
3
|
+
// Product (locked):
|
|
4
|
+
// The transport company (our Client's Company) no longer wants a clerk sitting at the
|
|
5
|
+
// school for trickle registrations. A school/site official is INVITED (no self-signup)
|
|
6
|
+
// as CLIENT_ROLES.CUSTOMER_CONTACT. They file structured change REQUESTS. Operators in
|
|
7
|
+
// the existing admin panel fulfil them (student/route/accounting writes stay with the
|
|
8
|
+
// operator). The official does NOT get a student/service directory, does NOT create or
|
|
9
|
+
// delete students, does NOT touch accounting.
|
|
10
|
+
//
|
|
11
|
+
// Company-wide opt-in: COMPANY_FEATURES.CUSTOMER_PORTAL (PLAN_FEATURES all false).
|
|
12
|
+
// Operator queue + inviting contacts: PERMISSION_TYPES.ACCESS_TO_CUSTOMER_PORTAL
|
|
13
|
+
// (never grant that permission to CUSTOMER_CONTACT — they would see every school).
|
|
14
|
+
// Owner may operate the queue by role even without the flag (backend decision).
|
|
15
|
+
//
|
|
16
|
+
// Identity for remove/change requests: typeahead search (narrow DTO), not a list page.
|
|
17
|
+
// Files: private tenant storage first; do not dump into the existing inventory Drive.
|
|
18
|
+
//
|
|
19
|
+
// Backend module (after this package is released): backend/modules/customer-portal/
|
|
20
|
+
// Frontend (after): frontend/src/features/customer-portal/ + restricted App.js shell
|
|
21
|
+
// (same pattern as ACCESS_TO_TRACK_VEHICLE_ONLY).
|
|
22
|
+
//
|
|
23
|
+
// This file is the enum/contract source of truth. Behavioral middleware stays in backend.
|
|
24
|
+
|
|
25
|
+
const { PERMISSION_TYPES } = require("./permission.constants");
|
|
26
|
+
|
|
27
|
+
const CUSTOMER_PORTAL_API_SEGMENT = "customer-portal";
|
|
28
|
+
|
|
29
|
+
const CUSTOMER_PORTAL_INVITE_EXPIRATION_DAYS = 7;
|
|
30
|
+
|
|
31
|
+
const CUSTOMER_PORTAL_STUDENT_SEARCH_MIN_LENGTH = 2;
|
|
32
|
+
|
|
33
|
+
const CUSTOMER_REQUEST_TYPES = {
|
|
34
|
+
STUDENT_ADD: "student_add",
|
|
35
|
+
STUDENT_ADD_BULK: "student_add_bulk",
|
|
36
|
+
STUDENT_REMOVE: "student_remove",
|
|
37
|
+
STUDENT_CHANGE: "student_change",
|
|
38
|
+
DOCUMENT: "document",
|
|
39
|
+
OTHER: "other",
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const CUSTOMER_REQUEST_CHANGE_KINDS = {
|
|
43
|
+
ADDRESS: "address",
|
|
44
|
+
SERVICE: "service",
|
|
45
|
+
GRADE: "grade",
|
|
46
|
+
PARENT: "parent",
|
|
47
|
+
OTHER: "other",
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const CUSTOMER_REQUEST_STATUSES = {
|
|
51
|
+
OPEN: "open",
|
|
52
|
+
IN_PROGRESS: "in_progress",
|
|
53
|
+
WAITING_CUSTOMER: "waiting_customer",
|
|
54
|
+
RESOLVED: "resolved",
|
|
55
|
+
CANCELLED: "cancelled",
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
// Statuses that still need operator attention (nav badge).
|
|
59
|
+
const CUSTOMER_REQUEST_OPEN_STATUSES = [
|
|
60
|
+
CUSTOMER_REQUEST_STATUSES.OPEN,
|
|
61
|
+
CUSTOMER_REQUEST_STATUSES.IN_PROGRESS,
|
|
62
|
+
CUSTOMER_REQUEST_STATUSES.WAITING_CUSTOMER,
|
|
63
|
+
];
|
|
64
|
+
|
|
65
|
+
// Allowed next statuses. Who may fire a transition is enforced in backend (operator vs contact).
|
|
66
|
+
const CUSTOMER_REQUEST_STATUS_TRANSITIONS = {
|
|
67
|
+
[CUSTOMER_REQUEST_STATUSES.OPEN]: [
|
|
68
|
+
CUSTOMER_REQUEST_STATUSES.IN_PROGRESS,
|
|
69
|
+
CUSTOMER_REQUEST_STATUSES.CANCELLED,
|
|
70
|
+
CUSTOMER_REQUEST_STATUSES.RESOLVED,
|
|
71
|
+
],
|
|
72
|
+
[CUSTOMER_REQUEST_STATUSES.IN_PROGRESS]: [
|
|
73
|
+
CUSTOMER_REQUEST_STATUSES.WAITING_CUSTOMER,
|
|
74
|
+
CUSTOMER_REQUEST_STATUSES.RESOLVED,
|
|
75
|
+
CUSTOMER_REQUEST_STATUSES.CANCELLED,
|
|
76
|
+
CUSTOMER_REQUEST_STATUSES.OPEN,
|
|
77
|
+
],
|
|
78
|
+
[CUSTOMER_REQUEST_STATUSES.WAITING_CUSTOMER]: [
|
|
79
|
+
CUSTOMER_REQUEST_STATUSES.IN_PROGRESS,
|
|
80
|
+
CUSTOMER_REQUEST_STATUSES.RESOLVED,
|
|
81
|
+
CUSTOMER_REQUEST_STATUSES.CANCELLED,
|
|
82
|
+
],
|
|
83
|
+
[CUSTOMER_REQUEST_STATUSES.RESOLVED]: [CUSTOMER_REQUEST_STATUSES.OPEN],
|
|
84
|
+
[CUSTOMER_REQUEST_STATUSES.CANCELLED]: [CUSTOMER_REQUEST_STATUSES.OPEN],
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const CUSTOMER_REQUEST_COMMENT_VISIBILITY = {
|
|
88
|
+
CUSTOMER: "customer",
|
|
89
|
+
INTERNAL: "internal",
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const CUSTOMER_PORTAL_UPLOAD = {
|
|
93
|
+
MAX_FILE_BYTES: 10 * 1024 * 1024,
|
|
94
|
+
MAX_FILES_PER_REQUEST: 10,
|
|
95
|
+
ALLOWED_MIME_TYPES: {
|
|
96
|
+
"application/pdf": ["pdf"],
|
|
97
|
+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": ["xlsx"],
|
|
98
|
+
"application/vnd.ms-excel": ["xls"],
|
|
99
|
+
"text/csv": ["csv"],
|
|
100
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document": ["docx"],
|
|
101
|
+
"application/msword": ["doc"],
|
|
102
|
+
"image/jpeg": ["jpg", "jpeg"],
|
|
103
|
+
"image/png": ["png"],
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const getCustomerPortalAllowedExtensions = () => {
|
|
108
|
+
const seen = new Set();
|
|
109
|
+
const extensions = [];
|
|
110
|
+
Object.values(CUSTOMER_PORTAL_UPLOAD.ALLOWED_MIME_TYPES).forEach((list) => {
|
|
111
|
+
list.forEach((ext) => {
|
|
112
|
+
if (!seen.has(ext)) {
|
|
113
|
+
seen.add(ext);
|
|
114
|
+
extensions.push(ext);
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
return extensions;
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
const isAllowedCustomerPortalUpload = ({ mimeType, fileName } = {}) => {
|
|
122
|
+
const allowed = CUSTOMER_PORTAL_UPLOAD.ALLOWED_MIME_TYPES;
|
|
123
|
+
if (mimeType && Object.prototype.hasOwnProperty.call(allowed, mimeType)) {
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
if (typeof fileName !== "string" || !fileName.includes(".")) {
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
const ext = fileName.split(".").pop().toLowerCase();
|
|
130
|
+
return getCustomerPortalAllowedExtensions().includes(ext);
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const isCustomerRequestStatusTransitionAllowed = (fromStatus, toStatus) => {
|
|
134
|
+
const next = CUSTOMER_REQUEST_STATUS_TRANSITIONS[fromStatus] || [];
|
|
135
|
+
return next.includes(toStatus);
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
// Invite payload: every permission flag false. CUSTOMER_CONTACT is authorized by role,
|
|
139
|
+
// not by ACCESS_TO_STUDENTS / ACCESS_TO_CUSTOMER_PORTAL.
|
|
140
|
+
const getCustomerContactPermissions = () => {
|
|
141
|
+
const permissions = {};
|
|
142
|
+
Object.values(PERMISSION_TYPES).forEach((key) => {
|
|
143
|
+
permissions[key] = false;
|
|
144
|
+
});
|
|
145
|
+
return permissions;
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
module.exports = {
|
|
149
|
+
CUSTOMER_PORTAL_API_SEGMENT,
|
|
150
|
+
CUSTOMER_PORTAL_INVITE_EXPIRATION_DAYS,
|
|
151
|
+
CUSTOMER_PORTAL_STUDENT_SEARCH_MIN_LENGTH,
|
|
152
|
+
CUSTOMER_REQUEST_TYPES,
|
|
153
|
+
CUSTOMER_REQUEST_CHANGE_KINDS,
|
|
154
|
+
CUSTOMER_REQUEST_STATUSES,
|
|
155
|
+
CUSTOMER_REQUEST_OPEN_STATUSES,
|
|
156
|
+
CUSTOMER_REQUEST_STATUS_TRANSITIONS,
|
|
157
|
+
CUSTOMER_REQUEST_COMMENT_VISIBILITY,
|
|
158
|
+
CUSTOMER_PORTAL_UPLOAD,
|
|
159
|
+
getCustomerPortalAllowedExtensions,
|
|
160
|
+
isAllowedCustomerPortalUpload,
|
|
161
|
+
isCustomerRequestStatusTransitionAllowed,
|
|
162
|
+
getCustomerContactPermissions,
|
|
163
|
+
};
|
|
@@ -14,6 +14,7 @@ const PERMISSION_TYPES = {
|
|
|
14
14
|
ACCESS_TO_SERVISTESIN: "accessToServistesin", // feature based
|
|
15
15
|
ACCESS_TO_TRACK_VEHICLE_ONLY: "accessToTrackVehicleOnly", // manual integration
|
|
16
16
|
ACCESS_TO_TELEMETRY: "accessToTelemetry", // feature based - Arvento integration. See backend/docs/arvento-telemetry.md
|
|
17
|
+
ACCESS_TO_CUSTOMER_PORTAL: "accessToCustomerPortal", // feature based - operator queue + invite school contacts. NEVER grant to CUSTOMER_CONTACT.
|
|
17
18
|
ACCESS_TO_SCHOOLS: "accessToSchools", // Navigation level - always true for now
|
|
18
19
|
ACCESS_TO_USERS: "accessToUsers", // Navigation level - always true for now
|
|
19
20
|
ACCESS_TO_STOPS: "accessToStops", // should be always false for now
|
|
@@ -167,6 +167,7 @@ const PLAN_FEATURES = {
|
|
|
167
167
|
[COMPANY_FEATURES.BILLING]: false,
|
|
168
168
|
[COMPANY_FEATURES.UETDS]: false,
|
|
169
169
|
[COMPANY_FEATURES.BUNDLED_STUDENT_ROUTES]: false,
|
|
170
|
+
[COMPANY_FEATURES.CUSTOMER_PORTAL]: false,
|
|
170
171
|
},
|
|
171
172
|
|
|
172
173
|
[PLANS.FREE]: {
|
|
@@ -181,6 +182,7 @@ const PLAN_FEATURES = {
|
|
|
181
182
|
[COMPANY_FEATURES.BILLING]: false,
|
|
182
183
|
[COMPANY_FEATURES.UETDS]: false,
|
|
183
184
|
[COMPANY_FEATURES.BUNDLED_STUDENT_ROUTES]: false,
|
|
185
|
+
[COMPANY_FEATURES.CUSTOMER_PORTAL]: false,
|
|
184
186
|
},
|
|
185
187
|
[PLANS.STARTER]: {
|
|
186
188
|
[COMPANY_FEATURES.ACCOUNTING]: false,
|
|
@@ -194,6 +196,7 @@ const PLAN_FEATURES = {
|
|
|
194
196
|
[COMPANY_FEATURES.BILLING]: false,
|
|
195
197
|
[COMPANY_FEATURES.UETDS]: false,
|
|
196
198
|
[COMPANY_FEATURES.BUNDLED_STUDENT_ROUTES]: false,
|
|
199
|
+
[COMPANY_FEATURES.CUSTOMER_PORTAL]: false,
|
|
197
200
|
},
|
|
198
201
|
[PLANS.PRO]: {
|
|
199
202
|
[COMPANY_FEATURES.ACCOUNTING]: false,
|
|
@@ -207,6 +210,7 @@ const PLAN_FEATURES = {
|
|
|
207
210
|
[COMPANY_FEATURES.BILLING]: false,
|
|
208
211
|
[COMPANY_FEATURES.UETDS]: false,
|
|
209
212
|
[COMPANY_FEATURES.BUNDLED_STUDENT_ROUTES]: false,
|
|
213
|
+
[COMPANY_FEATURES.CUSTOMER_PORTAL]: false,
|
|
210
214
|
},
|
|
211
215
|
[PLANS.BUSINESS]: {
|
|
212
216
|
[COMPANY_FEATURES.ACCOUNTING]: false,
|
|
@@ -220,6 +224,7 @@ const PLAN_FEATURES = {
|
|
|
220
224
|
[COMPANY_FEATURES.BILLING]: false,
|
|
221
225
|
[COMPANY_FEATURES.UETDS]: false,
|
|
222
226
|
[COMPANY_FEATURES.BUNDLED_STUDENT_ROUTES]: false,
|
|
227
|
+
[COMPANY_FEATURES.CUSTOMER_PORTAL]: false,
|
|
223
228
|
},
|
|
224
229
|
[PLANS.ENTERPRISE]: {
|
|
225
230
|
[COMPANY_FEATURES.ACCOUNTING]: false,
|
|
@@ -233,6 +238,7 @@ const PLAN_FEATURES = {
|
|
|
233
238
|
[COMPANY_FEATURES.BILLING]: false,
|
|
234
239
|
[COMPANY_FEATURES.UETDS]: false,
|
|
235
240
|
[COMPANY_FEATURES.BUNDLED_STUDENT_ROUTES]: false,
|
|
241
|
+
[COMPANY_FEATURES.CUSTOMER_PORTAL]: false,
|
|
236
242
|
},
|
|
237
243
|
[PLANS.UNLIMITED]: {
|
|
238
244
|
[COMPANY_FEATURES.ACCOUNTING]: false,
|
|
@@ -246,6 +252,7 @@ const PLAN_FEATURES = {
|
|
|
246
252
|
[COMPANY_FEATURES.BILLING]: false,
|
|
247
253
|
[COMPANY_FEATURES.UETDS]: false,
|
|
248
254
|
[COMPANY_FEATURES.BUNDLED_STUDENT_ROUTES]: false,
|
|
255
|
+
[COMPANY_FEATURES.CUSTOMER_PORTAL]: false,
|
|
249
256
|
},
|
|
250
257
|
};
|
|
251
258
|
|
package/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@routebot/constants",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.74",
|
|
4
4
|
"description": "Shared constants for RouteBot FE/BE",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"files": [
|
|
@@ -21,6 +21,6 @@
|
|
|
21
21
|
"release:minor": "npm version minor -m \"chore(release): %s\" && npm publish --access public && git push && git push --tags",
|
|
22
22
|
"release:major": "npm version major -m \"chore(release): %s\" && npm publish --access public && git push && git push --tags",
|
|
23
23
|
"prepublishOnly": "npm test",
|
|
24
|
-
"test": "node -
|
|
24
|
+
"test": "node ./scripts/check-exports.js"
|
|
25
25
|
}
|
|
26
26
|
}
|