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/esm/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as React from 'react';
2
2
  import React__default, { useState, useRef, useEffect, useReducer, forwardRef, useContext, useLayoutEffect, createContext, useMemo, useCallback, Component, Fragment } from 'react';
3
- import { faExclamationTriangle, faHourglassEnd, faCircle, faDotCircle, faCheckSquare, faHourglass, faClipboard, faChevronDown, faChevronRight, faCloudDownloadAlt, faMinus, faCheckCircle, faXmarkCircle, faSort, faSortDown, faSortUp, faCalendar, faTag, faHammer, faList, faTimes, faSave, faTrash, faCog, faPlus } from '@fortawesome/free-solid-svg-icons';
3
+ import { faExclamationTriangle, faHourglassEnd, faCircle, faDotCircle, faCheckSquare, faHourglass, faClipboard, faChevronDown, faChevronRight, faCloudDownloadAlt, faMinus, faCheckCircle, faXmarkCircle, faSort, faSortDown, faSortUp, faArrowLeft, faArrowRight, faCalendar, faTag, faHammer, faList, faTimes, faSearch, faSave, faTrash, faCog, faPlus } from '@fortawesome/free-solid-svg-icons';
4
4
  import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
5
5
  import ReactDOM, { createPortal } from 'react-dom';
6
6
  import { faCircle as faCircle$1, faSquareMinus, faSquare } from '@fortawesome/free-regular-svg-icons';
@@ -158,7 +158,6 @@ var ModalButtonType$1 = ModalButtonType;
158
158
  var ModalType;
159
159
  (function (ModalType) {
160
160
  ModalType["Okay"] = "okay";
161
- ModalType["Cancel"] = "cancel";
162
161
  ModalType["OkayCancel"] = "okay-cancel";
163
162
  ModalType["YesNo"] = "yes-no";
164
163
  ModalType["YesNoCancel"] = "yes-no-cancel";
@@ -313,9 +312,6 @@ const modalTypeToModalButtonTypes = {
313
312
  [ModalType$1.Okay]: [
314
313
  ModalButtonType$1.Okay,
315
314
  ],
316
- [ModalType$1.Cancel]: [
317
- ModalButtonType$1.Cancel,
318
- ],
319
315
  [ModalType$1.OkayCancel]: [
320
316
  ModalButtonType$1.Okay,
321
317
  ModalButtonType$1.Cancel,
@@ -1056,74 +1052,6 @@ const confirm = (title, text, opts) => __awaiter(void 0, void 0, void 0, functio
1056
1052
  });
1057
1053
  });
1058
1054
  /*----------------------------------------*/
1059
- /* --------------- Prompt -------------- */
1060
- /*----------------------------------------*/
1061
- // Stored copies of setters
1062
- let setPromptInfo;
1063
- // Function to call when prompt is closed
1064
- let onPromptClosed;
1065
- /**
1066
- * Show a prompt modal asking the user for input
1067
- * @author Yuen Ler Chow
1068
- * @param title the title text to display at the top of the prompt
1069
- * @param text the text to display in the prompt
1070
- * @param [opts={}] additional options for the prompt dialog
1071
- * @param [opts.placeholder] the placeholder text for the input field
1072
- * @param [opts.defaultText] the default text for the input field
1073
- * @param [opts.confirmButtonText=Okay] the text of the confirm button
1074
- * @param [opts.confirmButtonVariant=Variant.Dark] the variant of the confirm button
1075
- * @param [opts.cancelButtonText=Cancel] the text of the cancel button
1076
- * @param [opts.cancelButtonVariant=Variant.Secondary] the variant of the cancel button
1077
- * @returns Promise that resolves with the input string or null if canceled
1078
- */
1079
- const prompt = (title, text, opts) => __awaiter(void 0, void 0, void 0, function* () {
1080
- var _a;
1081
- // Wait for helper to exist
1082
- yield waitForHelper(() => {
1083
- return !!setPromptInfo;
1084
- });
1085
- // Fallback if prompt is not available
1086
- if (!setPromptInfo) {
1087
- const resultPassesValidation = false;
1088
- while (!resultPassesValidation) {
1089
- // eslint-disable-next-line no-alert
1090
- const result = window.prompt(`${title}\n\n${text}`, (_a = opts === null || opts === void 0 ? void 0 : opts.defaultText) !== null && _a !== void 0 ? _a : '');
1091
- if (result === null) {
1092
- return null;
1093
- }
1094
- // Validate min num chars
1095
- const minNumCharsValidationError = (((opts === null || opts === void 0 ? void 0 : opts.minNumChars) && result.length < opts.minNumChars)
1096
- ? `Please enter at least ${opts.minNumChars} characters.`
1097
- : undefined);
1098
- // Run custom validation
1099
- const customValidationError = ((opts === null || opts === void 0 ? void 0 : opts.findValidationError)
1100
- && opts.findValidationError(result));
1101
- // Show validation issue
1102
- if (minNumCharsValidationError || customValidationError) {
1103
- alert('Invalid Input', `${minNumCharsValidationError !== null && minNumCharsValidationError !== void 0 ? minNumCharsValidationError : ''} ${customValidationError !== null && customValidationError !== void 0 ? customValidationError : ''}`);
1104
- }
1105
- else {
1106
- return result;
1107
- }
1108
- }
1109
- }
1110
- // Return promise that resolves with result of prompt
1111
- return new Promise((resolve) => {
1112
- var _a;
1113
- // Setup handler
1114
- onPromptClosed = (result) => {
1115
- resolve(result);
1116
- };
1117
- // Show the prompt
1118
- setPromptInfo({
1119
- title,
1120
- text,
1121
- currentInputFieldText: ((_a = opts === null || opts === void 0 ? void 0 : opts.defaultText) !== null && _a !== void 0 ? _a : ''),
1122
- opts: (opts !== null && opts !== void 0 ? opts : {}),
1123
- });
1124
- });
1125
- });
1126
- /*----------------------------------------*/
1127
1055
  /* ------------- Fatal Error ------------ */
1128
1056
  /*----------------------------------------*/
1129
1057
  // Stored copies of setters
@@ -1139,14 +1067,14 @@ const fatalErrorHandlers = [];
1139
1067
  * @param [errorTitle] title of the error box
1140
1068
  */
1141
1069
  const showFatalError = (error, errorTitle = 'An Error Occurred') => __awaiter(void 0, void 0, void 0, function* () {
1142
- var _b, _c;
1070
+ var _a, _b;
1143
1071
  // Determine message and code
1144
1072
  const message = (typeof error === 'string'
1145
1073
  ? error.trim()
1146
- : String((_b = error.message) !== null && _b !== void 0 ? _b : 'An unknown error occurred.'));
1074
+ : String((_a = error.message) !== null && _a !== void 0 ? _a : 'An unknown error occurred.'));
1147
1075
  const code = (typeof error === 'string'
1148
1076
  ? ReactKitErrorCode$1.NoCode
1149
- : String((_c = error.code) !== null && _c !== void 0 ? _c : ReactKitErrorCode$1.NoCode));
1077
+ : String((_b = error.code) !== null && _b !== void 0 ? _b : ReactKitErrorCode$1.NoCode));
1150
1078
  // Call all fatal error listeners
1151
1079
  try {
1152
1080
  fatalErrorHandlers.forEach((handler) => {
@@ -1273,9 +1201,6 @@ const AppWrapper = (props) => {
1273
1201
  // Confirm
1274
1202
  const [confirmInfo, setConfirmInfoInner,] = useState(undefined);
1275
1203
  setConfirmInfo = setConfirmInfoInner;
1276
- // Prompt
1277
- const [promptInfo, setPromptInfoInner] = useState(undefined);
1278
- setPromptInfo = setPromptInfoInner;
1279
1204
  // Session expired
1280
1205
  const [sessionHasExpired, setSessionHasExpiredInner,] = useState(false);
1281
1206
  setSessionHasExpired = setSessionHasExpiredInner;
@@ -1306,37 +1231,6 @@ const AppWrapper = (props) => {
1306
1231
  }
1307
1232
  }, onTopOfOtherModals: true, dontAllowBackdropExit: true }, confirmInfo.text));
1308
1233
  }
1309
- /* ------------- Prompt ------------ */
1310
- if (promptInfo) {
1311
- // Run min char validation
1312
- const minNumCharsValidationError = ((promptInfo.opts.minNumChars
1313
- && promptInfo.currentInputFieldText.length < promptInfo.opts.minNumChars)
1314
- ? `Please enter at least ${promptInfo.opts.minNumChars} characters.`
1315
- : undefined);
1316
- // Run custom validation
1317
- const customValidationError = (promptInfo.opts.findValidationError
1318
- && promptInfo.opts.findValidationError(promptInfo.currentInputFieldText));
1319
- modal = (React__default.createElement(Modal, { key: `prompt-${promptInfo.title}-${promptInfo.text}`, title: promptInfo.title,
1320
- // Don't show ok button if there is a validation error
1321
- type: ((customValidationError || minNumCharsValidationError)
1322
- ? ModalType$1.Cancel
1323
- : ModalType$1.OkayCancel), okayLabel: promptInfo.opts.confirmButtonText, okayVariant: promptInfo.opts.confirmButtonVariant, cancelLabel: promptInfo.opts.cancelButtonText, cancelVariant: promptInfo.opts.cancelButtonVariant, onClose: (buttonType) => {
1324
- const result = (buttonType === ModalButtonType$1.Okay
1325
- ? promptInfo.currentInputFieldText
1326
- : null);
1327
- setPromptInfo(undefined);
1328
- if (onPromptClosed) {
1329
- onPromptClosed(result);
1330
- }
1331
- }, onTopOfOtherModals: true, dontAllowBackdropExit: true },
1332
- React__default.createElement("div", { className: "p-3" },
1333
- React__default.createElement("p", null, promptInfo.text),
1334
- React__default.createElement("input", { type: "text", className: "form-control mb-2", placeholder: promptInfo.opts.placeholder, value: promptInfo.currentInputFieldText, onChange: (e) => {
1335
- return setPromptInfo(Object.assign(Object.assign({}, promptInfo), { currentInputFieldText: e.target.value }));
1336
- } }),
1337
- minNumCharsValidationError && (React__default.createElement("div", { className: "text-danger fw-bold mt-2" }, minNumCharsValidationError)),
1338
- customValidationError && (React__default.createElement("div", { className: "text-danger fw-bold mt-2" }, customValidationError)))));
1339
- }
1340
1234
  /* ------ Custom Modal Portals ------ */
1341
1235
  // Custom modal portals
1342
1236
  const customModalPortals = [];
@@ -2652,10 +2546,10 @@ const getHumanReadableDate = (dateOrTimestamp) => {
2652
2546
  };
2653
2547
 
2654
2548
  /**
2655
- * Path of the route for storing client-side logs
2549
+ * Path of the route for getting logs for log review
2656
2550
  * @author Gabe Abrams
2657
2551
  */
2658
- const LOG_REVIEW_ROUTE_PATH_PREFIX = `/admin${ROUTE_PATH_PREFIX}/logs`;
2552
+ const LOG_REVIEW_GET_LOGS_ROUTE = `/admin${ROUTE_PATH_PREFIX}/logs`;
2659
2553
 
2660
2554
  /**
2661
2555
  * Source of a log event
@@ -3615,7 +3509,7 @@ var ActionType$6;
3615
3509
  (function (ActionType) {
3616
3510
  // Show the loading bar
3617
3511
  ActionType["StartLoading"] = "start-loading";
3618
- // Finish loading one or more months of logs
3512
+ // Finish loading logs
3619
3513
  ActionType["FinishLoading"] = "finish-loading";
3620
3514
  // Reset filters to initial values
3621
3515
  ActionType["ResetFilters"] = "reset-filters";
@@ -3633,6 +3527,12 @@ var ActionType$6;
3633
3527
  ActionType["UpdateActionErrorFilterState"] = "update-action-error-filter-state";
3634
3528
  // Update the advanced filter state
3635
3529
  ActionType["UpdateAdvancedFilterState"] = "update-advanced-filter-state";
3530
+ // Increment the page number
3531
+ ActionType["IncrementPageNumber"] = "increment-page-number";
3532
+ // Decrement the page number
3533
+ ActionType["DecrementPageNumber"] = "decrement-page-number";
3534
+ // Set has another page
3535
+ ActionType["SetHasAnotherPage"] = "set-has-another-page";
3636
3536
  })(ActionType$6 || (ActionType$6 = {}));
3637
3537
  /**
3638
3538
  * Reducer that executes actions
@@ -3646,7 +3546,7 @@ const reducer$7 = (state, action) => {
3646
3546
  return Object.assign(Object.assign({}, state), { loading: true });
3647
3547
  }
3648
3548
  case ActionType$6.FinishLoading: {
3649
- return Object.assign(Object.assign({}, state), { loading: false, logMap: action.logMap });
3549
+ return Object.assign(Object.assign({}, state), { loading: false, logs: action.logs });
3650
3550
  }
3651
3551
  case ActionType$6.ToggleFilterDrawer: {
3652
3552
  return Object.assign(Object.assign({}, state), { expandedFilterDrawer: (state.expandedFilterDrawer === action.filterDrawer
@@ -3674,6 +3574,15 @@ const reducer$7 = (state, action) => {
3674
3574
  case ActionType$6.UpdateAdvancedFilterState: {
3675
3575
  return Object.assign(Object.assign({}, state), { advancedFilterState: action.advancedFilterState });
3676
3576
  }
3577
+ case ActionType$6.IncrementPageNumber: {
3578
+ return Object.assign(Object.assign({}, state), { pageNumber: state.pageNumber + 1 });
3579
+ }
3580
+ case ActionType$6.DecrementPageNumber: {
3581
+ return Object.assign(Object.assign({}, state), { pageNumber: state.pageNumber - 1 });
3582
+ }
3583
+ case ActionType$6.SetHasAnotherPage: {
3584
+ return Object.assign(Object.assign({}, state), { hasAnotherPage: action.hasAnotherPage });
3585
+ }
3677
3586
  default: {
3678
3587
  return state;
3679
3588
  }
@@ -3791,115 +3700,74 @@ const LogReviewer = (props) => {
3791
3700
  // Initial state
3792
3701
  const initialState = {
3793
3702
  loading: true,
3794
- logMap: {},
3703
+ logs: [],
3795
3704
  expandedFilterDrawer: undefined,
3796
3705
  dateFilterState: initDateFilterState,
3797
3706
  contextFilterState: initContextFilterState,
3798
3707
  tagFilterState: initTagFilterState,
3799
3708
  actionErrorFilterState: initActionErrorFilterState,
3800
3709
  advancedFilterState: initAdvancedFilterState,
3710
+ pageNumber: 1,
3711
+ hasAnotherPage: false,
3801
3712
  };
3802
3713
  // Initialize state
3803
3714
  const [state, dispatch] = useReducer(reducer$7, initialState);
3804
3715
  // Destructure common state
3805
- const { loading, logMap, expandedFilterDrawer, dateFilterState, contextFilterState, tagFilterState, actionErrorFilterState, advancedFilterState, } = state;
3716
+ const { loading, logs, expandedFilterDrawer, dateFilterState, contextFilterState, tagFilterState, actionErrorFilterState, advancedFilterState, pageNumber, hasAnotherPage, } = state;
3806
3717
  /*------------------------------------------------------------------------*/
3807
3718
  /* ------------------------- Component Functions ------------------------ */
3808
3719
  /*------------------------------------------------------------------------*/
3809
3720
  /**
3810
- * Get the list of year/month combos that need to be loaded given a new
3811
- * start or end date and the existing logMap
3812
- * @author Gabe Abrams
3813
- * @param newDateFilterState the new date filter state
3814
- * @returns list of year/month combos that need to be loaded
3815
- */
3816
- const listMonthsToLoad = (newDateFilterState) => {
3817
- // List of year/month combos that need to be loaded
3818
- const toLoad = [];
3819
- // Loop through dates
3820
- let { year, month } = newDateFilterState.startDate;
3821
- while (
3822
- // Earlier year
3823
- (year < newDateFilterState.endDate.year)
3824
- // Current year but included month
3825
- || (year === newDateFilterState.endDate.year
3826
- && month <= newDateFilterState.endDate.month)) {
3827
- // Add to list if not already loaded
3828
- if (!logMap[year]
3829
- || !logMap[year][month]) {
3830
- toLoad.push({
3831
- year,
3832
- month,
3833
- });
3834
- }
3835
- // Increment
3836
- month += 1;
3837
- if (month > 12) {
3838
- month -= 12;
3839
- year += 1;
3840
- }
3841
- }
3842
- // Return
3843
- return toLoad;
3844
- };
3845
- /**
3846
- * Handle updated start/end dates (updates state, loads if necessary)
3847
- * @author Gabe Abrams
3848
- * @param newDateFilterState the new date filter state
3721
+ * Fetch logs from the server based on current filters
3849
3722
  */
3850
- const handleDateRangeUpdated = (newDateFilterState) => __awaiter(void 0, void 0, void 0, function* () {
3851
- // Update state
3852
- dispatch({
3853
- type: ActionType$6.UpdateDateFilterState,
3854
- dateFilterState: newDateFilterState,
3855
- });
3856
- // Check which year/month combos we need to load
3857
- const toLoad = listMonthsToLoad(newDateFilterState);
3858
- // If nothing to load, finished
3859
- if (toLoad.length === 0) {
3860
- return;
3861
- }
3862
- // Start loading
3863
- dispatch({
3864
- type: ActionType$6.StartLoading,
3865
- });
3866
- // Load required months
3723
+ const fetchLogs = () => __awaiter(void 0, void 0, void 0, function* () {
3724
+ dispatch({ type: ActionType$6.StartLoading });
3867
3725
  try {
3868
- for (let i = 0; i < toLoad.length; i++) {
3869
- // Destructure
3870
- const { year, month } = toLoad[i];
3871
- // Load
3872
- const logs = yield visitServerEndpoint({
3873
- path: `${LOG_REVIEW_ROUTE_PATH_PREFIX}/years/${year}/months/${month}`,
3874
- method: 'GET',
3875
- });
3876
- // Add to map
3877
- if (!logMap[year]) {
3878
- logMap[year] = {};
3879
- }
3880
- logMap[year][month] = logs;
3881
- }
3726
+ // Prepare filter parameters
3727
+ const filters = {
3728
+ dateFilterState,
3729
+ contextFilterState,
3730
+ tagFilterState,
3731
+ actionErrorFilterState,
3732
+ advancedFilterState,
3733
+ };
3734
+ // Send filters to the server
3735
+ let fetchedLogs = [];
3736
+ // Get logs from server
3737
+ const response = yield visitServerEndpoint({
3738
+ path: LOG_REVIEW_GET_LOGS_ROUTE,
3739
+ method: 'GET',
3740
+ params: {
3741
+ pageNumber,
3742
+ filters,
3743
+ },
3744
+ });
3745
+ fetchedLogs = fetchedLogs.concat(response.items);
3746
+ dispatch({
3747
+ type: ActionType$6.SetHasAnotherPage,
3748
+ hasAnotherPage: response.hasAnotherPage,
3749
+ });
3750
+ // Update logs in state
3751
+ dispatch({
3752
+ type: ActionType$6.FinishLoading,
3753
+ logs: fetchedLogs,
3754
+ });
3882
3755
  }
3883
3756
  catch (err) {
3884
3757
  return showFatalError(err);
3885
3758
  }
3886
- // Finish loading
3887
- dispatch({
3888
- type: ActionType$6.FinishLoading,
3889
- logMap,
3890
- });
3891
3759
  });
3892
3760
  /*------------------------------------------------------------------------*/
3893
3761
  /* ------------------------- Lifecycle Functions ------------------------ */
3894
3762
  /*------------------------------------------------------------------------*/
3895
3763
  /**
3896
- * Mount
3897
- * @author Gabe Abrams
3764
+ * Fetch logs whenever page number changes
3898
3765
  */
3899
3766
  useEffect(() => {
3900
- // Perform initial load
3901
- handleDateRangeUpdated(dateFilterState);
3902
- }, []);
3767
+ fetchLogs();
3768
+ }, [
3769
+ pageNumber,
3770
+ ]);
3903
3771
  /*------------------------------------------------------------------------*/
3904
3772
  /* ------------------------------- Render ------------------------------- */
3905
3773
  /*------------------------------------------------------------------------*/
@@ -3914,703 +3782,481 @@ const LogReviewer = (props) => {
3914
3782
  React__default.createElement(LoadingSpinner, null)));
3915
3783
  }
3916
3784
  /* ------------ Review UI ----------- */
3917
- if (!loading) {
3918
- /*----------------------------------------*/
3919
- /* --------------- Filters -------------- */
3920
- /*----------------------------------------*/
3921
- // Filter toggle
3922
- const filterToggles = (React__default.createElement("div", { className: "LogReviewer-filter-toggles" },
3923
- React__default.createElement("h3", { className: "m-0" }, "Filters:"),
3924
- React__default.createElement("div", { className: "LogReviewer-filter-toggle-buttons alert alert-secondary p-2 m-0" },
3925
- React__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: () => {
3926
- dispatch({
3927
- type: ActionType$6.ToggleFilterDrawer,
3928
- filterDrawer: FilterDrawer.Date,
3929
- });
3930
- } },
3931
- React__default.createElement(FontAwesomeIcon, { icon: faCalendar, className: "me-2" }),
3932
- "Date"),
3933
- React__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: () => {
3934
- dispatch({
3935
- type: ActionType$6.ToggleFilterDrawer,
3936
- filterDrawer: FilterDrawer.Context,
3937
- });
3938
- } },
3939
- React__default.createElement(FontAwesomeIcon, { icon: faCircle, className: "me-2" }),
3940
- "Context"),
3941
- (LogMetadata.Tag && Object.keys(LogMetadata.Tag).length > 0) && (React__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: () => {
3942
- dispatch({
3943
- type: ActionType$6.ToggleFilterDrawer,
3944
- filterDrawer: FilterDrawer.Tag,
3945
- });
3946
- } },
3947
- React__default.createElement(FontAwesomeIcon, { icon: faTag, className: "me-2" }),
3948
- "Tag")),
3949
- React__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: () => {
3950
- dispatch({
3951
- type: ActionType$6.ToggleFilterDrawer,
3952
- filterDrawer: FilterDrawer.Action,
3953
- });
3954
- } },
3955
- React__default.createElement(FontAwesomeIcon, { icon: faHammer, className: "me-2" }),
3956
- "Action"),
3957
- React__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: () => {
3785
+ /*----------------------------------------*/
3786
+ /* ------------ Pagination -------------- */
3787
+ /*----------------------------------------*/
3788
+ const paginationControls = logs.length > 0 && (React__default.createElement("div", { className: "text-center mt-3" },
3789
+ React__default.createElement("button", { type: "button", className: "btn btn-secondary me-2", disabled: pageNumber <= 1, onClick: () => {
3790
+ dispatch({
3791
+ type: ActionType$6.DecrementPageNumber,
3792
+ });
3793
+ } },
3794
+ React__default.createElement(FontAwesomeIcon, { icon: faArrowLeft, className: "me-2" }),
3795
+ "Previous Page"),
3796
+ React__default.createElement("span", { className: "mx-3" },
3797
+ "Page",
3798
+ ' ',
3799
+ pageNumber),
3800
+ React__default.createElement("button", { type: "button", className: "btn btn-secondary ms-2", disabled: !hasAnotherPage, onClick: () => {
3801
+ dispatch({
3802
+ type: ActionType$6.IncrementPageNumber,
3803
+ });
3804
+ } },
3805
+ "Next Page",
3806
+ React__default.createElement(FontAwesomeIcon, { icon: faArrowRight, className: "ms-2" }))));
3807
+ /*----------------------------------------*/
3808
+ /* --------------- Filters -------------- */
3809
+ /*----------------------------------------*/
3810
+ // Filter toggle
3811
+ const filterToggles = (React__default.createElement("div", { className: "LogReviewer-filter-toggles" },
3812
+ React__default.createElement("h3", { className: "m-0" }, "Filters:"),
3813
+ React__default.createElement("div", { className: "LogReviewer-filter-toggle-buttons alert alert-secondary p-2 m-0" },
3814
+ React__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: () => {
3815
+ dispatch({
3816
+ type: ActionType$6.ToggleFilterDrawer,
3817
+ filterDrawer: FilterDrawer.Date,
3818
+ });
3819
+ } },
3820
+ React__default.createElement(FontAwesomeIcon, { icon: faCalendar, className: "me-2" }),
3821
+ "Date"),
3822
+ React__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: () => {
3823
+ dispatch({
3824
+ type: ActionType$6.ToggleFilterDrawer,
3825
+ filterDrawer: FilterDrawer.Context,
3826
+ });
3827
+ } },
3828
+ React__default.createElement(FontAwesomeIcon, { icon: faCircle, className: "me-2" }),
3829
+ "Context"),
3830
+ (LogMetadata.Tag && Object.keys(LogMetadata.Tag).length > 0) && (React__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: () => {
3831
+ dispatch({
3832
+ type: ActionType$6.ToggleFilterDrawer,
3833
+ filterDrawer: FilterDrawer.Tag,
3834
+ });
3835
+ } },
3836
+ React__default.createElement(FontAwesomeIcon, { icon: faTag, className: "me-2" }),
3837
+ "Tag")),
3838
+ React__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: () => {
3839
+ dispatch({
3840
+ type: ActionType$6.ToggleFilterDrawer,
3841
+ filterDrawer: FilterDrawer.Action,
3842
+ });
3843
+ } },
3844
+ React__default.createElement(FontAwesomeIcon, { icon: faHammer, className: "me-2" }),
3845
+ "Action"),
3846
+ React__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: () => {
3847
+ dispatch({
3848
+ type: ActionType$6.ToggleFilterDrawer,
3849
+ filterDrawer: FilterDrawer.Advanced,
3850
+ });
3851
+ } },
3852
+ React__default.createElement(FontAwesomeIcon, { icon: faList, className: "me-2" }),
3853
+ "Advanced"),
3854
+ React__default.createElement("button", { type: "button", id: "LogReviewer-reset-filters-button", className: "btn btn-light", "aria-label": "reset filters", onClick: () => {
3855
+ dispatch({
3856
+ type: ActionType$6.ResetFilters,
3857
+ initActionErrorFilterState,
3858
+ initAdvancedFilterState,
3859
+ initContextFilterState,
3860
+ initDateFilterState,
3861
+ initTagFilterState,
3862
+ });
3863
+ } },
3864
+ React__default.createElement(FontAwesomeIcon, { icon: faTimes }),
3865
+ ' ',
3866
+ "Reset"),
3867
+ React__default.createElement("button", { type: "button", id: "LogReviewer-submit-filters-button", className: "btn btn-primary ms-2", "aria-label": "submit filters", onClick: () => {
3868
+ dispatch({
3869
+ type: ActionType$6.HideFilterDrawer,
3870
+ });
3871
+ fetchLogs();
3872
+ } },
3873
+ React__default.createElement(FontAwesomeIcon, { icon: faSearch }),
3874
+ ' ',
3875
+ "Filter"))));
3876
+ // Filter drawer
3877
+ let filterDrawer;
3878
+ if (expandedFilterDrawer) {
3879
+ if (expandedFilterDrawer === FilterDrawer.Date) {
3880
+ filterDrawer = (React__default.createElement(TabBox, { title: "Date" },
3881
+ React__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) => {
3882
+ dateFilterState.startDate = { month, day, year };
3958
3883
  dispatch({
3959
- type: ActionType$6.ToggleFilterDrawer,
3960
- filterDrawer: FilterDrawer.Advanced,
3884
+ type: ActionType$6.UpdateDateFilterState,
3885
+ dateFilterState,
3961
3886
  });
3962
- } },
3963
- React__default.createElement(FontAwesomeIcon, { icon: faList, className: "me-2" }),
3964
- "Advanced"),
3965
- React__default.createElement("button", { type: "button", id: "LogReviewer-reset-filters-button", className: "btn btn-light", "aria-label": "reset filters", onClick: () => {
3887
+ } }),
3888
+ ' ',
3889
+ "to",
3890
+ ' ',
3891
+ React__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) => {
3892
+ if (year < dateFilterState.startDate.year
3893
+ || (year === dateFilterState.startDate.year
3894
+ && month < dateFilterState.startDate.month)
3895
+ || (year === dateFilterState.startDate.year
3896
+ && month === dateFilterState.startDate.month
3897
+ && day < dateFilterState.startDate.day)) {
3898
+ return alert('Invalid Start Date', 'The start date cannot be before the end date.');
3899
+ }
3900
+ dateFilterState.endDate = { month, day, year };
3966
3901
  dispatch({
3967
- type: ActionType$6.ResetFilters,
3968
- initActionErrorFilterState,
3969
- initAdvancedFilterState,
3970
- initContextFilterState,
3971
- initDateFilterState,
3972
- initTagFilterState,
3902
+ type: ActionType$6.UpdateDateFilterState,
3903
+ dateFilterState,
3973
3904
  });
3974
- } },
3975
- React__default.createElement(FontAwesomeIcon, { icon: faTimes }),
3976
- ' ',
3977
- "Reset"))));
3978
- // Filter drawer
3979
- let filterDrawer;
3980
- if (expandedFilterDrawer) {
3981
- if (expandedFilterDrawer === FilterDrawer.Date) {
3982
- filterDrawer = (React__default.createElement(TabBox, { title: "Date" },
3983
- React__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) => {
3984
- dateFilterState.startDate = { month, day, year };
3985
- handleDateRangeUpdated(dateFilterState);
3986
- } }),
3987
- ' ',
3988
- "to",
3989
- ' ',
3990
- React__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) => {
3991
- if (year < dateFilterState.startDate.year
3992
- || (year === dateFilterState.startDate.year
3993
- && month < dateFilterState.startDate.month)
3994
- || (year === dateFilterState.startDate.year
3995
- && month === dateFilterState.startDate.month
3996
- && day < dateFilterState.startDate.day)) {
3997
- return alert('Invalid Start Date', 'The start date cannot be before the end date.');
3998
- }
3999
- dateFilterState.endDate = { month, day, year };
4000
- handleDateRangeUpdated(dateFilterState);
4001
- } })));
4002
- }
4003
- else if (expandedFilterDrawer === FilterDrawer.Context) {
4004
- // Create item picker items
4005
- const builtInPickableItem = {
4006
- id: 'built-in-contexts',
4007
- name: 'Auto-logged',
4008
- isGroup: true,
4009
- children: [],
4010
- };
4011
- const pickableItems = [];
4012
- Object.keys(contextMap)
4013
- .forEach((context) => {
4014
- const value = contextMap[context];
4015
- if (typeof value === 'string') {
4016
- // No subcategories
4017
- const item = {
4018
- id: context,
4019
- name: genHumanReadableName(context),
4020
- isGroup: false,
4021
- checked: !!contextFilterState[context],
4022
- };
4023
- // Add built-in items to its own folder
4024
- const isBuiltIn = context in LogBuiltInMetadata.Context;
4025
- if (isBuiltIn) {
4026
- // Add to built-in pickable item
4027
- builtInPickableItem.children.push(item);
4028
- }
4029
- else {
4030
- // Add to pickable items list
4031
- pickableItems.push(item);
4032
- }
4033
- return;
4034
- }
4035
- // Has subcategories
4036
- const children = (Object.keys(value)
4037
- // Remove parent name
4038
- .filter((subcontext) => {
4039
- return subcontext !== '_';
4040
- })
4041
- // Create child pickable items
4042
- .map((subcontext) => {
4043
- return {
4044
- id: subcontext,
4045
- name: genHumanReadableName(subcontext),
4046
- isGroup: false,
4047
- checked: contextFilterState[context][subcontext],
4048
- };
4049
- }));
3905
+ } })));
3906
+ }
3907
+ else if (expandedFilterDrawer === FilterDrawer.Context) {
3908
+ // Create item picker items
3909
+ const builtInPickableItem = {
3910
+ id: 'built-in-contexts',
3911
+ name: 'Auto-logged',
3912
+ isGroup: true,
3913
+ children: [],
3914
+ };
3915
+ const pickableItems = [];
3916
+ Object.keys(contextMap)
3917
+ .forEach((context) => {
3918
+ const value = contextMap[context];
3919
+ if (typeof value === 'string') {
3920
+ // No subcategories
4050
3921
  const item = {
4051
3922
  id: context,
4052
3923
  name: genHumanReadableName(context),
4053
- isGroup: true,
4054
- children,
3924
+ isGroup: false,
3925
+ checked: !!contextFilterState[context],
4055
3926
  };
4056
- pickableItems.push(item);
4057
- });
4058
- // Add built-in contexts to end ofl ist
4059
- pickableItems.push(builtInPickableItem);
4060
- // Create filter UI
4061
- filterDrawer = (React__default.createElement(ItemPicker, { title: "Context", items: pickableItems, onChanged: (updatedItems) => {
4062
- // Update our state
4063
- updatedItems.forEach((pickableItem) => {
4064
- if (pickableItem.isGroup) {
4065
- // Has subcontexts
4066
- if (pickableItem.id === 'built-in-contexts') {
4067
- // Built-in
4068
- // Treat as if these were top-level contexts
4069
- pickableItem.children.forEach((subcontextItem) => {
4070
- contextFilterState[subcontextItem.id] = ('checked' in subcontextItem
4071
- && subcontextItem.checked);
4072
- });
4073
- }
4074
- else {
4075
- // Not built-in
4076
- pickableItem.children.forEach((subcontextItem) => {
4077
- if (!subcontextItem.isGroup) {
4078
- contextFilterState[pickableItem.id][subcontextItem.id] = (subcontextItem.checked);
4079
- }
4080
- });
4081
- }
3927
+ // Add built-in items to its own folder
3928
+ const isBuiltIn = context in LogBuiltInMetadata.Context;
3929
+ if (isBuiltIn) {
3930
+ // Add to built-in pickable item
3931
+ builtInPickableItem.children.push(item);
3932
+ }
3933
+ else {
3934
+ // Add to pickable items list
3935
+ pickableItems.push(item);
3936
+ }
3937
+ return;
3938
+ }
3939
+ // Has subcategories
3940
+ const children = (Object.keys(value)
3941
+ // Remove parent name
3942
+ .filter((subcontext) => {
3943
+ return subcontext !== '_';
3944
+ })
3945
+ // Create child pickable items
3946
+ .map((subcontext) => {
3947
+ return {
3948
+ id: subcontext,
3949
+ name: genHumanReadableName(subcontext),
3950
+ isGroup: false,
3951
+ checked: contextFilterState[context][subcontext],
3952
+ };
3953
+ }));
3954
+ const item = {
3955
+ id: context,
3956
+ name: genHumanReadableName(context),
3957
+ isGroup: true,
3958
+ children,
3959
+ };
3960
+ pickableItems.push(item);
3961
+ });
3962
+ // Add built-in contexts to end ofl ist
3963
+ pickableItems.push(builtInPickableItem);
3964
+ // Create filter UI
3965
+ filterDrawer = (React__default.createElement(ItemPicker, { title: "Context", items: pickableItems, onChanged: (updatedItems) => {
3966
+ // Update our state
3967
+ updatedItems.forEach((pickableItem) => {
3968
+ if (pickableItem.isGroup) {
3969
+ // Has subcontexts
3970
+ if (pickableItem.id === 'built-in-contexts') {
3971
+ // Built-in
3972
+ // Treat as if these were top-level contexts
3973
+ pickableItem.children.forEach((subcontextItem) => {
3974
+ contextFilterState[subcontextItem.id] = ('checked' in subcontextItem
3975
+ && subcontextItem.checked);
3976
+ });
4082
3977
  }
4083
3978
  else {
4084
- // No subcontexts
4085
- contextFilterState[pickableItem.id] = (pickableItem.checked);
3979
+ // Not built-in
3980
+ pickableItem.children.forEach((subcontextItem) => {
3981
+ if (!subcontextItem.isGroup) {
3982
+ contextFilterState[pickableItem.id][subcontextItem.id] = (subcontextItem.checked);
3983
+ }
3984
+ });
4086
3985
  }
4087
- });
4088
- dispatch({
4089
- type: ActionType$6.UpdateContextFilterState,
4090
- contextFilterState,
4091
- });
4092
- } }));
4093
- }
4094
- else if (expandedFilterDrawer === FilterDrawer.Tag) {
4095
- // Create filter UI
4096
- filterDrawer = (React__default.createElement(TabBox, { title: "Tags" },
4097
- React__default.createElement("div", null, "If any tags are selected, logs must contain at least one (but not necessarily all) of the selected tags."),
4098
- React__default.createElement("div", { className: "d-flex gap-1 flex-wrap" }, Object.keys((_d = LogMetadata.Tag) !== null && _d !== void 0 ? _d : {})
4099
- .map((tag) => {
4100
- const description = genHumanReadableName(tag);
4101
- return (React__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) => {
4102
- tagFilterState[tag] = checked;
3986
+ }
3987
+ else {
3988
+ // No subcontexts
3989
+ contextFilterState[pickableItem.id] = (pickableItem.checked);
3990
+ }
3991
+ });
3992
+ dispatch({
3993
+ type: ActionType$6.UpdateContextFilterState,
3994
+ contextFilterState,
3995
+ });
3996
+ } }));
3997
+ }
3998
+ else if (expandedFilterDrawer === FilterDrawer.Tag) {
3999
+ // Create filter UI
4000
+ filterDrawer = (React__default.createElement(TabBox, { title: "Tags" },
4001
+ React__default.createElement("div", null, "If any tags are selected, logs must contain at least one (but not necessarily all) of the selected tags."),
4002
+ React__default.createElement("div", { className: "d-flex gap-1 flex-wrap" }, Object.keys((_d = LogMetadata.Tag) !== null && _d !== void 0 ? _d : {})
4003
+ .map((tag) => {
4004
+ const description = genHumanReadableName(tag);
4005
+ return (React__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) => {
4006
+ tagFilterState[tag] = checked;
4007
+ dispatch({
4008
+ type: ActionType$6.UpdateTagFilterState,
4009
+ tagFilterState,
4010
+ });
4011
+ } }));
4012
+ }))));
4013
+ }
4014
+ else if (expandedFilterDrawer === FilterDrawer.Action) {
4015
+ // Create filter UI
4016
+ filterDrawer = (React__default.createElement(React__default.Fragment, null,
4017
+ React__default.createElement(TabBox, { title: "Log Type" },
4018
+ React__default.createElement(RadioButton, { id: "LogReviewer-type-all", text: "All Logs", onSelected: () => {
4019
+ actionErrorFilterState.type = undefined;
4020
+ dispatch({
4021
+ type: ActionType$6.UpdateActionErrorFilterState,
4022
+ actionErrorFilterState,
4023
+ });
4024
+ }, ariaLabel: "show logs of all types", selected: actionErrorFilterState.type === undefined }),
4025
+ React__default.createElement(RadioButton, { id: "LogReviewer-type-action-only", text: "Action Logs Only", onSelected: () => {
4026
+ actionErrorFilterState.type = LogType$1.Action;
4027
+ dispatch({
4028
+ type: ActionType$6.UpdateActionErrorFilterState,
4029
+ actionErrorFilterState,
4030
+ });
4031
+ }, ariaLabel: "only show action logs", selected: actionErrorFilterState.type === LogType$1.Action }),
4032
+ React__default.createElement(RadioButton, { id: "LogReviewer-type-error-only", text: "Action Error Only", onSelected: () => {
4033
+ actionErrorFilterState.type = LogType$1.Error;
4034
+ dispatch({
4035
+ type: ActionType$6.UpdateActionErrorFilterState,
4036
+ actionErrorFilterState,
4037
+ });
4038
+ }, ariaLabel: "only show error logs", selected: actionErrorFilterState.type === LogType$1.Error, noMarginOnRight: true })),
4039
+ (actionErrorFilterState.type === undefined
4040
+ || actionErrorFilterState.type === LogType$1.Action) && (React__default.createElement(TabBox, { title: "Action Log Details" },
4041
+ React__default.createElement(ButtonInputGroup, { label: "Action", className: "mb-2", wrapButtonsAndAddGaps: true }, Object.keys(LogAction$1)
4042
+ .map((action) => {
4043
+ const description = genHumanReadableName(action);
4044
+ return (React__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) => {
4045
+ actionErrorFilterState.action[action] = checked;
4103
4046
  dispatch({
4104
- type: ActionType$6.UpdateTagFilterState,
4105
- tagFilterState,
4047
+ type: ActionType$6.UpdateActionErrorFilterState,
4048
+ actionErrorFilterState,
4106
4049
  });
4107
4050
  } }));
4108
- }))));
4109
- }
4110
- else if (expandedFilterDrawer === FilterDrawer.Action) {
4111
- // Create filter UI
4112
- filterDrawer = (React__default.createElement(React__default.Fragment, null,
4113
- React__default.createElement(TabBox, { title: "Log Type" },
4114
- React__default.createElement(RadioButton, { id: "LogReviewer-type-all", text: "All Logs", onSelected: () => {
4115
- actionErrorFilterState.type = undefined;
4051
+ })),
4052
+ React__default.createElement(ButtonInputGroup, { label: "Target", wrapButtonsAndAddGaps: true }, Object.keys(targetMap)
4053
+ .map((target) => {
4054
+ const description = genHumanReadableName(target);
4055
+ return (React__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) => {
4056
+ actionErrorFilterState.target[target] = checked;
4116
4057
  dispatch({
4117
4058
  type: ActionType$6.UpdateActionErrorFilterState,
4118
4059
  actionErrorFilterState,
4119
4060
  });
4120
- }, ariaLabel: "show logs of all types", selected: actionErrorFilterState.type === undefined }),
4121
- React__default.createElement(RadioButton, { id: "LogReviewer-type-action-only", text: "Action Logs Only", onSelected: () => {
4122
- actionErrorFilterState.type = LogType$1.Action;
4061
+ } }));
4062
+ })))),
4063
+ (actionErrorFilterState.type === undefined
4064
+ || actionErrorFilterState.type === LogType$1.Error) && (React__default.createElement(TabBox, { title: "Error Log Details" },
4065
+ React__default.createElement("div", { className: "input-group mb-2" },
4066
+ React__default.createElement("span", { className: "input-group-text" }, "Error Message"),
4067
+ React__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) => {
4068
+ actionErrorFilterState.errorMessage = e.target.value;
4123
4069
  dispatch({
4124
4070
  type: ActionType$6.UpdateActionErrorFilterState,
4125
4071
  actionErrorFilterState,
4126
4072
  });
4127
- }, ariaLabel: "only show action logs", selected: actionErrorFilterState.type === LogType$1.Action }),
4128
- React__default.createElement(RadioButton, { id: "LogReviewer-type-error-only", text: "Action Error Only", onSelected: () => {
4129
- actionErrorFilterState.type = LogType$1.Error;
4073
+ } })),
4074
+ React__default.createElement("div", { className: "input-group mb-2" },
4075
+ React__default.createElement("span", { className: "input-group-text" }, "Error Code"),
4076
+ React__default.createElement("input", { type: "text", className: "form-control", "aria-label": "query for error code", value: actionErrorFilterState.errorCode, placeholder: "e.g. GC22", onChange: (e) => {
4077
+ actionErrorFilterState.errorCode = ((e.target.value)
4078
+ .trim()
4079
+ .toUpperCase());
4130
4080
  dispatch({
4131
4081
  type: ActionType$6.UpdateActionErrorFilterState,
4132
4082
  actionErrorFilterState,
4133
4083
  });
4134
- }, ariaLabel: "only show error logs", selected: actionErrorFilterState.type === LogType$1.Error, noMarginOnRight: true })),
4135
- (actionErrorFilterState.type === undefined
4136
- || actionErrorFilterState.type === LogType$1.Action) && (React__default.createElement(TabBox, { title: "Action Log Details" },
4137
- React__default.createElement(ButtonInputGroup, { label: "Action", className: "mb-2", wrapButtonsAndAddGaps: true }, Object.keys(LogAction$1)
4138
- .map((action) => {
4139
- const description = genHumanReadableName(action);
4140
- return (React__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) => {
4141
- actionErrorFilterState.action[action] = checked;
4142
- dispatch({
4143
- type: ActionType$6.UpdateActionErrorFilterState,
4144
- actionErrorFilterState,
4145
- });
4146
- } }));
4147
- })),
4148
- React__default.createElement(ButtonInputGroup, { label: "Target", wrapButtonsAndAddGaps: true }, Object.keys(targetMap)
4149
- .map((target) => {
4150
- const description = genHumanReadableName(target);
4151
- return (React__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) => {
4152
- actionErrorFilterState.target[target] = checked;
4153
- dispatch({
4154
- type: ActionType$6.UpdateActionErrorFilterState,
4155
- actionErrorFilterState,
4156
- });
4157
- } }));
4158
- })))),
4159
- (actionErrorFilterState.type === undefined
4160
- || actionErrorFilterState.type === LogType$1.Error) && (React__default.createElement(TabBox, { title: "Error Log Details" },
4161
- React__default.createElement("div", { className: "input-group mb-2" },
4162
- React__default.createElement("span", { className: "input-group-text" }, "Error Message"),
4163
- React__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) => {
4164
- actionErrorFilterState.errorMessage = e.target.value;
4165
- dispatch({
4166
- type: ActionType$6.UpdateActionErrorFilterState,
4167
- actionErrorFilterState,
4168
- });
4169
- } })),
4170
- React__default.createElement("div", { className: "input-group mb-2" },
4171
- React__default.createElement("span", { className: "input-group-text" }, "Error Code"),
4172
- React__default.createElement("input", { type: "text", className: "form-control", "aria-label": "query for error code", value: actionErrorFilterState.errorCode, placeholder: "e.g. GC22", onChange: (e) => {
4173
- actionErrorFilterState.errorCode = ((e.target.value)
4174
- .trim()
4175
- .toUpperCase());
4176
- dispatch({
4177
- type: ActionType$6.UpdateActionErrorFilterState,
4178
- actionErrorFilterState,
4179
- });
4180
- } }))))));
4181
- }
4182
- else if (expandedFilterDrawer === FilterDrawer.Advanced) {
4183
- // Create advanced filter ui
4184
- filterDrawer = (React__default.createElement(React__default.Fragment, null,
4185
- React__default.createElement(TabBox, { title: "User" },
4186
- React__default.createElement("div", { className: "input-group mb-2" },
4187
- React__default.createElement("span", { className: "input-group-text" }, "User First Name"),
4188
- React__default.createElement("input", { type: "text", className: "form-control", "aria-label": "query for user first name", value: advancedFilterState.userFirstName, placeholder: "e.g. Divardo", onChange: (e) => {
4189
- advancedFilterState.userFirstName = e.target.value;
4190
- dispatch({
4191
- type: ActionType$6.UpdateAdvancedFilterState,
4192
- advancedFilterState,
4193
- });
4194
- } })),
4195
- React__default.createElement("div", { className: "input-group mb-2" },
4196
- React__default.createElement("span", { className: "input-group-text" }, "User Last Name"),
4197
- React__default.createElement("input", { type: "text", className: "form-control", "aria-label": "query for user last name", value: advancedFilterState.userLastName, placeholder: "e.g. Calicci", onChange: (e) => {
4198
- advancedFilterState.userLastName = e.target.value;
4199
- dispatch({
4200
- type: ActionType$6.UpdateAdvancedFilterState,
4201
- advancedFilterState,
4202
- });
4203
- } })),
4204
- React__default.createElement("div", { className: "input-group mb-2" },
4205
- React__default.createElement("span", { className: "input-group-text" }, "User Email"),
4206
- React__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) => {
4207
- advancedFilterState.userEmail = ((e.target.value)
4084
+ } }))))));
4085
+ }
4086
+ else if (expandedFilterDrawer === FilterDrawer.Advanced) {
4087
+ // Create advanced filter ui
4088
+ filterDrawer = (React__default.createElement(React__default.Fragment, null,
4089
+ React__default.createElement(TabBox, { title: "User" },
4090
+ React__default.createElement("div", { className: "input-group mb-2" },
4091
+ React__default.createElement("span", { className: "input-group-text" }, "User First Name"),
4092
+ React__default.createElement("input", { type: "text", className: "form-control", "aria-label": "query for user first name", value: advancedFilterState.userFirstName, placeholder: "e.g. Divardo", onChange: (e) => {
4093
+ advancedFilterState.userFirstName = e.target.value;
4094
+ dispatch({
4095
+ type: ActionType$6.UpdateAdvancedFilterState,
4096
+ advancedFilterState,
4097
+ });
4098
+ } })),
4099
+ React__default.createElement("div", { className: "input-group mb-2" },
4100
+ React__default.createElement("span", { className: "input-group-text" }, "User Last Name"),
4101
+ React__default.createElement("input", { type: "text", className: "form-control", "aria-label": "query for user last name", value: advancedFilterState.userLastName, placeholder: "e.g. Calicci", onChange: (e) => {
4102
+ advancedFilterState.userLastName = e.target.value;
4103
+ dispatch({
4104
+ type: ActionType$6.UpdateAdvancedFilterState,
4105
+ advancedFilterState,
4106
+ });
4107
+ } })),
4108
+ React__default.createElement("div", { className: "input-group mb-2" },
4109
+ React__default.createElement("span", { className: "input-group-text" }, "User Email"),
4110
+ React__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) => {
4111
+ advancedFilterState.userEmail = ((e.target.value)
4112
+ .trim());
4113
+ dispatch({
4114
+ type: ActionType$6.UpdateAdvancedFilterState,
4115
+ advancedFilterState,
4116
+ });
4117
+ } })),
4118
+ React__default.createElement("div", { className: "input-group mb-2" },
4119
+ React__default.createElement("span", { className: "input-group-text" }, "User Canvas Id"),
4120
+ React__default.createElement("input", { type: "text", className: "form-control", "aria-label": "query for user canvas id", value: advancedFilterState.userId, placeholder: "e.g. 104985", onChange: (e) => {
4121
+ const { value } = e.target;
4122
+ // Only update if value contains only numbers
4123
+ if (/^\d+$/.test(value)) {
4124
+ advancedFilterState.userId = ((e.target.value)
4208
4125
  .trim());
4209
- dispatch({
4210
- type: ActionType$6.UpdateAdvancedFilterState,
4211
- advancedFilterState,
4212
- });
4213
- } })),
4214
- React__default.createElement("div", { className: "input-group mb-2" },
4215
- React__default.createElement("span", { className: "input-group-text" }, "User Canvas Id"),
4216
- React__default.createElement("input", { type: "text", className: "form-control", "aria-label": "query for user canvas id", value: advancedFilterState.userId, placeholder: "e.g. 104985", onChange: (e) => {
4217
- const { value } = e.target;
4218
- // Only update if value contains only numbers
4219
- if (/^\d+$/.test(value)) {
4220
- advancedFilterState.userId = ((e.target.value)
4221
- .trim());
4222
- }
4223
- dispatch({
4224
- type: ActionType$6.UpdateAdvancedFilterState,
4225
- advancedFilterState,
4226
- });
4227
- } })),
4228
- React__default.createElement(ButtonInputGroup, { label: "Role" },
4229
- React__default.createElement(CheckboxButton, { text: "Students", onChanged: (checked) => {
4230
- advancedFilterState.includeLearners = checked;
4231
- dispatch({
4232
- type: ActionType$6.UpdateAdvancedFilterState,
4233
- advancedFilterState,
4234
- });
4235
- }, checked: advancedFilterState.includeLearners, ariaLabel: "show logs from students" }),
4236
- React__default.createElement(CheckboxButton, { text: "Teaching Team Members", onChanged: (checked) => {
4237
- advancedFilterState.includeTTMs = checked;
4238
- dispatch({
4239
- type: ActionType$6.UpdateAdvancedFilterState,
4240
- advancedFilterState,
4241
- });
4242
- }, checked: advancedFilterState.includeTTMs, ariaLabel: "show logs from teaching team members" }),
4243
- React__default.createElement(CheckboxButton, { text: "Admins", onChanged: (checked) => {
4244
- advancedFilterState.includeAdmins = checked;
4245
- dispatch({
4246
- type: ActionType$6.UpdateAdvancedFilterState,
4247
- advancedFilterState,
4248
- });
4249
- }, checked: advancedFilterState.includeAdmins, ariaLabel: "show logs from admins" }))),
4250
- React__default.createElement(TabBox, { title: "Course" },
4126
+ }
4127
+ dispatch({
4128
+ type: ActionType$6.UpdateAdvancedFilterState,
4129
+ advancedFilterState,
4130
+ });
4131
+ } })),
4132
+ React__default.createElement(ButtonInputGroup, { label: "Role" },
4133
+ React__default.createElement(CheckboxButton, { text: "Students", onChanged: (checked) => {
4134
+ advancedFilterState.includeLearners = checked;
4135
+ dispatch({
4136
+ type: ActionType$6.UpdateAdvancedFilterState,
4137
+ advancedFilterState,
4138
+ });
4139
+ }, checked: advancedFilterState.includeLearners, ariaLabel: "show logs from students" }),
4140
+ React__default.createElement(CheckboxButton, { text: "Teaching Team Members", onChanged: (checked) => {
4141
+ advancedFilterState.includeTTMs = checked;
4142
+ dispatch({
4143
+ type: ActionType$6.UpdateAdvancedFilterState,
4144
+ advancedFilterState,
4145
+ });
4146
+ }, checked: advancedFilterState.includeTTMs, ariaLabel: "show logs from teaching team members" }),
4147
+ React__default.createElement(CheckboxButton, { text: "Admins", onChanged: (checked) => {
4148
+ advancedFilterState.includeAdmins = checked;
4149
+ dispatch({
4150
+ type: ActionType$6.UpdateAdvancedFilterState,
4151
+ advancedFilterState,
4152
+ });
4153
+ }, checked: advancedFilterState.includeAdmins, ariaLabel: "show logs from admins" }))),
4154
+ React__default.createElement(TabBox, { title: "Course" },
4155
+ React__default.createElement("div", { className: "input-group mb-2" },
4156
+ React__default.createElement("span", { className: "input-group-text" }, "Course Name"),
4157
+ React__default.createElement("input", { type: "text", className: "form-control", "aria-label": "query for course name", value: advancedFilterState.courseName, placeholder: "e.g. GLC 200", onChange: (e) => {
4158
+ advancedFilterState.courseName = e.target.value;
4159
+ dispatch({
4160
+ type: ActionType$6.UpdateAdvancedFilterState,
4161
+ advancedFilterState,
4162
+ });
4163
+ } })),
4164
+ React__default.createElement("div", { className: "input-group mb-2" },
4165
+ React__default.createElement("span", { className: "input-group-text" }, "Course Canvas Id"),
4166
+ React__default.createElement("input", { type: "text", className: "form-control", "aria-label": "query for course canvas id", value: advancedFilterState.courseId, placeholder: "e.g. 15948", onChange: (e) => {
4167
+ const { value } = e.target;
4168
+ // Only update if value contains only numbers
4169
+ if (/^\d+$/.test(value)) {
4170
+ advancedFilterState.courseId = ((e.target.value)
4171
+ .trim());
4172
+ }
4173
+ dispatch({
4174
+ type: ActionType$6.UpdateAdvancedFilterState,
4175
+ advancedFilterState,
4176
+ });
4177
+ } }))),
4178
+ React__default.createElement(TabBox, { title: "Device" },
4179
+ React__default.createElement(ButtonInputGroup, { label: "Device Type" },
4180
+ React__default.createElement(RadioButton, { text: "All Devices", ariaLabel: "show logs from all devices", selected: advancedFilterState.isMobile === undefined, onSelected: () => {
4181
+ advancedFilterState.isMobile = undefined;
4182
+ dispatch({
4183
+ type: ActionType$6.UpdateAdvancedFilterState,
4184
+ advancedFilterState,
4185
+ });
4186
+ } }),
4187
+ React__default.createElement(RadioButton, { text: "Mobile Only", ariaLabel: "show logs from mobile devices", selected: advancedFilterState.isMobile === true, onSelected: () => {
4188
+ advancedFilterState.isMobile = true;
4189
+ dispatch({
4190
+ type: ActionType$6.UpdateAdvancedFilterState,
4191
+ advancedFilterState,
4192
+ });
4193
+ } }),
4194
+ React__default.createElement(RadioButton, { text: "Desktop Only", ariaLabel: "show logs from desktop devices", selected: advancedFilterState.isMobile === false, onSelected: () => {
4195
+ advancedFilterState.isMobile = false;
4196
+ dispatch({
4197
+ type: ActionType$6.UpdateAdvancedFilterState,
4198
+ advancedFilterState,
4199
+ });
4200
+ }, noMarginOnRight: true }))),
4201
+ React__default.createElement(TabBox, { title: "Source" },
4202
+ React__default.createElement(ButtonInputGroup, { label: "Source Type" },
4203
+ React__default.createElement(RadioButton, { text: "Both", ariaLabel: "show logs from all sources", selected: advancedFilterState.source === undefined, onSelected: () => {
4204
+ advancedFilterState.source = undefined;
4205
+ dispatch({
4206
+ type: ActionType$6.UpdateAdvancedFilterState,
4207
+ advancedFilterState,
4208
+ });
4209
+ } }),
4210
+ React__default.createElement(RadioButton, { text: "Client Only", ariaLabel: "show logs from client source", selected: advancedFilterState.source === LogSource$1.Client, onSelected: () => {
4211
+ advancedFilterState.source = LogSource$1.Client;
4212
+ dispatch({
4213
+ type: ActionType$6.UpdateAdvancedFilterState,
4214
+ advancedFilterState,
4215
+ });
4216
+ } }),
4217
+ React__default.createElement(RadioButton, { text: "Server Only", ariaLabel: "show logs from server source", selected: advancedFilterState.source === LogSource$1.Server, onSelected: () => {
4218
+ advancedFilterState.source = LogSource$1.Server;
4219
+ dispatch({
4220
+ type: ActionType$6.UpdateAdvancedFilterState,
4221
+ advancedFilterState,
4222
+ });
4223
+ }, noMarginOnRight: true })),
4224
+ advancedFilterState.source !== LogSource$1.Client && (React__default.createElement("div", { className: "mt-2" },
4251
4225
  React__default.createElement("div", { className: "input-group mb-2" },
4252
- React__default.createElement("span", { className: "input-group-text" }, "Course Name"),
4253
- React__default.createElement("input", { type: "text", className: "form-control", "aria-label": "query for course name", value: advancedFilterState.courseName, placeholder: "e.g. GLC 200", onChange: (e) => {
4254
- advancedFilterState.courseName = e.target.value;
4226
+ React__default.createElement("span", { className: "input-group-text" }, "Server Route Path"),
4227
+ React__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) => {
4228
+ advancedFilterState.courseName = ((e.target.value)
4229
+ .trim());
4255
4230
  dispatch({
4256
4231
  type: ActionType$6.UpdateAdvancedFilterState,
4257
4232
  advancedFilterState,
4258
4233
  });
4259
4234
  } })),
4260
4235
  React__default.createElement("div", { className: "input-group mb-2" },
4261
- React__default.createElement("span", { className: "input-group-text" }, "Course Canvas Id"),
4262
- React__default.createElement("input", { type: "text", className: "form-control", "aria-label": "query for course canvas id", value: advancedFilterState.courseId, placeholder: "e.g. 15948", onChange: (e) => {
4263
- const { value } = e.target;
4264
- // Only update if value contains only numbers
4265
- if (/^\d+$/.test(value)) {
4266
- advancedFilterState.courseId = ((e.target.value)
4267
- .trim());
4268
- }
4269
- dispatch({
4270
- type: ActionType$6.UpdateAdvancedFilterState,
4271
- advancedFilterState,
4272
- });
4273
- } }))),
4274
- React__default.createElement(TabBox, { title: "Device" },
4275
- React__default.createElement(ButtonInputGroup, { label: "Device Type" },
4276
- React__default.createElement(RadioButton, { text: "All Devices", ariaLabel: "show logs from all devices", selected: advancedFilterState.isMobile === undefined, onSelected: () => {
4277
- advancedFilterState.isMobile = undefined;
4278
- dispatch({
4279
- type: ActionType$6.UpdateAdvancedFilterState,
4280
- advancedFilterState,
4281
- });
4282
- } }),
4283
- React__default.createElement(RadioButton, { text: "Mobile Only", ariaLabel: "show logs from mobile devices", selected: advancedFilterState.isMobile === true, onSelected: () => {
4284
- advancedFilterState.isMobile = true;
4285
- dispatch({
4286
- type: ActionType$6.UpdateAdvancedFilterState,
4287
- advancedFilterState,
4288
- });
4289
- } }),
4290
- React__default.createElement(RadioButton, { text: "Desktop Only", ariaLabel: "show logs from desktop devices", selected: advancedFilterState.isMobile === false, onSelected: () => {
4291
- advancedFilterState.isMobile = false;
4292
- dispatch({
4293
- type: ActionType$6.UpdateAdvancedFilterState,
4294
- advancedFilterState,
4295
- });
4296
- }, noMarginOnRight: true }))),
4297
- React__default.createElement(TabBox, { title: "Source" },
4298
- React__default.createElement(ButtonInputGroup, { label: "Source Type" },
4299
- React__default.createElement(RadioButton, { text: "Both", ariaLabel: "show logs from all sources", selected: advancedFilterState.source === undefined, onSelected: () => {
4300
- advancedFilterState.source = undefined;
4301
- dispatch({
4302
- type: ActionType$6.UpdateAdvancedFilterState,
4303
- advancedFilterState,
4304
- });
4305
- } }),
4306
- React__default.createElement(RadioButton, { text: "Client Only", ariaLabel: "show logs from client source", selected: advancedFilterState.source === LogSource$1.Client, onSelected: () => {
4307
- advancedFilterState.source = LogSource$1.Client;
4308
- dispatch({
4309
- type: ActionType$6.UpdateAdvancedFilterState,
4310
- advancedFilterState,
4311
- });
4312
- } }),
4313
- React__default.createElement(RadioButton, { text: "Server Only", ariaLabel: "show logs from server source", selected: advancedFilterState.source === LogSource$1.Server, onSelected: () => {
4314
- advancedFilterState.source = LogSource$1.Server;
4236
+ React__default.createElement("span", { className: "input-group-text" }, "Server Route Template"),
4237
+ React__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) => {
4238
+ advancedFilterState.courseName = ((e.target.value)
4239
+ .trim());
4315
4240
  dispatch({
4316
4241
  type: ActionType$6.UpdateAdvancedFilterState,
4317
4242
  advancedFilterState,
4318
4243
  });
4319
- }, noMarginOnRight: true })),
4320
- advancedFilterState.source !== LogSource$1.Client && (React__default.createElement("div", { className: "mt-2" },
4321
- React__default.createElement("div", { className: "input-group mb-2" },
4322
- React__default.createElement("span", { className: "input-group-text" }, "Server Route Path"),
4323
- React__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) => {
4324
- advancedFilterState.courseName = ((e.target.value)
4325
- .trim());
4326
- dispatch({
4327
- type: ActionType$6.UpdateAdvancedFilterState,
4328
- advancedFilterState,
4329
- });
4330
- } })),
4331
- React__default.createElement("div", { className: "input-group mb-2" },
4332
- React__default.createElement("span", { className: "input-group-text" }, "Server Route Template"),
4333
- React__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) => {
4334
- advancedFilterState.courseName = ((e.target.value)
4335
- .trim());
4336
- dispatch({
4337
- type: ActionType$6.UpdateAdvancedFilterState,
4338
- advancedFilterState,
4339
- });
4340
- } })))))));
4341
- }
4342
- }
4343
- // Filters UI
4344
- const filters = (React__default.createElement(React__default.Fragment, null,
4345
- filterToggles,
4346
- filterDrawer && (React__default.createElement(Drawer, { customBackgroundColor: "#eee" }, filterDrawer))));
4347
- // Actually filter the logs
4348
- // > Perform filters
4349
- const logs = [];
4350
- Object.keys(logMap).forEach((year) => {
4351
- Object.keys(logMap[year]).forEach((month) => {
4352
- logMap[year][month].forEach((log) => {
4353
- /* ----------- Date Filter ---------- */
4354
- var _a;
4355
- // Before start date
4356
- if (
4357
- // Previous year
4358
- log.year < dateFilterState.startDate.year
4359
- // Same year, earlier month
4360
- || ((log.year === dateFilterState.startDate.year)
4361
- && (log.month < dateFilterState.startDate.month))
4362
- // Same year, same month, earlier day
4363
- || ((log.year === dateFilterState.startDate.year)
4364
- && (log.month === dateFilterState.startDate.month)
4365
- && (log.day < dateFilterState.startDate.day))) {
4366
- return;
4367
- }
4368
- // After end date
4369
- if (
4370
- // Later year
4371
- log.year > dateFilterState.endDate.year
4372
- // Same year, later month
4373
- || ((log.year === dateFilterState.endDate.year)
4374
- && (log.month > dateFilterState.endDate.month))
4375
- // Same year, same month, later day
4376
- || ((log.year === dateFilterState.endDate.year)
4377
- && (log.month === dateFilterState.endDate.month)
4378
- && (log.day > dateFilterState.endDate.day))) {
4379
- return;
4380
- }
4381
- /* --------- Context Filter --------- */
4382
- // Context doesn't match
4383
- if (
4384
- // Whole context is deselected
4385
- contextFilterState[log.context] === false
4386
- // None of the subcontexts are selected
4387
- || (
4388
- // Has subcontexts
4389
- typeof contextFilterState[log.context] !== 'boolean'
4390
- // None of the subcontexts are selected
4391
- && Object.values((_a = contextFilterState[log.context]) !== null && _a !== void 0 ? _a : {})
4392
- .every((isSelected) => {
4393
- return !isSelected;
4394
- }))) {
4395
- return;
4396
- }
4397
- // Subcontext doesn't match
4398
- if (
4399
- // Log context is not "uncategorized" (no point in further filters)
4400
- log.context !== LogBuiltInMetadata.Context.Uncategorized
4401
- // Log has a subcontext
4402
- && log.subcontext
4403
- // Context has subcontexts
4404
- && (contextFilterState[log.context]
4405
- && contextFilterState[log.context] !== false
4406
- && contextFilterState[log.context] !== true)
4407
- // Subcontext is not selected
4408
- && !contextFilterState[log.context][log.subcontext]) {
4409
- return;
4410
- }
4411
- /* -------------- Tags -------------- */
4412
- // No tags match
4413
- if (
4414
- // At least one tag is required
4415
- Object.values(tagFilterState)
4416
- .filter((isSelected) => {
4417
- return isSelected;
4418
- })
4419
- .length > 0
4420
- // No tags match
4421
- && log.tags.every((tag) => {
4422
- return !tagFilterState[tag];
4423
- })) {
4424
- return;
4425
- }
4426
- /* ------- Actions and Errors ------- */
4427
- // Log type doesn't match
4428
- if (
4429
- // Filter won't allow all types
4430
- actionErrorFilterState.type !== undefined
4431
- // Log type doesn't match
4432
- && actionErrorFilterState.type !== log.type) {
4433
- return;
4434
- }
4435
- // Filter errors
4436
- if (log.type === LogType$1.Error) {
4437
- // Message doesn't match
4438
- if (
4439
- // Message exists
4440
- log.errorMessage
4441
- // Message filter exists
4442
- && actionErrorFilterState.errorMessage.trim().length > 0
4443
- // Message doesn't match
4444
- && log.errorMessage.toLowerCase().includes(actionErrorFilterState.errorMessage.trim().toLowerCase())) {
4445
- return;
4446
- }
4447
- // Code doesn't match
4448
- if (
4449
- // Code exists
4450
- log.errorCode
4451
- // Code filter exists
4452
- && actionErrorFilterState.errorCode.trim().length > 0
4453
- // Code doesn't match
4454
- && log.errorCode.toUpperCase().includes(actionErrorFilterState.errorCode.trim().toUpperCase())) {
4455
- return;
4456
- }
4457
- }
4458
- // Filter actions
4459
- if (log.type === LogType$1.Action) {
4460
- // Target isn't selected
4461
- if (
4462
- // Target exists
4463
- log.target
4464
- // Target isn't selected
4465
- && !actionErrorFilterState.target[log.target]) {
4466
- return;
4467
- }
4468
- // Action
4469
- if (
4470
- // Action exists
4471
- log.action
4472
- // Action isn't selected
4473
- && !actionErrorFilterState.action[log.action]) {
4474
- return;
4475
- }
4476
- }
4477
- /* --------- Advanced Filter -------- */
4478
- // First name doesn't match
4479
- if (
4480
- // First name exists
4481
- log.userFirstName
4482
- // First name query doesn't match
4483
- && !log.userFirstName.toLowerCase().includes(advancedFilterState.userFirstName.toLowerCase().trim())) {
4484
- return;
4485
- }
4486
- // Last name doesn't match
4487
- if (
4488
- // Last name exists
4489
- log.userLastName
4490
- // Last name query doesn't match
4491
- && !log.userLastName.toLowerCase().includes(advancedFilterState.userLastName.toLowerCase().trim())) {
4492
- return;
4493
- }
4494
- // Email doesn't match
4495
- if (
4496
- // Email exists
4497
- log.userEmail
4498
- // Email query doesn't match
4499
- && !log.userEmail.toLowerCase().includes(advancedFilterState.userEmail.toLowerCase().trim())) {
4500
- return;
4501
- }
4502
- // User id doesn't match
4503
- if (
4504
- // User id exists
4505
- log.userId
4506
- // User id doesn't match
4507
- && !String(log.userId).includes(advancedFilterState.userId.trim())) {
4508
- return;
4509
- }
4510
- // Learner not allowed
4511
- if (
4512
- // User is a learner
4513
- log.isLearner
4514
- // Learners aren't included
4515
- && !advancedFilterState.includeLearners) {
4516
- return;
4517
- }
4518
- // TTM not allowed
4519
- if (
4520
- // User is a ttm
4521
- log.isTTM
4522
- // TTMs aren't included
4523
- && !advancedFilterState.includeTTMs) {
4524
- return;
4525
- }
4526
- // Admin not allowed
4527
- if (
4528
- // User is an admin
4529
- log.isAdmin
4530
- // Admins aren't included
4531
- && !advancedFilterState.includeAdmins) {
4532
- return;
4533
- }
4534
- // Course Id doesn't match
4535
- if (
4536
- // Course Id exists
4537
- log.courseId
4538
- // Course Id doesn't match
4539
- && !String(log.courseId).includes(advancedFilterState.courseId.trim())) {
4540
- return;
4541
- }
4542
- // Course name doesn't match
4543
- if (
4544
- // Course name exists
4545
- log.courseName
4546
- // Course name doesn't match
4547
- && !String(log.courseName).includes(advancedFilterState.courseName.trim())) {
4548
- return;
4549
- }
4550
- // Mobile filter doesn't match
4551
- if (
4552
- // Mobile filter exists
4553
- advancedFilterState.isMobile !== undefined
4554
- // Device info exists
4555
- && log.device
4556
- // Mobile filter doesn't match
4557
- && (advancedFilterState.isMobile !== log.device.isMobile)) {
4558
- return;
4559
- }
4560
- // Log source doesn't match
4561
- if (
4562
- // Source filter exists
4563
- advancedFilterState.source !== undefined
4564
- // Source info exists
4565
- && log.source
4566
- // Source filter doesn't match
4567
- && (advancedFilterState.source !== log.source)) {
4568
- return;
4569
- }
4570
- // Route path doesn't match (Only for server source)
4571
- if (
4572
- // Source is server
4573
- (log.source === LogSource$1.Server)
4574
- // Route path is being filtered
4575
- && (advancedFilterState.routePath.trim().length)
4576
- // Route path doesn't match
4577
- && !(log.routePath.includes(advancedFilterState.routePath.trim()))) {
4578
- return;
4579
- }
4580
- // Route template doesn't match (Only for server source)
4581
- if (
4582
- // Source is server
4583
- (log.source === LogSource$1.Server)
4584
- // Route template is being filtered
4585
- && (advancedFilterState.routeTemplate.trim().length)
4586
- // Route template doesn't match
4587
- && !(log.routeTemplate.includes(advancedFilterState.routeTemplate.trim()))) {
4588
- return;
4589
- }
4590
- /* -------------- Done -------------- */
4591
- // Made it past all filters. Add to the list
4592
- logs.push(log);
4593
- });
4594
- });
4595
- });
4596
- /*----------------------------------------*/
4597
- /* ---------------- Data ---------------- */
4598
- /*----------------------------------------*/
4599
- // Nothing to show notice
4600
- const noLogsNotice = (logs.length === 0
4601
- ? (React__default.createElement("div", { className: "alert alert-warning text-center mt-2" },
4244
+ } })))))));
4245
+ }
4246
+ }
4247
+ // Filters UI
4248
+ const filters = (React__default.createElement(React__default.Fragment, null,
4249
+ filterToggles,
4250
+ filterDrawer && (React__default.createElement(Drawer, { customBackgroundColor: "#eee" }, filterDrawer))));
4251
+ // Main body
4252
+ body = (React__default.createElement(React__default.Fragment, null,
4253
+ filters,
4254
+ React__default.createElement("div", { className: "mt-2" },
4255
+ React__default.createElement(IntelliTable, { title: "Matching Logs:", csvName: `Logs from ${getHumanReadableDate()}`, id: "logs", data: logs, columns: columns }),
4256
+ logs.length === 0 && (React__default.createElement("div", { className: "alert alert-warning text-center mt-2" },
4602
4257
  React__default.createElement("h4", { className: "m-1" }, "No Logs to Show"),
4603
- React__default.createElement("div", null, "Either your filters are too strict or no matching logs have been created yet.")))
4604
- : undefined);
4605
- // Create intelliTable
4606
- const dataTable = (React__default.createElement(IntelliTable, { title: "Matching Logs:", csvName: `Logs from ${getHumanReadableDate()}`, id: "logs", data: logs, columns: columns }));
4607
- // Main body
4608
- body = (React__default.createElement(React__default.Fragment, null,
4609
- filters,
4610
- React__default.createElement("div", { className: "mt-2" },
4611
- dataTable,
4612
- noLogsNotice)));
4613
- }
4258
+ React__default.createElement("div", null, "Either your filters are too strict or no matching logs have been created yet."))),
4259
+ paginationControls)));
4614
4260
  /* ---------- Wrap in Modal --------- */
4615
4261
  return (React__default.createElement("div", { className: "LogReviewer-outer-container" },
4616
4262
  React__default.createElement("style", null, style$2),
@@ -14122,32 +13768,188 @@ const initServer = (opts) => {
14122
13768
  }),
14123
13769
  }));
14124
13770
  /**
14125
- * Get all logs for a certain month
14126
- * @author Gabe Abrams
14127
- * @param {number} year the year to query (e.g. 2022)
14128
- * @param {number} month the month to query (e.g. 1 = January)
14129
- * @returns {Log[]} list of logs from the given month
14130
- */
14131
- opts.app.get(`${LOG_REVIEW_ROUTE_PATH_PREFIX}/years/:year/months/:month`, genRouteHandler({
13771
+ * Get filtered logs based on provided filters
13772
+ * @author Gabe Abrams, Yuen Ler Chow
13773
+ * @param pageNumber the page number to get
13774
+ * @param filters the filters to apply to the logs
13775
+ * @returns {Log[]} list of logs that match the filters
13776
+ */
13777
+ opts.app.get(`${LOG_REVIEW_GET_LOGS_ROUTE}`, genRouteHandler({
14132
13778
  paramTypes: {
14133
- year: ParamType$1.Int,
14134
- month: ParamType$1.Int,
13779
+ pageNumber: ParamType$1.Int,
13780
+ filters: ParamType$1.JSON,
14135
13781
  },
14136
13782
  handler: ({ params }) => __awaiter(void 0, void 0, void 0, function* () {
14137
13783
  // Get user info
14138
- const { year, month, userId, isAdmin, } = params;
13784
+ const { pageNumber, userId, isAdmin, filters, } = params;
13785
+ const { dateFilterState, contextFilterState, tagFilterState, actionErrorFilterState, advancedFilterState, } = filters;
14139
13786
  // Validate user
14140
13787
  const canReview = yield canReviewLogs(userId, isAdmin);
14141
13788
  if (!canReview) {
14142
13789
  throw new ErrorWithCode('You cannot access this resource because you do not have the appropriate permissions.', ReactKitErrorCode$1.NotAllowedToReviewLogs);
14143
13790
  }
13791
+ // Build MongoDB query based on filters
13792
+ const query = {};
13793
+ /* -------------- Date Filter ------------- */
13794
+ // Convert start and end dates from the dateFilterState into timestamps
13795
+ const { startDate, endDate } = dateFilterState;
13796
+ const startTimestamp = new Date(startDate.year, startDate.month - 1, startDate.day).getTime();
13797
+ const endTimestamp = new Date(endDate.year, endDate.month - 1, endDate.day + 1).getTime() - 1;
13798
+ // Add a date range condition to the query
13799
+ query.timestamp = {
13800
+ $gte: startTimestamp,
13801
+ $lte: endTimestamp,
13802
+ };
13803
+ /* ------------ Context Filter ------------ */
13804
+ // Process context filters to include selected contexts and subcontexts
13805
+ const selectedContexts = [];
13806
+ const selectedSubcontexts = [];
13807
+ // Process each context filter
13808
+ Object.keys(contextFilterState).forEach((context) => {
13809
+ const value = contextFilterState[context];
13810
+ if (typeof value === 'boolean') {
13811
+ if (value) {
13812
+ selectedContexts.push(context);
13813
+ }
13814
+ }
13815
+ else {
13816
+ // At least one subcontext is selected
13817
+ const atLeastOneSubcontextSelected = (Object.values(value)
13818
+ .some((subcontextValue) => {
13819
+ return subcontextValue;
13820
+ }));
13821
+ if (atLeastOneSubcontextSelected) {
13822
+ selectedContexts.push(context);
13823
+ }
13824
+ // Add all selected subcontexts
13825
+ Object.keys(value).forEach((subcontext) => {
13826
+ if (value[subcontext]) {
13827
+ selectedSubcontexts.push(subcontext);
13828
+ }
13829
+ });
13830
+ }
13831
+ });
13832
+ // Add context and subcontext conditions to the query if any are selected
13833
+ if (selectedContexts.length > 0) {
13834
+ query.context = { $in: selectedContexts };
13835
+ }
13836
+ if (selectedSubcontexts.length > 0) {
13837
+ query.subcontext = { $in: selectedSubcontexts };
13838
+ }
13839
+ /* -------------- Tag Filter -------------- */
13840
+ const selectedTags = Object.keys(tagFilterState).filter((tag) => { return tagFilterState[tag]; });
13841
+ if (selectedTags.length > 0) {
13842
+ query.tags = { $in: selectedTags };
13843
+ }
13844
+ /* --------- Action/Error Filter ---------- */
13845
+ if (actionErrorFilterState.type) {
13846
+ query.type = actionErrorFilterState.type;
13847
+ }
13848
+ if (actionErrorFilterState.type === LogType$1.Error) {
13849
+ if (actionErrorFilterState.errorMessage) {
13850
+ // Add error message to the query.
13851
+ // $i is used for case-insensitive search, and $regex is used for partial matching
13852
+ query.errorMessage = {
13853
+ $regex: actionErrorFilterState.errorMessage,
13854
+ $options: 'i',
13855
+ };
13856
+ }
13857
+ if (actionErrorFilterState.errorCode) {
13858
+ query.errorCode = {
13859
+ $regex: actionErrorFilterState.errorCode,
13860
+ $options: 'i',
13861
+ };
13862
+ }
13863
+ }
13864
+ if (actionErrorFilterState.type === LogType$1.Action) {
13865
+ const selectedTargets = (Object.keys(actionErrorFilterState.target)
13866
+ .filter((target) => {
13867
+ return actionErrorFilterState.target[target];
13868
+ }));
13869
+ const selectedActions = (Object.keys(actionErrorFilterState.action)
13870
+ .filter((action) => {
13871
+ return actionErrorFilterState.action[action];
13872
+ }));
13873
+ if (selectedTargets.length > 0) {
13874
+ query.target = { $in: selectedTargets };
13875
+ }
13876
+ if (selectedActions.length > 0) {
13877
+ query.action = { $in: selectedActions };
13878
+ }
13879
+ }
13880
+ /* ------------ Advanced Filter ----------- */
13881
+ if (advancedFilterState.userFirstName) {
13882
+ query.userFirstName = {
13883
+ $regex: advancedFilterState.userFirstName,
13884
+ $options: 'i',
13885
+ };
13886
+ }
13887
+ if (advancedFilterState.userLastName) {
13888
+ query.userLastName = {
13889
+ $regex: advancedFilterState.userLastName,
13890
+ $options: 'i',
13891
+ };
13892
+ }
13893
+ if (advancedFilterState.userEmail) {
13894
+ query.userEmail = {
13895
+ $regex: advancedFilterState.userEmail,
13896
+ $options: 'i',
13897
+ };
13898
+ }
13899
+ if (advancedFilterState.userId) {
13900
+ query.userId = Number.parseInt(advancedFilterState.userId, 10);
13901
+ }
13902
+ const roles = [];
13903
+ if (advancedFilterState.includeLearners) {
13904
+ roles.push({ isLearner: true });
13905
+ }
13906
+ if (advancedFilterState.includeTTMs) {
13907
+ roles.push({ isTTM: true });
13908
+ }
13909
+ if (advancedFilterState.includeAdmins) {
13910
+ roles.push({ isAdmin: true });
13911
+ }
13912
+ // If any roles are selected, add them to the query
13913
+ if (roles.length > 0) {
13914
+ // The $or operator is used to match any of the roles
13915
+ // The $and operator is to ensure that other conditions in the query are met
13916
+ query.$and = [{ $or: roles }];
13917
+ }
13918
+ if (advancedFilterState.courseId) {
13919
+ query.courseId = Number.parseInt(advancedFilterState.courseId, 10);
13920
+ }
13921
+ if (advancedFilterState.courseName) {
13922
+ query.courseName = {
13923
+ $regex: advancedFilterState.courseName,
13924
+ $options: 'i',
13925
+ };
13926
+ }
13927
+ if (advancedFilterState.isMobile !== undefined) {
13928
+ query['device.isMobile'] = Boolean(advancedFilterState.isMobile);
13929
+ }
13930
+ if (advancedFilterState.source) {
13931
+ query.source = advancedFilterState.source;
13932
+ }
13933
+ if (advancedFilterState.routePath) {
13934
+ query.routePath = {
13935
+ $regex: advancedFilterState.routePath,
13936
+ $options: 'i',
13937
+ };
13938
+ }
13939
+ if (advancedFilterState.routeTemplate) {
13940
+ query.routeTemplate = {
13941
+ $regex: advancedFilterState.routeTemplate,
13942
+ $options: 'i',
13943
+ };
13944
+ }
14144
13945
  // Query for logs
14145
- const logs = yield _logCollection.find({
14146
- year,
14147
- month,
13946
+ const response = yield _logCollection.findPaged({
13947
+ query,
13948
+ perPage: 50,
13949
+ pageNumber,
14148
13950
  });
14149
- // Return logs
14150
- return logs;
13951
+ // Return response
13952
+ return response;
14151
13953
  }),
14152
13954
  }));
14153
13955
  };
@@ -16315,5 +16117,5 @@ var DayOfWeek;
16315
16117
  })(DayOfWeek || (DayOfWeek = {}));
16316
16118
  var DayOfWeek$1 = DayOfWeek;
16317
16119
 
16318
- export { AppWrapper, AutoscrollToBottomContainer, ButtonInputGroup, CSVDownloadButton, CheckboxButton, CopiableBox, DAY_IN_MS, DBEntryFieldType$1 as DBEntryFieldType, DBEntryManagerPanel, DayOfWeek$1 as DayOfWeek, Drawer, Dropdown, DropdownItemType$1 as DropdownItemType, DynamicWord, ErrorBox, ErrorWithCode, HOUR_IN_MS, IntelliTable, ItemPicker, LoadingSpinner, LogAction$1 as LogAction, LogBuiltInMetadata, LogReviewer, LogSource$1 as LogSource, LogType$1 as LogType, MINUTE_IN_MS, Modal, ModalButtonType$1 as ModalButtonType, ModalSize$1 as ModalSize, ModalType$1 as ModalType, MultiSwitch, ParamType$1 as ParamType, PopFailureMark, PopPendingMark, PopSuccessMark, RadioButton, ReactKitErrorCode$1 as ReactKitErrorCode, SimpleDateChooser, TabBox, ToggleSwitch, Tooltip, Variant$1 as Variant, abbreviate, addDBEditorEndpoints, addFatalErrorHandler, alert, avg, canReviewLogs, capitalize, ceilToNumDecimals, combineClassNames, compareArraysByProp, confirm, everyAsync, extractProp, filterAsync, floorToNumDecimals, forEachAsync, forceNumIntoBounds, genCSV, genCommaList, genRouteHandler, getHumanReadableDate, getLocalTimeInfo, getMonthName, getOrdinal, getPartOfDay, getTimeInfoInET, handleError, handleSuccess, idify, initClient, initLogCollection, initServer, isMobileOrTablet, leaveToURL, logClientEvent, makeLinksClickable, mapAsync, onlyKeepLetters, padDecimalZeros, padZerosLeft, parallelLimit, prefixWithAOrAn, prompt, roundToNumDecimals, setClientEventMetadataPopulator, showFatalError, shuffleArray, someAsync, startMinWait, stringsToHumanReadableList, stubServerEndpoint, sum, useForceRender, validateEmail, validatePhoneNumber, validateString, visitEndpointOnAnotherServer, visitServerEndpoint, waitMs };
16120
+ export { AppWrapper, AutoscrollToBottomContainer, ButtonInputGroup, CSVDownloadButton, CheckboxButton, CopiableBox, DAY_IN_MS, DBEntryFieldType$1 as DBEntryFieldType, DBEntryManagerPanel, DayOfWeek$1 as DayOfWeek, Drawer, Dropdown, DropdownItemType$1 as DropdownItemType, DynamicWord, ErrorBox, ErrorWithCode, HOUR_IN_MS, IntelliTable, ItemPicker, LoadingSpinner, LogAction$1 as LogAction, LogBuiltInMetadata, LogReviewer, LogSource$1 as LogSource, LogType$1 as LogType, MINUTE_IN_MS, Modal, ModalButtonType$1 as ModalButtonType, ModalSize$1 as ModalSize, ModalType$1 as ModalType, MultiSwitch, ParamType$1 as ParamType, PopFailureMark, PopPendingMark, PopSuccessMark, RadioButton, ReactKitErrorCode$1 as ReactKitErrorCode, SimpleDateChooser, TabBox, ToggleSwitch, Tooltip, Variant$1 as Variant, abbreviate, addDBEditorEndpoints, addFatalErrorHandler, alert, avg, canReviewLogs, capitalize, ceilToNumDecimals, combineClassNames, compareArraysByProp, confirm, everyAsync, extractProp, filterAsync, floorToNumDecimals, forEachAsync, forceNumIntoBounds, genCSV, genCommaList, genRouteHandler, getHumanReadableDate, getLocalTimeInfo, getMonthName, getOrdinal, getPartOfDay, getTimeInfoInET, handleError, handleSuccess, idify, initClient, initLogCollection, initServer, isMobileOrTablet, leaveToURL, logClientEvent, makeLinksClickable, mapAsync, onlyKeepLetters, padDecimalZeros, padZerosLeft, parallelLimit, prefixWithAOrAn, roundToNumDecimals, setClientEventMetadataPopulator, showFatalError, shuffleArray, someAsync, startMinWait, stringsToHumanReadableList, stubServerEndpoint, sum, useForceRender, validateEmail, validatePhoneNumber, validateString, visitEndpointOnAnotherServer, visitServerEndpoint, waitMs };
16319
16121
  //# sourceMappingURL=index.js.map