dce-reactkit 3.9.4-beta-prompt.2 → 3.9.4-beta-logreviewer.3
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/dist/cjs/index.js +670 -869
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/types/components/AppWrapper.d.ts +0 -24
- package/dist/cjs/types/constants/LOG_REVIEW_GET_LOGS_ROUTE.d.ts +6 -0
- package/dist/cjs/types/index.d.ts +2 -2
- package/dist/cjs/types/types/LogReviewerFilterState/ActionErrorFilterState.d.ts +13 -0
- package/dist/cjs/types/types/LogReviewerFilterState/AdvancedFilterState.d.ts +17 -0
- package/dist/cjs/types/types/LogReviewerFilterState/ContextFilterState.d.ts +6 -0
- package/dist/cjs/types/types/LogReviewerFilterState/DateFilterState.d.ts +13 -0
- package/dist/cjs/types/types/LogReviewerFilterState/TagFilterState.d.ts +4 -0
- package/dist/cjs/types/types/LogReviewerFilterState/index.d.ts +17 -0
- package/dist/cjs/types/types/ModalType.d.ts +0 -1
- package/dist/esm/index.js +672 -870
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/types/components/AppWrapper.d.ts +0 -24
- package/dist/esm/types/constants/LOG_REVIEW_GET_LOGS_ROUTE.d.ts +6 -0
- package/dist/esm/types/index.d.ts +2 -2
- package/dist/esm/types/types/LogReviewerFilterState/ActionErrorFilterState.d.ts +13 -0
- package/dist/esm/types/types/LogReviewerFilterState/AdvancedFilterState.d.ts +17 -0
- package/dist/esm/types/types/LogReviewerFilterState/ContextFilterState.d.ts +6 -0
- package/dist/esm/types/types/LogReviewerFilterState/DateFilterState.d.ts +13 -0
- package/dist/esm/types/types/LogReviewerFilterState/TagFilterState.d.ts +4 -0
- package/dist/esm/types/types/LogReviewerFilterState/index.d.ts +17 -0
- package/dist/esm/types/types/ModalType.d.ts +0 -1
- package/dist/index.d.ts +1 -26
- package/package.json +1 -1
- package/src/components/AppWrapper.tsx +0 -216
- package/src/components/LogReviewer.tsx +840 -1232
- package/src/components/Modal/index.tsx +0 -3
- package/src/constants/LOG_REVIEW_GET_LOGS_ROUTE.ts +9 -0
- package/src/index.ts +0 -2
- package/src/server/initServer.ts +216 -19
- package/src/types/LogReviewerFilterState/ActionErrorFilterState.ts +21 -0
- package/src/types/LogReviewerFilterState/AdvancedFilterState.ts +33 -0
- package/src/types/LogReviewerFilterState/ContextFilterState.ts +13 -0
- package/src/types/LogReviewerFilterState/DateFilterState.ts +23 -0
- package/src/types/LogReviewerFilterState/TagFilterState.ts +6 -0
- package/src/types/LogReviewerFilterState/index.ts +24 -0
- package/src/types/ModalType.tsx +0 -1
- package/dist/cjs/types/constants/LOG_REVIEW_ROUTE_PATH_PREFIX.d.ts +0 -6
- package/dist/esm/types/constants/LOG_REVIEW_ROUTE_PATH_PREFIX.d.ts +0 -6
- package/src/constants/LOG_REVIEW_ROUTE_PATH_PREFIX.ts +0 -9
package/src/index.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
// Import components
|
|
2
2
|
import AppWrapper, {
|
|
3
3
|
alert,
|
|
4
|
-
prompt,
|
|
5
4
|
confirm,
|
|
6
5
|
showFatalError,
|
|
7
6
|
addFatalErrorHandler,
|
|
@@ -149,7 +148,6 @@ export {
|
|
|
149
148
|
Dropdown,
|
|
150
149
|
// Global functions
|
|
151
150
|
alert,
|
|
152
|
-
prompt,
|
|
153
151
|
confirm,
|
|
154
152
|
showFatalError,
|
|
155
153
|
// Errors
|
package/src/server/initServer.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import ErrorWithCode from '../errors/ErrorWithCode';
|
|
3
3
|
|
|
4
4
|
// Import shared constants
|
|
5
|
-
import LOG_REVIEW_ROUTE_PATH_PREFIX from '../constants/
|
|
5
|
+
import LOG_REVIEW_ROUTE_PATH_PREFIX from '../constants/LOG_REVIEW_GET_LOGS_ROUTE';
|
|
6
6
|
import LOG_ROUTE_PATH from '../constants/LOG_ROUTE_PATH';
|
|
7
7
|
import LOG_REVIEW_STATUS_ROUTE from '../constants/LOG_REVIEW_STATUS_ROUTE';
|
|
8
8
|
|
|
@@ -10,10 +10,11 @@ import LOG_REVIEW_STATUS_ROUTE from '../constants/LOG_REVIEW_STATUS_ROUTE';
|
|
|
10
10
|
import genRouteHandler from '../helpers/genRouteHandler';
|
|
11
11
|
|
|
12
12
|
// Import shared types
|
|
13
|
-
import Log from '../types/Log';
|
|
14
13
|
import LogFunction from '../types/LogFunction';
|
|
15
14
|
import ParamType from '../types/ParamType';
|
|
16
15
|
import ReactKitErrorCode from '../types/ReactKitErrorCode';
|
|
16
|
+
import LogReviewerFilterState from '../types/LogReviewerFilterState';
|
|
17
|
+
import LogType from '../types/LogType';
|
|
17
18
|
|
|
18
19
|
// Types
|
|
19
20
|
type GetLaunchInfoFunction = (req: any) => {
|
|
@@ -234,28 +235,36 @@ const initServer = (
|
|
|
234
235
|
);
|
|
235
236
|
|
|
236
237
|
/**
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
238
|
+
* Get filtered logs based on provided filters
|
|
239
|
+
* @author Gabe Abrams, Yuen Ler Chow
|
|
240
|
+
* @param pageNumber the page number to get
|
|
241
|
+
* @param filters the filters to apply to the logs
|
|
242
|
+
* @returns {Log[]} list of logs that match the filters
|
|
243
|
+
*/
|
|
243
244
|
opts.app.get(
|
|
244
|
-
`${LOG_REVIEW_ROUTE_PATH_PREFIX}
|
|
245
|
+
`${LOG_REVIEW_ROUTE_PATH_PREFIX}`,
|
|
245
246
|
genRouteHandler({
|
|
246
247
|
paramTypes: {
|
|
247
|
-
|
|
248
|
-
|
|
248
|
+
pageNumber: ParamType.Int,
|
|
249
|
+
filters: ParamType.JSON,
|
|
249
250
|
},
|
|
250
251
|
handler: async ({ params }) => {
|
|
251
|
-
|
|
252
|
+
// Get user info
|
|
252
253
|
const {
|
|
253
|
-
|
|
254
|
-
month,
|
|
254
|
+
pageNumber,
|
|
255
255
|
userId,
|
|
256
256
|
isAdmin,
|
|
257
|
+
filters,
|
|
257
258
|
} = params;
|
|
258
259
|
|
|
260
|
+
const {
|
|
261
|
+
dateFilterState,
|
|
262
|
+
contextFilterState,
|
|
263
|
+
tagFilterState,
|
|
264
|
+
actionErrorFilterState,
|
|
265
|
+
advancedFilterState,
|
|
266
|
+
} = filters as LogReviewerFilterState;
|
|
267
|
+
|
|
259
268
|
// Validate user
|
|
260
269
|
const canReview = await canReviewLogs(userId, isAdmin);
|
|
261
270
|
if (!canReview) {
|
|
@@ -265,14 +274,202 @@ const initServer = (
|
|
|
265
274
|
);
|
|
266
275
|
}
|
|
267
276
|
|
|
277
|
+
// Build MongoDB query based on filters
|
|
278
|
+
const query: { [k: string]: any } = {};
|
|
279
|
+
|
|
280
|
+
/* -------------- Date Filter ------------- */
|
|
281
|
+
|
|
282
|
+
// Convert start and end dates from the dateFilterState into timestamps
|
|
283
|
+
const { startDate, endDate } = dateFilterState;
|
|
284
|
+
const startTimestamp = new Date(startDate.year, startDate.month - 1, startDate.day).getTime();
|
|
285
|
+
const endTimestamp = new Date(endDate.year, endDate.month - 1, endDate.day + 1).getTime() - 1;
|
|
286
|
+
|
|
287
|
+
// Add a date range condition to the query
|
|
288
|
+
query.timestamp = {
|
|
289
|
+
$gte: startTimestamp,
|
|
290
|
+
$lte: endTimestamp,
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
/* ------------ Context Filter ------------ */
|
|
294
|
+
|
|
295
|
+
// Process context filters to include selected contexts and subcontexts
|
|
296
|
+
const selectedContexts: string[] = [];
|
|
297
|
+
const selectedSubcontexts: string[] = [];
|
|
298
|
+
|
|
299
|
+
// Process each context filter
|
|
300
|
+
Object.keys(contextFilterState).forEach((context) => {
|
|
301
|
+
const value = contextFilterState[context];
|
|
302
|
+
if (typeof value === 'boolean') {
|
|
303
|
+
if (value) {
|
|
304
|
+
selectedContexts.push(context);
|
|
305
|
+
}
|
|
306
|
+
} else {
|
|
307
|
+
// At least one subcontext is selected
|
|
308
|
+
const atLeastOneSubcontextSelected = (
|
|
309
|
+
Object.values(value)
|
|
310
|
+
.some((subcontextValue) => {
|
|
311
|
+
return subcontextValue;
|
|
312
|
+
})
|
|
313
|
+
);
|
|
314
|
+
if (atLeastOneSubcontextSelected) {
|
|
315
|
+
selectedContexts.push(context);
|
|
316
|
+
}
|
|
317
|
+
// Add all selected subcontexts
|
|
318
|
+
Object.keys(value).forEach((subcontext) => {
|
|
319
|
+
if (value[subcontext]) {
|
|
320
|
+
selectedSubcontexts.push(subcontext);
|
|
321
|
+
}
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
// Add context and subcontext conditions to the query if any are selected
|
|
327
|
+
if (selectedContexts.length > 0) {
|
|
328
|
+
query.context = { $in: selectedContexts };
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
if (selectedSubcontexts.length > 0) {
|
|
332
|
+
query.subcontext = { $in: selectedSubcontexts };
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/* -------------- Tag Filter -------------- */
|
|
336
|
+
|
|
337
|
+
const selectedTags = Object.keys(tagFilterState).filter((tag) => { return tagFilterState[tag]; });
|
|
338
|
+
if (selectedTags.length > 0) {
|
|
339
|
+
query.tags = { $in: selectedTags };
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/* --------- Action/Error Filter ---------- */
|
|
343
|
+
|
|
344
|
+
if (actionErrorFilterState.type) {
|
|
345
|
+
query.type = actionErrorFilterState.type;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
if (actionErrorFilterState.type === LogType.Error) {
|
|
349
|
+
if (actionErrorFilterState.errorMessage) {
|
|
350
|
+
// Add error message to the query.
|
|
351
|
+
// $i is used for case-insensitive search, and $regex is used for partial matching
|
|
352
|
+
query.errorMessage = {
|
|
353
|
+
$regex: actionErrorFilterState.errorMessage,
|
|
354
|
+
$options: 'i',
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
if (actionErrorFilterState.errorCode) {
|
|
359
|
+
query.errorCode = {
|
|
360
|
+
$regex: actionErrorFilterState.errorCode,
|
|
361
|
+
$options: 'i',
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
if (actionErrorFilterState.type === LogType.Action) {
|
|
367
|
+
const selectedTargets = (
|
|
368
|
+
Object.keys(actionErrorFilterState.target)
|
|
369
|
+
.filter((target) => {
|
|
370
|
+
return actionErrorFilterState.target[target];
|
|
371
|
+
})
|
|
372
|
+
);
|
|
373
|
+
const selectedActions = (
|
|
374
|
+
Object.keys(actionErrorFilterState.action)
|
|
375
|
+
.filter((action) => {
|
|
376
|
+
return actionErrorFilterState.action[action];
|
|
377
|
+
})
|
|
378
|
+
);
|
|
379
|
+
if (selectedTargets.length > 0) {
|
|
380
|
+
query.target = { $in: selectedTargets };
|
|
381
|
+
}
|
|
382
|
+
if (selectedActions.length > 0) {
|
|
383
|
+
query.action = { $in: selectedActions };
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/* ------------ Advanced Filter ----------- */
|
|
388
|
+
|
|
389
|
+
if (advancedFilterState.userFirstName) {
|
|
390
|
+
query.userFirstName = {
|
|
391
|
+
$regex: advancedFilterState.userFirstName,
|
|
392
|
+
$options: 'i',
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
if (advancedFilterState.userLastName) {
|
|
397
|
+
query.userLastName = {
|
|
398
|
+
$regex: advancedFilterState.userLastName,
|
|
399
|
+
$options: 'i',
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
if (advancedFilterState.userEmail) {
|
|
404
|
+
query.userEmail = {
|
|
405
|
+
$regex: advancedFilterState.userEmail,
|
|
406
|
+
$options: 'i',
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
if (advancedFilterState.userId) {
|
|
411
|
+
query.userId = Number.parseInt(advancedFilterState.userId, 10);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
const roles = [];
|
|
415
|
+
if (advancedFilterState.includeLearners) {
|
|
416
|
+
roles.push({ isLearner: true });
|
|
417
|
+
}
|
|
418
|
+
if (advancedFilterState.includeTTMs) {
|
|
419
|
+
roles.push({ isTTM: true });
|
|
420
|
+
}
|
|
421
|
+
if (advancedFilterState.includeAdmins) {
|
|
422
|
+
roles.push({ isAdmin: true });
|
|
423
|
+
}
|
|
424
|
+
// If any roles are selected, add them to the query
|
|
425
|
+
if (roles.length > 0) {
|
|
426
|
+
// The $or operator is used to match any of the roles
|
|
427
|
+
// The $and operator is to ensure that other conditions in the query are met
|
|
428
|
+
query.$and = [{ $or: roles }];
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
if (advancedFilterState.courseId) {
|
|
432
|
+
query.courseId = Number.parseInt(advancedFilterState.courseId, 10);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
if (advancedFilterState.courseName) {
|
|
436
|
+
query.courseName = {
|
|
437
|
+
$regex: advancedFilterState.courseName,
|
|
438
|
+
$options: 'i',
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
if (advancedFilterState.isMobile !== undefined) {
|
|
443
|
+
query['device.isMobile'] = Boolean(advancedFilterState.isMobile);
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
if (advancedFilterState.source) {
|
|
447
|
+
query.source = advancedFilterState.source;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
if (advancedFilterState.routePath) {
|
|
451
|
+
query.routePath = {
|
|
452
|
+
$regex: advancedFilterState.routePath,
|
|
453
|
+
$options: 'i',
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
if (advancedFilterState.routeTemplate) {
|
|
458
|
+
query.routeTemplate = {
|
|
459
|
+
$regex: advancedFilterState.routeTemplate,
|
|
460
|
+
$options: 'i',
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
|
|
268
464
|
// Query for logs
|
|
269
|
-
const
|
|
270
|
-
|
|
271
|
-
|
|
465
|
+
const response = await _logCollection.findPaged({
|
|
466
|
+
query,
|
|
467
|
+
perPage: 50,
|
|
468
|
+
pageNumber,
|
|
272
469
|
});
|
|
273
470
|
|
|
274
|
-
// Return
|
|
275
|
-
return
|
|
471
|
+
// Return response
|
|
472
|
+
return response;
|
|
276
473
|
},
|
|
277
474
|
}),
|
|
278
475
|
);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import LogType from '../LogType';
|
|
2
|
+
|
|
3
|
+
// Action filter state (only relevant for action logs)
|
|
4
|
+
type ActionErrorFilterState = {
|
|
5
|
+
// Required type of log
|
|
6
|
+
type: LogType | undefined, // If undefined, no filter applied
|
|
7
|
+
// Query for error message (only relevant if type is error)
|
|
8
|
+
errorMessage: string, // If empty, no filter applied
|
|
9
|
+
// Query for error code (only relevant if type is error)
|
|
10
|
+
errorCode: string, // If empty, no filter applied
|
|
11
|
+
// Action targets to include (only relevant if type is action)
|
|
12
|
+
target: {
|
|
13
|
+
[k: string]: boolean
|
|
14
|
+
},
|
|
15
|
+
// Action types to include (only relevant if type is action)
|
|
16
|
+
action: {
|
|
17
|
+
[k: string]: boolean
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export default ActionErrorFilterState;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import LogSource from '../LogSource';
|
|
2
|
+
|
|
3
|
+
// Advanced filter state
|
|
4
|
+
type AdvancedFilterState = {
|
|
5
|
+
// Query for user first name (case insensitive)
|
|
6
|
+
userFirstName: string, // If empty, no filter applied
|
|
7
|
+
// Query for user last name (case insensitive)
|
|
8
|
+
userLastName: string, // If empty, no filter applied
|
|
9
|
+
// Query for user email (case insensitive)
|
|
10
|
+
userEmail: string, // If empty, no filter applied
|
|
11
|
+
// Match for userId (numerical)
|
|
12
|
+
userId: string, // If empty, no filter applied
|
|
13
|
+
// If true, include students
|
|
14
|
+
includeLearners: boolean,
|
|
15
|
+
// If true, include ttms
|
|
16
|
+
includeTTMs: boolean,
|
|
17
|
+
// If true, include admins
|
|
18
|
+
includeAdmins: boolean,
|
|
19
|
+
// Match for courseId (numerical)
|
|
20
|
+
courseId: string, // If empty, no filter applied
|
|
21
|
+
// Query for course name (case insensitive)
|
|
22
|
+
courseName: string, // If empty, no filter applied
|
|
23
|
+
// Required isMobile value
|
|
24
|
+
isMobile: (true | false | undefined), // If undefined, no filter applied
|
|
25
|
+
// Required log source value
|
|
26
|
+
source: LogSource | undefined, // If undefined, no filter applied
|
|
27
|
+
// Query for route path (only relevant if source is server)
|
|
28
|
+
routePath: string, // If empty, no filter applied
|
|
29
|
+
// Query for route template (only relevant if source is server)
|
|
30
|
+
routeTemplate: string, // If empty, no filter applied
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export default AdvancedFilterState;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// Date filter state
|
|
2
|
+
type DateFilterState = {
|
|
3
|
+
// Current start date
|
|
4
|
+
startDate: {
|
|
5
|
+
// Full year
|
|
6
|
+
year: number,
|
|
7
|
+
// 1-indexed month
|
|
8
|
+
month: number,
|
|
9
|
+
// 1-indexed day
|
|
10
|
+
day: number,
|
|
11
|
+
},
|
|
12
|
+
// Current end date
|
|
13
|
+
endDate: {
|
|
14
|
+
// Full year
|
|
15
|
+
year: number,
|
|
16
|
+
// 1-indexed month
|
|
17
|
+
month: number,
|
|
18
|
+
// 1-indexed day
|
|
19
|
+
day: number,
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export default DateFilterState;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import DateFilterState from './DateFilterState';
|
|
2
|
+
import ContextFilterState from './ContextFilterState';
|
|
3
|
+
import TagFilterState from './TagFilterState';
|
|
4
|
+
import ActionErrorFilterState from './ActionErrorFilterState';
|
|
5
|
+
import AdvancedFilterState from './AdvancedFilterState';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* A bundle of filter state objects for each type of filter
|
|
9
|
+
* @author Gabe Abrams
|
|
10
|
+
*/
|
|
11
|
+
type LogReviewerFilterState = {
|
|
12
|
+
// Date filter state
|
|
13
|
+
dateFilterState: DateFilterState,
|
|
14
|
+
// Context filter state
|
|
15
|
+
contextFilterState: ContextFilterState,
|
|
16
|
+
// Tag filter state
|
|
17
|
+
tagFilterState: TagFilterState,
|
|
18
|
+
// Action error filter state
|
|
19
|
+
actionErrorFilterState: ActionErrorFilterState,
|
|
20
|
+
// Advanced filter state
|
|
21
|
+
advancedFilterState: AdvancedFilterState,
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export default LogReviewerFilterState;
|
package/src/types/ModalType.tsx
CHANGED