dce-reactkit 3.9.4-beta-logreviewer.6 → 3.9.4-beta-logreviewer.8

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 (38) hide show
  1. package/dist/cjs/index.js +426 -204
  2. package/dist/cjs/index.js.map +1 -1
  3. package/dist/cjs/types/client/initClient.d.ts +16 -2
  4. package/dist/cjs/types/components/AppWrapper.d.ts +32 -0
  5. package/dist/cjs/types/components/CheckboxButton.d.ts +1 -0
  6. package/dist/cjs/types/components/Pagination.d.ts +9 -0
  7. package/dist/cjs/types/components/RadioButton.d.ts +2 -0
  8. package/dist/cjs/types/constants/LOG_REVIEW_PAGE_SIZE.d.ts +6 -0
  9. package/dist/cjs/types/index.d.ts +2 -2
  10. package/dist/cjs/types/types/ModalType.d.ts +1 -0
  11. package/dist/esm/index.js +426 -205
  12. package/dist/esm/index.js.map +1 -1
  13. package/dist/esm/types/client/initClient.d.ts +16 -2
  14. package/dist/esm/types/components/AppWrapper.d.ts +32 -0
  15. package/dist/esm/types/components/CheckboxButton.d.ts +1 -0
  16. package/dist/esm/types/components/Pagination.d.ts +9 -0
  17. package/dist/esm/types/components/RadioButton.d.ts +2 -0
  18. package/dist/esm/types/constants/LOG_REVIEW_PAGE_SIZE.d.ts +6 -0
  19. package/dist/esm/types/index.d.ts +2 -2
  20. package/dist/esm/types/types/ModalType.d.ts +1 -0
  21. package/dist/index.d.ts +47 -3
  22. package/package.json +1 -1
  23. package/src/client/initClient.tsx +49 -11
  24. package/src/components/AppWrapper.tsx +258 -2
  25. package/src/components/CheckboxButton.tsx +24 -6
  26. package/src/components/LogReviewer.tsx +271 -221
  27. package/src/components/Modal/index.tsx +3 -0
  28. package/src/components/Pagination.tsx +147 -0
  29. package/src/components/RadioButton.tsx +33 -6
  30. package/src/components/SimpleDateChooser.tsx +8 -2
  31. package/src/constants/LOG_REVIEW_PAGE_SIZE.ts +7 -0
  32. package/src/helpers/genRouteHandler.ts +7 -7
  33. package/src/helpers/logClientEvent.tsx +8 -0
  34. package/src/helpers/visitServerEndpoint.tsx +1 -0
  35. package/src/index.ts +2 -0
  36. package/src/server/initLogCollection.ts +5 -0
  37. package/src/server/initServer.ts +3 -2
  38. package/src/types/ModalType.tsx +1 -0
package/dist/cjs/index.js CHANGED
@@ -186,6 +186,7 @@ var ModalButtonType$1 = ModalButtonType;
186
186
  var ModalType;
187
187
  (function (ModalType) {
188
188
  ModalType["Okay"] = "okay";
189
+ ModalType["Cancel"] = "cancel";
189
190
  ModalType["OkayCancel"] = "okay-cancel";
190
191
  ModalType["YesNo"] = "yes-no";
191
192
  ModalType["YesNoCancel"] = "yes-no-cancel";
@@ -316,11 +317,23 @@ const isDarkModeOn = () => {
316
317
  * @param opts object containing all arguments
317
318
  * @param opts.sendRequest caccl send request functions
318
319
  * @param [opts.sessionExpiredMessage] a custom session expired message
320
+ * @param [opts.darkModeOn] if true, dark mode is enabled
321
+ * @param [opts.noServer] if true, there is no server for this app
319
322
  */
320
323
  const initClient = (opts) => {
321
- // Store values
322
- storedSendRequest = opts.sendRequest;
323
- sessionExpiredMessage = opts.sessionExpiredMessage;
324
+ // Handle separately if there is no server
325
+ if (opts.noServer) {
326
+ // Store values
327
+ storedSendRequest = () => __awaiter(void 0, void 0, void 0, function* () {
328
+ throw new Error('Cannot send requests because there is no server');
329
+ });
330
+ }
331
+ else {
332
+ // Store values
333
+ storedSendRequest = opts.sendRequest;
334
+ sessionExpiredMessage = opts.sessionExpiredMessage;
335
+ }
336
+ // Handle universal parts
324
337
  darkModeOn = !!opts.darkModeOn;
325
338
  // Mark as initialized
326
339
  onInitialized(null);
@@ -340,6 +353,9 @@ const modalTypeToModalButtonTypes = {
340
353
  [ModalType$1.Okay]: [
341
354
  ModalButtonType$1.Okay,
342
355
  ],
356
+ [ModalType$1.Cancel]: [
357
+ ModalButtonType$1.Cancel,
358
+ ],
343
359
  [ModalType$1.OkayCancel]: [
344
360
  ModalButtonType$1.Okay,
345
361
  ModalButtonType$1.Cancel,
@@ -781,6 +797,7 @@ const _setStubResponse = (opts) => {
781
797
  */
782
798
  const visitServerEndpoint = (opts) => __awaiter(void 0, void 0, void 0, function* () {
783
799
  var _a, _b, _c;
800
+ // Set default method
784
801
  const method = ((_a = opts.method) !== null && _a !== void 0 ? _a : 'GET');
785
802
  // Handle stubs
786
803
  const stubResponse = (_b = stubResponses[method]) === null || _b === void 0 ? void 0 : _b[opts.path];
@@ -1080,6 +1097,92 @@ const confirm = (title, text, opts) => __awaiter(void 0, void 0, void 0, functio
1080
1097
  });
1081
1098
  });
1082
1099
  /*----------------------------------------*/
1100
+ /* --------------- Prompt -------------- */
1101
+ /*----------------------------------------*/
1102
+ // Stored copies of setters
1103
+ let setPromptInfo;
1104
+ // Function to call when prompt is closed
1105
+ let onPromptClosed;
1106
+ /**
1107
+ * Show a prompt modal asking the user for input
1108
+ * @author Yuen Ler Chow
1109
+ * @param title the title text to display at the top of the prompt
1110
+ * @param [opts={}] additional options for the prompt dialog
1111
+ * @param [opts.textAboveInputField] the text to display in the prompt
1112
+ * @param [opts.defaultText] the default text for the input field
1113
+ * @param [opts.placeholder] the placeholder text for the input field
1114
+ * @param [opts.confirmButtonText=Okay] the text of the confirm button
1115
+ * @param [opts.confirmButtonVariant=Variant.Dark] the variant of the confirm button
1116
+ * @param [opts.cancelButtonText=Cancel] the text of the cancel button
1117
+ * @param [opts.cancelButtonVariant=Variant.Secondary] the variant of the cancel button
1118
+ * @param [opts.minNumChars] the minimum number of characters required for
1119
+ * the input to be valid
1120
+ * @param [opts.findValidationError] a function that takes the input text and
1121
+ * returns an error message if the input is invalid, returns undefined if the
1122
+ * input is valid
1123
+ * @param [opts.ariaLabel] the aria label for the input field
1124
+ * @returns Promise that resolves with the input string or null if canceled
1125
+ */
1126
+ const prompt = (title, opts) => __awaiter(void 0, void 0, void 0, function* () {
1127
+ var _a, _b;
1128
+ // Wait for helper to exist
1129
+ yield waitForHelper(() => {
1130
+ return !!setPromptInfo;
1131
+ });
1132
+ // Fallback if prompt is not available
1133
+ if (!setPromptInfo) {
1134
+ const resultPassesValidation = false;
1135
+ while (!resultPassesValidation) {
1136
+ // eslint-disable-next-line no-alert
1137
+ const result = window.prompt(`${title}\n\n${(_a = opts === null || opts === void 0 ? void 0 : opts.textAboveInputField) !== null && _a !== void 0 ? _a : ''}`, (_b = opts === null || opts === void 0 ? void 0 : opts.defaultText) !== null && _b !== void 0 ? _b : '');
1138
+ // Exit loop if user cancels
1139
+ if (result === null) {
1140
+ return null;
1141
+ }
1142
+ // Validate min num chars
1143
+ const minNumCharsValidationError = (((opts === null || opts === void 0 ? void 0 : opts.minNumChars) && result.length < opts.minNumChars)
1144
+ ? `Please enter at least ${opts.minNumChars} characters.`
1145
+ : undefined);
1146
+ // Run custom validation
1147
+ const customValidationError = ((opts === null || opts === void 0 ? void 0 : opts.findValidationError)
1148
+ && opts.findValidationError(result));
1149
+ // Show validation issue
1150
+ if (minNumCharsValidationError || customValidationError) {
1151
+ // Create error message
1152
+ const errorMessage = ([
1153
+ minNumCharsValidationError,
1154
+ customValidationError,
1155
+ ]
1156
+ // Filter out undefined messages
1157
+ .filter((msg) => {
1158
+ return !!msg;
1159
+ })
1160
+ // Join messages with newlines
1161
+ .join('\n'));
1162
+ // Show alert
1163
+ alert('Invalid Input', errorMessage);
1164
+ }
1165
+ else {
1166
+ return result;
1167
+ }
1168
+ }
1169
+ }
1170
+ // Return promise that resolves with result of prompt
1171
+ return new Promise((resolve) => {
1172
+ var _a;
1173
+ // Setup handler
1174
+ onPromptClosed = (result) => {
1175
+ resolve(result);
1176
+ };
1177
+ // Show the prompt
1178
+ setPromptInfo({
1179
+ title,
1180
+ currentInputFieldText: ((_a = opts === null || opts === void 0 ? void 0 : opts.defaultText) !== null && _a !== void 0 ? _a : ''),
1181
+ opts: (opts !== null && opts !== void 0 ? opts : {}),
1182
+ });
1183
+ });
1184
+ });
1185
+ /*----------------------------------------*/
1083
1186
  /* ------------- Fatal Error ------------ */
1084
1187
  /*----------------------------------------*/
1085
1188
  // Stored copies of setters
@@ -1095,14 +1198,14 @@ const fatalErrorHandlers = [];
1095
1198
  * @param [errorTitle] title of the error box
1096
1199
  */
1097
1200
  const showFatalError = (error, errorTitle = 'An Error Occurred') => __awaiter(void 0, void 0, void 0, function* () {
1098
- var _a, _b;
1201
+ var _c, _d;
1099
1202
  // Determine message and code
1100
1203
  const message = (typeof error === 'string'
1101
1204
  ? error.trim()
1102
- : String((_a = error.message) !== null && _a !== void 0 ? _a : 'An unknown error occurred.'));
1205
+ : String((_c = error.message) !== null && _c !== void 0 ? _c : 'An unknown error occurred.'));
1103
1206
  const code = (typeof error === 'string'
1104
1207
  ? ReactKitErrorCode$1.NoCode
1105
- : String((_b = error.code) !== null && _b !== void 0 ? _b : ReactKitErrorCode$1.NoCode));
1208
+ : String((_d = error.code) !== null && _d !== void 0 ? _d : ReactKitErrorCode$1.NoCode));
1106
1209
  // Call all fatal error listeners
1107
1210
  try {
1108
1211
  fatalErrorHandlers.forEach((handler) => {
@@ -1229,6 +1332,9 @@ const AppWrapper = (props) => {
1229
1332
  // Confirm
1230
1333
  const [confirmInfo, setConfirmInfoInner,] = React.useState(undefined);
1231
1334
  setConfirmInfo = setConfirmInfoInner;
1335
+ // Prompt
1336
+ const [promptInfo, setPromptInfoInner] = React.useState(undefined);
1337
+ setPromptInfo = setPromptInfoInner;
1232
1338
  // Session expired
1233
1339
  const [sessionHasExpired, setSessionHasExpiredInner,] = React.useState(false);
1234
1340
  setSessionHasExpired = setSessionHasExpiredInner;
@@ -1259,6 +1365,42 @@ const AppWrapper = (props) => {
1259
1365
  }
1260
1366
  }, onTopOfOtherModals: true, dontAllowBackdropExit: true }, confirmInfo.text));
1261
1367
  }
1368
+ /* ------------- Prompt ------------ */
1369
+ if (promptInfo) {
1370
+ // Run min char validation
1371
+ const minNumCharsValidationError = ((promptInfo.opts.minNumChars
1372
+ && promptInfo.currentInputFieldText.length < promptInfo.opts.minNumChars)
1373
+ ? `Please enter at least ${promptInfo.opts.minNumChars} characters.`
1374
+ : undefined);
1375
+ // Run custom validation
1376
+ const customValidationError = (promptInfo.opts.findValidationError
1377
+ && promptInfo.opts.findValidationError(promptInfo.currentInputFieldText));
1378
+ modal = (React__default["default"].createElement(Modal, { key: `prompt-${promptInfo.title}`, title: promptInfo.title,
1379
+ // Don't show ok button if there is a validation error
1380
+ type: ((customValidationError || minNumCharsValidationError)
1381
+ ? ModalType$1.Cancel
1382
+ : ModalType$1.OkayCancel), okayLabel: promptInfo.opts.confirmButtonText, okayVariant: promptInfo.opts.confirmButtonVariant, cancelLabel: promptInfo.opts.cancelButtonText, cancelVariant: promptInfo.opts.cancelButtonVariant, onClose: (buttonType) => {
1383
+ // Get result
1384
+ const result = (buttonType === ModalButtonType$1.Okay
1385
+ ? promptInfo.currentInputFieldText
1386
+ : null);
1387
+ // Close prompt
1388
+ setPromptInfo(undefined);
1389
+ // Call handler
1390
+ if (onPromptClosed) {
1391
+ onPromptClosed(result);
1392
+ }
1393
+ }, onTopOfOtherModals: true, dontAllowBackdropExit: true },
1394
+ React__default["default"].createElement("div", null,
1395
+ promptInfo.opts.textAboveInputField && (React__default["default"].createElement("div", null, promptInfo.opts.textAboveInputField)),
1396
+ React__default["default"].createElement("input", { type: "text", className: "form-control", "aria-label": promptInfo.opts.ariaLabel, placeholder: promptInfo.opts.placeholder, value: promptInfo.currentInputFieldText, onChange: (e) => {
1397
+ return setPromptInfo(Object.assign(Object.assign({}, promptInfo), { currentInputFieldText: e.target.value }));
1398
+ },
1399
+ // eslint-disable-next-line jsx-a11y/no-autofocus
1400
+ autoFocus: true }),
1401
+ minNumCharsValidationError && (React__default["default"].createElement("div", { className: "text-danger fw-bold mt-2" }, minNumCharsValidationError)),
1402
+ customValidationError && (React__default["default"].createElement("div", { className: "text-danger fw-bold mt-2" }, customValidationError)))));
1403
+ }
1262
1404
  /* ------ Custom Modal Portals ------ */
1263
1405
  // Custom modal portals
1264
1406
  const customModalPortals = [];
@@ -1328,7 +1470,7 @@ const AppWrapper = (props) => {
1328
1470
  ? `
1329
1471
  .tooltip-inner {
1330
1472
  background-color: white;
1331
- color: black;
1473
+ color: black !important;
1332
1474
  border: 0.1rem solid black;
1333
1475
  pointer-events: none;
1334
1476
  }
@@ -1341,7 +1483,7 @@ const AppWrapper = (props) => {
1341
1483
  : `
1342
1484
  .tooltip-inner {
1343
1485
  background-color: black;
1344
- color: white;
1486
+ color: white !important;
1345
1487
  border: 0.1rem solid white;
1346
1488
  pointer-events: none;
1347
1489
  }
@@ -1560,24 +1702,31 @@ const RadioButton = (props) => {
1560
1702
  /* -------------------------------- Setup ------------------------------- */
1561
1703
  /*------------------------------------------------------------------------*/
1562
1704
  /* -------------- Props ------------- */
1563
- const { text, onSelected, ariaLabel, title, selected, id, noMarginOnRight, selectedVariant = (isDarkModeOn()
1705
+ const { text, onSelected, ariaLabel, title, selected, id, className, noMarginOnRight, selectedVariant = (isDarkModeOn()
1564
1706
  ? Variant$1.Light
1565
1707
  : Variant$1.Secondary), unselectedVariant = (isDarkModeOn()
1566
1708
  ? Variant$1.Secondary
1567
- : Variant$1.Light), small, } = props;
1709
+ : Variant$1.Light), small, useComplexFormatting, } = props;
1568
1710
  /*------------------------------------------------------------------------*/
1569
1711
  /* ------------------------------- Render ------------------------------- */
1570
1712
  /*------------------------------------------------------------------------*/
1571
1713
  /*----------------------------------------*/
1572
1714
  /* --------------- Main UI -------------- */
1573
1715
  /*----------------------------------------*/
1574
- return (React__default["default"].createElement("button", { type: "button", id: id, title: title, className: `btn btn-${selected ? selectedVariant : unselectedVariant}${selected ? ' selected' : ''}${small ? ' btn-sm' : ''} m-0${noMarginOnRight ? '' : ' me-1'}`, "aria-label": `${ariaLabel}${selected ? ': currently selected' : ''}`, onClick: () => {
1716
+ return (React__default["default"].createElement("button", { type: "button", id: id, title: title, className: `btn btn-${selected ? selectedVariant : unselectedVariant}${selected ? ' selected' : ''}${small ? ' btn-sm' : ''} m-0${noMarginOnRight ? '' : ' me-1'} ${className !== null && className !== void 0 ? className : ''}`, "aria-label": `${ariaLabel}${selected ? ': currently selected' : ''}`, onClick: () => {
1575
1717
  if (!selected) {
1576
1718
  onSelected();
1577
1719
  }
1578
1720
  } },
1579
- React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: selected ? freeSolidSvgIcons.faDotCircle : freeRegularSvgIcons.faCircle, className: "me-1" }),
1580
- text));
1721
+ React__default["default"].createElement("div", { className: "d-flex flex-row align-items-center" },
1722
+ React__default["default"].createElement("div", { className: "me-1" },
1723
+ React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: selected ? freeSolidSvgIcons.faDotCircle : freeRegularSvgIcons.faCircle })),
1724
+ useComplexFormatting
1725
+ ? (React__default["default"].createElement("pre", { className: "ps-1 text-start text-break", style: {
1726
+ whiteSpace: 'pre-wrap',
1727
+ tabSize: 2,
1728
+ } }, text))
1729
+ : (React__default["default"].createElement("div", { className: "flex-grow-1 text-start text-break" }, text)))));
1581
1730
  };
1582
1731
 
1583
1732
  /**
@@ -1596,7 +1745,7 @@ const CheckboxButton = (props) => {
1596
1745
  ? Variant$1.Light
1597
1746
  : Variant$1.Secondary), uncheckedVariant = (isDarkModeOn()
1598
1747
  ? Variant$1.Secondary
1599
- : Variant$1.Light), small, dashed, } = props;
1748
+ : Variant$1.Light), small, dashed, useComplexFormatting, } = props;
1600
1749
  /*------------------------------------------------------------------------*/
1601
1750
  /* ------------------------------- Render ------------------------------- */
1602
1751
  /*------------------------------------------------------------------------*/
@@ -1617,10 +1766,15 @@ const CheckboxButton = (props) => {
1617
1766
  return (React__default["default"].createElement("button", { type: "button", id: id, title: title, className: `CheckboxButton-status-${checked ? 'checked' : 'unchecked'} ${dashed ? 'CheckboxButton-dashed ' : ''}btn btn-${checked ? checkedVariant : uncheckedVariant}${checked ? ' selected' : ''}${small ? ' btn-sm' : ''} m-0${noMarginOnRight ? '' : ' me-1'} ${className !== null && className !== void 0 ? className : ''}`, "aria-label": `${ariaLabel}${checked ? ': currently checked' : ''}`, onClick: () => {
1618
1767
  onChanged(!checked);
1619
1768
  } },
1620
- React__default["default"].createElement("div", { className: "d-flex" },
1621
- React__default["default"].createElement("div", { className: "align-items-center" },
1622
- React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: icon, className: "me-1" })),
1623
- React__default["default"].createElement("div", { className: "flex-grow-1" }, text))));
1769
+ React__default["default"].createElement("div", { className: "d-flex align-items-center" },
1770
+ React__default["default"].createElement("div", { className: "me-1" },
1771
+ React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: icon })),
1772
+ useComplexFormatting
1773
+ ? (React__default["default"].createElement("pre", { className: "ps-1 text-start text-break", style: {
1774
+ whiteSpace: 'pre-wrap',
1775
+ tabSize: 2,
1776
+ } }, text))
1777
+ : (React__default["default"].createElement("div", { className: "flex-grow-1 text-start text-break" }, text)))));
1624
1778
  };
1625
1779
 
1626
1780
  /**
@@ -1801,7 +1955,13 @@ const SimpleDateChooser = (props) => {
1801
1955
  }
1802
1956
  const monthName = getMonthName(month).full;
1803
1957
  // Year is start year +1 for each 12 months
1804
- const year = (startYear + (Math.floor(unmoddedMonth / 12)));
1958
+ let yearsToAdd = 0;
1959
+ let monthsOfYearsToAdd = unmoddedMonth;
1960
+ while (monthsOfYearsToAdd > 12) {
1961
+ monthsOfYearsToAdd -= 12;
1962
+ yearsToAdd += 1;
1963
+ }
1964
+ const year = startYear + yearsToAdd;
1805
1965
  // Figure out which days are allowed
1806
1966
  const days = [];
1807
1967
  const numDaysInMonth = (new Date(year, month, 0)).getDate();
@@ -1852,7 +2012,7 @@ const SimpleDateChooser = (props) => {
1852
2012
  });
1853
2013
  }
1854
2014
  });
1855
- return (React__default["default"].createElement("div", { className: "SimpleDateChooser d-inline-block", "aria-label": `date chooser with selected date: ${month} ${day}, ${year}` },
2015
+ return (React__default["default"].createElement("div", { className: "SimpleDateChooser d-inline-block", "aria-label": `date chooser with selected date: ${month}/${day}/${year}` },
1856
2016
  React__default["default"].createElement("select", { "aria-label": `month for ${ariaLabel}`, className: "custom-select d-inline-block mr-1", style: { width: 'auto' }, id: `SimpleDateChooser-${name}-month`, value: `${month}-${year}`, onChange: (e) => {
1857
2017
  const choice = choices[e.target.selectedIndex];
1858
2018
  // Change day, month, and year
@@ -3182,6 +3342,55 @@ const IntelliTable = (props) => {
3182
3342
  } }, table)));
3183
3343
  };
3184
3344
 
3345
+ /*------------------------------------------------------------------------*/
3346
+ /* ------------------------------ Component ----------------------------- */
3347
+ /*------------------------------------------------------------------------*/
3348
+ const Pagination = ({ currentPage, numPages, loading = false, onPageChanged, }) => {
3349
+ // computes an array of page numbers to display.
3350
+ const getPageNumbers = () => {
3351
+ const pages = [];
3352
+ const delta = 2; // how many pages to show on either side of current
3353
+ let start = Math.max(1, currentPage - delta);
3354
+ let end = Math.min(numPages, currentPage + delta);
3355
+ // If we are too close to the beginning or end, shift the window.
3356
+ if (currentPage - delta < 1) {
3357
+ end = Math.min(numPages, end + (1 - (currentPage - delta)));
3358
+ }
3359
+ if (currentPage + delta > numPages) {
3360
+ start = Math.max(1, start - ((currentPage + delta) - numPages));
3361
+ }
3362
+ for (let i = start; i <= end; i++) {
3363
+ pages.push(i);
3364
+ }
3365
+ return pages;
3366
+ };
3367
+ const pages = getPageNumbers();
3368
+ return (React__default["default"].createElement("nav", { "aria-label": "Page navigation", className: "mt-3" },
3369
+ React__default["default"].createElement("ul", { className: "pagination justify-content-center" },
3370
+ React__default["default"].createElement("li", { className: `page-item ${(currentPage <= 1 || loading) ? 'disabled' : ''}` },
3371
+ React__default["default"].createElement("button", { type: "button", className: "page-link", onClick: () => { return onPageChanged(currentPage - 1); }, disabled: currentPage <= 1 || loading },
3372
+ React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.faArrowLeft }),
3373
+ ' ',
3374
+ "Prev")),
3375
+ currentPage > 3 && (React__default["default"].createElement("li", { className: "page-item" },
3376
+ React__default["default"].createElement("button", { type: "button", className: "page-link", onClick: () => { return onPageChanged(1); }, disabled: loading }, "1"))),
3377
+ currentPage > 4 && (React__default["default"].createElement("li", { className: "page-item disabled" },
3378
+ React__default["default"].createElement("span", { className: "page-link" }, "..."))),
3379
+ pages.map((pageNum) => {
3380
+ return (React__default["default"].createElement("li", { key: pageNum, className: `page-item ${pageNum === currentPage ? 'active' : ''}` },
3381
+ React__default["default"].createElement("button", { type: "button", className: "page-link", onClick: () => { return onPageChanged(pageNum); }, disabled: loading }, pageNum)));
3382
+ }),
3383
+ currentPage < numPages - 3 && (React__default["default"].createElement("li", { className: "page-item disabled" },
3384
+ React__default["default"].createElement("span", { className: "page-link" }, "..."))),
3385
+ currentPage < numPages - 2 && (React__default["default"].createElement("li", { className: "page-item" },
3386
+ React__default["default"].createElement("button", { type: "button", className: "page-link", onClick: () => { return onPageChanged(numPages); }, disabled: loading }, numPages))),
3387
+ React__default["default"].createElement("li", { className: `page-item ${(currentPage >= numPages || loading) ? 'disabled' : ''}` },
3388
+ React__default["default"].createElement("button", { type: "button", className: "page-link", onClick: () => { return onPageChanged(currentPage + 1); }, disabled: currentPage >= numPages || loading },
3389
+ "Next",
3390
+ ' ',
3391
+ React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.faArrowRight }))))));
3392
+ };
3393
+
3185
3394
  /**
3186
3395
  * Log reviewer panel that allows users (must be approved admins) to
3187
3396
  * review logs written by dce-reactkit
@@ -3561,6 +3770,8 @@ var ActionType$6;
3561
3770
  ActionType["SetNumPages"] = "set-num-pages";
3562
3771
  // Set page number
3563
3772
  ActionType["SetPageNumber"] = "set-page-number";
3773
+ // Reset user made filter change indicator
3774
+ ActionType["ResetUserMadeFilterChange"] = "reset-user-made-filter-change";
3564
3775
  })(ActionType$6 || (ActionType$6 = {}));
3565
3776
  /**
3566
3777
  * Reducer that executes actions
@@ -3585,22 +3796,22 @@ const reducer$7 = (state, action) => {
3585
3796
  return Object.assign(Object.assign({}, state), { expandedFilterDrawer: undefined });
3586
3797
  }
3587
3798
  case ActionType$6.ResetFilters: {
3588
- return Object.assign(Object.assign({}, state), { dateFilterState: action.initDateFilterState, contextFilterState: action.initContextFilterState, tagFilterState: action.initTagFilterState, actionErrorFilterState: action.initActionErrorFilterState, advancedFilterState: action.initAdvancedFilterState, pageNumber: 1 });
3799
+ return Object.assign(Object.assign({}, state), { pendingDateFilterState: action.initDateFilterState, pendingContextFilterState: action.initContextFilterState, pendingTagFilterState: action.initTagFilterState, pendingActionErrorFilterState: action.initActionErrorFilterState, pendingAdvancedFilterState: action.initAdvancedFilterState, pageNumber: 1 });
3589
3800
  }
3590
3801
  case ActionType$6.UpdateDateFilterState: {
3591
- return Object.assign(Object.assign({}, state), { dateFilterState: action.dateFilterState });
3802
+ return Object.assign(Object.assign({}, state), { pendingDateFilterState: action.dateFilterState, userMadeFilterChange: true });
3592
3803
  }
3593
3804
  case ActionType$6.UpdateContextFilterState: {
3594
- return Object.assign(Object.assign({}, state), { contextFilterState: action.contextFilterState });
3805
+ return Object.assign(Object.assign({}, state), { pendingContextFilterState: action.contextFilterState, userMadeFilterChange: true });
3595
3806
  }
3596
3807
  case ActionType$6.UpdateTagFilterState: {
3597
- return Object.assign(Object.assign({}, state), { tagFilterState: action.tagFilterState });
3808
+ return Object.assign(Object.assign({}, state), { pendingTagFilterState: action.tagFilterState, userMadeFilterChange: true });
3598
3809
  }
3599
3810
  case ActionType$6.UpdateActionErrorFilterState: {
3600
- return Object.assign(Object.assign({}, state), { actionErrorFilterState: action.actionErrorFilterState });
3811
+ return Object.assign(Object.assign({}, state), { pendingActionErrorFilterState: action.actionErrorFilterState, userMadeFilterChange: true });
3601
3812
  }
3602
3813
  case ActionType$6.UpdateAdvancedFilterState: {
3603
- return Object.assign(Object.assign({}, state), { advancedFilterState: action.advancedFilterState });
3814
+ return Object.assign(Object.assign({}, state), { pendingAdvancedFilterState: action.advancedFilterState, userMadeFilterChange: true });
3604
3815
  }
3605
3816
  case ActionType$6.SetHasAnotherPage: {
3606
3817
  return Object.assign(Object.assign({}, state), { hasAnotherPage: action.hasAnotherPage });
@@ -3611,6 +3822,9 @@ const reducer$7 = (state, action) => {
3611
3822
  case ActionType$6.SetPageNumber: {
3612
3823
  return Object.assign(Object.assign({}, state), { pageNumber: action.pageNumber });
3613
3824
  }
3825
+ case ActionType$6.ResetUserMadeFilterChange: {
3826
+ return Object.assign(Object.assign({}, state), { userMadeFilterChange: false });
3827
+ }
3614
3828
  default: {
3615
3829
  return state;
3616
3830
  }
@@ -3731,19 +3945,29 @@ const LogReviewer = (props) => {
3731
3945
  showSpinner: true,
3732
3946
  logs: [],
3733
3947
  expandedFilterDrawer: undefined,
3734
- dateFilterState: initDateFilterState,
3735
- contextFilterState: initContextFilterState,
3736
- tagFilterState: initTagFilterState,
3737
- actionErrorFilterState: initActionErrorFilterState,
3738
- advancedFilterState: initAdvancedFilterState,
3948
+ pendingDateFilterState: initDateFilterState,
3949
+ pendingContextFilterState: initContextFilterState,
3950
+ pendingTagFilterState: initTagFilterState,
3951
+ pendingActionErrorFilterState: initActionErrorFilterState,
3952
+ pendingAdvancedFilterState: initAdvancedFilterState,
3739
3953
  pageNumber: 1,
3740
3954
  hasAnotherPage: false,
3741
3955
  numPages: 1,
3956
+ userMadeFilterChange: false,
3742
3957
  };
3743
3958
  // Initialize state
3744
3959
  const [state, dispatch] = React.useReducer(reducer$7, initialState);
3745
3960
  // Destructure common state
3746
- const { loading, showSpinner, logs, expandedFilterDrawer, dateFilterState, contextFilterState, tagFilterState, actionErrorFilterState, advancedFilterState, pageNumber, hasAnotherPage, numPages, } = state;
3961
+ const { loading, showSpinner, logs, expandedFilterDrawer, pendingDateFilterState, pendingContextFilterState, pendingTagFilterState, pendingActionErrorFilterState, pendingAdvancedFilterState, pageNumber, numPages, userMadeFilterChange, } = state;
3962
+ /* -------------- Refs -------------- */
3963
+ // Initialize refs
3964
+ const activeFiltersRef = React.useRef({
3965
+ dateFilterState: JSON.parse(JSON.stringify(pendingDateFilterState)),
3966
+ contextFilterState: JSON.parse(JSON.stringify(pendingContextFilterState)),
3967
+ tagFilterState: JSON.parse(JSON.stringify(pendingTagFilterState)),
3968
+ actionErrorFilterState: JSON.parse(JSON.stringify(pendingActionErrorFilterState)),
3969
+ advancedFilterState: JSON.parse(JSON.stringify(pendingAdvancedFilterState)),
3970
+ });
3747
3971
  /*------------------------------------------------------------------------*/
3748
3972
  /* ------------------------- Component Functions ------------------------ */
3749
3973
  /*------------------------------------------------------------------------*/
@@ -3802,11 +4026,11 @@ const LogReviewer = (props) => {
3802
4026
  React.useEffect(() => {
3803
4027
  fetchLogs({
3804
4028
  filters: {
3805
- dateFilterState,
3806
- contextFilterState,
3807
- tagFilterState,
3808
- actionErrorFilterState,
3809
- advancedFilterState,
4029
+ dateFilterState: pendingDateFilterState,
4030
+ contextFilterState: pendingContextFilterState,
4031
+ tagFilterState: pendingTagFilterState,
4032
+ actionErrorFilterState: pendingActionErrorFilterState,
4033
+ advancedFilterState: pendingAdvancedFilterState,
3810
4034
  },
3811
4035
  pageNum: 1,
3812
4036
  filtersChanged: true,
@@ -3829,51 +4053,25 @@ const LogReviewer = (props) => {
3829
4053
  /*----------------------------------------*/
3830
4054
  /* ------------ Pagination -------------- */
3831
4055
  /*----------------------------------------*/
3832
- const paginationControls = logs.length > 0 && (React__default["default"].createElement("div", { className: "text-center mt-3" },
3833
- React__default["default"].createElement("button", { type: "button", className: "btn btn-secondary me-2", disabled: pageNumber <= 1 || loading, onClick: () => {
3834
- fetchLogs({
3835
- filters: {
3836
- dateFilterState,
3837
- contextFilterState,
3838
- tagFilterState,
3839
- actionErrorFilterState,
3840
- advancedFilterState,
3841
- },
3842
- pageNum: pageNumber - 1,
3843
- filtersChanged: false,
3844
- });
3845
- } },
3846
- React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.faArrowLeft, className: "me-2" }),
3847
- "Previous Page"),
3848
- React__default["default"].createElement("span", { className: "mx-3" },
3849
- "Page",
3850
- ' ',
3851
- pageNumber,
3852
- ' ',
3853
- "of",
3854
- ' ',
3855
- numPages),
3856
- React__default["default"].createElement("button", { type: "button", className: "btn btn-secondary ms-2", disabled: !hasAnotherPage || loading, onClick: () => {
3857
- fetchLogs({
3858
- filters: {
3859
- dateFilterState,
3860
- contextFilterState,
3861
- tagFilterState,
3862
- actionErrorFilterState,
3863
- advancedFilterState,
3864
- },
3865
- pageNum: pageNumber + 1,
3866
- filtersChanged: false,
3867
- });
3868
- } },
3869
- "Next Page",
3870
- React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.faArrowRight, className: "ms-2" }))));
4056
+ const paginationControls = logs.length > 0 && (React__default["default"].createElement(Pagination, { currentPage: pageNumber, numPages: numPages, loading: loading, onPageChanged: (targetPage) => {
4057
+ const { current: activeFilters } = activeFiltersRef;
4058
+ fetchLogs({
4059
+ filters: {
4060
+ dateFilterState: activeFilters.dateFilterState,
4061
+ contextFilterState: activeFilters.contextFilterState,
4062
+ tagFilterState: activeFilters.tagFilterState,
4063
+ actionErrorFilterState: activeFilters.actionErrorFilterState,
4064
+ advancedFilterState: activeFilters.advancedFilterState,
4065
+ },
4066
+ pageNum: targetPage,
4067
+ filtersChanged: false,
4068
+ });
4069
+ } }));
3871
4070
  /*----------------------------------------*/
3872
4071
  /* --------------- Filters -------------- */
3873
4072
  /*----------------------------------------*/
3874
4073
  // Filter toggle
3875
4074
  const filterToggles = (React__default["default"].createElement("div", { className: "LogReviewer-filter-toggles" },
3876
- React__default["default"].createElement("h3", { className: "m-0" }, "Filters:"),
3877
4075
  React__default["default"].createElement("div", { className: "LogReviewer-filter-toggle-buttons alert alert-secondary p-2 m-0" },
3878
4076
  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: () => {
3879
4077
  dispatch({
@@ -3942,17 +4140,29 @@ const LogReviewer = (props) => {
3942
4140
  React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.faTimes }),
3943
4141
  ' ',
3944
4142
  "Reset"),
3945
- React__default["default"].createElement("button", { type: "button", id: "LogReviewer-submit-filters-button", className: "btn btn-primary ms-2", "aria-label": "submit filters", onClick: () => {
4143
+ userMadeFilterChange && (React__default["default"].createElement("button", { type: "button", id: "LogReviewer-submit-filters-button", className: "btn btn-primary ms-2", "aria-label": "submit filters", onClick: () => {
3946
4144
  dispatch({
3947
4145
  type: ActionType$6.HideFilterDrawer,
3948
4146
  });
4147
+ // Reset user made filter change indicator
4148
+ dispatch({
4149
+ type: ActionType$6.ResetUserMadeFilterChange,
4150
+ });
4151
+ // Save active filters
4152
+ activeFiltersRef.current = {
4153
+ dateFilterState: JSON.parse(JSON.stringify(pendingDateFilterState)),
4154
+ contextFilterState: JSON.parse(JSON.stringify(pendingContextFilterState)),
4155
+ tagFilterState: JSON.parse(JSON.stringify(pendingTagFilterState)),
4156
+ actionErrorFilterState: JSON.parse(JSON.stringify(pendingActionErrorFilterState)),
4157
+ advancedFilterState: JSON.parse(JSON.stringify(pendingAdvancedFilterState)),
4158
+ };
3949
4159
  fetchLogs({
3950
4160
  filters: {
3951
- dateFilterState,
3952
- contextFilterState,
3953
- tagFilterState,
3954
- actionErrorFilterState,
3955
- advancedFilterState,
4161
+ dateFilterState: pendingDateFilterState,
4162
+ contextFilterState: pendingContextFilterState,
4163
+ tagFilterState: pendingTagFilterState,
4164
+ actionErrorFilterState: pendingActionErrorFilterState,
4165
+ advancedFilterState: pendingAdvancedFilterState,
3956
4166
  },
3957
4167
  pageNum: 1,
3958
4168
  filtersChanged: true,
@@ -3960,35 +4170,35 @@ const LogReviewer = (props) => {
3960
4170
  } },
3961
4171
  React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.faSearch }),
3962
4172
  ' ',
3963
- "Filter"))));
4173
+ "Apply Filters")))));
3964
4174
  // Filter drawer
3965
4175
  let filterDrawer;
3966
4176
  if (expandedFilterDrawer) {
3967
4177
  if (expandedFilterDrawer === FilterDrawer.Date) {
3968
4178
  filterDrawer = (React__default["default"].createElement(TabBox, { title: "Date" },
3969
- 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) => {
3970
- dateFilterState.startDate = { month, day, year };
4179
+ React__default["default"].createElement(SimpleDateChooser, { ariaLabel: "filter start date", name: "filter-start-date", year: pendingDateFilterState.startDate.year, month: pendingDateFilterState.startDate.month, day: pendingDateFilterState.startDate.day, chooseFromPast: true, numMonthsToShow: 36, onChange: (month, day, year) => {
4180
+ pendingDateFilterState.startDate = { month, day, year };
3971
4181
  dispatch({
3972
4182
  type: ActionType$6.UpdateDateFilterState,
3973
- dateFilterState,
4183
+ dateFilterState: pendingDateFilterState,
3974
4184
  });
3975
4185
  } }),
3976
4186
  ' ',
3977
4187
  "to",
3978
4188
  ' ',
3979
- 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) => {
3980
- if (year < dateFilterState.startDate.year
3981
- || (year === dateFilterState.startDate.year
3982
- && month < dateFilterState.startDate.month)
3983
- || (year === dateFilterState.startDate.year
3984
- && month === dateFilterState.startDate.month
3985
- && day < dateFilterState.startDate.day)) {
4189
+ React__default["default"].createElement(SimpleDateChooser, { ariaLabel: "filter end date", name: "filter-end-date", year: pendingDateFilterState.endDate.year, month: pendingDateFilterState.endDate.month, day: pendingDateFilterState.endDate.day, chooseFromPast: true, numMonthsToShow: 12, onChange: (month, day, year) => {
4190
+ if (year < pendingDateFilterState.startDate.year
4191
+ || (year === pendingDateFilterState.startDate.year
4192
+ && month < pendingDateFilterState.startDate.month)
4193
+ || (year === pendingDateFilterState.startDate.year
4194
+ && month === pendingDateFilterState.startDate.month
4195
+ && day < pendingDateFilterState.startDate.day)) {
3986
4196
  return alert('Invalid Start Date', 'The start date cannot be before the end date.');
3987
4197
  }
3988
- dateFilterState.endDate = { month, day, year };
4198
+ pendingDateFilterState.endDate = { month, day, year };
3989
4199
  dispatch({
3990
4200
  type: ActionType$6.UpdateDateFilterState,
3991
- dateFilterState,
4201
+ dateFilterState: pendingDateFilterState,
3992
4202
  });
3993
4203
  } })));
3994
4204
  }
@@ -4010,7 +4220,7 @@ const LogReviewer = (props) => {
4010
4220
  id: context,
4011
4221
  name: genHumanReadableName(context),
4012
4222
  isGroup: false,
4013
- checked: !!contextFilterState[context],
4223
+ checked: !!pendingContextFilterState[context],
4014
4224
  };
4015
4225
  // Add built-in items to its own folder
4016
4226
  const isBuiltIn = context in LogBuiltInMetadata.Context;
@@ -4036,7 +4246,7 @@ const LogReviewer = (props) => {
4036
4246
  id: subcontext,
4037
4247
  name: genHumanReadableName(subcontext),
4038
4248
  isGroup: false,
4039
- checked: contextFilterState[context][subcontext],
4249
+ checked: pendingContextFilterState[context][subcontext],
4040
4250
  };
4041
4251
  }));
4042
4252
  const item = {
@@ -4059,7 +4269,7 @@ const LogReviewer = (props) => {
4059
4269
  // Built-in
4060
4270
  // Treat as if these were top-level contexts
4061
4271
  pickableItem.children.forEach((subcontextItem) => {
4062
- contextFilterState[subcontextItem.id] = ('checked' in subcontextItem
4272
+ pendingContextFilterState[subcontextItem.id] = ('checked' in subcontextItem
4063
4273
  && subcontextItem.checked);
4064
4274
  });
4065
4275
  }
@@ -4067,19 +4277,19 @@ const LogReviewer = (props) => {
4067
4277
  // Not built-in
4068
4278
  pickableItem.children.forEach((subcontextItem) => {
4069
4279
  if (!subcontextItem.isGroup) {
4070
- contextFilterState[pickableItem.id][subcontextItem.id] = (subcontextItem.checked);
4280
+ pendingContextFilterState[pickableItem.id][subcontextItem.id] = (subcontextItem.checked);
4071
4281
  }
4072
4282
  });
4073
4283
  }
4074
4284
  }
4075
4285
  else {
4076
4286
  // No subcontexts
4077
- contextFilterState[pickableItem.id] = (pickableItem.checked);
4287
+ pendingContextFilterState[pickableItem.id] = (pickableItem.checked);
4078
4288
  }
4079
4289
  });
4080
4290
  dispatch({
4081
4291
  type: ActionType$6.UpdateContextFilterState,
4082
- contextFilterState,
4292
+ contextFilterState: pendingContextFilterState,
4083
4293
  });
4084
4294
  } }));
4085
4295
  }
@@ -4090,13 +4300,13 @@ const LogReviewer = (props) => {
4090
4300
  React__default["default"].createElement("div", { className: "d-flex gap-1 flex-wrap" }, Object.keys((_d = LogMetadata.Tag) !== null && _d !== void 0 ? _d : {})
4091
4301
  .map((tag) => {
4092
4302
  const description = genHumanReadableName(tag);
4093
- 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) => {
4094
- tagFilterState[tag] = checked;
4303
+ 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: pendingTagFilterState[tag], onChanged: (checked) => {
4304
+ pendingTagFilterState[tag] = checked;
4095
4305
  dispatch({
4096
4306
  type: ActionType$6.UpdateTagFilterState,
4097
- tagFilterState,
4307
+ tagFilterState: pendingTagFilterState,
4098
4308
  });
4099
- } }));
4309
+ }, checkedVariant: Variant$1.Light, uncheckedVariant: Variant$1.Light }));
4100
4310
  }))));
4101
4311
  }
4102
4312
  else if (expandedFilterDrawer === FilterDrawer.Action) {
@@ -4104,70 +4314,70 @@ const LogReviewer = (props) => {
4104
4314
  filterDrawer = (React__default["default"].createElement(React__default["default"].Fragment, null,
4105
4315
  React__default["default"].createElement(TabBox, { title: "Log Type" },
4106
4316
  React__default["default"].createElement(RadioButton, { id: "LogReviewer-type-all", text: "All Logs", onSelected: () => {
4107
- actionErrorFilterState.type = undefined;
4317
+ pendingActionErrorFilterState.type = undefined;
4108
4318
  dispatch({
4109
4319
  type: ActionType$6.UpdateActionErrorFilterState,
4110
- actionErrorFilterState,
4320
+ actionErrorFilterState: pendingActionErrorFilterState,
4111
4321
  });
4112
- }, ariaLabel: "show logs of all types", selected: actionErrorFilterState.type === undefined }),
4322
+ }, ariaLabel: "show logs of all types", selected: pendingActionErrorFilterState.type === undefined, unselectedVariant: Variant$1.Light }),
4113
4323
  React__default["default"].createElement(RadioButton, { id: "LogReviewer-type-action-only", text: "Action Logs Only", onSelected: () => {
4114
- actionErrorFilterState.type = LogType$1.Action;
4324
+ pendingActionErrorFilterState.type = LogType$1.Action;
4115
4325
  dispatch({
4116
4326
  type: ActionType$6.UpdateActionErrorFilterState,
4117
- actionErrorFilterState,
4327
+ actionErrorFilterState: pendingActionErrorFilterState,
4118
4328
  });
4119
- }, ariaLabel: "only show action logs", selected: actionErrorFilterState.type === LogType$1.Action }),
4329
+ }, ariaLabel: "only show action logs", selected: pendingActionErrorFilterState.type === LogType$1.Action, unselectedVariant: Variant$1.Light }),
4120
4330
  React__default["default"].createElement(RadioButton, { id: "LogReviewer-type-error-only", text: "Action Error Only", onSelected: () => {
4121
- actionErrorFilterState.type = LogType$1.Error;
4331
+ pendingActionErrorFilterState.type = LogType$1.Error;
4122
4332
  dispatch({
4123
4333
  type: ActionType$6.UpdateActionErrorFilterState,
4124
- actionErrorFilterState,
4334
+ actionErrorFilterState: pendingActionErrorFilterState,
4125
4335
  });
4126
- }, ariaLabel: "only show error logs", selected: actionErrorFilterState.type === LogType$1.Error, noMarginOnRight: true })),
4127
- (actionErrorFilterState.type === undefined
4128
- || actionErrorFilterState.type === LogType$1.Action) && (React__default["default"].createElement(TabBox, { title: "Action Log Details" },
4336
+ }, ariaLabel: "only show error logs", selected: pendingActionErrorFilterState.type === LogType$1.Error, noMarginOnRight: true, selectedVariant: Variant$1.Light, unselectedVariant: Variant$1.Light })),
4337
+ (pendingActionErrorFilterState.type === undefined
4338
+ || pendingActionErrorFilterState.type === LogType$1.Action) && (React__default["default"].createElement(TabBox, { title: "Action Log Details" },
4129
4339
  React__default["default"].createElement(ButtonInputGroup, { label: "Action", className: "mb-2", wrapButtonsAndAddGaps: true }, Object.keys(LogAction$1)
4130
4340
  .map((action) => {
4131
4341
  const description = genHumanReadableName(action);
4132
- 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) => {
4133
- actionErrorFilterState.action[action] = checked;
4342
+ 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: pendingActionErrorFilterState.action[action], onChanged: (checked) => {
4343
+ pendingActionErrorFilterState.action[action] = checked;
4134
4344
  dispatch({
4135
4345
  type: ActionType$6.UpdateActionErrorFilterState,
4136
- actionErrorFilterState,
4346
+ actionErrorFilterState: pendingActionErrorFilterState,
4137
4347
  });
4138
- } }));
4348
+ }, checkedVariant: Variant$1.Light, uncheckedVariant: Variant$1.Light }));
4139
4349
  })),
4140
4350
  React__default["default"].createElement(ButtonInputGroup, { label: "Target", wrapButtonsAndAddGaps: true }, Object.keys(targetMap)
4141
4351
  .map((target) => {
4142
4352
  const description = genHumanReadableName(target);
4143
- 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) => {
4144
- actionErrorFilterState.target[target] = checked;
4353
+ return (React__default["default"].createElement(CheckboxButton, { key: target, id: `LogReviewer-target-${target}-checkbox`, text: description, ariaLabel: `include logs with target "${description}" in results`, checked: pendingActionErrorFilterState.target[target], noMarginOnRight: true, onChanged: (checked) => {
4354
+ pendingActionErrorFilterState.target[target] = checked;
4145
4355
  dispatch({
4146
4356
  type: ActionType$6.UpdateActionErrorFilterState,
4147
- actionErrorFilterState,
4357
+ actionErrorFilterState: pendingActionErrorFilterState,
4148
4358
  });
4149
- } }));
4359
+ }, checkedVariant: Variant$1.Light, uncheckedVariant: Variant$1.Light }));
4150
4360
  })))),
4151
- (actionErrorFilterState.type === undefined
4152
- || actionErrorFilterState.type === LogType$1.Error) && (React__default["default"].createElement(TabBox, { title: "Error Log Details" },
4361
+ (pendingActionErrorFilterState.type === undefined
4362
+ || pendingActionErrorFilterState.type === LogType$1.Error) && (React__default["default"].createElement(TabBox, { title: "Error Log Details" },
4153
4363
  React__default["default"].createElement("div", { className: "input-group mb-2" },
4154
4364
  React__default["default"].createElement("span", { className: "input-group-text" }, "Error Message"),
4155
- 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) => {
4156
- actionErrorFilterState.errorMessage = e.target.value;
4365
+ React__default["default"].createElement("input", { type: "text", className: "form-control", "aria-label": "query for error message", value: pendingActionErrorFilterState.errorMessage, placeholder: "e.g. undefined is not a function", onChange: (e) => {
4366
+ pendingActionErrorFilterState.errorMessage = e.target.value;
4157
4367
  dispatch({
4158
4368
  type: ActionType$6.UpdateActionErrorFilterState,
4159
- actionErrorFilterState,
4369
+ actionErrorFilterState: pendingActionErrorFilterState,
4160
4370
  });
4161
4371
  } })),
4162
4372
  React__default["default"].createElement("div", { className: "input-group mb-2" },
4163
4373
  React__default["default"].createElement("span", { className: "input-group-text" }, "Error Code"),
4164
- 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) => {
4165
- actionErrorFilterState.errorCode = ((e.target.value)
4374
+ React__default["default"].createElement("input", { type: "text", className: "form-control", "aria-label": "query for error code", value: pendingActionErrorFilterState.errorCode, placeholder: "e.g. GC22", onChange: (e) => {
4375
+ pendingActionErrorFilterState.errorCode = ((e.target.value)
4166
4376
  .trim()
4167
4377
  .toUpperCase());
4168
4378
  dispatch({
4169
4379
  type: ActionType$6.UpdateActionErrorFilterState,
4170
- actionErrorFilterState,
4380
+ actionErrorFilterState: pendingActionErrorFilterState,
4171
4381
  });
4172
4382
  } }))))));
4173
4383
  }
@@ -4177,157 +4387,157 @@ const LogReviewer = (props) => {
4177
4387
  React__default["default"].createElement(TabBox, { title: "User" },
4178
4388
  React__default["default"].createElement("div", { className: "input-group mb-2" },
4179
4389
  React__default["default"].createElement("span", { className: "input-group-text" }, "User First Name"),
4180
- 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) => {
4181
- advancedFilterState.userFirstName = e.target.value;
4390
+ React__default["default"].createElement("input", { type: "text", className: "form-control", "aria-label": "query for user first name", value: pendingAdvancedFilterState.userFirstName, placeholder: "e.g. Divardo", onChange: (e) => {
4391
+ pendingAdvancedFilterState.userFirstName = e.target.value;
4182
4392
  dispatch({
4183
4393
  type: ActionType$6.UpdateAdvancedFilterState,
4184
- advancedFilterState,
4394
+ advancedFilterState: pendingAdvancedFilterState,
4185
4395
  });
4186
4396
  } })),
4187
4397
  React__default["default"].createElement("div", { className: "input-group mb-2" },
4188
4398
  React__default["default"].createElement("span", { className: "input-group-text" }, "User Last Name"),
4189
- 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) => {
4190
- advancedFilterState.userLastName = e.target.value;
4399
+ React__default["default"].createElement("input", { type: "text", className: "form-control", "aria-label": "query for user last name", value: pendingAdvancedFilterState.userLastName, placeholder: "e.g. Calicci", onChange: (e) => {
4400
+ pendingAdvancedFilterState.userLastName = e.target.value;
4191
4401
  dispatch({
4192
4402
  type: ActionType$6.UpdateAdvancedFilterState,
4193
- advancedFilterState,
4403
+ advancedFilterState: pendingAdvancedFilterState,
4194
4404
  });
4195
4405
  } })),
4196
4406
  React__default["default"].createElement("div", { className: "input-group mb-2" },
4197
4407
  React__default["default"].createElement("span", { className: "input-group-text" }, "User Email"),
4198
- 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) => {
4199
- advancedFilterState.userEmail = ((e.target.value)
4408
+ React__default["default"].createElement("input", { type: "text", className: "form-control", "aria-label": "query for user email", value: pendingAdvancedFilterState.userEmail, placeholder: "e.g. calicci@fas.harvard.edu", onChange: (e) => {
4409
+ pendingAdvancedFilterState.userEmail = ((e.target.value)
4200
4410
  .trim());
4201
4411
  dispatch({
4202
4412
  type: ActionType$6.UpdateAdvancedFilterState,
4203
- advancedFilterState,
4413
+ advancedFilterState: pendingAdvancedFilterState,
4204
4414
  });
4205
4415
  } })),
4206
4416
  React__default["default"].createElement("div", { className: "input-group mb-2" },
4207
4417
  React__default["default"].createElement("span", { className: "input-group-text" }, "User Canvas Id"),
4208
- 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) => {
4418
+ React__default["default"].createElement("input", { type: "text", className: "form-control", "aria-label": "query for user canvas id", value: pendingAdvancedFilterState.userId, placeholder: "e.g. 104985", onChange: (e) => {
4209
4419
  const { value } = e.target;
4210
4420
  // Only update if value contains only numbers
4211
4421
  if (/^\d+$/.test(value) || value === '') {
4212
- advancedFilterState.userId = ((e.target.value)
4422
+ pendingAdvancedFilterState.userId = ((e.target.value)
4213
4423
  .trim());
4214
4424
  }
4215
4425
  dispatch({
4216
4426
  type: ActionType$6.UpdateAdvancedFilterState,
4217
- advancedFilterState,
4427
+ advancedFilterState: pendingAdvancedFilterState,
4218
4428
  });
4219
4429
  } })),
4220
4430
  React__default["default"].createElement(ButtonInputGroup, { label: "Role" },
4221
4431
  React__default["default"].createElement(CheckboxButton, { text: "Students", onChanged: (checked) => {
4222
- advancedFilterState.includeLearners = checked;
4432
+ pendingAdvancedFilterState.includeLearners = checked;
4223
4433
  dispatch({
4224
4434
  type: ActionType$6.UpdateAdvancedFilterState,
4225
- advancedFilterState,
4435
+ advancedFilterState: pendingAdvancedFilterState,
4226
4436
  });
4227
- }, checked: advancedFilterState.includeLearners, ariaLabel: "show logs from students" }),
4437
+ }, checked: pendingAdvancedFilterState.includeLearners, ariaLabel: "show logs from students", checkedVariant: Variant$1.Light, uncheckedVariant: Variant$1.Light }),
4228
4438
  React__default["default"].createElement(CheckboxButton, { text: "Teaching Team Members", onChanged: (checked) => {
4229
- advancedFilterState.includeTTMs = checked;
4439
+ pendingAdvancedFilterState.includeTTMs = checked;
4230
4440
  dispatch({
4231
4441
  type: ActionType$6.UpdateAdvancedFilterState,
4232
- advancedFilterState,
4442
+ advancedFilterState: pendingAdvancedFilterState,
4233
4443
  });
4234
- }, checked: advancedFilterState.includeTTMs, ariaLabel: "show logs from teaching team members" }),
4444
+ }, checked: pendingAdvancedFilterState.includeTTMs, ariaLabel: "show logs from teaching team members", checkedVariant: Variant$1.Light, uncheckedVariant: Variant$1.Light }),
4235
4445
  React__default["default"].createElement(CheckboxButton, { text: "Admins", onChanged: (checked) => {
4236
- advancedFilterState.includeAdmins = checked;
4446
+ pendingAdvancedFilterState.includeAdmins = checked;
4237
4447
  dispatch({
4238
4448
  type: ActionType$6.UpdateAdvancedFilterState,
4239
- advancedFilterState,
4449
+ advancedFilterState: pendingAdvancedFilterState,
4240
4450
  });
4241
- }, checked: advancedFilterState.includeAdmins, ariaLabel: "show logs from admins" }))),
4451
+ }, checked: pendingAdvancedFilterState.includeAdmins, ariaLabel: "show logs from admins", checkedVariant: Variant$1.Light, uncheckedVariant: Variant$1.Light }))),
4242
4452
  React__default["default"].createElement(TabBox, { title: "Course" },
4243
4453
  React__default["default"].createElement("div", { className: "input-group mb-2" },
4244
4454
  React__default["default"].createElement("span", { className: "input-group-text" }, "Course Name"),
4245
- 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) => {
4246
- advancedFilterState.courseName = e.target.value;
4455
+ React__default["default"].createElement("input", { type: "text", className: "form-control", "aria-label": "query for course name", value: pendingAdvancedFilterState.courseName, placeholder: "e.g. GLC 200", onChange: (e) => {
4456
+ pendingAdvancedFilterState.courseName = e.target.value;
4247
4457
  dispatch({
4248
4458
  type: ActionType$6.UpdateAdvancedFilterState,
4249
- advancedFilterState,
4459
+ advancedFilterState: pendingAdvancedFilterState,
4250
4460
  });
4251
4461
  } })),
4252
4462
  React__default["default"].createElement("div", { className: "input-group mb-2" },
4253
4463
  React__default["default"].createElement("span", { className: "input-group-text" }, "Course Canvas Id"),
4254
- 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) => {
4464
+ React__default["default"].createElement("input", { type: "text", className: "form-control", "aria-label": "query for course canvas id", value: pendingAdvancedFilterState.courseId, placeholder: "e.g. 15948", onChange: (e) => {
4255
4465
  const { value } = e.target;
4256
4466
  // Only update if value contains only numbers
4257
4467
  if (/^\d+$/.test(value)) {
4258
- advancedFilterState.courseId = ((e.target.value)
4468
+ pendingAdvancedFilterState.courseId = ((e.target.value)
4259
4469
  .trim());
4260
4470
  }
4261
4471
  dispatch({
4262
4472
  type: ActionType$6.UpdateAdvancedFilterState,
4263
- advancedFilterState,
4473
+ advancedFilterState: pendingAdvancedFilterState,
4264
4474
  });
4265
4475
  } }))),
4266
4476
  React__default["default"].createElement(TabBox, { title: "Device" },
4267
4477
  React__default["default"].createElement(ButtonInputGroup, { label: "Device Type" },
4268
- React__default["default"].createElement(RadioButton, { text: "All Devices", ariaLabel: "show logs from all devices", selected: advancedFilterState.isMobile === undefined, onSelected: () => {
4269
- advancedFilterState.isMobile = undefined;
4478
+ React__default["default"].createElement(RadioButton, { text: "All Devices", ariaLabel: "show logs from all devices", selected: pendingAdvancedFilterState.isMobile === undefined, onSelected: () => {
4479
+ pendingAdvancedFilterState.isMobile = undefined;
4270
4480
  dispatch({
4271
4481
  type: ActionType$6.UpdateAdvancedFilterState,
4272
- advancedFilterState,
4482
+ advancedFilterState: pendingAdvancedFilterState,
4273
4483
  });
4274
- } }),
4275
- React__default["default"].createElement(RadioButton, { text: "Mobile Only", ariaLabel: "show logs from mobile devices", selected: advancedFilterState.isMobile === true, onSelected: () => {
4276
- advancedFilterState.isMobile = true;
4484
+ }, selectedVariant: Variant$1.Light, unselectedVariant: Variant$1.Light }),
4485
+ React__default["default"].createElement(RadioButton, { text: "Mobile Only", ariaLabel: "show logs from mobile devices", selected: pendingAdvancedFilterState.isMobile === true, onSelected: () => {
4486
+ pendingAdvancedFilterState.isMobile = true;
4277
4487
  dispatch({
4278
4488
  type: ActionType$6.UpdateAdvancedFilterState,
4279
- advancedFilterState,
4489
+ advancedFilterState: pendingAdvancedFilterState,
4280
4490
  });
4281
- } }),
4282
- React__default["default"].createElement(RadioButton, { text: "Desktop Only", ariaLabel: "show logs from desktop devices", selected: advancedFilterState.isMobile === false, onSelected: () => {
4283
- advancedFilterState.isMobile = false;
4491
+ }, selectedVariant: Variant$1.Light, unselectedVariant: Variant$1.Light }),
4492
+ React__default["default"].createElement(RadioButton, { text: "Desktop Only", ariaLabel: "show logs from desktop devices", selected: pendingAdvancedFilterState.isMobile === false, onSelected: () => {
4493
+ pendingAdvancedFilterState.isMobile = false;
4284
4494
  dispatch({
4285
4495
  type: ActionType$6.UpdateAdvancedFilterState,
4286
- advancedFilterState,
4496
+ advancedFilterState: pendingAdvancedFilterState,
4287
4497
  });
4288
- }, noMarginOnRight: true }))),
4498
+ }, noMarginOnRight: true, selectedVariant: Variant$1.Light, unselectedVariant: Variant$1.Light }))),
4289
4499
  React__default["default"].createElement(TabBox, { title: "Source" },
4290
4500
  React__default["default"].createElement(ButtonInputGroup, { label: "Source Type" },
4291
- React__default["default"].createElement(RadioButton, { text: "Both", ariaLabel: "show logs from all sources", selected: advancedFilterState.source === undefined, onSelected: () => {
4292
- advancedFilterState.source = undefined;
4501
+ React__default["default"].createElement(RadioButton, { text: "Both", ariaLabel: "show logs from all sources", selected: pendingAdvancedFilterState.source === undefined, onSelected: () => {
4502
+ pendingAdvancedFilterState.source = undefined;
4293
4503
  dispatch({
4294
4504
  type: ActionType$6.UpdateAdvancedFilterState,
4295
- advancedFilterState,
4505
+ advancedFilterState: pendingAdvancedFilterState,
4296
4506
  });
4297
- } }),
4298
- React__default["default"].createElement(RadioButton, { text: "Client Only", ariaLabel: "show logs from client source", selected: advancedFilterState.source === LogSource$1.Client, onSelected: () => {
4299
- advancedFilterState.source = LogSource$1.Client;
4507
+ }, selectedVariant: Variant$1.Light, unselectedVariant: Variant$1.Light }),
4508
+ React__default["default"].createElement(RadioButton, { text: "Client Only", ariaLabel: "show logs from client source", selected: pendingAdvancedFilterState.source === LogSource$1.Client, onSelected: () => {
4509
+ pendingAdvancedFilterState.source = LogSource$1.Client;
4300
4510
  dispatch({
4301
4511
  type: ActionType$6.UpdateAdvancedFilterState,
4302
- advancedFilterState,
4512
+ advancedFilterState: pendingAdvancedFilterState,
4303
4513
  });
4304
- } }),
4305
- React__default["default"].createElement(RadioButton, { text: "Server Only", ariaLabel: "show logs from server source", selected: advancedFilterState.source === LogSource$1.Server, onSelected: () => {
4306
- advancedFilterState.source = LogSource$1.Server;
4514
+ }, selectedVariant: Variant$1.Light, unselectedVariant: Variant$1.Light }),
4515
+ React__default["default"].createElement(RadioButton, { text: "Server Only", ariaLabel: "show logs from server source", selected: pendingAdvancedFilterState.source === LogSource$1.Server, onSelected: () => {
4516
+ pendingAdvancedFilterState.source = LogSource$1.Server;
4307
4517
  dispatch({
4308
4518
  type: ActionType$6.UpdateAdvancedFilterState,
4309
- advancedFilterState,
4519
+ advancedFilterState: pendingAdvancedFilterState,
4310
4520
  });
4311
- }, noMarginOnRight: true })),
4312
- advancedFilterState.source !== LogSource$1.Client && (React__default["default"].createElement("div", { className: "mt-2" },
4521
+ }, noMarginOnRight: true, selectedVariant: Variant$1.Light, unselectedVariant: Variant$1.Light })),
4522
+ pendingAdvancedFilterState.source !== LogSource$1.Client && (React__default["default"].createElement("div", { className: "mt-2" },
4313
4523
  React__default["default"].createElement("div", { className: "input-group mb-2" },
4314
4524
  React__default["default"].createElement("span", { className: "input-group-text" }, "Server Route Path"),
4315
- 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) => {
4316
- advancedFilterState.courseName = ((e.target.value)
4525
+ React__default["default"].createElement("input", { type: "text", className: "form-control", "aria-label": "query for server route path", value: pendingAdvancedFilterState.routePath, placeholder: "e.g. /api/ttm/courses/12345", onChange: (e) => {
4526
+ pendingAdvancedFilterState.courseName = ((e.target.value)
4317
4527
  .trim());
4318
4528
  dispatch({
4319
4529
  type: ActionType$6.UpdateAdvancedFilterState,
4320
- advancedFilterState,
4530
+ advancedFilterState: pendingAdvancedFilterState,
4321
4531
  });
4322
4532
  } })),
4323
4533
  React__default["default"].createElement("div", { className: "input-group mb-2" },
4324
4534
  React__default["default"].createElement("span", { className: "input-group-text" }, "Server Route Template"),
4325
- 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) => {
4326
- advancedFilterState.courseName = ((e.target.value)
4535
+ React__default["default"].createElement("input", { type: "text", className: "form-control", "aria-label": "query for server route template", value: pendingAdvancedFilterState.routeTemplate, placeholder: "e.g. /api/ttm/courses/:courseId", onChange: (e) => {
4536
+ pendingAdvancedFilterState.courseName = ((e.target.value)
4327
4537
  .trim());
4328
4538
  dispatch({
4329
4539
  type: ActionType$6.UpdateAdvancedFilterState,
4330
- advancedFilterState,
4540
+ advancedFilterState: pendingAdvancedFilterState,
4331
4541
  });
4332
4542
  } })))))));
4333
4543
  }
@@ -4341,10 +4551,10 @@ const LogReviewer = (props) => {
4341
4551
  body = (React__default["default"].createElement(React__default["default"].Fragment, null,
4342
4552
  filters,
4343
4553
  React__default["default"].createElement("div", { className: "mt-2" },
4344
- React__default["default"].createElement(IntelliTable, { title: "Matching Logs:", csvName: `Logs from ${getHumanReadableDate()}`, id: "logs", data: logs, columns: columns }),
4345
- logs.length === 0 && (React__default["default"].createElement("div", { className: "alert alert-warning text-center mt-2" },
4554
+ logs.length === 0 ? (React__default["default"].createElement("div", { className: "alert alert-warning text-center mt-2" },
4346
4555
  React__default["default"].createElement("h4", { className: "m-1" }, "No Logs to Show"),
4347
- React__default["default"].createElement("div", null, "Either your filters are too strict or no matching logs have been created yet."))),
4556
+ React__default["default"].createElement("div", null, "Either your filters are too strict or no matching logs have been created yet.")))
4557
+ : (React__default["default"].createElement(IntelliTable, { title: "Matching Logs", csvName: `Logs from ${getHumanReadableDate()}`, id: "logs", data: logs, columns: columns })),
4348
4558
  paginationControls)));
4349
4559
  }
4350
4560
  /* ---------- Wrap in Modal --------- */
@@ -4353,7 +4563,7 @@ const LogReviewer = (props) => {
4353
4563
  React__default["default"].createElement("div", { className: "LogReviewer-inner-container" },
4354
4564
  React__default["default"].createElement("div", { className: "LogReviewer-header" },
4355
4565
  React__default["default"].createElement("div", { className: "LogReviewer-header-title" },
4356
- React__default["default"].createElement("h3", { className: "text-center m-0" }, "Log Review Dashboard")),
4566
+ React__default["default"].createElement("h3", { className: "text-center" }, "Log Review Dashboard")),
4357
4567
  React__default["default"].createElement("div", { style: { width: 0 } },
4358
4568
  React__default["default"].createElement("button", { type: "button", className: "LogReviewer-header-close-button btn btn-dark btn-lg pe-0", "aria-label": "close log reviewer panel", onClick: onClose },
4359
4569
  React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.faTimes })))),
@@ -13688,6 +13898,12 @@ const padZerosLeft = (num, numDigits) => {
13688
13898
  */
13689
13899
  const LOG_REVIEW_STATUS_ROUTE = `${ROUTE_PATH_PREFIX}/logs/access_allowed`;
13690
13900
 
13901
+ /**
13902
+ * Log reviewer page size
13903
+ * @author Yuen Ler Chow
13904
+ */
13905
+ const LOG_REVIEW_PAGE_SIZE = 100;
13906
+
13691
13907
  // Stored copy of caccl functions
13692
13908
  let _cacclGetLaunchInfo;
13693
13909
  // Stored copy of dce-mango log collection
@@ -14036,12 +14252,12 @@ const initServer = (opts) => {
14036
14252
  // Query for logs
14037
14253
  const response = yield _logCollection.findPaged({
14038
14254
  query,
14039
- perPage: 50,
14255
+ perPage: LOG_REVIEW_PAGE_SIZE,
14040
14256
  pageNumber,
14041
14257
  });
14042
14258
  // Count documents if requested
14043
14259
  if (countDocuments) {
14044
- response.numPages = Math.ceil((yield _logCollection.count(query)) / 50);
14260
+ response.numPages = Math.ceil((yield _logCollection.count(query)) / LOG_REVIEW_PAGE_SIZE);
14045
14261
  }
14046
14262
  // Return response
14047
14263
  return response;
@@ -14859,16 +15075,16 @@ const genRouteHandler = (opts) => {
14859
15075
  const { timestamp, year, month, day, hour, minute, } = getTimeInfoInET();
14860
15076
  // Main log info
14861
15077
  const mainLogInfo = {
14862
- id: `${launchInfo ? 'unknown' : launchInfo.userId}-${Date.now()}-${Math.floor(Math.random() * 100000)}-${Math.floor(Math.random() * 100000)}`,
14863
- userFirstName: (launchInfo ? 'unknown' : launchInfo.userFirstName),
14864
- userLastName: (launchInfo ? 'unknown' : launchInfo.userLastName),
14865
- userEmail: (launchInfo ? 'unknown' : launchInfo.userEmail),
14866
- userId: (launchInfo ? 'unknown' : launchInfo.userId),
15078
+ id: `${launchInfo ? launchInfo.userId : 'unknown'}-${Date.now()}-${Math.floor(Math.random() * 100000)}-${Math.floor(Math.random() * 100000)}`,
15079
+ userFirstName: (launchInfo ? launchInfo.userFirstName : 'unknown'),
15080
+ userLastName: (launchInfo ? launchInfo.userLastName : 'unknown'),
15081
+ userEmail: (launchInfo ? launchInfo.userEmail : 'unknown'),
15082
+ userId: (launchInfo ? launchInfo.userId : 'unknown'),
14867
15083
  isLearner: (launchInfo && !!launchInfo.isLearner),
14868
15084
  isAdmin: (launchInfo && !!launchInfo.isAdmin),
14869
15085
  isTTM: (launchInfo && !!launchInfo.isTTM),
14870
- courseId: (launchInfo ? 'unknown' : launchInfo.courseId),
14871
- courseName: (launchInfo ? 'unknown' : launchInfo.contextLabel),
15086
+ courseId: (launchInfo ? launchInfo.courseId : 'unknown'),
15087
+ courseName: (launchInfo ? launchInfo.contextLabel : 'unknown'),
14872
15088
  browser,
14873
15089
  device,
14874
15090
  year,
@@ -15275,6 +15491,11 @@ const initLogCollection = (Collection) => {
15275
15491
  'context',
15276
15492
  'subcontext',
15277
15493
  'tags',
15494
+ 'year',
15495
+ 'month',
15496
+ 'day',
15497
+ 'hour',
15498
+ 'type',
15278
15499
  ],
15279
15500
  });
15280
15501
  };
@@ -16296,6 +16517,7 @@ exports.padDecimalZeros = padDecimalZeros;
16296
16517
  exports.padZerosLeft = padZerosLeft;
16297
16518
  exports.parallelLimit = parallelLimit;
16298
16519
  exports.prefixWithAOrAn = prefixWithAOrAn;
16520
+ exports.prompt = prompt;
16299
16521
  exports.roundToNumDecimals = roundToNumDecimals;
16300
16522
  exports.setClientEventMetadataPopulator = setClientEventMetadataPopulator;
16301
16523
  exports.showFatalError = showFatalError;