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.
Files changed (42) hide show
  1. package/dist/cjs/index.js +670 -869
  2. package/dist/cjs/index.js.map +1 -1
  3. package/dist/cjs/types/components/AppWrapper.d.ts +0 -24
  4. package/dist/cjs/types/constants/LOG_REVIEW_GET_LOGS_ROUTE.d.ts +6 -0
  5. package/dist/cjs/types/index.d.ts +2 -2
  6. package/dist/cjs/types/types/LogReviewerFilterState/ActionErrorFilterState.d.ts +13 -0
  7. package/dist/cjs/types/types/LogReviewerFilterState/AdvancedFilterState.d.ts +17 -0
  8. package/dist/cjs/types/types/LogReviewerFilterState/ContextFilterState.d.ts +6 -0
  9. package/dist/cjs/types/types/LogReviewerFilterState/DateFilterState.d.ts +13 -0
  10. package/dist/cjs/types/types/LogReviewerFilterState/TagFilterState.d.ts +4 -0
  11. package/dist/cjs/types/types/LogReviewerFilterState/index.d.ts +17 -0
  12. package/dist/cjs/types/types/ModalType.d.ts +0 -1
  13. package/dist/esm/index.js +672 -870
  14. package/dist/esm/index.js.map +1 -1
  15. package/dist/esm/types/components/AppWrapper.d.ts +0 -24
  16. package/dist/esm/types/constants/LOG_REVIEW_GET_LOGS_ROUTE.d.ts +6 -0
  17. package/dist/esm/types/index.d.ts +2 -2
  18. package/dist/esm/types/types/LogReviewerFilterState/ActionErrorFilterState.d.ts +13 -0
  19. package/dist/esm/types/types/LogReviewerFilterState/AdvancedFilterState.d.ts +17 -0
  20. package/dist/esm/types/types/LogReviewerFilterState/ContextFilterState.d.ts +6 -0
  21. package/dist/esm/types/types/LogReviewerFilterState/DateFilterState.d.ts +13 -0
  22. package/dist/esm/types/types/LogReviewerFilterState/TagFilterState.d.ts +4 -0
  23. package/dist/esm/types/types/LogReviewerFilterState/index.d.ts +17 -0
  24. package/dist/esm/types/types/ModalType.d.ts +0 -1
  25. package/dist/index.d.ts +1 -26
  26. package/package.json +1 -1
  27. package/src/components/AppWrapper.tsx +0 -216
  28. package/src/components/LogReviewer.tsx +840 -1232
  29. package/src/components/Modal/index.tsx +0 -3
  30. package/src/constants/LOG_REVIEW_GET_LOGS_ROUTE.ts +9 -0
  31. package/src/index.ts +0 -2
  32. package/src/server/initServer.ts +216 -19
  33. package/src/types/LogReviewerFilterState/ActionErrorFilterState.ts +21 -0
  34. package/src/types/LogReviewerFilterState/AdvancedFilterState.ts +33 -0
  35. package/src/types/LogReviewerFilterState/ContextFilterState.ts +13 -0
  36. package/src/types/LogReviewerFilterState/DateFilterState.ts +23 -0
  37. package/src/types/LogReviewerFilterState/TagFilterState.ts +6 -0
  38. package/src/types/LogReviewerFilterState/index.ts +24 -0
  39. package/src/types/ModalType.tsx +0 -1
  40. package/dist/cjs/types/constants/LOG_REVIEW_ROUTE_PATH_PREFIX.d.ts +0 -6
  41. package/dist/esm/types/constants/LOG_REVIEW_ROUTE_PATH_PREFIX.d.ts +0 -6
  42. package/src/constants/LOG_REVIEW_ROUTE_PATH_PREFIX.ts +0 -9
package/dist/cjs/index.js CHANGED
@@ -186,7 +186,6 @@ var ModalButtonType$1 = ModalButtonType;
186
186
  var ModalType;
187
187
  (function (ModalType) {
188
188
  ModalType["Okay"] = "okay";
189
- ModalType["Cancel"] = "cancel";
190
189
  ModalType["OkayCancel"] = "okay-cancel";
191
190
  ModalType["YesNo"] = "yes-no";
192
191
  ModalType["YesNoCancel"] = "yes-no-cancel";
@@ -341,9 +340,6 @@ const modalTypeToModalButtonTypes = {
341
340
  [ModalType$1.Okay]: [
342
341
  ModalButtonType$1.Okay,
343
342
  ],
344
- [ModalType$1.Cancel]: [
345
- ModalButtonType$1.Cancel,
346
- ],
347
343
  [ModalType$1.OkayCancel]: [
348
344
  ModalButtonType$1.Okay,
349
345
  ModalButtonType$1.Cancel,
@@ -1084,74 +1080,6 @@ const confirm = (title, text, opts) => __awaiter(void 0, void 0, void 0, functio
1084
1080
  });
1085
1081
  });
1086
1082
  /*----------------------------------------*/
1087
- /* --------------- Prompt -------------- */
1088
- /*----------------------------------------*/
1089
- // Stored copies of setters
1090
- let setPromptInfo;
1091
- // Function to call when prompt is closed
1092
- let onPromptClosed;
1093
- /**
1094
- * Show a prompt modal asking the user for input
1095
- * @author Yuen Ler Chow
1096
- * @param title the title text to display at the top of the prompt
1097
- * @param text the text to display in the prompt
1098
- * @param [opts={}] additional options for the prompt dialog
1099
- * @param [opts.placeholder] the placeholder text for the input field
1100
- * @param [opts.defaultText] the default text for the input field
1101
- * @param [opts.confirmButtonText=Okay] the text of the confirm button
1102
- * @param [opts.confirmButtonVariant=Variant.Dark] the variant of the confirm button
1103
- * @param [opts.cancelButtonText=Cancel] the text of the cancel button
1104
- * @param [opts.cancelButtonVariant=Variant.Secondary] the variant of the cancel button
1105
- * @returns Promise that resolves with the input string or null if canceled
1106
- */
1107
- const prompt = (title, text, opts) => __awaiter(void 0, void 0, void 0, function* () {
1108
- var _a;
1109
- // Wait for helper to exist
1110
- yield waitForHelper(() => {
1111
- return !!setPromptInfo;
1112
- });
1113
- // Fallback if prompt is not available
1114
- if (!setPromptInfo) {
1115
- const resultPassesValidation = false;
1116
- while (!resultPassesValidation) {
1117
- // eslint-disable-next-line no-alert
1118
- const result = window.prompt(`${title}\n\n${text}`, (_a = opts === null || opts === void 0 ? void 0 : opts.defaultText) !== null && _a !== void 0 ? _a : '');
1119
- if (result === null) {
1120
- return null;
1121
- }
1122
- // Validate min num chars
1123
- const minNumCharsValidationError = (((opts === null || opts === void 0 ? void 0 : opts.minNumChars) && result.length < opts.minNumChars)
1124
- ? `Please enter at least ${opts.minNumChars} characters.`
1125
- : undefined);
1126
- // Run custom validation
1127
- const customValidationError = ((opts === null || opts === void 0 ? void 0 : opts.findValidationError)
1128
- && opts.findValidationError(result));
1129
- // Show validation issue
1130
- if (minNumCharsValidationError || customValidationError) {
1131
- alert('Invalid Input', `${minNumCharsValidationError !== null && minNumCharsValidationError !== void 0 ? minNumCharsValidationError : ''} ${customValidationError !== null && customValidationError !== void 0 ? customValidationError : ''}`);
1132
- }
1133
- else {
1134
- return result;
1135
- }
1136
- }
1137
- }
1138
- // Return promise that resolves with result of prompt
1139
- return new Promise((resolve) => {
1140
- var _a;
1141
- // Setup handler
1142
- onPromptClosed = (result) => {
1143
- resolve(result);
1144
- };
1145
- // Show the prompt
1146
- setPromptInfo({
1147
- title,
1148
- text,
1149
- currentInputFieldText: ((_a = opts === null || opts === void 0 ? void 0 : opts.defaultText) !== null && _a !== void 0 ? _a : ''),
1150
- opts: (opts !== null && opts !== void 0 ? opts : {}),
1151
- });
1152
- });
1153
- });
1154
- /*----------------------------------------*/
1155
1083
  /* ------------- Fatal Error ------------ */
1156
1084
  /*----------------------------------------*/
1157
1085
  // Stored copies of setters
@@ -1167,14 +1095,14 @@ const fatalErrorHandlers = [];
1167
1095
  * @param [errorTitle] title of the error box
1168
1096
  */
1169
1097
  const showFatalError = (error, errorTitle = 'An Error Occurred') => __awaiter(void 0, void 0, void 0, function* () {
1170
- var _b, _c;
1098
+ var _a, _b;
1171
1099
  // Determine message and code
1172
1100
  const message = (typeof error === 'string'
1173
1101
  ? error.trim()
1174
- : String((_b = error.message) !== null && _b !== void 0 ? _b : 'An unknown error occurred.'));
1102
+ : String((_a = error.message) !== null && _a !== void 0 ? _a : 'An unknown error occurred.'));
1175
1103
  const code = (typeof error === 'string'
1176
1104
  ? ReactKitErrorCode$1.NoCode
1177
- : String((_c = error.code) !== null && _c !== void 0 ? _c : ReactKitErrorCode$1.NoCode));
1105
+ : String((_b = error.code) !== null && _b !== void 0 ? _b : ReactKitErrorCode$1.NoCode));
1178
1106
  // Call all fatal error listeners
1179
1107
  try {
1180
1108
  fatalErrorHandlers.forEach((handler) => {
@@ -1301,9 +1229,6 @@ const AppWrapper = (props) => {
1301
1229
  // Confirm
1302
1230
  const [confirmInfo, setConfirmInfoInner,] = React.useState(undefined);
1303
1231
  setConfirmInfo = setConfirmInfoInner;
1304
- // Prompt
1305
- const [promptInfo, setPromptInfoInner] = React.useState(undefined);
1306
- setPromptInfo = setPromptInfoInner;
1307
1232
  // Session expired
1308
1233
  const [sessionHasExpired, setSessionHasExpiredInner,] = React.useState(false);
1309
1234
  setSessionHasExpired = setSessionHasExpiredInner;
@@ -1334,37 +1259,6 @@ const AppWrapper = (props) => {
1334
1259
  }
1335
1260
  }, onTopOfOtherModals: true, dontAllowBackdropExit: true }, confirmInfo.text));
1336
1261
  }
1337
- /* ------------- Prompt ------------ */
1338
- if (promptInfo) {
1339
- // Run min char validation
1340
- const minNumCharsValidationError = ((promptInfo.opts.minNumChars
1341
- && promptInfo.currentInputFieldText.length < promptInfo.opts.minNumChars)
1342
- ? `Please enter at least ${promptInfo.opts.minNumChars} characters.`
1343
- : undefined);
1344
- // Run custom validation
1345
- const customValidationError = (promptInfo.opts.findValidationError
1346
- && promptInfo.opts.findValidationError(promptInfo.currentInputFieldText));
1347
- modal = (React__default["default"].createElement(Modal, { key: `prompt-${promptInfo.title}-${promptInfo.text}`, title: promptInfo.title,
1348
- // Don't show ok button if there is a validation error
1349
- type: ((customValidationError || minNumCharsValidationError)
1350
- ? ModalType$1.Cancel
1351
- : ModalType$1.OkayCancel), okayLabel: promptInfo.opts.confirmButtonText, okayVariant: promptInfo.opts.confirmButtonVariant, cancelLabel: promptInfo.opts.cancelButtonText, cancelVariant: promptInfo.opts.cancelButtonVariant, onClose: (buttonType) => {
1352
- const result = (buttonType === ModalButtonType$1.Okay
1353
- ? promptInfo.currentInputFieldText
1354
- : null);
1355
- setPromptInfo(undefined);
1356
- if (onPromptClosed) {
1357
- onPromptClosed(result);
1358
- }
1359
- }, onTopOfOtherModals: true, dontAllowBackdropExit: true },
1360
- React__default["default"].createElement("div", { className: "p-3" },
1361
- React__default["default"].createElement("p", null, promptInfo.text),
1362
- React__default["default"].createElement("input", { type: "text", className: "form-control mb-2", placeholder: promptInfo.opts.placeholder, value: promptInfo.currentInputFieldText, onChange: (e) => {
1363
- return setPromptInfo(Object.assign(Object.assign({}, promptInfo), { currentInputFieldText: e.target.value }));
1364
- } }),
1365
- minNumCharsValidationError && (React__default["default"].createElement("div", { className: "text-danger fw-bold mt-2" }, minNumCharsValidationError)),
1366
- customValidationError && (React__default["default"].createElement("div", { className: "text-danger fw-bold mt-2" }, customValidationError)))));
1367
- }
1368
1262
  /* ------ Custom Modal Portals ------ */
1369
1263
  // Custom modal portals
1370
1264
  const customModalPortals = [];
@@ -2680,10 +2574,10 @@ const getHumanReadableDate = (dateOrTimestamp) => {
2680
2574
  };
2681
2575
 
2682
2576
  /**
2683
- * Path of the route for storing client-side logs
2577
+ * Path of the route for getting logs for log review
2684
2578
  * @author Gabe Abrams
2685
2579
  */
2686
- const LOG_REVIEW_ROUTE_PATH_PREFIX = `/admin${ROUTE_PATH_PREFIX}/logs`;
2580
+ const LOG_REVIEW_GET_LOGS_ROUTE = `/admin${ROUTE_PATH_PREFIX}/logs`;
2687
2581
 
2688
2582
  /**
2689
2583
  * Source of a log event
@@ -3643,7 +3537,7 @@ var ActionType$6;
3643
3537
  (function (ActionType) {
3644
3538
  // Show the loading bar
3645
3539
  ActionType["StartLoading"] = "start-loading";
3646
- // Finish loading one or more months of logs
3540
+ // Finish loading logs
3647
3541
  ActionType["FinishLoading"] = "finish-loading";
3648
3542
  // Reset filters to initial values
3649
3543
  ActionType["ResetFilters"] = "reset-filters";
@@ -3661,6 +3555,12 @@ var ActionType$6;
3661
3555
  ActionType["UpdateActionErrorFilterState"] = "update-action-error-filter-state";
3662
3556
  // Update the advanced filter state
3663
3557
  ActionType["UpdateAdvancedFilterState"] = "update-advanced-filter-state";
3558
+ // Increment the page number
3559
+ ActionType["IncrementPageNumber"] = "increment-page-number";
3560
+ // Decrement the page number
3561
+ ActionType["DecrementPageNumber"] = "decrement-page-number";
3562
+ // Set has another page
3563
+ ActionType["SetHasAnotherPage"] = "set-has-another-page";
3664
3564
  })(ActionType$6 || (ActionType$6 = {}));
3665
3565
  /**
3666
3566
  * Reducer that executes actions
@@ -3674,7 +3574,7 @@ const reducer$7 = (state, action) => {
3674
3574
  return Object.assign(Object.assign({}, state), { loading: true });
3675
3575
  }
3676
3576
  case ActionType$6.FinishLoading: {
3677
- return Object.assign(Object.assign({}, state), { loading: false, logMap: action.logMap });
3577
+ return Object.assign(Object.assign({}, state), { loading: false, logs: action.logs });
3678
3578
  }
3679
3579
  case ActionType$6.ToggleFilterDrawer: {
3680
3580
  return Object.assign(Object.assign({}, state), { expandedFilterDrawer: (state.expandedFilterDrawer === action.filterDrawer
@@ -3702,6 +3602,15 @@ const reducer$7 = (state, action) => {
3702
3602
  case ActionType$6.UpdateAdvancedFilterState: {
3703
3603
  return Object.assign(Object.assign({}, state), { advancedFilterState: action.advancedFilterState });
3704
3604
  }
3605
+ case ActionType$6.IncrementPageNumber: {
3606
+ return Object.assign(Object.assign({}, state), { pageNumber: state.pageNumber + 1 });
3607
+ }
3608
+ case ActionType$6.DecrementPageNumber: {
3609
+ return Object.assign(Object.assign({}, state), { pageNumber: state.pageNumber - 1 });
3610
+ }
3611
+ case ActionType$6.SetHasAnotherPage: {
3612
+ return Object.assign(Object.assign({}, state), { hasAnotherPage: action.hasAnotherPage });
3613
+ }
3705
3614
  default: {
3706
3615
  return state;
3707
3616
  }
@@ -3819,115 +3728,74 @@ const LogReviewer = (props) => {
3819
3728
  // Initial state
3820
3729
  const initialState = {
3821
3730
  loading: true,
3822
- logMap: {},
3731
+ logs: [],
3823
3732
  expandedFilterDrawer: undefined,
3824
3733
  dateFilterState: initDateFilterState,
3825
3734
  contextFilterState: initContextFilterState,
3826
3735
  tagFilterState: initTagFilterState,
3827
3736
  actionErrorFilterState: initActionErrorFilterState,
3828
3737
  advancedFilterState: initAdvancedFilterState,
3738
+ pageNumber: 1,
3739
+ hasAnotherPage: false,
3829
3740
  };
3830
3741
  // Initialize state
3831
3742
  const [state, dispatch] = React.useReducer(reducer$7, initialState);
3832
3743
  // Destructure common state
3833
- const { loading, logMap, expandedFilterDrawer, dateFilterState, contextFilterState, tagFilterState, actionErrorFilterState, advancedFilterState, } = state;
3744
+ const { loading, logs, expandedFilterDrawer, dateFilterState, contextFilterState, tagFilterState, actionErrorFilterState, advancedFilterState, pageNumber, hasAnotherPage, } = state;
3834
3745
  /*------------------------------------------------------------------------*/
3835
3746
  /* ------------------------- Component Functions ------------------------ */
3836
3747
  /*------------------------------------------------------------------------*/
3837
3748
  /**
3838
- * Get the list of year/month combos that need to be loaded given a new
3839
- * start or end date and the existing logMap
3840
- * @author Gabe Abrams
3841
- * @param newDateFilterState the new date filter state
3842
- * @returns list of year/month combos that need to be loaded
3843
- */
3844
- const listMonthsToLoad = (newDateFilterState) => {
3845
- // List of year/month combos that need to be loaded
3846
- const toLoad = [];
3847
- // Loop through dates
3848
- let { year, month } = newDateFilterState.startDate;
3849
- while (
3850
- // Earlier year
3851
- (year < newDateFilterState.endDate.year)
3852
- // Current year but included month
3853
- || (year === newDateFilterState.endDate.year
3854
- && month <= newDateFilterState.endDate.month)) {
3855
- // Add to list if not already loaded
3856
- if (!logMap[year]
3857
- || !logMap[year][month]) {
3858
- toLoad.push({
3859
- year,
3860
- month,
3861
- });
3862
- }
3863
- // Increment
3864
- month += 1;
3865
- if (month > 12) {
3866
- month -= 12;
3867
- year += 1;
3868
- }
3869
- }
3870
- // Return
3871
- return toLoad;
3872
- };
3873
- /**
3874
- * Handle updated start/end dates (updates state, loads if necessary)
3875
- * @author Gabe Abrams
3876
- * @param newDateFilterState the new date filter state
3749
+ * Fetch logs from the server based on current filters
3877
3750
  */
3878
- const handleDateRangeUpdated = (newDateFilterState) => __awaiter(void 0, void 0, void 0, function* () {
3879
- // Update state
3880
- dispatch({
3881
- type: ActionType$6.UpdateDateFilterState,
3882
- dateFilterState: newDateFilterState,
3883
- });
3884
- // Check which year/month combos we need to load
3885
- const toLoad = listMonthsToLoad(newDateFilterState);
3886
- // If nothing to load, finished
3887
- if (toLoad.length === 0) {
3888
- return;
3889
- }
3890
- // Start loading
3891
- dispatch({
3892
- type: ActionType$6.StartLoading,
3893
- });
3894
- // Load required months
3751
+ const fetchLogs = () => __awaiter(void 0, void 0, void 0, function* () {
3752
+ dispatch({ type: ActionType$6.StartLoading });
3895
3753
  try {
3896
- for (let i = 0; i < toLoad.length; i++) {
3897
- // Destructure
3898
- const { year, month } = toLoad[i];
3899
- // Load
3900
- const logs = yield visitServerEndpoint({
3901
- path: `${LOG_REVIEW_ROUTE_PATH_PREFIX}/years/${year}/months/${month}`,
3902
- method: 'GET',
3903
- });
3904
- // Add to map
3905
- if (!logMap[year]) {
3906
- logMap[year] = {};
3907
- }
3908
- logMap[year][month] = logs;
3909
- }
3754
+ // Prepare filter parameters
3755
+ const filters = {
3756
+ dateFilterState,
3757
+ contextFilterState,
3758
+ tagFilterState,
3759
+ actionErrorFilterState,
3760
+ advancedFilterState,
3761
+ };
3762
+ // Send filters to the server
3763
+ let fetchedLogs = [];
3764
+ // Get logs from server
3765
+ const response = yield visitServerEndpoint({
3766
+ path: LOG_REVIEW_GET_LOGS_ROUTE,
3767
+ method: 'GET',
3768
+ params: {
3769
+ pageNumber,
3770
+ filters,
3771
+ },
3772
+ });
3773
+ fetchedLogs = fetchedLogs.concat(response.items);
3774
+ dispatch({
3775
+ type: ActionType$6.SetHasAnotherPage,
3776
+ hasAnotherPage: response.hasAnotherPage,
3777
+ });
3778
+ // Update logs in state
3779
+ dispatch({
3780
+ type: ActionType$6.FinishLoading,
3781
+ logs: fetchedLogs,
3782
+ });
3910
3783
  }
3911
3784
  catch (err) {
3912
3785
  return showFatalError(err);
3913
3786
  }
3914
- // Finish loading
3915
- dispatch({
3916
- type: ActionType$6.FinishLoading,
3917
- logMap,
3918
- });
3919
3787
  });
3920
3788
  /*------------------------------------------------------------------------*/
3921
3789
  /* ------------------------- Lifecycle Functions ------------------------ */
3922
3790
  /*------------------------------------------------------------------------*/
3923
3791
  /**
3924
- * Mount
3925
- * @author Gabe Abrams
3792
+ * Fetch logs whenever page number changes
3926
3793
  */
3927
3794
  React.useEffect(() => {
3928
- // Perform initial load
3929
- handleDateRangeUpdated(dateFilterState);
3930
- }, []);
3795
+ fetchLogs();
3796
+ }, [
3797
+ pageNumber,
3798
+ ]);
3931
3799
  /*------------------------------------------------------------------------*/
3932
3800
  /* ------------------------------- Render ------------------------------- */
3933
3801
  /*------------------------------------------------------------------------*/
@@ -3942,703 +3810,481 @@ const LogReviewer = (props) => {
3942
3810
  React__default["default"].createElement(LoadingSpinner, null)));
3943
3811
  }
3944
3812
  /* ------------ Review UI ----------- */
3945
- if (!loading) {
3946
- /*----------------------------------------*/
3947
- /* --------------- Filters -------------- */
3948
- /*----------------------------------------*/
3949
- // Filter toggle
3950
- const filterToggles = (React__default["default"].createElement("div", { className: "LogReviewer-filter-toggles" },
3951
- React__default["default"].createElement("h3", { className: "m-0" }, "Filters:"),
3952
- React__default["default"].createElement("div", { className: "LogReviewer-filter-toggle-buttons alert alert-secondary p-2 m-0" },
3953
- React__default["default"].createElement("button", { type: "button", id: "LogReviewer-toggle-date-filter-drawer", className: `btn btn-${FilterDrawer.Date === expandedFilterDrawer ? 'warning' : 'light'} me-2`, "aria-label": "toggle date filter drawer", onClick: () => {
3954
- dispatch({
3955
- type: ActionType$6.ToggleFilterDrawer,
3956
- filterDrawer: FilterDrawer.Date,
3957
- });
3958
- } },
3959
- React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.faCalendar, className: "me-2" }),
3960
- "Date"),
3961
- React__default["default"].createElement("button", { type: "button", id: "LogReviewer-toggle-context-filter-drawer", className: `btn btn-${FilterDrawer.Context === expandedFilterDrawer ? 'warning' : 'light'} me-2`, "aria-label": "toggle context filter drawer", onClick: () => {
3962
- dispatch({
3963
- type: ActionType$6.ToggleFilterDrawer,
3964
- filterDrawer: FilterDrawer.Context,
3965
- });
3966
- } },
3967
- React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.faCircle, className: "me-2" }),
3968
- "Context"),
3969
- (LogMetadata.Tag && Object.keys(LogMetadata.Tag).length > 0) && (React__default["default"].createElement("button", { type: "button", id: "LogReviewer-toggle-tag-filter-drawer", className: `btn btn-${FilterDrawer.Tag === expandedFilterDrawer ? 'warning' : 'light'} me-2`, "aria-label": "toggle tag filter drawer", onClick: () => {
3970
- dispatch({
3971
- type: ActionType$6.ToggleFilterDrawer,
3972
- filterDrawer: FilterDrawer.Tag,
3973
- });
3974
- } },
3975
- React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.faTag, className: "me-2" }),
3976
- "Tag")),
3977
- React__default["default"].createElement("button", { type: "button", id: "LogReviewer-toggle-action-filter-drawer", className: `btn btn-${FilterDrawer.Action === expandedFilterDrawer ? 'warning' : 'light'} me-2`, "aria-label": "toggle action and error filter drawer", onClick: () => {
3978
- dispatch({
3979
- type: ActionType$6.ToggleFilterDrawer,
3980
- filterDrawer: FilterDrawer.Action,
3981
- });
3982
- } },
3983
- React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.faHammer, className: "me-2" }),
3984
- "Action"),
3985
- React__default["default"].createElement("button", { type: "button", id: "LogReviewer-toggle-advanced-filter-drawer", className: `btn btn-${FilterDrawer.Advanced === expandedFilterDrawer ? 'warning' : 'light'} me-2`, "aria-label": "toggle advanced filter drawer", onClick: () => {
3813
+ /*----------------------------------------*/
3814
+ /* ------------ Pagination -------------- */
3815
+ /*----------------------------------------*/
3816
+ const paginationControls = logs.length > 0 && (React__default["default"].createElement("div", { className: "text-center mt-3" },
3817
+ React__default["default"].createElement("button", { type: "button", className: "btn btn-secondary me-2", disabled: pageNumber <= 1, onClick: () => {
3818
+ dispatch({
3819
+ type: ActionType$6.DecrementPageNumber,
3820
+ });
3821
+ } },
3822
+ React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.faArrowLeft, className: "me-2" }),
3823
+ "Previous Page"),
3824
+ React__default["default"].createElement("span", { className: "mx-3" },
3825
+ "Page",
3826
+ ' ',
3827
+ pageNumber),
3828
+ React__default["default"].createElement("button", { type: "button", className: "btn btn-secondary ms-2", disabled: !hasAnotherPage, onClick: () => {
3829
+ dispatch({
3830
+ type: ActionType$6.IncrementPageNumber,
3831
+ });
3832
+ } },
3833
+ "Next Page",
3834
+ React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.faArrowRight, className: "ms-2" }))));
3835
+ /*----------------------------------------*/
3836
+ /* --------------- Filters -------------- */
3837
+ /*----------------------------------------*/
3838
+ // Filter toggle
3839
+ const filterToggles = (React__default["default"].createElement("div", { className: "LogReviewer-filter-toggles" },
3840
+ React__default["default"].createElement("h3", { className: "m-0" }, "Filters:"),
3841
+ React__default["default"].createElement("div", { className: "LogReviewer-filter-toggle-buttons alert alert-secondary p-2 m-0" },
3842
+ React__default["default"].createElement("button", { type: "button", id: "LogReviewer-toggle-date-filter-drawer", className: `btn btn-${FilterDrawer.Date === expandedFilterDrawer ? 'warning' : 'light'} me-2`, "aria-label": "toggle date filter drawer", onClick: () => {
3843
+ dispatch({
3844
+ type: ActionType$6.ToggleFilterDrawer,
3845
+ filterDrawer: FilterDrawer.Date,
3846
+ });
3847
+ } },
3848
+ React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.faCalendar, className: "me-2" }),
3849
+ "Date"),
3850
+ React__default["default"].createElement("button", { type: "button", id: "LogReviewer-toggle-context-filter-drawer", className: `btn btn-${FilterDrawer.Context === expandedFilterDrawer ? 'warning' : 'light'} me-2`, "aria-label": "toggle context filter drawer", onClick: () => {
3851
+ dispatch({
3852
+ type: ActionType$6.ToggleFilterDrawer,
3853
+ filterDrawer: FilterDrawer.Context,
3854
+ });
3855
+ } },
3856
+ React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.faCircle, className: "me-2" }),
3857
+ "Context"),
3858
+ (LogMetadata.Tag && Object.keys(LogMetadata.Tag).length > 0) && (React__default["default"].createElement("button", { type: "button", id: "LogReviewer-toggle-tag-filter-drawer", className: `btn btn-${FilterDrawer.Tag === expandedFilterDrawer ? 'warning' : 'light'} me-2`, "aria-label": "toggle tag filter drawer", onClick: () => {
3859
+ dispatch({
3860
+ type: ActionType$6.ToggleFilterDrawer,
3861
+ filterDrawer: FilterDrawer.Tag,
3862
+ });
3863
+ } },
3864
+ React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.faTag, className: "me-2" }),
3865
+ "Tag")),
3866
+ React__default["default"].createElement("button", { type: "button", id: "LogReviewer-toggle-action-filter-drawer", className: `btn btn-${FilterDrawer.Action === expandedFilterDrawer ? 'warning' : 'light'} me-2`, "aria-label": "toggle action and error filter drawer", onClick: () => {
3867
+ dispatch({
3868
+ type: ActionType$6.ToggleFilterDrawer,
3869
+ filterDrawer: FilterDrawer.Action,
3870
+ });
3871
+ } },
3872
+ React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.faHammer, className: "me-2" }),
3873
+ "Action"),
3874
+ React__default["default"].createElement("button", { type: "button", id: "LogReviewer-toggle-advanced-filter-drawer", className: `btn btn-${FilterDrawer.Advanced === expandedFilterDrawer ? 'warning' : 'light'} me-2`, "aria-label": "toggle advanced filter drawer", onClick: () => {
3875
+ dispatch({
3876
+ type: ActionType$6.ToggleFilterDrawer,
3877
+ filterDrawer: FilterDrawer.Advanced,
3878
+ });
3879
+ } },
3880
+ React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.faList, className: "me-2" }),
3881
+ "Advanced"),
3882
+ React__default["default"].createElement("button", { type: "button", id: "LogReviewer-reset-filters-button", className: "btn btn-light", "aria-label": "reset filters", onClick: () => {
3883
+ dispatch({
3884
+ type: ActionType$6.ResetFilters,
3885
+ initActionErrorFilterState,
3886
+ initAdvancedFilterState,
3887
+ initContextFilterState,
3888
+ initDateFilterState,
3889
+ initTagFilterState,
3890
+ });
3891
+ } },
3892
+ React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.faTimes }),
3893
+ ' ',
3894
+ "Reset"),
3895
+ React__default["default"].createElement("button", { type: "button", id: "LogReviewer-submit-filters-button", className: "btn btn-primary ms-2", "aria-label": "submit filters", onClick: () => {
3896
+ dispatch({
3897
+ type: ActionType$6.HideFilterDrawer,
3898
+ });
3899
+ fetchLogs();
3900
+ } },
3901
+ React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.faSearch }),
3902
+ ' ',
3903
+ "Filter"))));
3904
+ // Filter drawer
3905
+ let filterDrawer;
3906
+ if (expandedFilterDrawer) {
3907
+ if (expandedFilterDrawer === FilterDrawer.Date) {
3908
+ filterDrawer = (React__default["default"].createElement(TabBox, { title: "Date" },
3909
+ React__default["default"].createElement(SimpleDateChooser, { ariaLabel: "filter start date", name: "filter-start-date", year: dateFilterState.startDate.year, month: dateFilterState.startDate.month, day: dateFilterState.startDate.day, chooseFromPast: true, numMonthsToShow: 36, onChange: (month, day, year) => {
3910
+ dateFilterState.startDate = { month, day, year };
3986
3911
  dispatch({
3987
- type: ActionType$6.ToggleFilterDrawer,
3988
- filterDrawer: FilterDrawer.Advanced,
3912
+ type: ActionType$6.UpdateDateFilterState,
3913
+ dateFilterState,
3989
3914
  });
3990
- } },
3991
- React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.faList, className: "me-2" }),
3992
- "Advanced"),
3993
- React__default["default"].createElement("button", { type: "button", id: "LogReviewer-reset-filters-button", className: "btn btn-light", "aria-label": "reset filters", onClick: () => {
3915
+ } }),
3916
+ ' ',
3917
+ "to",
3918
+ ' ',
3919
+ React__default["default"].createElement(SimpleDateChooser, { ariaLabel: "filter end date", name: "filter-end-date", year: dateFilterState.endDate.year, month: dateFilterState.endDate.month, day: dateFilterState.endDate.day, chooseFromPast: true, numMonthsToShow: 12, onChange: (month, day, year) => {
3920
+ if (year < dateFilterState.startDate.year
3921
+ || (year === dateFilterState.startDate.year
3922
+ && month < dateFilterState.startDate.month)
3923
+ || (year === dateFilterState.startDate.year
3924
+ && month === dateFilterState.startDate.month
3925
+ && day < dateFilterState.startDate.day)) {
3926
+ return alert('Invalid Start Date', 'The start date cannot be before the end date.');
3927
+ }
3928
+ dateFilterState.endDate = { month, day, year };
3994
3929
  dispatch({
3995
- type: ActionType$6.ResetFilters,
3996
- initActionErrorFilterState,
3997
- initAdvancedFilterState,
3998
- initContextFilterState,
3999
- initDateFilterState,
4000
- initTagFilterState,
3930
+ type: ActionType$6.UpdateDateFilterState,
3931
+ dateFilterState,
4001
3932
  });
4002
- } },
4003
- React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.faTimes }),
4004
- ' ',
4005
- "Reset"))));
4006
- // Filter drawer
4007
- let filterDrawer;
4008
- if (expandedFilterDrawer) {
4009
- if (expandedFilterDrawer === FilterDrawer.Date) {
4010
- filterDrawer = (React__default["default"].createElement(TabBox, { title: "Date" },
4011
- React__default["default"].createElement(SimpleDateChooser, { ariaLabel: "filter start date", name: "filter-start-date", year: dateFilterState.startDate.year, month: dateFilterState.startDate.month, day: dateFilterState.startDate.day, chooseFromPast: true, numMonthsToShow: 36, onChange: (month, day, year) => {
4012
- dateFilterState.startDate = { month, day, year };
4013
- handleDateRangeUpdated(dateFilterState);
4014
- } }),
4015
- ' ',
4016
- "to",
4017
- ' ',
4018
- React__default["default"].createElement(SimpleDateChooser, { ariaLabel: "filter end date", name: "filter-end-date", year: dateFilterState.endDate.year, month: dateFilterState.endDate.month, day: dateFilterState.endDate.day, chooseFromPast: true, numMonthsToShow: 12, onChange: (month, day, year) => {
4019
- if (year < dateFilterState.startDate.year
4020
- || (year === dateFilterState.startDate.year
4021
- && month < dateFilterState.startDate.month)
4022
- || (year === dateFilterState.startDate.year
4023
- && month === dateFilterState.startDate.month
4024
- && day < dateFilterState.startDate.day)) {
4025
- return alert('Invalid Start Date', 'The start date cannot be before the end date.');
4026
- }
4027
- dateFilterState.endDate = { month, day, year };
4028
- handleDateRangeUpdated(dateFilterState);
4029
- } })));
4030
- }
4031
- else if (expandedFilterDrawer === FilterDrawer.Context) {
4032
- // Create item picker items
4033
- const builtInPickableItem = {
4034
- id: 'built-in-contexts',
4035
- name: 'Auto-logged',
4036
- isGroup: true,
4037
- children: [],
4038
- };
4039
- const pickableItems = [];
4040
- Object.keys(contextMap)
4041
- .forEach((context) => {
4042
- const value = contextMap[context];
4043
- if (typeof value === 'string') {
4044
- // No subcategories
4045
- const item = {
4046
- id: context,
4047
- name: genHumanReadableName(context),
4048
- isGroup: false,
4049
- checked: !!contextFilterState[context],
4050
- };
4051
- // Add built-in items to its own folder
4052
- const isBuiltIn = context in LogBuiltInMetadata.Context;
4053
- if (isBuiltIn) {
4054
- // Add to built-in pickable item
4055
- builtInPickableItem.children.push(item);
4056
- }
4057
- else {
4058
- // Add to pickable items list
4059
- pickableItems.push(item);
4060
- }
4061
- return;
4062
- }
4063
- // Has subcategories
4064
- const children = (Object.keys(value)
4065
- // Remove parent name
4066
- .filter((subcontext) => {
4067
- return subcontext !== '_';
4068
- })
4069
- // Create child pickable items
4070
- .map((subcontext) => {
4071
- return {
4072
- id: subcontext,
4073
- name: genHumanReadableName(subcontext),
4074
- isGroup: false,
4075
- checked: contextFilterState[context][subcontext],
4076
- };
4077
- }));
3933
+ } })));
3934
+ }
3935
+ else if (expandedFilterDrawer === FilterDrawer.Context) {
3936
+ // Create item picker items
3937
+ const builtInPickableItem = {
3938
+ id: 'built-in-contexts',
3939
+ name: 'Auto-logged',
3940
+ isGroup: true,
3941
+ children: [],
3942
+ };
3943
+ const pickableItems = [];
3944
+ Object.keys(contextMap)
3945
+ .forEach((context) => {
3946
+ const value = contextMap[context];
3947
+ if (typeof value === 'string') {
3948
+ // No subcategories
4078
3949
  const item = {
4079
3950
  id: context,
4080
3951
  name: genHumanReadableName(context),
4081
- isGroup: true,
4082
- children,
3952
+ isGroup: false,
3953
+ checked: !!contextFilterState[context],
4083
3954
  };
4084
- pickableItems.push(item);
4085
- });
4086
- // Add built-in contexts to end ofl ist
4087
- pickableItems.push(builtInPickableItem);
4088
- // Create filter UI
4089
- filterDrawer = (React__default["default"].createElement(ItemPicker, { title: "Context", items: pickableItems, onChanged: (updatedItems) => {
4090
- // Update our state
4091
- updatedItems.forEach((pickableItem) => {
4092
- if (pickableItem.isGroup) {
4093
- // Has subcontexts
4094
- if (pickableItem.id === 'built-in-contexts') {
4095
- // Built-in
4096
- // Treat as if these were top-level contexts
4097
- pickableItem.children.forEach((subcontextItem) => {
4098
- contextFilterState[subcontextItem.id] = ('checked' in subcontextItem
4099
- && subcontextItem.checked);
4100
- });
4101
- }
4102
- else {
4103
- // Not built-in
4104
- pickableItem.children.forEach((subcontextItem) => {
4105
- if (!subcontextItem.isGroup) {
4106
- contextFilterState[pickableItem.id][subcontextItem.id] = (subcontextItem.checked);
4107
- }
4108
- });
4109
- }
3955
+ // Add built-in items to its own folder
3956
+ const isBuiltIn = context in LogBuiltInMetadata.Context;
3957
+ if (isBuiltIn) {
3958
+ // Add to built-in pickable item
3959
+ builtInPickableItem.children.push(item);
3960
+ }
3961
+ else {
3962
+ // Add to pickable items list
3963
+ pickableItems.push(item);
3964
+ }
3965
+ return;
3966
+ }
3967
+ // Has subcategories
3968
+ const children = (Object.keys(value)
3969
+ // Remove parent name
3970
+ .filter((subcontext) => {
3971
+ return subcontext !== '_';
3972
+ })
3973
+ // Create child pickable items
3974
+ .map((subcontext) => {
3975
+ return {
3976
+ id: subcontext,
3977
+ name: genHumanReadableName(subcontext),
3978
+ isGroup: false,
3979
+ checked: contextFilterState[context][subcontext],
3980
+ };
3981
+ }));
3982
+ const item = {
3983
+ id: context,
3984
+ name: genHumanReadableName(context),
3985
+ isGroup: true,
3986
+ children,
3987
+ };
3988
+ pickableItems.push(item);
3989
+ });
3990
+ // Add built-in contexts to end ofl ist
3991
+ pickableItems.push(builtInPickableItem);
3992
+ // Create filter UI
3993
+ filterDrawer = (React__default["default"].createElement(ItemPicker, { title: "Context", items: pickableItems, onChanged: (updatedItems) => {
3994
+ // Update our state
3995
+ updatedItems.forEach((pickableItem) => {
3996
+ if (pickableItem.isGroup) {
3997
+ // Has subcontexts
3998
+ if (pickableItem.id === 'built-in-contexts') {
3999
+ // Built-in
4000
+ // Treat as if these were top-level contexts
4001
+ pickableItem.children.forEach((subcontextItem) => {
4002
+ contextFilterState[subcontextItem.id] = ('checked' in subcontextItem
4003
+ && subcontextItem.checked);
4004
+ });
4110
4005
  }
4111
4006
  else {
4112
- // No subcontexts
4113
- contextFilterState[pickableItem.id] = (pickableItem.checked);
4007
+ // Not built-in
4008
+ pickableItem.children.forEach((subcontextItem) => {
4009
+ if (!subcontextItem.isGroup) {
4010
+ contextFilterState[pickableItem.id][subcontextItem.id] = (subcontextItem.checked);
4011
+ }
4012
+ });
4114
4013
  }
4115
- });
4116
- dispatch({
4117
- type: ActionType$6.UpdateContextFilterState,
4118
- contextFilterState,
4119
- });
4120
- } }));
4121
- }
4122
- else if (expandedFilterDrawer === FilterDrawer.Tag) {
4123
- // Create filter UI
4124
- filterDrawer = (React__default["default"].createElement(TabBox, { title: "Tags" },
4125
- React__default["default"].createElement("div", null, "If any tags are selected, logs must contain at least one (but not necessarily all) of the selected tags."),
4126
- React__default["default"].createElement("div", { className: "d-flex gap-1 flex-wrap" }, Object.keys((_d = LogMetadata.Tag) !== null && _d !== void 0 ? _d : {})
4127
- .map((tag) => {
4128
- const description = genHumanReadableName(tag);
4129
- return (React__default["default"].createElement(CheckboxButton, { key: tag, id: `LogReviewer-tag-${tag}-checkbox`, text: description, ariaLabel: `require that logs be tagged with "${description}" or any other selected tag`, checked: tagFilterState[tag], onChanged: (checked) => {
4130
- tagFilterState[tag] = checked;
4014
+ }
4015
+ else {
4016
+ // No subcontexts
4017
+ contextFilterState[pickableItem.id] = (pickableItem.checked);
4018
+ }
4019
+ });
4020
+ dispatch({
4021
+ type: ActionType$6.UpdateContextFilterState,
4022
+ contextFilterState,
4023
+ });
4024
+ } }));
4025
+ }
4026
+ else if (expandedFilterDrawer === FilterDrawer.Tag) {
4027
+ // Create filter UI
4028
+ filterDrawer = (React__default["default"].createElement(TabBox, { title: "Tags" },
4029
+ React__default["default"].createElement("div", null, "If any tags are selected, logs must contain at least one (but not necessarily all) of the selected tags."),
4030
+ React__default["default"].createElement("div", { className: "d-flex gap-1 flex-wrap" }, Object.keys((_d = LogMetadata.Tag) !== null && _d !== void 0 ? _d : {})
4031
+ .map((tag) => {
4032
+ const description = genHumanReadableName(tag);
4033
+ return (React__default["default"].createElement(CheckboxButton, { key: tag, id: `LogReviewer-tag-${tag}-checkbox`, text: description, ariaLabel: `require that logs be tagged with "${description}" or any other selected tag`, checked: tagFilterState[tag], onChanged: (checked) => {
4034
+ tagFilterState[tag] = checked;
4035
+ dispatch({
4036
+ type: ActionType$6.UpdateTagFilterState,
4037
+ tagFilterState,
4038
+ });
4039
+ } }));
4040
+ }))));
4041
+ }
4042
+ else if (expandedFilterDrawer === FilterDrawer.Action) {
4043
+ // Create filter UI
4044
+ filterDrawer = (React__default["default"].createElement(React__default["default"].Fragment, null,
4045
+ React__default["default"].createElement(TabBox, { title: "Log Type" },
4046
+ React__default["default"].createElement(RadioButton, { id: "LogReviewer-type-all", text: "All Logs", onSelected: () => {
4047
+ actionErrorFilterState.type = undefined;
4048
+ dispatch({
4049
+ type: ActionType$6.UpdateActionErrorFilterState,
4050
+ actionErrorFilterState,
4051
+ });
4052
+ }, ariaLabel: "show logs of all types", selected: actionErrorFilterState.type === undefined }),
4053
+ React__default["default"].createElement(RadioButton, { id: "LogReviewer-type-action-only", text: "Action Logs Only", onSelected: () => {
4054
+ actionErrorFilterState.type = LogType$1.Action;
4055
+ dispatch({
4056
+ type: ActionType$6.UpdateActionErrorFilterState,
4057
+ actionErrorFilterState,
4058
+ });
4059
+ }, ariaLabel: "only show action logs", selected: actionErrorFilterState.type === LogType$1.Action }),
4060
+ React__default["default"].createElement(RadioButton, { id: "LogReviewer-type-error-only", text: "Action Error Only", onSelected: () => {
4061
+ actionErrorFilterState.type = LogType$1.Error;
4062
+ dispatch({
4063
+ type: ActionType$6.UpdateActionErrorFilterState,
4064
+ actionErrorFilterState,
4065
+ });
4066
+ }, ariaLabel: "only show error logs", selected: actionErrorFilterState.type === LogType$1.Error, noMarginOnRight: true })),
4067
+ (actionErrorFilterState.type === undefined
4068
+ || actionErrorFilterState.type === LogType$1.Action) && (React__default["default"].createElement(TabBox, { title: "Action Log Details" },
4069
+ React__default["default"].createElement(ButtonInputGroup, { label: "Action", className: "mb-2", wrapButtonsAndAddGaps: true }, Object.keys(LogAction$1)
4070
+ .map((action) => {
4071
+ const description = genHumanReadableName(action);
4072
+ return (React__default["default"].createElement(CheckboxButton, { key: action, id: `LogReviewer-action-${action}-checkbox`, text: description, ariaLabel: `include logs with action type "${description}" in results`, noMarginOnRight: true, checked: actionErrorFilterState.action[action], onChanged: (checked) => {
4073
+ actionErrorFilterState.action[action] = checked;
4131
4074
  dispatch({
4132
- type: ActionType$6.UpdateTagFilterState,
4133
- tagFilterState,
4075
+ type: ActionType$6.UpdateActionErrorFilterState,
4076
+ actionErrorFilterState,
4134
4077
  });
4135
4078
  } }));
4136
- }))));
4137
- }
4138
- else if (expandedFilterDrawer === FilterDrawer.Action) {
4139
- // Create filter UI
4140
- filterDrawer = (React__default["default"].createElement(React__default["default"].Fragment, null,
4141
- React__default["default"].createElement(TabBox, { title: "Log Type" },
4142
- React__default["default"].createElement(RadioButton, { id: "LogReviewer-type-all", text: "All Logs", onSelected: () => {
4143
- actionErrorFilterState.type = undefined;
4079
+ })),
4080
+ React__default["default"].createElement(ButtonInputGroup, { label: "Target", wrapButtonsAndAddGaps: true }, Object.keys(targetMap)
4081
+ .map((target) => {
4082
+ const description = genHumanReadableName(target);
4083
+ return (React__default["default"].createElement(CheckboxButton, { key: target, id: `LogReviewer-target-${target}-checkbox`, text: description, ariaLabel: `include logs with target "${description}" in results`, checked: actionErrorFilterState.target[target], noMarginOnRight: true, onChanged: (checked) => {
4084
+ actionErrorFilterState.target[target] = checked;
4144
4085
  dispatch({
4145
4086
  type: ActionType$6.UpdateActionErrorFilterState,
4146
4087
  actionErrorFilterState,
4147
4088
  });
4148
- }, ariaLabel: "show logs of all types", selected: actionErrorFilterState.type === undefined }),
4149
- React__default["default"].createElement(RadioButton, { id: "LogReviewer-type-action-only", text: "Action Logs Only", onSelected: () => {
4150
- actionErrorFilterState.type = LogType$1.Action;
4089
+ } }));
4090
+ })))),
4091
+ (actionErrorFilterState.type === undefined
4092
+ || actionErrorFilterState.type === LogType$1.Error) && (React__default["default"].createElement(TabBox, { title: "Error Log Details" },
4093
+ React__default["default"].createElement("div", { className: "input-group mb-2" },
4094
+ React__default["default"].createElement("span", { className: "input-group-text" }, "Error Message"),
4095
+ React__default["default"].createElement("input", { type: "text", className: "form-control", "aria-label": "query for error message", value: actionErrorFilterState.errorMessage, placeholder: "e.g. undefined is not a function", onChange: (e) => {
4096
+ actionErrorFilterState.errorMessage = e.target.value;
4151
4097
  dispatch({
4152
4098
  type: ActionType$6.UpdateActionErrorFilterState,
4153
4099
  actionErrorFilterState,
4154
4100
  });
4155
- }, ariaLabel: "only show action logs", selected: actionErrorFilterState.type === LogType$1.Action }),
4156
- React__default["default"].createElement(RadioButton, { id: "LogReviewer-type-error-only", text: "Action Error Only", onSelected: () => {
4157
- actionErrorFilterState.type = LogType$1.Error;
4101
+ } })),
4102
+ React__default["default"].createElement("div", { className: "input-group mb-2" },
4103
+ React__default["default"].createElement("span", { className: "input-group-text" }, "Error Code"),
4104
+ React__default["default"].createElement("input", { type: "text", className: "form-control", "aria-label": "query for error code", value: actionErrorFilterState.errorCode, placeholder: "e.g. GC22", onChange: (e) => {
4105
+ actionErrorFilterState.errorCode = ((e.target.value)
4106
+ .trim()
4107
+ .toUpperCase());
4158
4108
  dispatch({
4159
4109
  type: ActionType$6.UpdateActionErrorFilterState,
4160
4110
  actionErrorFilterState,
4161
4111
  });
4162
- }, ariaLabel: "only show error logs", selected: actionErrorFilterState.type === LogType$1.Error, noMarginOnRight: true })),
4163
- (actionErrorFilterState.type === undefined
4164
- || actionErrorFilterState.type === LogType$1.Action) && (React__default["default"].createElement(TabBox, { title: "Action Log Details" },
4165
- React__default["default"].createElement(ButtonInputGroup, { label: "Action", className: "mb-2", wrapButtonsAndAddGaps: true }, Object.keys(LogAction$1)
4166
- .map((action) => {
4167
- const description = genHumanReadableName(action);
4168
- return (React__default["default"].createElement(CheckboxButton, { key: action, id: `LogReviewer-action-${action}-checkbox`, text: description, ariaLabel: `include logs with action type "${description}" in results`, noMarginOnRight: true, checked: actionErrorFilterState.action[action], onChanged: (checked) => {
4169
- actionErrorFilterState.action[action] = checked;
4170
- dispatch({
4171
- type: ActionType$6.UpdateActionErrorFilterState,
4172
- actionErrorFilterState,
4173
- });
4174
- } }));
4175
- })),
4176
- React__default["default"].createElement(ButtonInputGroup, { label: "Target", wrapButtonsAndAddGaps: true }, Object.keys(targetMap)
4177
- .map((target) => {
4178
- const description = genHumanReadableName(target);
4179
- return (React__default["default"].createElement(CheckboxButton, { key: target, id: `LogReviewer-target-${target}-checkbox`, text: description, ariaLabel: `include logs with target "${description}" in results`, checked: actionErrorFilterState.target[target], noMarginOnRight: true, onChanged: (checked) => {
4180
- actionErrorFilterState.target[target] = checked;
4181
- dispatch({
4182
- type: ActionType$6.UpdateActionErrorFilterState,
4183
- actionErrorFilterState,
4184
- });
4185
- } }));
4186
- })))),
4187
- (actionErrorFilterState.type === undefined
4188
- || actionErrorFilterState.type === LogType$1.Error) && (React__default["default"].createElement(TabBox, { title: "Error Log Details" },
4189
- React__default["default"].createElement("div", { className: "input-group mb-2" },
4190
- React__default["default"].createElement("span", { className: "input-group-text" }, "Error Message"),
4191
- React__default["default"].createElement("input", { type: "text", className: "form-control", "aria-label": "query for error message", value: actionErrorFilterState.errorMessage, placeholder: "e.g. undefined is not a function", onChange: (e) => {
4192
- actionErrorFilterState.errorMessage = e.target.value;
4193
- dispatch({
4194
- type: ActionType$6.UpdateActionErrorFilterState,
4195
- actionErrorFilterState,
4196
- });
4197
- } })),
4198
- React__default["default"].createElement("div", { className: "input-group mb-2" },
4199
- React__default["default"].createElement("span", { className: "input-group-text" }, "Error Code"),
4200
- React__default["default"].createElement("input", { type: "text", className: "form-control", "aria-label": "query for error code", value: actionErrorFilterState.errorCode, placeholder: "e.g. GC22", onChange: (e) => {
4201
- actionErrorFilterState.errorCode = ((e.target.value)
4202
- .trim()
4203
- .toUpperCase());
4204
- dispatch({
4205
- type: ActionType$6.UpdateActionErrorFilterState,
4206
- actionErrorFilterState,
4207
- });
4208
- } }))))));
4209
- }
4210
- else if (expandedFilterDrawer === FilterDrawer.Advanced) {
4211
- // Create advanced filter ui
4212
- filterDrawer = (React__default["default"].createElement(React__default["default"].Fragment, null,
4213
- React__default["default"].createElement(TabBox, { title: "User" },
4214
- React__default["default"].createElement("div", { className: "input-group mb-2" },
4215
- React__default["default"].createElement("span", { className: "input-group-text" }, "User First Name"),
4216
- React__default["default"].createElement("input", { type: "text", className: "form-control", "aria-label": "query for user first name", value: advancedFilterState.userFirstName, placeholder: "e.g. Divardo", onChange: (e) => {
4217
- advancedFilterState.userFirstName = e.target.value;
4218
- dispatch({
4219
- type: ActionType$6.UpdateAdvancedFilterState,
4220
- advancedFilterState,
4221
- });
4222
- } })),
4223
- React__default["default"].createElement("div", { className: "input-group mb-2" },
4224
- React__default["default"].createElement("span", { className: "input-group-text" }, "User Last Name"),
4225
- React__default["default"].createElement("input", { type: "text", className: "form-control", "aria-label": "query for user last name", value: advancedFilterState.userLastName, placeholder: "e.g. Calicci", onChange: (e) => {
4226
- advancedFilterState.userLastName = e.target.value;
4227
- dispatch({
4228
- type: ActionType$6.UpdateAdvancedFilterState,
4229
- advancedFilterState,
4230
- });
4231
- } })),
4232
- React__default["default"].createElement("div", { className: "input-group mb-2" },
4233
- React__default["default"].createElement("span", { className: "input-group-text" }, "User Email"),
4234
- React__default["default"].createElement("input", { type: "text", className: "form-control", "aria-label": "query for user email", value: advancedFilterState.userEmail, placeholder: "e.g. calicci@fas.harvard.edu", onChange: (e) => {
4235
- advancedFilterState.userEmail = ((e.target.value)
4112
+ } }))))));
4113
+ }
4114
+ else if (expandedFilterDrawer === FilterDrawer.Advanced) {
4115
+ // Create advanced filter ui
4116
+ filterDrawer = (React__default["default"].createElement(React__default["default"].Fragment, null,
4117
+ React__default["default"].createElement(TabBox, { title: "User" },
4118
+ React__default["default"].createElement("div", { className: "input-group mb-2" },
4119
+ React__default["default"].createElement("span", { className: "input-group-text" }, "User First Name"),
4120
+ React__default["default"].createElement("input", { type: "text", className: "form-control", "aria-label": "query for user first name", value: advancedFilterState.userFirstName, placeholder: "e.g. Divardo", onChange: (e) => {
4121
+ advancedFilterState.userFirstName = e.target.value;
4122
+ dispatch({
4123
+ type: ActionType$6.UpdateAdvancedFilterState,
4124
+ advancedFilterState,
4125
+ });
4126
+ } })),
4127
+ React__default["default"].createElement("div", { className: "input-group mb-2" },
4128
+ React__default["default"].createElement("span", { className: "input-group-text" }, "User Last Name"),
4129
+ React__default["default"].createElement("input", { type: "text", className: "form-control", "aria-label": "query for user last name", value: advancedFilterState.userLastName, placeholder: "e.g. Calicci", onChange: (e) => {
4130
+ advancedFilterState.userLastName = e.target.value;
4131
+ dispatch({
4132
+ type: ActionType$6.UpdateAdvancedFilterState,
4133
+ advancedFilterState,
4134
+ });
4135
+ } })),
4136
+ React__default["default"].createElement("div", { className: "input-group mb-2" },
4137
+ React__default["default"].createElement("span", { className: "input-group-text" }, "User Email"),
4138
+ React__default["default"].createElement("input", { type: "text", className: "form-control", "aria-label": "query for user email", value: advancedFilterState.userEmail, placeholder: "e.g. calicci@fas.harvard.edu", onChange: (e) => {
4139
+ advancedFilterState.userEmail = ((e.target.value)
4140
+ .trim());
4141
+ dispatch({
4142
+ type: ActionType$6.UpdateAdvancedFilterState,
4143
+ advancedFilterState,
4144
+ });
4145
+ } })),
4146
+ React__default["default"].createElement("div", { className: "input-group mb-2" },
4147
+ React__default["default"].createElement("span", { className: "input-group-text" }, "User Canvas Id"),
4148
+ 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) => {
4149
+ const { value } = e.target;
4150
+ // Only update if value contains only numbers
4151
+ if (/^\d+$/.test(value)) {
4152
+ advancedFilterState.userId = ((e.target.value)
4236
4153
  .trim());
4237
- dispatch({
4238
- type: ActionType$6.UpdateAdvancedFilterState,
4239
- advancedFilterState,
4240
- });
4241
- } })),
4242
- React__default["default"].createElement("div", { className: "input-group mb-2" },
4243
- React__default["default"].createElement("span", { className: "input-group-text" }, "User Canvas Id"),
4244
- 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) => {
4245
- const { value } = e.target;
4246
- // Only update if value contains only numbers
4247
- if (/^\d+$/.test(value)) {
4248
- advancedFilterState.userId = ((e.target.value)
4249
- .trim());
4250
- }
4251
- dispatch({
4252
- type: ActionType$6.UpdateAdvancedFilterState,
4253
- advancedFilterState,
4254
- });
4255
- } })),
4256
- React__default["default"].createElement(ButtonInputGroup, { label: "Role" },
4257
- React__default["default"].createElement(CheckboxButton, { text: "Students", onChanged: (checked) => {
4258
- advancedFilterState.includeLearners = checked;
4259
- dispatch({
4260
- type: ActionType$6.UpdateAdvancedFilterState,
4261
- advancedFilterState,
4262
- });
4263
- }, checked: advancedFilterState.includeLearners, ariaLabel: "show logs from students" }),
4264
- React__default["default"].createElement(CheckboxButton, { text: "Teaching Team Members", onChanged: (checked) => {
4265
- advancedFilterState.includeTTMs = checked;
4266
- dispatch({
4267
- type: ActionType$6.UpdateAdvancedFilterState,
4268
- advancedFilterState,
4269
- });
4270
- }, checked: advancedFilterState.includeTTMs, ariaLabel: "show logs from teaching team members" }),
4271
- React__default["default"].createElement(CheckboxButton, { text: "Admins", onChanged: (checked) => {
4272
- advancedFilterState.includeAdmins = checked;
4273
- dispatch({
4274
- type: ActionType$6.UpdateAdvancedFilterState,
4275
- advancedFilterState,
4276
- });
4277
- }, checked: advancedFilterState.includeAdmins, ariaLabel: "show logs from admins" }))),
4278
- React__default["default"].createElement(TabBox, { title: "Course" },
4154
+ }
4155
+ dispatch({
4156
+ type: ActionType$6.UpdateAdvancedFilterState,
4157
+ advancedFilterState,
4158
+ });
4159
+ } })),
4160
+ React__default["default"].createElement(ButtonInputGroup, { label: "Role" },
4161
+ React__default["default"].createElement(CheckboxButton, { text: "Students", onChanged: (checked) => {
4162
+ advancedFilterState.includeLearners = checked;
4163
+ dispatch({
4164
+ type: ActionType$6.UpdateAdvancedFilterState,
4165
+ advancedFilterState,
4166
+ });
4167
+ }, checked: advancedFilterState.includeLearners, ariaLabel: "show logs from students" }),
4168
+ React__default["default"].createElement(CheckboxButton, { text: "Teaching Team Members", onChanged: (checked) => {
4169
+ advancedFilterState.includeTTMs = checked;
4170
+ dispatch({
4171
+ type: ActionType$6.UpdateAdvancedFilterState,
4172
+ advancedFilterState,
4173
+ });
4174
+ }, checked: advancedFilterState.includeTTMs, ariaLabel: "show logs from teaching team members" }),
4175
+ React__default["default"].createElement(CheckboxButton, { text: "Admins", onChanged: (checked) => {
4176
+ advancedFilterState.includeAdmins = checked;
4177
+ dispatch({
4178
+ type: ActionType$6.UpdateAdvancedFilterState,
4179
+ advancedFilterState,
4180
+ });
4181
+ }, checked: advancedFilterState.includeAdmins, ariaLabel: "show logs from admins" }))),
4182
+ React__default["default"].createElement(TabBox, { title: "Course" },
4183
+ React__default["default"].createElement("div", { className: "input-group mb-2" },
4184
+ React__default["default"].createElement("span", { className: "input-group-text" }, "Course Name"),
4185
+ React__default["default"].createElement("input", { type: "text", className: "form-control", "aria-label": "query for course name", value: advancedFilterState.courseName, placeholder: "e.g. GLC 200", onChange: (e) => {
4186
+ advancedFilterState.courseName = e.target.value;
4187
+ dispatch({
4188
+ type: ActionType$6.UpdateAdvancedFilterState,
4189
+ advancedFilterState,
4190
+ });
4191
+ } })),
4192
+ React__default["default"].createElement("div", { className: "input-group mb-2" },
4193
+ React__default["default"].createElement("span", { className: "input-group-text" }, "Course Canvas Id"),
4194
+ React__default["default"].createElement("input", { type: "text", className: "form-control", "aria-label": "query for course canvas id", value: advancedFilterState.courseId, placeholder: "e.g. 15948", onChange: (e) => {
4195
+ const { value } = e.target;
4196
+ // Only update if value contains only numbers
4197
+ if (/^\d+$/.test(value)) {
4198
+ advancedFilterState.courseId = ((e.target.value)
4199
+ .trim());
4200
+ }
4201
+ dispatch({
4202
+ type: ActionType$6.UpdateAdvancedFilterState,
4203
+ advancedFilterState,
4204
+ });
4205
+ } }))),
4206
+ React__default["default"].createElement(TabBox, { title: "Device" },
4207
+ React__default["default"].createElement(ButtonInputGroup, { label: "Device Type" },
4208
+ React__default["default"].createElement(RadioButton, { text: "All Devices", ariaLabel: "show logs from all devices", selected: advancedFilterState.isMobile === undefined, onSelected: () => {
4209
+ advancedFilterState.isMobile = undefined;
4210
+ dispatch({
4211
+ type: ActionType$6.UpdateAdvancedFilterState,
4212
+ advancedFilterState,
4213
+ });
4214
+ } }),
4215
+ React__default["default"].createElement(RadioButton, { text: "Mobile Only", ariaLabel: "show logs from mobile devices", selected: advancedFilterState.isMobile === true, onSelected: () => {
4216
+ advancedFilterState.isMobile = true;
4217
+ dispatch({
4218
+ type: ActionType$6.UpdateAdvancedFilterState,
4219
+ advancedFilterState,
4220
+ });
4221
+ } }),
4222
+ React__default["default"].createElement(RadioButton, { text: "Desktop Only", ariaLabel: "show logs from desktop devices", selected: advancedFilterState.isMobile === false, onSelected: () => {
4223
+ advancedFilterState.isMobile = false;
4224
+ dispatch({
4225
+ type: ActionType$6.UpdateAdvancedFilterState,
4226
+ advancedFilterState,
4227
+ });
4228
+ }, noMarginOnRight: true }))),
4229
+ React__default["default"].createElement(TabBox, { title: "Source" },
4230
+ React__default["default"].createElement(ButtonInputGroup, { label: "Source Type" },
4231
+ React__default["default"].createElement(RadioButton, { text: "Both", ariaLabel: "show logs from all sources", selected: advancedFilterState.source === undefined, onSelected: () => {
4232
+ advancedFilterState.source = undefined;
4233
+ dispatch({
4234
+ type: ActionType$6.UpdateAdvancedFilterState,
4235
+ advancedFilterState,
4236
+ });
4237
+ } }),
4238
+ React__default["default"].createElement(RadioButton, { text: "Client Only", ariaLabel: "show logs from client source", selected: advancedFilterState.source === LogSource$1.Client, onSelected: () => {
4239
+ advancedFilterState.source = LogSource$1.Client;
4240
+ dispatch({
4241
+ type: ActionType$6.UpdateAdvancedFilterState,
4242
+ advancedFilterState,
4243
+ });
4244
+ } }),
4245
+ React__default["default"].createElement(RadioButton, { text: "Server Only", ariaLabel: "show logs from server source", selected: advancedFilterState.source === LogSource$1.Server, onSelected: () => {
4246
+ advancedFilterState.source = LogSource$1.Server;
4247
+ dispatch({
4248
+ type: ActionType$6.UpdateAdvancedFilterState,
4249
+ advancedFilterState,
4250
+ });
4251
+ }, noMarginOnRight: true })),
4252
+ advancedFilterState.source !== LogSource$1.Client && (React__default["default"].createElement("div", { className: "mt-2" },
4279
4253
  React__default["default"].createElement("div", { className: "input-group mb-2" },
4280
- React__default["default"].createElement("span", { className: "input-group-text" }, "Course Name"),
4281
- React__default["default"].createElement("input", { type: "text", className: "form-control", "aria-label": "query for course name", value: advancedFilterState.courseName, placeholder: "e.g. GLC 200", onChange: (e) => {
4282
- advancedFilterState.courseName = e.target.value;
4254
+ React__default["default"].createElement("span", { className: "input-group-text" }, "Server Route Path"),
4255
+ React__default["default"].createElement("input", { type: "text", className: "form-control", "aria-label": "query for server route path", value: advancedFilterState.routePath, placeholder: "e.g. /api/ttm/courses/12345", onChange: (e) => {
4256
+ advancedFilterState.courseName = ((e.target.value)
4257
+ .trim());
4283
4258
  dispatch({
4284
4259
  type: ActionType$6.UpdateAdvancedFilterState,
4285
4260
  advancedFilterState,
4286
4261
  });
4287
4262
  } })),
4288
4263
  React__default["default"].createElement("div", { className: "input-group mb-2" },
4289
- React__default["default"].createElement("span", { className: "input-group-text" }, "Course Canvas Id"),
4290
- React__default["default"].createElement("input", { type: "text", className: "form-control", "aria-label": "query for course canvas id", value: advancedFilterState.courseId, placeholder: "e.g. 15948", onChange: (e) => {
4291
- const { value } = e.target;
4292
- // Only update if value contains only numbers
4293
- if (/^\d+$/.test(value)) {
4294
- advancedFilterState.courseId = ((e.target.value)
4295
- .trim());
4296
- }
4297
- dispatch({
4298
- type: ActionType$6.UpdateAdvancedFilterState,
4299
- advancedFilterState,
4300
- });
4301
- } }))),
4302
- React__default["default"].createElement(TabBox, { title: "Device" },
4303
- React__default["default"].createElement(ButtonInputGroup, { label: "Device Type" },
4304
- React__default["default"].createElement(RadioButton, { text: "All Devices", ariaLabel: "show logs from all devices", selected: advancedFilterState.isMobile === undefined, onSelected: () => {
4305
- advancedFilterState.isMobile = undefined;
4306
- dispatch({
4307
- type: ActionType$6.UpdateAdvancedFilterState,
4308
- advancedFilterState,
4309
- });
4310
- } }),
4311
- React__default["default"].createElement(RadioButton, { text: "Mobile Only", ariaLabel: "show logs from mobile devices", selected: advancedFilterState.isMobile === true, onSelected: () => {
4312
- advancedFilterState.isMobile = true;
4313
- dispatch({
4314
- type: ActionType$6.UpdateAdvancedFilterState,
4315
- advancedFilterState,
4316
- });
4317
- } }),
4318
- React__default["default"].createElement(RadioButton, { text: "Desktop Only", ariaLabel: "show logs from desktop devices", selected: advancedFilterState.isMobile === false, onSelected: () => {
4319
- advancedFilterState.isMobile = false;
4320
- dispatch({
4321
- type: ActionType$6.UpdateAdvancedFilterState,
4322
- advancedFilterState,
4323
- });
4324
- }, noMarginOnRight: true }))),
4325
- React__default["default"].createElement(TabBox, { title: "Source" },
4326
- React__default["default"].createElement(ButtonInputGroup, { label: "Source Type" },
4327
- React__default["default"].createElement(RadioButton, { text: "Both", ariaLabel: "show logs from all sources", selected: advancedFilterState.source === undefined, onSelected: () => {
4328
- advancedFilterState.source = undefined;
4329
- dispatch({
4330
- type: ActionType$6.UpdateAdvancedFilterState,
4331
- advancedFilterState,
4332
- });
4333
- } }),
4334
- React__default["default"].createElement(RadioButton, { text: "Client Only", ariaLabel: "show logs from client source", selected: advancedFilterState.source === LogSource$1.Client, onSelected: () => {
4335
- advancedFilterState.source = LogSource$1.Client;
4336
- dispatch({
4337
- type: ActionType$6.UpdateAdvancedFilterState,
4338
- advancedFilterState,
4339
- });
4340
- } }),
4341
- React__default["default"].createElement(RadioButton, { text: "Server Only", ariaLabel: "show logs from server source", selected: advancedFilterState.source === LogSource$1.Server, onSelected: () => {
4342
- advancedFilterState.source = LogSource$1.Server;
4264
+ React__default["default"].createElement("span", { className: "input-group-text" }, "Server Route Template"),
4265
+ React__default["default"].createElement("input", { type: "text", className: "form-control", "aria-label": "query for server route template", value: advancedFilterState.routeTemplate, placeholder: "e.g. /api/ttm/courses/:courseId", onChange: (e) => {
4266
+ advancedFilterState.courseName = ((e.target.value)
4267
+ .trim());
4343
4268
  dispatch({
4344
4269
  type: ActionType$6.UpdateAdvancedFilterState,
4345
4270
  advancedFilterState,
4346
4271
  });
4347
- }, noMarginOnRight: true })),
4348
- advancedFilterState.source !== LogSource$1.Client && (React__default["default"].createElement("div", { className: "mt-2" },
4349
- React__default["default"].createElement("div", { className: "input-group mb-2" },
4350
- React__default["default"].createElement("span", { className: "input-group-text" }, "Server Route Path"),
4351
- React__default["default"].createElement("input", { type: "text", className: "form-control", "aria-label": "query for server route path", value: advancedFilterState.routePath, placeholder: "e.g. /api/ttm/courses/12345", onChange: (e) => {
4352
- advancedFilterState.courseName = ((e.target.value)
4353
- .trim());
4354
- dispatch({
4355
- type: ActionType$6.UpdateAdvancedFilterState,
4356
- advancedFilterState,
4357
- });
4358
- } })),
4359
- React__default["default"].createElement("div", { className: "input-group mb-2" },
4360
- React__default["default"].createElement("span", { className: "input-group-text" }, "Server Route Template"),
4361
- React__default["default"].createElement("input", { type: "text", className: "form-control", "aria-label": "query for server route template", value: advancedFilterState.routeTemplate, placeholder: "e.g. /api/ttm/courses/:courseId", onChange: (e) => {
4362
- advancedFilterState.courseName = ((e.target.value)
4363
- .trim());
4364
- dispatch({
4365
- type: ActionType$6.UpdateAdvancedFilterState,
4366
- advancedFilterState,
4367
- });
4368
- } })))))));
4369
- }
4370
- }
4371
- // Filters UI
4372
- const filters = (React__default["default"].createElement(React__default["default"].Fragment, null,
4373
- filterToggles,
4374
- filterDrawer && (React__default["default"].createElement(Drawer, { customBackgroundColor: "#eee" }, filterDrawer))));
4375
- // Actually filter the logs
4376
- // > Perform filters
4377
- const logs = [];
4378
- Object.keys(logMap).forEach((year) => {
4379
- Object.keys(logMap[year]).forEach((month) => {
4380
- logMap[year][month].forEach((log) => {
4381
- /* ----------- Date Filter ---------- */
4382
- var _a;
4383
- // Before start date
4384
- if (
4385
- // Previous year
4386
- log.year < dateFilterState.startDate.year
4387
- // Same year, earlier month
4388
- || ((log.year === dateFilterState.startDate.year)
4389
- && (log.month < dateFilterState.startDate.month))
4390
- // Same year, same month, earlier day
4391
- || ((log.year === dateFilterState.startDate.year)
4392
- && (log.month === dateFilterState.startDate.month)
4393
- && (log.day < dateFilterState.startDate.day))) {
4394
- return;
4395
- }
4396
- // After end date
4397
- if (
4398
- // Later year
4399
- log.year > dateFilterState.endDate.year
4400
- // Same year, later month
4401
- || ((log.year === dateFilterState.endDate.year)
4402
- && (log.month > dateFilterState.endDate.month))
4403
- // Same year, same month, later day
4404
- || ((log.year === dateFilterState.endDate.year)
4405
- && (log.month === dateFilterState.endDate.month)
4406
- && (log.day > dateFilterState.endDate.day))) {
4407
- return;
4408
- }
4409
- /* --------- Context Filter --------- */
4410
- // Context doesn't match
4411
- if (
4412
- // Whole context is deselected
4413
- contextFilterState[log.context] === false
4414
- // None of the subcontexts are selected
4415
- || (
4416
- // Has subcontexts
4417
- typeof contextFilterState[log.context] !== 'boolean'
4418
- // None of the subcontexts are selected
4419
- && Object.values((_a = contextFilterState[log.context]) !== null && _a !== void 0 ? _a : {})
4420
- .every((isSelected) => {
4421
- return !isSelected;
4422
- }))) {
4423
- return;
4424
- }
4425
- // Subcontext doesn't match
4426
- if (
4427
- // Log context is not "uncategorized" (no point in further filters)
4428
- log.context !== LogBuiltInMetadata.Context.Uncategorized
4429
- // Log has a subcontext
4430
- && log.subcontext
4431
- // Context has subcontexts
4432
- && (contextFilterState[log.context]
4433
- && contextFilterState[log.context] !== false
4434
- && contextFilterState[log.context] !== true)
4435
- // Subcontext is not selected
4436
- && !contextFilterState[log.context][log.subcontext]) {
4437
- return;
4438
- }
4439
- /* -------------- Tags -------------- */
4440
- // No tags match
4441
- if (
4442
- // At least one tag is required
4443
- Object.values(tagFilterState)
4444
- .filter((isSelected) => {
4445
- return isSelected;
4446
- })
4447
- .length > 0
4448
- // No tags match
4449
- && log.tags.every((tag) => {
4450
- return !tagFilterState[tag];
4451
- })) {
4452
- return;
4453
- }
4454
- /* ------- Actions and Errors ------- */
4455
- // Log type doesn't match
4456
- if (
4457
- // Filter won't allow all types
4458
- actionErrorFilterState.type !== undefined
4459
- // Log type doesn't match
4460
- && actionErrorFilterState.type !== log.type) {
4461
- return;
4462
- }
4463
- // Filter errors
4464
- if (log.type === LogType$1.Error) {
4465
- // Message doesn't match
4466
- if (
4467
- // Message exists
4468
- log.errorMessage
4469
- // Message filter exists
4470
- && actionErrorFilterState.errorMessage.trim().length > 0
4471
- // Message doesn't match
4472
- && log.errorMessage.toLowerCase().includes(actionErrorFilterState.errorMessage.trim().toLowerCase())) {
4473
- return;
4474
- }
4475
- // Code doesn't match
4476
- if (
4477
- // Code exists
4478
- log.errorCode
4479
- // Code filter exists
4480
- && actionErrorFilterState.errorCode.trim().length > 0
4481
- // Code doesn't match
4482
- && log.errorCode.toUpperCase().includes(actionErrorFilterState.errorCode.trim().toUpperCase())) {
4483
- return;
4484
- }
4485
- }
4486
- // Filter actions
4487
- if (log.type === LogType$1.Action) {
4488
- // Target isn't selected
4489
- if (
4490
- // Target exists
4491
- log.target
4492
- // Target isn't selected
4493
- && !actionErrorFilterState.target[log.target]) {
4494
- return;
4495
- }
4496
- // Action
4497
- if (
4498
- // Action exists
4499
- log.action
4500
- // Action isn't selected
4501
- && !actionErrorFilterState.action[log.action]) {
4502
- return;
4503
- }
4504
- }
4505
- /* --------- Advanced Filter -------- */
4506
- // First name doesn't match
4507
- if (
4508
- // First name exists
4509
- log.userFirstName
4510
- // First name query doesn't match
4511
- && !log.userFirstName.toLowerCase().includes(advancedFilterState.userFirstName.toLowerCase().trim())) {
4512
- return;
4513
- }
4514
- // Last name doesn't match
4515
- if (
4516
- // Last name exists
4517
- log.userLastName
4518
- // Last name query doesn't match
4519
- && !log.userLastName.toLowerCase().includes(advancedFilterState.userLastName.toLowerCase().trim())) {
4520
- return;
4521
- }
4522
- // Email doesn't match
4523
- if (
4524
- // Email exists
4525
- log.userEmail
4526
- // Email query doesn't match
4527
- && !log.userEmail.toLowerCase().includes(advancedFilterState.userEmail.toLowerCase().trim())) {
4528
- return;
4529
- }
4530
- // User id doesn't match
4531
- if (
4532
- // User id exists
4533
- log.userId
4534
- // User id doesn't match
4535
- && !String(log.userId).includes(advancedFilterState.userId.trim())) {
4536
- return;
4537
- }
4538
- // Learner not allowed
4539
- if (
4540
- // User is a learner
4541
- log.isLearner
4542
- // Learners aren't included
4543
- && !advancedFilterState.includeLearners) {
4544
- return;
4545
- }
4546
- // TTM not allowed
4547
- if (
4548
- // User is a ttm
4549
- log.isTTM
4550
- // TTMs aren't included
4551
- && !advancedFilterState.includeTTMs) {
4552
- return;
4553
- }
4554
- // Admin not allowed
4555
- if (
4556
- // User is an admin
4557
- log.isAdmin
4558
- // Admins aren't included
4559
- && !advancedFilterState.includeAdmins) {
4560
- return;
4561
- }
4562
- // Course Id doesn't match
4563
- if (
4564
- // Course Id exists
4565
- log.courseId
4566
- // Course Id doesn't match
4567
- && !String(log.courseId).includes(advancedFilterState.courseId.trim())) {
4568
- return;
4569
- }
4570
- // Course name doesn't match
4571
- if (
4572
- // Course name exists
4573
- log.courseName
4574
- // Course name doesn't match
4575
- && !String(log.courseName).includes(advancedFilterState.courseName.trim())) {
4576
- return;
4577
- }
4578
- // Mobile filter doesn't match
4579
- if (
4580
- // Mobile filter exists
4581
- advancedFilterState.isMobile !== undefined
4582
- // Device info exists
4583
- && log.device
4584
- // Mobile filter doesn't match
4585
- && (advancedFilterState.isMobile !== log.device.isMobile)) {
4586
- return;
4587
- }
4588
- // Log source doesn't match
4589
- if (
4590
- // Source filter exists
4591
- advancedFilterState.source !== undefined
4592
- // Source info exists
4593
- && log.source
4594
- // Source filter doesn't match
4595
- && (advancedFilterState.source !== log.source)) {
4596
- return;
4597
- }
4598
- // Route path doesn't match (Only for server source)
4599
- if (
4600
- // Source is server
4601
- (log.source === LogSource$1.Server)
4602
- // Route path is being filtered
4603
- && (advancedFilterState.routePath.trim().length)
4604
- // Route path doesn't match
4605
- && !(log.routePath.includes(advancedFilterState.routePath.trim()))) {
4606
- return;
4607
- }
4608
- // Route template doesn't match (Only for server source)
4609
- if (
4610
- // Source is server
4611
- (log.source === LogSource$1.Server)
4612
- // Route template is being filtered
4613
- && (advancedFilterState.routeTemplate.trim().length)
4614
- // Route template doesn't match
4615
- && !(log.routeTemplate.includes(advancedFilterState.routeTemplate.trim()))) {
4616
- return;
4617
- }
4618
- /* -------------- Done -------------- */
4619
- // Made it past all filters. Add to the list
4620
- logs.push(log);
4621
- });
4622
- });
4623
- });
4624
- /*----------------------------------------*/
4625
- /* ---------------- Data ---------------- */
4626
- /*----------------------------------------*/
4627
- // Nothing to show notice
4628
- const noLogsNotice = (logs.length === 0
4629
- ? (React__default["default"].createElement("div", { className: "alert alert-warning text-center mt-2" },
4272
+ } })))))));
4273
+ }
4274
+ }
4275
+ // Filters UI
4276
+ const filters = (React__default["default"].createElement(React__default["default"].Fragment, null,
4277
+ filterToggles,
4278
+ filterDrawer && (React__default["default"].createElement(Drawer, { customBackgroundColor: "#eee" }, filterDrawer))));
4279
+ // Main body
4280
+ body = (React__default["default"].createElement(React__default["default"].Fragment, null,
4281
+ filters,
4282
+ React__default["default"].createElement("div", { className: "mt-2" },
4283
+ React__default["default"].createElement(IntelliTable, { title: "Matching Logs:", csvName: `Logs from ${getHumanReadableDate()}`, id: "logs", data: logs, columns: columns }),
4284
+ logs.length === 0 && (React__default["default"].createElement("div", { className: "alert alert-warning text-center mt-2" },
4630
4285
  React__default["default"].createElement("h4", { className: "m-1" }, "No Logs to Show"),
4631
- React__default["default"].createElement("div", null, "Either your filters are too strict or no matching logs have been created yet.")))
4632
- : undefined);
4633
- // Create intelliTable
4634
- const dataTable = (React__default["default"].createElement(IntelliTable, { title: "Matching Logs:", csvName: `Logs from ${getHumanReadableDate()}`, id: "logs", data: logs, columns: columns }));
4635
- // Main body
4636
- body = (React__default["default"].createElement(React__default["default"].Fragment, null,
4637
- filters,
4638
- React__default["default"].createElement("div", { className: "mt-2" },
4639
- dataTable,
4640
- noLogsNotice)));
4641
- }
4286
+ React__default["default"].createElement("div", null, "Either your filters are too strict or no matching logs have been created yet."))),
4287
+ paginationControls)));
4642
4288
  /* ---------- Wrap in Modal --------- */
4643
4289
  return (React__default["default"].createElement("div", { className: "LogReviewer-outer-container" },
4644
4290
  React__default["default"].createElement("style", null, style$2),
@@ -14150,32 +13796,188 @@ const initServer = (opts) => {
14150
13796
  }),
14151
13797
  }));
14152
13798
  /**
14153
- * Get all logs for a certain month
14154
- * @author Gabe Abrams
14155
- * @param {number} year the year to query (e.g. 2022)
14156
- * @param {number} month the month to query (e.g. 1 = January)
14157
- * @returns {Log[]} list of logs from the given month
14158
- */
14159
- opts.app.get(`${LOG_REVIEW_ROUTE_PATH_PREFIX}/years/:year/months/:month`, genRouteHandler({
13799
+ * Get filtered logs based on provided filters
13800
+ * @author Gabe Abrams, Yuen Ler Chow
13801
+ * @param pageNumber the page number to get
13802
+ * @param filters the filters to apply to the logs
13803
+ * @returns {Log[]} list of logs that match the filters
13804
+ */
13805
+ opts.app.get(`${LOG_REVIEW_GET_LOGS_ROUTE}`, genRouteHandler({
14160
13806
  paramTypes: {
14161
- year: ParamType$1.Int,
14162
- month: ParamType$1.Int,
13807
+ pageNumber: ParamType$1.Int,
13808
+ filters: ParamType$1.JSON,
14163
13809
  },
14164
13810
  handler: ({ params }) => __awaiter(void 0, void 0, void 0, function* () {
14165
13811
  // Get user info
14166
- const { year, month, userId, isAdmin, } = params;
13812
+ const { pageNumber, userId, isAdmin, filters, } = params;
13813
+ const { dateFilterState, contextFilterState, tagFilterState, actionErrorFilterState, advancedFilterState, } = filters;
14167
13814
  // Validate user
14168
13815
  const canReview = yield canReviewLogs(userId, isAdmin);
14169
13816
  if (!canReview) {
14170
13817
  throw new ErrorWithCode('You cannot access this resource because you do not have the appropriate permissions.', ReactKitErrorCode$1.NotAllowedToReviewLogs);
14171
13818
  }
13819
+ // Build MongoDB query based on filters
13820
+ const query = {};
13821
+ /* -------------- Date Filter ------------- */
13822
+ // Convert start and end dates from the dateFilterState into timestamps
13823
+ const { startDate, endDate } = dateFilterState;
13824
+ const startTimestamp = new Date(startDate.year, startDate.month - 1, startDate.day).getTime();
13825
+ const endTimestamp = new Date(endDate.year, endDate.month - 1, endDate.day + 1).getTime() - 1;
13826
+ // Add a date range condition to the query
13827
+ query.timestamp = {
13828
+ $gte: startTimestamp,
13829
+ $lte: endTimestamp,
13830
+ };
13831
+ /* ------------ Context Filter ------------ */
13832
+ // Process context filters to include selected contexts and subcontexts
13833
+ const selectedContexts = [];
13834
+ const selectedSubcontexts = [];
13835
+ // Process each context filter
13836
+ Object.keys(contextFilterState).forEach((context) => {
13837
+ const value = contextFilterState[context];
13838
+ if (typeof value === 'boolean') {
13839
+ if (value) {
13840
+ selectedContexts.push(context);
13841
+ }
13842
+ }
13843
+ else {
13844
+ // At least one subcontext is selected
13845
+ const atLeastOneSubcontextSelected = (Object.values(value)
13846
+ .some((subcontextValue) => {
13847
+ return subcontextValue;
13848
+ }));
13849
+ if (atLeastOneSubcontextSelected) {
13850
+ selectedContexts.push(context);
13851
+ }
13852
+ // Add all selected subcontexts
13853
+ Object.keys(value).forEach((subcontext) => {
13854
+ if (value[subcontext]) {
13855
+ selectedSubcontexts.push(subcontext);
13856
+ }
13857
+ });
13858
+ }
13859
+ });
13860
+ // Add context and subcontext conditions to the query if any are selected
13861
+ if (selectedContexts.length > 0) {
13862
+ query.context = { $in: selectedContexts };
13863
+ }
13864
+ if (selectedSubcontexts.length > 0) {
13865
+ query.subcontext = { $in: selectedSubcontexts };
13866
+ }
13867
+ /* -------------- Tag Filter -------------- */
13868
+ const selectedTags = Object.keys(tagFilterState).filter((tag) => { return tagFilterState[tag]; });
13869
+ if (selectedTags.length > 0) {
13870
+ query.tags = { $in: selectedTags };
13871
+ }
13872
+ /* --------- Action/Error Filter ---------- */
13873
+ if (actionErrorFilterState.type) {
13874
+ query.type = actionErrorFilterState.type;
13875
+ }
13876
+ if (actionErrorFilterState.type === LogType$1.Error) {
13877
+ if (actionErrorFilterState.errorMessage) {
13878
+ // Add error message to the query.
13879
+ // $i is used for case-insensitive search, and $regex is used for partial matching
13880
+ query.errorMessage = {
13881
+ $regex: actionErrorFilterState.errorMessage,
13882
+ $options: 'i',
13883
+ };
13884
+ }
13885
+ if (actionErrorFilterState.errorCode) {
13886
+ query.errorCode = {
13887
+ $regex: actionErrorFilterState.errorCode,
13888
+ $options: 'i',
13889
+ };
13890
+ }
13891
+ }
13892
+ if (actionErrorFilterState.type === LogType$1.Action) {
13893
+ const selectedTargets = (Object.keys(actionErrorFilterState.target)
13894
+ .filter((target) => {
13895
+ return actionErrorFilterState.target[target];
13896
+ }));
13897
+ const selectedActions = (Object.keys(actionErrorFilterState.action)
13898
+ .filter((action) => {
13899
+ return actionErrorFilterState.action[action];
13900
+ }));
13901
+ if (selectedTargets.length > 0) {
13902
+ query.target = { $in: selectedTargets };
13903
+ }
13904
+ if (selectedActions.length > 0) {
13905
+ query.action = { $in: selectedActions };
13906
+ }
13907
+ }
13908
+ /* ------------ Advanced Filter ----------- */
13909
+ if (advancedFilterState.userFirstName) {
13910
+ query.userFirstName = {
13911
+ $regex: advancedFilterState.userFirstName,
13912
+ $options: 'i',
13913
+ };
13914
+ }
13915
+ if (advancedFilterState.userLastName) {
13916
+ query.userLastName = {
13917
+ $regex: advancedFilterState.userLastName,
13918
+ $options: 'i',
13919
+ };
13920
+ }
13921
+ if (advancedFilterState.userEmail) {
13922
+ query.userEmail = {
13923
+ $regex: advancedFilterState.userEmail,
13924
+ $options: 'i',
13925
+ };
13926
+ }
13927
+ if (advancedFilterState.userId) {
13928
+ query.userId = Number.parseInt(advancedFilterState.userId, 10);
13929
+ }
13930
+ const roles = [];
13931
+ if (advancedFilterState.includeLearners) {
13932
+ roles.push({ isLearner: true });
13933
+ }
13934
+ if (advancedFilterState.includeTTMs) {
13935
+ roles.push({ isTTM: true });
13936
+ }
13937
+ if (advancedFilterState.includeAdmins) {
13938
+ roles.push({ isAdmin: true });
13939
+ }
13940
+ // If any roles are selected, add them to the query
13941
+ if (roles.length > 0) {
13942
+ // The $or operator is used to match any of the roles
13943
+ // The $and operator is to ensure that other conditions in the query are met
13944
+ query.$and = [{ $or: roles }];
13945
+ }
13946
+ if (advancedFilterState.courseId) {
13947
+ query.courseId = Number.parseInt(advancedFilterState.courseId, 10);
13948
+ }
13949
+ if (advancedFilterState.courseName) {
13950
+ query.courseName = {
13951
+ $regex: advancedFilterState.courseName,
13952
+ $options: 'i',
13953
+ };
13954
+ }
13955
+ if (advancedFilterState.isMobile !== undefined) {
13956
+ query['device.isMobile'] = Boolean(advancedFilterState.isMobile);
13957
+ }
13958
+ if (advancedFilterState.source) {
13959
+ query.source = advancedFilterState.source;
13960
+ }
13961
+ if (advancedFilterState.routePath) {
13962
+ query.routePath = {
13963
+ $regex: advancedFilterState.routePath,
13964
+ $options: 'i',
13965
+ };
13966
+ }
13967
+ if (advancedFilterState.routeTemplate) {
13968
+ query.routeTemplate = {
13969
+ $regex: advancedFilterState.routeTemplate,
13970
+ $options: 'i',
13971
+ };
13972
+ }
14172
13973
  // Query for logs
14173
- const logs = yield _logCollection.find({
14174
- year,
14175
- month,
13974
+ const response = yield _logCollection.findPaged({
13975
+ query,
13976
+ perPage: 50,
13977
+ pageNumber,
14176
13978
  });
14177
- // Return logs
14178
- return logs;
13979
+ // Return response
13980
+ return response;
14179
13981
  }),
14180
13982
  }));
14181
13983
  };
@@ -16427,7 +16229,6 @@ exports.padDecimalZeros = padDecimalZeros;
16427
16229
  exports.padZerosLeft = padZerosLeft;
16428
16230
  exports.parallelLimit = parallelLimit;
16429
16231
  exports.prefixWithAOrAn = prefixWithAOrAn;
16430
- exports.prompt = prompt;
16431
16232
  exports.roundToNumDecimals = roundToNumDecimals;
16432
16233
  exports.setClientEventMetadataPopulator = setClientEventMetadataPopulator;
16433
16234
  exports.showFatalError = showFatalError;