dce-reactkit 3.9.4-beta-logreviewer.2 → 3.9.4-beta-logreviewer.4

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.
Files changed (31) hide show
  1. package/dist/cjs/index.js +112 -50
  2. package/dist/cjs/index.js.map +1 -1
  3. package/dist/cjs/types/constants/LOG_REVIEW_GET_LOGS_ROUTE.d.ts +6 -0
  4. package/dist/cjs/types/types/LogReviewerFilterState/ActionErrorFilterState.d.ts +13 -0
  5. package/dist/cjs/types/types/LogReviewerFilterState/AdvancedFilterState.d.ts +17 -0
  6. package/dist/cjs/types/types/LogReviewerFilterState/ContextFilterState.d.ts +6 -0
  7. package/dist/cjs/types/types/LogReviewerFilterState/DateFilterState.d.ts +13 -0
  8. package/dist/cjs/types/types/LogReviewerFilterState/TagFilterState.d.ts +4 -0
  9. package/dist/cjs/types/types/LogReviewerFilterState/index.d.ts +17 -0
  10. package/dist/esm/index.js +113 -51
  11. package/dist/esm/index.js.map +1 -1
  12. package/dist/esm/types/constants/LOG_REVIEW_GET_LOGS_ROUTE.d.ts +6 -0
  13. package/dist/esm/types/types/LogReviewerFilterState/ActionErrorFilterState.d.ts +13 -0
  14. package/dist/esm/types/types/LogReviewerFilterState/AdvancedFilterState.d.ts +17 -0
  15. package/dist/esm/types/types/LogReviewerFilterState/ContextFilterState.d.ts +6 -0
  16. package/dist/esm/types/types/LogReviewerFilterState/DateFilterState.d.ts +13 -0
  17. package/dist/esm/types/types/LogReviewerFilterState/TagFilterState.d.ts +4 -0
  18. package/dist/esm/types/types/LogReviewerFilterState/index.d.ts +17 -0
  19. package/package.json +1 -1
  20. package/src/components/LogReviewer.tsx +49 -99
  21. package/src/constants/LOG_REVIEW_GET_LOGS_ROUTE.ts +9 -0
  22. package/src/server/initServer.ts +104 -40
  23. package/src/types/LogReviewerFilterState/ActionErrorFilterState.ts +21 -0
  24. package/src/types/LogReviewerFilterState/AdvancedFilterState.ts +33 -0
  25. package/src/types/LogReviewerFilterState/ContextFilterState.ts +13 -0
  26. package/src/types/LogReviewerFilterState/DateFilterState.ts +23 -0
  27. package/src/types/LogReviewerFilterState/TagFilterState.ts +6 -0
  28. package/src/types/LogReviewerFilterState/index.ts +24 -0
  29. package/dist/cjs/types/constants/LOG_REVIEW_ROUTE_PATH_PREFIX.d.ts +0 -6
  30. package/dist/esm/types/constants/LOG_REVIEW_ROUTE_PATH_PREFIX.d.ts +0 -6
  31. package/src/constants/LOG_REVIEW_ROUTE_PATH_PREFIX.ts +0 -9
package/dist/cjs/index.js CHANGED
@@ -2574,10 +2574,10 @@ const getHumanReadableDate = (dateOrTimestamp) => {
2574
2574
  };
2575
2575
 
2576
2576
  /**
2577
- * Path of the route for storing client-side logs
2577
+ * Path of the route for getting logs for log review
2578
2578
  * @author Gabe Abrams
2579
2579
  */
2580
- const LOG_REVIEW_ROUTE_PATH_PREFIX = `/admin${ROUTE_PATH_PREFIX}/logs`;
2580
+ const LOG_REVIEW_GET_LOGS_ROUTE = `/admin${ROUTE_PATH_PREFIX}/logs`;
2581
2581
 
2582
2582
  /**
2583
2583
  * Source of a log event
@@ -3585,7 +3585,7 @@ const reducer$7 = (state, action) => {
3585
3585
  return Object.assign(Object.assign({}, state), { expandedFilterDrawer: undefined });
3586
3586
  }
3587
3587
  case ActionType$6.ResetFilters: {
3588
- return Object.assign(Object.assign({}, state), { dateFilterState: action.initDateFilterState, contextFilterState: action.initContextFilterState, tagFilterState: action.initTagFilterState, actionErrorFilterState: action.initActionErrorFilterState, advancedFilterState: action.initAdvancedFilterState });
3588
+ return Object.assign(Object.assign({}, state), { dateFilterState: action.initDateFilterState, contextFilterState: action.initContextFilterState, tagFilterState: action.initTagFilterState, actionErrorFilterState: action.initActionErrorFilterState, advancedFilterState: action.initAdvancedFilterState, numTimesFiltersReset: state.numTimesFiltersReset + 1 });
3589
3589
  }
3590
3590
  case ActionType$6.UpdateDateFilterState: {
3591
3591
  return Object.assign(Object.assign({}, state), { dateFilterState: action.dateFilterState });
@@ -3608,6 +3608,9 @@ const reducer$7 = (state, action) => {
3608
3608
  case ActionType$6.DecrementPageNumber: {
3609
3609
  return Object.assign(Object.assign({}, state), { pageNumber: state.pageNumber - 1 });
3610
3610
  }
3611
+ case ActionType$6.SetHasAnotherPage: {
3612
+ return Object.assign(Object.assign({}, state), { hasAnotherPage: action.hasAnotherPage });
3613
+ }
3611
3614
  default: {
3612
3615
  return state;
3613
3616
  }
@@ -3734,11 +3737,12 @@ const LogReviewer = (props) => {
3734
3737
  advancedFilterState: initAdvancedFilterState,
3735
3738
  pageNumber: 1,
3736
3739
  hasAnotherPage: false,
3740
+ numTimesFiltersReset: 0,
3737
3741
  };
3738
3742
  // Initialize state
3739
3743
  const [state, dispatch] = React.useReducer(reducer$7, initialState);
3740
3744
  // Destructure common state
3741
- const { loading, logs, expandedFilterDrawer, dateFilterState, contextFilterState, tagFilterState, actionErrorFilterState, advancedFilterState, pageNumber, hasAnotherPage, } = state;
3745
+ const { loading, logs, expandedFilterDrawer, dateFilterState, contextFilterState, tagFilterState, actionErrorFilterState, advancedFilterState, pageNumber, hasAnotherPage, numTimesFiltersReset, } = state;
3742
3746
  /*------------------------------------------------------------------------*/
3743
3747
  /* ------------------------- Component Functions ------------------------ */
3744
3748
  /*------------------------------------------------------------------------*/
@@ -3750,8 +3754,7 @@ const LogReviewer = (props) => {
3750
3754
  try {
3751
3755
  // Prepare filter parameters
3752
3756
  const filters = {
3753
- startDate: dateFilterState.startDate,
3754
- endDate: dateFilterState.endDate,
3757
+ dateFilterState,
3755
3758
  contextFilterState,
3756
3759
  tagFilterState,
3757
3760
  actionErrorFilterState,
@@ -3759,8 +3762,9 @@ const LogReviewer = (props) => {
3759
3762
  };
3760
3763
  // Send filters to the server
3761
3764
  let fetchedLogs = [];
3765
+ // Get logs from server
3762
3766
  const response = yield visitServerEndpoint({
3763
- path: `${LOG_REVIEW_ROUTE_PATH_PREFIX}/logs`,
3767
+ path: LOG_REVIEW_GET_LOGS_ROUTE,
3764
3768
  method: 'GET',
3765
3769
  params: {
3766
3770
  pageNumber,
@@ -3786,16 +3790,12 @@ const LogReviewer = (props) => {
3786
3790
  /* ------------------------- Lifecycle Functions ------------------------ */
3787
3791
  /*------------------------------------------------------------------------*/
3788
3792
  /**
3789
- * Fetch logs whenever filters change
3793
+ * Fetch logs whenever page number changes or filters are reset
3790
3794
  */
3791
3795
  React.useEffect(() => {
3792
3796
  fetchLogs();
3793
3797
  }, [
3794
- dateFilterState,
3795
- contextFilterState,
3796
- tagFilterState,
3797
- actionErrorFilterState,
3798
- advancedFilterState,
3798
+ pageNumber, numTimesFiltersReset,
3799
3799
  ]);
3800
3800
  /*------------------------------------------------------------------------*/
3801
3801
  /* ------------------------------- Render ------------------------------- */
@@ -3889,10 +3889,22 @@ const LogReviewer = (props) => {
3889
3889
  initDateFilterState,
3890
3890
  initTagFilterState,
3891
3891
  });
3892
+ dispatch({
3893
+ type: ActionType$6.HideFilterDrawer,
3894
+ });
3892
3895
  } },
3893
3896
  React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.faTimes }),
3894
3897
  ' ',
3895
- "Reset"))));
3898
+ "Reset"),
3899
+ React__default["default"].createElement("button", { type: "button", id: "LogReviewer-submit-filters-button", className: "btn btn-primary ms-2", "aria-label": "submit filters", onClick: () => {
3900
+ dispatch({
3901
+ type: ActionType$6.HideFilterDrawer,
3902
+ });
3903
+ fetchLogs();
3904
+ } },
3905
+ React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.faSearch }),
3906
+ ' ',
3907
+ "Filter"))));
3896
3908
  // Filter drawer
3897
3909
  let filterDrawer;
3898
3910
  if (expandedFilterDrawer) {
@@ -4140,7 +4152,7 @@ const LogReviewer = (props) => {
4140
4152
  React__default["default"].createElement("input", { type: "text", className: "form-control", "aria-label": "query for user canvas id", value: advancedFilterState.userId, placeholder: "e.g. 104985", onChange: (e) => {
4141
4153
  const { value } = e.target;
4142
4154
  // Only update if value contains only numbers
4143
- if (/^\d+$/.test(value)) {
4155
+ if (/^\d+$/.test(value) || value === '') {
4144
4156
  advancedFilterState.userId = ((e.target.value)
4145
4157
  .trim());
4146
4158
  }
@@ -13790,9 +13802,11 @@ const initServer = (opts) => {
13790
13802
  /**
13791
13803
  * Get filtered logs based on provided filters
13792
13804
  * @author Gabe Abrams, Yuen Ler Chow
13805
+ * @param pageNumber the page number to get
13806
+ * @param filters the filters to apply to the logs
13793
13807
  * @returns {Log[]} list of logs that match the filters
13794
13808
  */
13795
- opts.app.get(`${LOG_REVIEW_ROUTE_PATH_PREFIX}/logs`, genRouteHandler({
13809
+ opts.app.get(LOG_REVIEW_GET_LOGS_ROUTE, genRouteHandler({
13796
13810
  paramTypes: {
13797
13811
  pageNumber: ParamType$1.Int,
13798
13812
  filters: ParamType$1.JSON,
@@ -13800,6 +13814,7 @@ const initServer = (opts) => {
13800
13814
  handler: ({ params }) => __awaiter(void 0, void 0, void 0, function* () {
13801
13815
  // Get user info
13802
13816
  const { pageNumber, userId, isAdmin, filters, } = params;
13817
+ const { dateFilterState, contextFilterState, tagFilterState, actionErrorFilterState, advancedFilterState, } = filters;
13803
13818
  // Validate user
13804
13819
  const canReview = yield canReviewLogs(userId, isAdmin);
13805
13820
  if (!canReview) {
@@ -13808,58 +13823,85 @@ const initServer = (opts) => {
13808
13823
  // Build MongoDB query based on filters
13809
13824
  const query = {};
13810
13825
  /* -------------- Date Filter ------------- */
13811
- const { startDate, endDate } = filters.dateFilterState;
13826
+ // Convert start and end dates from the dateFilterState into timestamps
13827
+ const { startDate, endDate } = dateFilterState;
13812
13828
  const startTimestamp = new Date(startDate.year, startDate.month - 1, startDate.day).getTime();
13813
13829
  const endTimestamp = new Date(endDate.year, endDate.month - 1, endDate.day + 1).getTime() - 1;
13830
+ // Add a date range condition to the query
13814
13831
  query.timestamp = {
13815
13832
  $gte: startTimestamp,
13816
13833
  $lte: endTimestamp,
13817
13834
  };
13818
13835
  /* ------------ Context Filter ------------ */
13819
- const { contextFilterState } = filters;
13820
- const contextConditions = [];
13836
+ // Process context filters to include selected contexts and subcontexts
13837
+ const selectedContexts = [];
13838
+ const selectedSubcontexts = [];
13839
+ // Process each context filter
13821
13840
  Object.keys(contextFilterState).forEach((context) => {
13822
13841
  const value = contextFilterState[context];
13823
13842
  if (typeof value === 'boolean') {
13824
13843
  if (value) {
13825
- // The entire context is selected
13826
- contextConditions.push({ context });
13844
+ selectedContexts.push(context);
13827
13845
  }
13828
13846
  }
13829
13847
  else {
13830
- // The context has subcontexts
13831
- const subcontexts = Object.keys(value).filter((subcontext) => { return value[subcontext]; });
13832
- if (subcontexts.length > 0) {
13833
- contextConditions.push({
13834
- context,
13835
- subcontext: { $in: subcontexts },
13836
- });
13848
+ // At least one subcontext is selected
13849
+ const atLeastOneSubcontextSelected = (Object.values(value)
13850
+ .some((subcontextValue) => {
13851
+ return subcontextValue;
13852
+ }));
13853
+ if (atLeastOneSubcontextSelected) {
13854
+ selectedContexts.push(context);
13837
13855
  }
13856
+ // Add all selected subcontexts
13857
+ Object.keys(value).forEach((subcontext) => {
13858
+ if (value[subcontext]) {
13859
+ selectedSubcontexts.push(subcontext);
13860
+ }
13861
+ });
13838
13862
  }
13839
13863
  });
13840
- if (contextConditions.length > 0) {
13841
- query.$or = contextConditions;
13864
+ // Add context and subcontext conditions to the query if any are selected
13865
+ if (selectedContexts.length > 0) {
13866
+ query.context = { $in: selectedContexts };
13867
+ }
13868
+ if (selectedSubcontexts.length > 0) {
13869
+ query.subcontext = { $in: selectedSubcontexts };
13842
13870
  }
13843
13871
  /* -------------- Tag Filter -------------- */
13844
- const { tagFilterState } = filters;
13845
13872
  const selectedTags = Object.keys(tagFilterState).filter((tag) => { return tagFilterState[tag]; });
13846
13873
  if (selectedTags.length > 0) {
13847
13874
  query.tags = { $in: selectedTags };
13848
13875
  }
13849
13876
  /* --------- Action/Error Filter ---------- */
13850
- const { actionErrorFilterState } = filters;
13851
13877
  if (actionErrorFilterState.type) {
13852
13878
  query.type = actionErrorFilterState.type;
13853
13879
  }
13854
- if (actionErrorFilterState.type === 'error' && actionErrorFilterState.errorMessage) {
13855
- query.errorMessage = { $regex: actionErrorFilterState.errorMessage, $options: 'i' };
13856
- }
13857
- if (actionErrorFilterState.type === 'error' && actionErrorFilterState.errorCode) {
13858
- query.errorCode = { $regex: actionErrorFilterState.errorCode, $options: 'i' };
13880
+ if (actionErrorFilterState.type === LogType$1.Error) {
13881
+ if (actionErrorFilterState.errorMessage) {
13882
+ // Add error message to the query.
13883
+ // $i is used for case-insensitive search, and $regex is used for partial matching
13884
+ query.errorMessage = {
13885
+ $regex: actionErrorFilterState.errorMessage,
13886
+ $options: 'i',
13887
+ };
13888
+ }
13889
+ if (actionErrorFilterState.errorCode) {
13890
+ query.errorCode = {
13891
+ $regex: actionErrorFilterState.errorCode,
13892
+ $options: 'i',
13893
+ };
13894
+ }
13859
13895
  }
13860
- if (actionErrorFilterState.type === 'action') {
13861
- const selectedTargets = Object.keys(actionErrorFilterState.target).filter((target) => { return actionErrorFilterState.target[target]; });
13862
- const selectedActions = Object.keys(actionErrorFilterState.action).filter((action) => { return actionErrorFilterState.action[action]; });
13896
+ if (actionErrorFilterState.type === LogType$1.Action) {
13897
+ const selectedTargets = (Object.keys(actionErrorFilterState.target)
13898
+ .filter((target) => {
13899
+ return actionErrorFilterState.target[target];
13900
+ }));
13901
+ const selectedActions = (Object.keys(actionErrorFilterState.action)
13902
+ .filter((action) => {
13903
+ return actionErrorFilterState.action[action];
13904
+ }));
13863
13905
  if (selectedTargets.length > 0) {
13864
13906
  query.target = { $in: selectedTargets };
13865
13907
  }
@@ -13868,18 +13910,26 @@ const initServer = (opts) => {
13868
13910
  }
13869
13911
  }
13870
13912
  /* ------------ Advanced Filter ----------- */
13871
- const { advancedFilterState } = filters;
13872
13913
  if (advancedFilterState.userFirstName) {
13873
- query.userFirstName = { $regex: advancedFilterState.userFirstName, $options: 'i' };
13914
+ query.userFirstName = {
13915
+ $regex: advancedFilterState.userFirstName,
13916
+ $options: 'i',
13917
+ };
13874
13918
  }
13875
13919
  if (advancedFilterState.userLastName) {
13876
- query.userLastName = { $regex: advancedFilterState.userLastName, $options: 'i' };
13920
+ query.userLastName = {
13921
+ $regex: advancedFilterState.userLastName,
13922
+ $options: 'i',
13923
+ };
13877
13924
  }
13878
13925
  if (advancedFilterState.userEmail) {
13879
- query.userEmail = { $regex: advancedFilterState.userEmail, $options: 'i' };
13926
+ query.userEmail = {
13927
+ $regex: advancedFilterState.userEmail,
13928
+ $options: 'i',
13929
+ };
13880
13930
  }
13881
13931
  if (advancedFilterState.userId) {
13882
- query.userId = parseInt(advancedFilterState.userId, 10);
13932
+ query.userId = Number.parseInt(advancedFilterState.userId, 10);
13883
13933
  }
13884
13934
  const roles = [];
13885
13935
  if (advancedFilterState.includeLearners) {
@@ -13891,26 +13941,38 @@ const initServer = (opts) => {
13891
13941
  if (advancedFilterState.includeAdmins) {
13892
13942
  roles.push({ isAdmin: true });
13893
13943
  }
13944
+ // If any roles are selected, add them to the query
13894
13945
  if (roles.length > 0) {
13946
+ // The $or operator is used to match any of the roles
13947
+ // The $and operator is to ensure that other conditions in the query are met
13895
13948
  query.$and = [{ $or: roles }];
13896
13949
  }
13897
13950
  if (advancedFilterState.courseId) {
13898
- query.courseId = parseInt(advancedFilterState.courseId, 10);
13951
+ query.courseId = Number.parseInt(advancedFilterState.courseId, 10);
13899
13952
  }
13900
13953
  if (advancedFilterState.courseName) {
13901
- query.courseName = { $regex: advancedFilterState.courseName, $options: 'i' };
13954
+ query.courseName = {
13955
+ $regex: advancedFilterState.courseName,
13956
+ $options: 'i',
13957
+ };
13902
13958
  }
13903
13959
  if (advancedFilterState.isMobile !== undefined) {
13904
- query['device.isMobile'] = advancedFilterState.isMobile;
13960
+ query['device.isMobile'] = Boolean(advancedFilterState.isMobile);
13905
13961
  }
13906
13962
  if (advancedFilterState.source) {
13907
13963
  query.source = advancedFilterState.source;
13908
13964
  }
13909
13965
  if (advancedFilterState.routePath) {
13910
- query.routePath = { $regex: advancedFilterState.routePath, $options: 'i' };
13966
+ query.routePath = {
13967
+ $regex: advancedFilterState.routePath,
13968
+ $options: 'i',
13969
+ };
13911
13970
  }
13912
13971
  if (advancedFilterState.routeTemplate) {
13913
- query.routeTemplate = { $regex: advancedFilterState.routeTemplate, $options: 'i' };
13972
+ query.routeTemplate = {
13973
+ $regex: advancedFilterState.routeTemplate,
13974
+ $options: 'i',
13975
+ };
13914
13976
  }
13915
13977
  // Query for logs
13916
13978
  const response = yield _logCollection.findPaged({