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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cjs/index.js 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 = [];
@@ -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,12 @@ 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
+ }
3705
3611
  default: {
3706
3612
  return state;
3707
3613
  }
@@ -3819,115 +3725,78 @@ const LogReviewer = (props) => {
3819
3725
  // Initial state
3820
3726
  const initialState = {
3821
3727
  loading: true,
3822
- logMap: {},
3728
+ logs: [],
3823
3729
  expandedFilterDrawer: undefined,
3824
3730
  dateFilterState: initDateFilterState,
3825
3731
  contextFilterState: initContextFilterState,
3826
3732
  tagFilterState: initTagFilterState,
3827
3733
  actionErrorFilterState: initActionErrorFilterState,
3828
3734
  advancedFilterState: initAdvancedFilterState,
3735
+ pageNumber: 1,
3736
+ hasAnotherPage: false,
3829
3737
  };
3830
3738
  // Initialize state
3831
3739
  const [state, dispatch] = React.useReducer(reducer$7, initialState);
3832
3740
  // Destructure common state
3833
- const { loading, logMap, expandedFilterDrawer, dateFilterState, contextFilterState, tagFilterState, actionErrorFilterState, advancedFilterState, } = state;
3741
+ const { loading, logs, expandedFilterDrawer, dateFilterState, contextFilterState, tagFilterState, actionErrorFilterState, advancedFilterState, pageNumber, hasAnotherPage, } = state;
3834
3742
  /*------------------------------------------------------------------------*/
3835
3743
  /* ------------------------- Component Functions ------------------------ */
3836
3744
  /*------------------------------------------------------------------------*/
3837
3745
  /**
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
3746
+ * Fetch logs from the server based on current filters
3843
3747
  */
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
3877
- */
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
3748
+ const fetchLogs = () => __awaiter(void 0, void 0, void 0, function* () {
3749
+ dispatch({ type: ActionType$6.StartLoading });
3895
3750
  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
- }
3751
+ // Prepare filter parameters
3752
+ const filters = {
3753
+ startDate: dateFilterState.startDate,
3754
+ endDate: dateFilterState.endDate,
3755
+ contextFilterState,
3756
+ tagFilterState,
3757
+ actionErrorFilterState,
3758
+ advancedFilterState,
3759
+ };
3760
+ // Send filters to the server
3761
+ let fetchedLogs = [];
3762
+ const response = yield visitServerEndpoint({
3763
+ path: `${LOG_REVIEW_ROUTE_PATH_PREFIX}/logs`,
3764
+ method: 'GET',
3765
+ params: {
3766
+ pageNumber,
3767
+ filters,
3768
+ },
3769
+ });
3770
+ fetchedLogs = fetchedLogs.concat(response.items);
3771
+ dispatch({
3772
+ type: ActionType$6.SetHasAnotherPage,
3773
+ hasAnotherPage: response.hasAnotherPage,
3774
+ });
3775
+ // Update logs in state
3776
+ dispatch({
3777
+ type: ActionType$6.FinishLoading,
3778
+ logs: fetchedLogs,
3779
+ });
3910
3780
  }
3911
3781
  catch (err) {
3912
3782
  return showFatalError(err);
3913
3783
  }
3914
- // Finish loading
3915
- dispatch({
3916
- type: ActionType$6.FinishLoading,
3917
- logMap,
3918
- });
3919
3784
  });
3920
3785
  /*------------------------------------------------------------------------*/
3921
3786
  /* ------------------------- Lifecycle Functions ------------------------ */
3922
3787
  /*------------------------------------------------------------------------*/
3923
3788
  /**
3924
- * Mount
3925
- * @author Gabe Abrams
3789
+ * Fetch logs whenever filters change
3926
3790
  */
3927
3791
  React.useEffect(() => {
3928
- // Perform initial load
3929
- handleDateRangeUpdated(dateFilterState);
3930
- }, []);
3792
+ fetchLogs();
3793
+ }, [
3794
+ dateFilterState,
3795
+ contextFilterState,
3796
+ tagFilterState,
3797
+ actionErrorFilterState,
3798
+ advancedFilterState,
3799
+ ]);
3931
3800
  /*------------------------------------------------------------------------*/
3932
3801
  /* ------------------------------- Render ------------------------------- */
3933
3802
  /*------------------------------------------------------------------------*/
@@ -3942,703 +3811,472 @@ const LogReviewer = (props) => {
3942
3811
  React__default["default"].createElement(LoadingSpinner, null)));
3943
3812
  }
3944
3813
  /* ------------ 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: () => {
3814
+ /*----------------------------------------*/
3815
+ /* ------------ Pagination -------------- */
3816
+ /*----------------------------------------*/
3817
+ const paginationControls = logs.length > 0 && (React__default["default"].createElement("div", { className: "text-center mt-3" },
3818
+ React__default["default"].createElement("button", { type: "button", className: "btn btn-secondary me-2", disabled: pageNumber <= 1, onClick: () => {
3819
+ dispatch({
3820
+ type: ActionType$6.DecrementPageNumber,
3821
+ });
3822
+ } },
3823
+ React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.faArrowLeft, className: "me-2" }),
3824
+ "Previous Page"),
3825
+ React__default["default"].createElement("span", { className: "mx-3" },
3826
+ "Page",
3827
+ ' ',
3828
+ pageNumber),
3829
+ React__default["default"].createElement("button", { type: "button", className: "btn btn-secondary ms-2", disabled: !hasAnotherPage, onClick: () => {
3830
+ dispatch({
3831
+ type: ActionType$6.IncrementPageNumber,
3832
+ });
3833
+ } },
3834
+ "Next Page",
3835
+ React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.faArrowRight, className: "ms-2" }))));
3836
+ /*----------------------------------------*/
3837
+ /* --------------- Filters -------------- */
3838
+ /*----------------------------------------*/
3839
+ // Filter toggle
3840
+ const filterToggles = (React__default["default"].createElement("div", { className: "LogReviewer-filter-toggles" },
3841
+ React__default["default"].createElement("h3", { className: "m-0" }, "Filters:"),
3842
+ React__default["default"].createElement("div", { className: "LogReviewer-filter-toggle-buttons alert alert-secondary p-2 m-0" },
3843
+ 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: () => {
3844
+ dispatch({
3845
+ type: ActionType$6.ToggleFilterDrawer,
3846
+ filterDrawer: FilterDrawer.Date,
3847
+ });
3848
+ } },
3849
+ React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.faCalendar, className: "me-2" }),
3850
+ "Date"),
3851
+ 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: () => {
3852
+ dispatch({
3853
+ type: ActionType$6.ToggleFilterDrawer,
3854
+ filterDrawer: FilterDrawer.Context,
3855
+ });
3856
+ } },
3857
+ React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.faCircle, className: "me-2" }),
3858
+ "Context"),
3859
+ (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: () => {
3860
+ dispatch({
3861
+ type: ActionType$6.ToggleFilterDrawer,
3862
+ filterDrawer: FilterDrawer.Tag,
3863
+ });
3864
+ } },
3865
+ React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.faTag, className: "me-2" }),
3866
+ "Tag")),
3867
+ 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: () => {
3868
+ dispatch({
3869
+ type: ActionType$6.ToggleFilterDrawer,
3870
+ filterDrawer: FilterDrawer.Action,
3871
+ });
3872
+ } },
3873
+ React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.faHammer, className: "me-2" }),
3874
+ "Action"),
3875
+ 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: () => {
3876
+ dispatch({
3877
+ type: ActionType$6.ToggleFilterDrawer,
3878
+ filterDrawer: FilterDrawer.Advanced,
3879
+ });
3880
+ } },
3881
+ React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.faList, className: "me-2" }),
3882
+ "Advanced"),
3883
+ React__default["default"].createElement("button", { type: "button", id: "LogReviewer-reset-filters-button", className: "btn btn-light", "aria-label": "reset filters", onClick: () => {
3884
+ dispatch({
3885
+ type: ActionType$6.ResetFilters,
3886
+ initActionErrorFilterState,
3887
+ initAdvancedFilterState,
3888
+ initContextFilterState,
3889
+ initDateFilterState,
3890
+ initTagFilterState,
3891
+ });
3892
+ } },
3893
+ React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.faTimes }),
3894
+ ' ',
3895
+ "Reset"))));
3896
+ // Filter drawer
3897
+ let filterDrawer;
3898
+ if (expandedFilterDrawer) {
3899
+ if (expandedFilterDrawer === FilterDrawer.Date) {
3900
+ filterDrawer = (React__default["default"].createElement(TabBox, { title: "Date" },
3901
+ 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) => {
3902
+ dateFilterState.startDate = { month, day, year };
3986
3903
  dispatch({
3987
- type: ActionType$6.ToggleFilterDrawer,
3988
- filterDrawer: FilterDrawer.Advanced,
3904
+ type: ActionType$6.UpdateDateFilterState,
3905
+ dateFilterState,
3989
3906
  });
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: () => {
3907
+ } }),
3908
+ ' ',
3909
+ "to",
3910
+ ' ',
3911
+ 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) => {
3912
+ if (year < dateFilterState.startDate.year
3913
+ || (year === dateFilterState.startDate.year
3914
+ && month < dateFilterState.startDate.month)
3915
+ || (year === dateFilterState.startDate.year
3916
+ && month === dateFilterState.startDate.month
3917
+ && day < dateFilterState.startDate.day)) {
3918
+ return alert('Invalid Start Date', 'The start date cannot be before the end date.');
3919
+ }
3920
+ dateFilterState.endDate = { month, day, year };
3994
3921
  dispatch({
3995
- type: ActionType$6.ResetFilters,
3996
- initActionErrorFilterState,
3997
- initAdvancedFilterState,
3998
- initContextFilterState,
3999
- initDateFilterState,
4000
- initTagFilterState,
3922
+ type: ActionType$6.UpdateDateFilterState,
3923
+ dateFilterState,
4001
3924
  });
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
- }));
3925
+ } })));
3926
+ }
3927
+ else if (expandedFilterDrawer === FilterDrawer.Context) {
3928
+ // Create item picker items
3929
+ const builtInPickableItem = {
3930
+ id: 'built-in-contexts',
3931
+ name: 'Auto-logged',
3932
+ isGroup: true,
3933
+ children: [],
3934
+ };
3935
+ const pickableItems = [];
3936
+ Object.keys(contextMap)
3937
+ .forEach((context) => {
3938
+ const value = contextMap[context];
3939
+ if (typeof value === 'string') {
3940
+ // No subcategories
4078
3941
  const item = {
4079
3942
  id: context,
4080
3943
  name: genHumanReadableName(context),
4081
- isGroup: true,
4082
- children,
3944
+ isGroup: false,
3945
+ checked: !!contextFilterState[context],
4083
3946
  };
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
- }
3947
+ // Add built-in items to its own folder
3948
+ const isBuiltIn = context in LogBuiltInMetadata.Context;
3949
+ if (isBuiltIn) {
3950
+ // Add to built-in pickable item
3951
+ builtInPickableItem.children.push(item);
3952
+ }
3953
+ else {
3954
+ // Add to pickable items list
3955
+ pickableItems.push(item);
3956
+ }
3957
+ return;
3958
+ }
3959
+ // Has subcategories
3960
+ const children = (Object.keys(value)
3961
+ // Remove parent name
3962
+ .filter((subcontext) => {
3963
+ return subcontext !== '_';
3964
+ })
3965
+ // Create child pickable items
3966
+ .map((subcontext) => {
3967
+ return {
3968
+ id: subcontext,
3969
+ name: genHumanReadableName(subcontext),
3970
+ isGroup: false,
3971
+ checked: contextFilterState[context][subcontext],
3972
+ };
3973
+ }));
3974
+ const item = {
3975
+ id: context,
3976
+ name: genHumanReadableName(context),
3977
+ isGroup: true,
3978
+ children,
3979
+ };
3980
+ pickableItems.push(item);
3981
+ });
3982
+ // Add built-in contexts to end ofl ist
3983
+ pickableItems.push(builtInPickableItem);
3984
+ // Create filter UI
3985
+ filterDrawer = (React__default["default"].createElement(ItemPicker, { title: "Context", items: pickableItems, onChanged: (updatedItems) => {
3986
+ // Update our state
3987
+ updatedItems.forEach((pickableItem) => {
3988
+ if (pickableItem.isGroup) {
3989
+ // Has subcontexts
3990
+ if (pickableItem.id === 'built-in-contexts') {
3991
+ // Built-in
3992
+ // Treat as if these were top-level contexts
3993
+ pickableItem.children.forEach((subcontextItem) => {
3994
+ contextFilterState[subcontextItem.id] = ('checked' in subcontextItem
3995
+ && subcontextItem.checked);
3996
+ });
4110
3997
  }
4111
3998
  else {
4112
- // No subcontexts
4113
- contextFilterState[pickableItem.id] = (pickableItem.checked);
3999
+ // Not built-in
4000
+ pickableItem.children.forEach((subcontextItem) => {
4001
+ if (!subcontextItem.isGroup) {
4002
+ contextFilterState[pickableItem.id][subcontextItem.id] = (subcontextItem.checked);
4003
+ }
4004
+ });
4114
4005
  }
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;
4006
+ }
4007
+ else {
4008
+ // No subcontexts
4009
+ contextFilterState[pickableItem.id] = (pickableItem.checked);
4010
+ }
4011
+ });
4012
+ dispatch({
4013
+ type: ActionType$6.UpdateContextFilterState,
4014
+ contextFilterState,
4015
+ });
4016
+ } }));
4017
+ }
4018
+ else if (expandedFilterDrawer === FilterDrawer.Tag) {
4019
+ // Create filter UI
4020
+ filterDrawer = (React__default["default"].createElement(TabBox, { title: "Tags" },
4021
+ 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."),
4022
+ React__default["default"].createElement("div", { className: "d-flex gap-1 flex-wrap" }, Object.keys((_d = LogMetadata.Tag) !== null && _d !== void 0 ? _d : {})
4023
+ .map((tag) => {
4024
+ const description = genHumanReadableName(tag);
4025
+ 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) => {
4026
+ tagFilterState[tag] = checked;
4027
+ dispatch({
4028
+ type: ActionType$6.UpdateTagFilterState,
4029
+ tagFilterState,
4030
+ });
4031
+ } }));
4032
+ }))));
4033
+ }
4034
+ else if (expandedFilterDrawer === FilterDrawer.Action) {
4035
+ // Create filter UI
4036
+ filterDrawer = (React__default["default"].createElement(React__default["default"].Fragment, null,
4037
+ React__default["default"].createElement(TabBox, { title: "Log Type" },
4038
+ React__default["default"].createElement(RadioButton, { id: "LogReviewer-type-all", text: "All Logs", onSelected: () => {
4039
+ actionErrorFilterState.type = undefined;
4040
+ dispatch({
4041
+ type: ActionType$6.UpdateActionErrorFilterState,
4042
+ actionErrorFilterState,
4043
+ });
4044
+ }, ariaLabel: "show logs of all types", selected: actionErrorFilterState.type === undefined }),
4045
+ React__default["default"].createElement(RadioButton, { id: "LogReviewer-type-action-only", text: "Action Logs Only", onSelected: () => {
4046
+ actionErrorFilterState.type = LogType$1.Action;
4047
+ dispatch({
4048
+ type: ActionType$6.UpdateActionErrorFilterState,
4049
+ actionErrorFilterState,
4050
+ });
4051
+ }, ariaLabel: "only show action logs", selected: actionErrorFilterState.type === LogType$1.Action }),
4052
+ React__default["default"].createElement(RadioButton, { id: "LogReviewer-type-error-only", text: "Action Error Only", onSelected: () => {
4053
+ actionErrorFilterState.type = LogType$1.Error;
4054
+ dispatch({
4055
+ type: ActionType$6.UpdateActionErrorFilterState,
4056
+ actionErrorFilterState,
4057
+ });
4058
+ }, ariaLabel: "only show error logs", selected: actionErrorFilterState.type === LogType$1.Error, noMarginOnRight: true })),
4059
+ (actionErrorFilterState.type === undefined
4060
+ || actionErrorFilterState.type === LogType$1.Action) && (React__default["default"].createElement(TabBox, { title: "Action Log Details" },
4061
+ React__default["default"].createElement(ButtonInputGroup, { label: "Action", className: "mb-2", wrapButtonsAndAddGaps: true }, Object.keys(LogAction$1)
4062
+ .map((action) => {
4063
+ const description = genHumanReadableName(action);
4064
+ 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) => {
4065
+ actionErrorFilterState.action[action] = checked;
4131
4066
  dispatch({
4132
- type: ActionType$6.UpdateTagFilterState,
4133
- tagFilterState,
4067
+ type: ActionType$6.UpdateActionErrorFilterState,
4068
+ actionErrorFilterState,
4134
4069
  });
4135
4070
  } }));
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;
4071
+ })),
4072
+ React__default["default"].createElement(ButtonInputGroup, { label: "Target", wrapButtonsAndAddGaps: true }, Object.keys(targetMap)
4073
+ .map((target) => {
4074
+ const description = genHumanReadableName(target);
4075
+ 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) => {
4076
+ actionErrorFilterState.target[target] = checked;
4144
4077
  dispatch({
4145
4078
  type: ActionType$6.UpdateActionErrorFilterState,
4146
4079
  actionErrorFilterState,
4147
4080
  });
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;
4081
+ } }));
4082
+ })))),
4083
+ (actionErrorFilterState.type === undefined
4084
+ || actionErrorFilterState.type === LogType$1.Error) && (React__default["default"].createElement(TabBox, { title: "Error Log Details" },
4085
+ React__default["default"].createElement("div", { className: "input-group mb-2" },
4086
+ React__default["default"].createElement("span", { className: "input-group-text" }, "Error Message"),
4087
+ 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) => {
4088
+ actionErrorFilterState.errorMessage = e.target.value;
4151
4089
  dispatch({
4152
4090
  type: ActionType$6.UpdateActionErrorFilterState,
4153
4091
  actionErrorFilterState,
4154
4092
  });
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;
4093
+ } })),
4094
+ React__default["default"].createElement("div", { className: "input-group mb-2" },
4095
+ React__default["default"].createElement("span", { className: "input-group-text" }, "Error Code"),
4096
+ 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) => {
4097
+ actionErrorFilterState.errorCode = ((e.target.value)
4098
+ .trim()
4099
+ .toUpperCase());
4158
4100
  dispatch({
4159
4101
  type: ActionType$6.UpdateActionErrorFilterState,
4160
4102
  actionErrorFilterState,
4161
4103
  });
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)
4104
+ } }))))));
4105
+ }
4106
+ else if (expandedFilterDrawer === FilterDrawer.Advanced) {
4107
+ // Create advanced filter ui
4108
+ filterDrawer = (React__default["default"].createElement(React__default["default"].Fragment, null,
4109
+ React__default["default"].createElement(TabBox, { title: "User" },
4110
+ React__default["default"].createElement("div", { className: "input-group mb-2" },
4111
+ React__default["default"].createElement("span", { className: "input-group-text" }, "User First Name"),
4112
+ 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) => {
4113
+ advancedFilterState.userFirstName = e.target.value;
4114
+ dispatch({
4115
+ type: ActionType$6.UpdateAdvancedFilterState,
4116
+ advancedFilterState,
4117
+ });
4118
+ } })),
4119
+ React__default["default"].createElement("div", { className: "input-group mb-2" },
4120
+ React__default["default"].createElement("span", { className: "input-group-text" }, "User Last Name"),
4121
+ 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) => {
4122
+ advancedFilterState.userLastName = e.target.value;
4123
+ dispatch({
4124
+ type: ActionType$6.UpdateAdvancedFilterState,
4125
+ advancedFilterState,
4126
+ });
4127
+ } })),
4128
+ React__default["default"].createElement("div", { className: "input-group mb-2" },
4129
+ React__default["default"].createElement("span", { className: "input-group-text" }, "User Email"),
4130
+ 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) => {
4131
+ advancedFilterState.userEmail = ((e.target.value)
4132
+ .trim());
4133
+ dispatch({
4134
+ type: ActionType$6.UpdateAdvancedFilterState,
4135
+ advancedFilterState,
4136
+ });
4137
+ } })),
4138
+ React__default["default"].createElement("div", { className: "input-group mb-2" },
4139
+ React__default["default"].createElement("span", { className: "input-group-text" }, "User Canvas Id"),
4140
+ React__default["default"].createElement("input", { type: "text", className: "form-control", "aria-label": "query for user canvas id", value: advancedFilterState.userId, placeholder: "e.g. 104985", onChange: (e) => {
4141
+ const { value } = e.target;
4142
+ // Only update if value contains only numbers
4143
+ if (/^\d+$/.test(value)) {
4144
+ advancedFilterState.userId = ((e.target.value)
4236
4145
  .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" },
4146
+ }
4147
+ dispatch({
4148
+ type: ActionType$6.UpdateAdvancedFilterState,
4149
+ advancedFilterState,
4150
+ });
4151
+ } })),
4152
+ React__default["default"].createElement(ButtonInputGroup, { label: "Role" },
4153
+ React__default["default"].createElement(CheckboxButton, { text: "Students", onChanged: (checked) => {
4154
+ advancedFilterState.includeLearners = checked;
4155
+ dispatch({
4156
+ type: ActionType$6.UpdateAdvancedFilterState,
4157
+ advancedFilterState,
4158
+ });
4159
+ }, checked: advancedFilterState.includeLearners, ariaLabel: "show logs from students" }),
4160
+ React__default["default"].createElement(CheckboxButton, { text: "Teaching Team Members", onChanged: (checked) => {
4161
+ advancedFilterState.includeTTMs = checked;
4162
+ dispatch({
4163
+ type: ActionType$6.UpdateAdvancedFilterState,
4164
+ advancedFilterState,
4165
+ });
4166
+ }, checked: advancedFilterState.includeTTMs, ariaLabel: "show logs from teaching team members" }),
4167
+ React__default["default"].createElement(CheckboxButton, { text: "Admins", onChanged: (checked) => {
4168
+ advancedFilterState.includeAdmins = checked;
4169
+ dispatch({
4170
+ type: ActionType$6.UpdateAdvancedFilterState,
4171
+ advancedFilterState,
4172
+ });
4173
+ }, checked: advancedFilterState.includeAdmins, ariaLabel: "show logs from admins" }))),
4174
+ React__default["default"].createElement(TabBox, { title: "Course" },
4175
+ React__default["default"].createElement("div", { className: "input-group mb-2" },
4176
+ React__default["default"].createElement("span", { className: "input-group-text" }, "Course Name"),
4177
+ 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) => {
4178
+ advancedFilterState.courseName = e.target.value;
4179
+ dispatch({
4180
+ type: ActionType$6.UpdateAdvancedFilterState,
4181
+ advancedFilterState,
4182
+ });
4183
+ } })),
4184
+ React__default["default"].createElement("div", { className: "input-group mb-2" },
4185
+ React__default["default"].createElement("span", { className: "input-group-text" }, "Course Canvas Id"),
4186
+ 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) => {
4187
+ const { value } = e.target;
4188
+ // Only update if value contains only numbers
4189
+ if (/^\d+$/.test(value)) {
4190
+ advancedFilterState.courseId = ((e.target.value)
4191
+ .trim());
4192
+ }
4193
+ dispatch({
4194
+ type: ActionType$6.UpdateAdvancedFilterState,
4195
+ advancedFilterState,
4196
+ });
4197
+ } }))),
4198
+ React__default["default"].createElement(TabBox, { title: "Device" },
4199
+ React__default["default"].createElement(ButtonInputGroup, { label: "Device Type" },
4200
+ React__default["default"].createElement(RadioButton, { text: "All Devices", ariaLabel: "show logs from all devices", selected: advancedFilterState.isMobile === undefined, onSelected: () => {
4201
+ advancedFilterState.isMobile = undefined;
4202
+ dispatch({
4203
+ type: ActionType$6.UpdateAdvancedFilterState,
4204
+ advancedFilterState,
4205
+ });
4206
+ } }),
4207
+ React__default["default"].createElement(RadioButton, { text: "Mobile Only", ariaLabel: "show logs from mobile devices", selected: advancedFilterState.isMobile === true, onSelected: () => {
4208
+ advancedFilterState.isMobile = true;
4209
+ dispatch({
4210
+ type: ActionType$6.UpdateAdvancedFilterState,
4211
+ advancedFilterState,
4212
+ });
4213
+ } }),
4214
+ React__default["default"].createElement(RadioButton, { text: "Desktop Only", ariaLabel: "show logs from desktop devices", selected: advancedFilterState.isMobile === false, onSelected: () => {
4215
+ advancedFilterState.isMobile = false;
4216
+ dispatch({
4217
+ type: ActionType$6.UpdateAdvancedFilterState,
4218
+ advancedFilterState,
4219
+ });
4220
+ }, noMarginOnRight: true }))),
4221
+ React__default["default"].createElement(TabBox, { title: "Source" },
4222
+ React__default["default"].createElement(ButtonInputGroup, { label: "Source Type" },
4223
+ React__default["default"].createElement(RadioButton, { text: "Both", ariaLabel: "show logs from all sources", selected: advancedFilterState.source === undefined, onSelected: () => {
4224
+ advancedFilterState.source = undefined;
4225
+ dispatch({
4226
+ type: ActionType$6.UpdateAdvancedFilterState,
4227
+ advancedFilterState,
4228
+ });
4229
+ } }),
4230
+ React__default["default"].createElement(RadioButton, { text: "Client Only", ariaLabel: "show logs from client source", selected: advancedFilterState.source === LogSource$1.Client, onSelected: () => {
4231
+ advancedFilterState.source = LogSource$1.Client;
4232
+ dispatch({
4233
+ type: ActionType$6.UpdateAdvancedFilterState,
4234
+ advancedFilterState,
4235
+ });
4236
+ } }),
4237
+ React__default["default"].createElement(RadioButton, { text: "Server Only", ariaLabel: "show logs from server source", selected: advancedFilterState.source === LogSource$1.Server, onSelected: () => {
4238
+ advancedFilterState.source = LogSource$1.Server;
4239
+ dispatch({
4240
+ type: ActionType$6.UpdateAdvancedFilterState,
4241
+ advancedFilterState,
4242
+ });
4243
+ }, noMarginOnRight: true })),
4244
+ advancedFilterState.source !== LogSource$1.Client && (React__default["default"].createElement("div", { className: "mt-2" },
4279
4245
  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;
4246
+ React__default["default"].createElement("span", { className: "input-group-text" }, "Server Route Path"),
4247
+ 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) => {
4248
+ advancedFilterState.courseName = ((e.target.value)
4249
+ .trim());
4283
4250
  dispatch({
4284
4251
  type: ActionType$6.UpdateAdvancedFilterState,
4285
4252
  advancedFilterState,
4286
4253
  });
4287
4254
  } })),
4288
4255
  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;
4256
+ React__default["default"].createElement("span", { className: "input-group-text" }, "Server Route Template"),
4257
+ 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) => {
4258
+ advancedFilterState.courseName = ((e.target.value)
4259
+ .trim());
4343
4260
  dispatch({
4344
4261
  type: ActionType$6.UpdateAdvancedFilterState,
4345
4262
  advancedFilterState,
4346
4263
  });
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" },
4264
+ } })))))));
4265
+ }
4266
+ }
4267
+ // Filters UI
4268
+ const filters = (React__default["default"].createElement(React__default["default"].Fragment, null,
4269
+ filterToggles,
4270
+ filterDrawer && (React__default["default"].createElement(Drawer, { customBackgroundColor: "#eee" }, filterDrawer))));
4271
+ // Main body
4272
+ body = (React__default["default"].createElement(React__default["default"].Fragment, null,
4273
+ filters,
4274
+ React__default["default"].createElement("div", { className: "mt-2" },
4275
+ React__default["default"].createElement(IntelliTable, { title: "Matching Logs:", csvName: `Logs from ${getHumanReadableDate()}`, id: "logs", data: logs, columns: columns }),
4276
+ logs.length === 0 && (React__default["default"].createElement("div", { className: "alert alert-warning text-center mt-2" },
4630
4277
  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
- }
4278
+ React__default["default"].createElement("div", null, "Either your filters are too strict or no matching logs have been created yet."))),
4279
+ paginationControls)));
4642
4280
  /* ---------- Wrap in Modal --------- */
4643
4281
  return (React__default["default"].createElement("div", { className: "LogReviewer-outer-container" },
4644
4282
  React__default["default"].createElement("style", null, style$2),
@@ -14150,32 +13788,138 @@ const initServer = (opts) => {
14150
13788
  }),
14151
13789
  }));
14152
13790
  /**
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({
13791
+ * Get filtered logs based on provided filters
13792
+ * @author Gabe Abrams, Yuen Ler Chow
13793
+ * @returns {Log[]} list of logs that match the filters
13794
+ */
13795
+ opts.app.get(`${LOG_REVIEW_ROUTE_PATH_PREFIX}/logs`, genRouteHandler({
14160
13796
  paramTypes: {
14161
- year: ParamType$1.Int,
14162
- month: ParamType$1.Int,
13797
+ pageNumber: ParamType$1.Int,
13798
+ filters: ParamType$1.JSON,
14163
13799
  },
14164
13800
  handler: ({ params }) => __awaiter(void 0, void 0, void 0, function* () {
14165
13801
  // Get user info
14166
- const { year, month, userId, isAdmin, } = params;
13802
+ const { pageNumber, userId, isAdmin, filters, } = params;
14167
13803
  // Validate user
14168
13804
  const canReview = yield canReviewLogs(userId, isAdmin);
14169
13805
  if (!canReview) {
14170
13806
  throw new ErrorWithCode('You cannot access this resource because you do not have the appropriate permissions.', ReactKitErrorCode$1.NotAllowedToReviewLogs);
14171
13807
  }
13808
+ // Build MongoDB query based on filters
13809
+ const query = {};
13810
+ /* -------------- Date Filter ------------- */
13811
+ const { startDate, endDate } = filters.dateFilterState;
13812
+ const startTimestamp = new Date(startDate.year, startDate.month - 1, startDate.day).getTime();
13813
+ const endTimestamp = new Date(endDate.year, endDate.month - 1, endDate.day + 1).getTime() - 1;
13814
+ query.timestamp = {
13815
+ $gte: startTimestamp,
13816
+ $lte: endTimestamp,
13817
+ };
13818
+ /* ------------ Context Filter ------------ */
13819
+ const { contextFilterState } = filters;
13820
+ const contextConditions = [];
13821
+ Object.keys(contextFilterState).forEach((context) => {
13822
+ const value = contextFilterState[context];
13823
+ if (typeof value === 'boolean') {
13824
+ if (value) {
13825
+ // The entire context is selected
13826
+ contextConditions.push({ context });
13827
+ }
13828
+ }
13829
+ else {
13830
+ // The context has subcontexts
13831
+ const subcontexts = Object.keys(value).filter((subcontext) => { return value[subcontext]; });
13832
+ if (subcontexts.length > 0) {
13833
+ contextConditions.push({
13834
+ context,
13835
+ subcontext: { $in: subcontexts },
13836
+ });
13837
+ }
13838
+ }
13839
+ });
13840
+ if (contextConditions.length > 0) {
13841
+ query.$or = contextConditions;
13842
+ }
13843
+ /* -------------- Tag Filter -------------- */
13844
+ const { tagFilterState } = filters;
13845
+ const selectedTags = Object.keys(tagFilterState).filter((tag) => { return tagFilterState[tag]; });
13846
+ if (selectedTags.length > 0) {
13847
+ query.tags = { $in: selectedTags };
13848
+ }
13849
+ /* --------- Action/Error Filter ---------- */
13850
+ const { actionErrorFilterState } = filters;
13851
+ if (actionErrorFilterState.type) {
13852
+ query.type = actionErrorFilterState.type;
13853
+ }
13854
+ if (actionErrorFilterState.type === 'error' && actionErrorFilterState.errorMessage) {
13855
+ query.errorMessage = { $regex: actionErrorFilterState.errorMessage, $options: 'i' };
13856
+ }
13857
+ if (actionErrorFilterState.type === 'error' && actionErrorFilterState.errorCode) {
13858
+ query.errorCode = { $regex: actionErrorFilterState.errorCode, $options: 'i' };
13859
+ }
13860
+ if (actionErrorFilterState.type === 'action') {
13861
+ const selectedTargets = Object.keys(actionErrorFilterState.target).filter((target) => { return actionErrorFilterState.target[target]; });
13862
+ const selectedActions = Object.keys(actionErrorFilterState.action).filter((action) => { return actionErrorFilterState.action[action]; });
13863
+ if (selectedTargets.length > 0) {
13864
+ query.target = { $in: selectedTargets };
13865
+ }
13866
+ if (selectedActions.length > 0) {
13867
+ query.action = { $in: selectedActions };
13868
+ }
13869
+ }
13870
+ /* ------------ Advanced Filter ----------- */
13871
+ const { advancedFilterState } = filters;
13872
+ if (advancedFilterState.userFirstName) {
13873
+ query.userFirstName = { $regex: advancedFilterState.userFirstName, $options: 'i' };
13874
+ }
13875
+ if (advancedFilterState.userLastName) {
13876
+ query.userLastName = { $regex: advancedFilterState.userLastName, $options: 'i' };
13877
+ }
13878
+ if (advancedFilterState.userEmail) {
13879
+ query.userEmail = { $regex: advancedFilterState.userEmail, $options: 'i' };
13880
+ }
13881
+ if (advancedFilterState.userId) {
13882
+ query.userId = parseInt(advancedFilterState.userId, 10);
13883
+ }
13884
+ const roles = [];
13885
+ if (advancedFilterState.includeLearners) {
13886
+ roles.push({ isLearner: true });
13887
+ }
13888
+ if (advancedFilterState.includeTTMs) {
13889
+ roles.push({ isTTM: true });
13890
+ }
13891
+ if (advancedFilterState.includeAdmins) {
13892
+ roles.push({ isAdmin: true });
13893
+ }
13894
+ if (roles.length > 0) {
13895
+ query.$and = [{ $or: roles }];
13896
+ }
13897
+ if (advancedFilterState.courseId) {
13898
+ query.courseId = parseInt(advancedFilterState.courseId, 10);
13899
+ }
13900
+ if (advancedFilterState.courseName) {
13901
+ query.courseName = { $regex: advancedFilterState.courseName, $options: 'i' };
13902
+ }
13903
+ if (advancedFilterState.isMobile !== undefined) {
13904
+ query['device.isMobile'] = advancedFilterState.isMobile;
13905
+ }
13906
+ if (advancedFilterState.source) {
13907
+ query.source = advancedFilterState.source;
13908
+ }
13909
+ if (advancedFilterState.routePath) {
13910
+ query.routePath = { $regex: advancedFilterState.routePath, $options: 'i' };
13911
+ }
13912
+ if (advancedFilterState.routeTemplate) {
13913
+ query.routeTemplate = { $regex: advancedFilterState.routeTemplate, $options: 'i' };
13914
+ }
14172
13915
  // Query for logs
14173
- const logs = yield _logCollection.find({
14174
- year,
14175
- month,
13916
+ const response = yield _logCollection.findPaged({
13917
+ query,
13918
+ perPage: 50,
13919
+ pageNumber,
14176
13920
  });
14177
- // Return logs
14178
- return logs;
13921
+ // Return response
13922
+ return response;
14179
13923
  }),
14180
13924
  }));
14181
13925
  };
@@ -16427,7 +16171,6 @@ exports.padDecimalZeros = padDecimalZeros;
16427
16171
  exports.padZerosLeft = padZerosLeft;
16428
16172
  exports.parallelLimit = parallelLimit;
16429
16173
  exports.prefixWithAOrAn = prefixWithAOrAn;
16430
- exports.prompt = prompt;
16431
16174
  exports.roundToNumDecimals = roundToNumDecimals;
16432
16175
  exports.setClientEventMetadataPopulator = setClientEventMetadataPopulator;
16433
16176
  exports.showFatalError = showFatalError;