dce-reactkit 3.9.0-beta-loading-modal.1 → 3.9.0-beta-loading-modal.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/dist/cjs/index.js +784 -202
  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/Dropdown.d.ts +32 -0
  7. package/dist/cjs/types/components/RadioButton.d.ts +2 -0
  8. package/dist/cjs/types/helpers/genRouteHandler.d.ts +8 -0
  9. package/dist/cjs/types/helpers/validators/ChicagoTitleCase.d.ts +9 -0
  10. package/dist/cjs/types/helpers/validators/validURL.d.ts +8 -0
  11. package/dist/cjs/types/helpers/visitEndpointOnAnotherServer/index.d.ts +24 -0
  12. package/dist/cjs/types/helpers/visitEndpointOnAnotherServer/sendServerToServerRequest.d.ts +33 -0
  13. package/dist/cjs/types/index.d.ts +5 -2
  14. package/dist/cjs/types/types/DropdownItemType.d.ts +6 -0
  15. package/dist/cjs/types/types/ModalType.d.ts +1 -0
  16. package/dist/cjs/types/types/ReactKitErrorCode.d.ts +6 -1
  17. package/dist/esm/index.js +780 -203
  18. package/dist/esm/index.js.map +1 -1
  19. package/dist/esm/types/client/initClient.d.ts +16 -2
  20. package/dist/esm/types/components/AppWrapper.d.ts +32 -0
  21. package/dist/esm/types/components/CheckboxButton.d.ts +1 -0
  22. package/dist/esm/types/components/Dropdown.d.ts +32 -0
  23. package/dist/esm/types/components/RadioButton.d.ts +2 -0
  24. package/dist/esm/types/helpers/genRouteHandler.d.ts +8 -0
  25. package/dist/esm/types/helpers/validators/ChicagoTitleCase.d.ts +9 -0
  26. package/dist/esm/types/helpers/validators/validURL.d.ts +8 -0
  27. package/dist/esm/types/helpers/visitEndpointOnAnotherServer/index.d.ts +24 -0
  28. package/dist/esm/types/helpers/visitEndpointOnAnotherServer/sendServerToServerRequest.d.ts +33 -0
  29. package/dist/esm/types/index.d.ts +5 -2
  30. package/dist/esm/types/types/DropdownItemType.d.ts +6 -0
  31. package/dist/esm/types/types/ModalType.d.ts +1 -0
  32. package/dist/esm/types/types/ReactKitErrorCode.d.ts +6 -1
  33. package/dist/index.d.ts +163 -46
  34. package/package.json +2 -1
  35. package/src/client/initClient.tsx +49 -11
  36. package/src/components/AppWrapper.tsx +258 -2
  37. package/src/components/CheckboxButton.tsx +24 -6
  38. package/src/components/Dropdown.tsx +317 -0
  39. package/src/components/IntelliTable.tsx +1 -1
  40. package/src/components/ItemPicker/NestableItemList.tsx +1 -0
  41. package/src/components/LogReviewer.tsx +17 -4
  42. package/src/components/Modal/index.tsx +5 -2
  43. package/src/components/MultiSwitch.tsx +1 -1
  44. package/src/components/RadioButton.tsx +33 -6
  45. package/src/components/SimpleDateChooser.tsx +8 -2
  46. package/src/helpers/genRouteHandler.ts +116 -17
  47. package/src/helpers/logClientEvent.tsx +8 -0
  48. package/src/helpers/validators/ChicagoTitleCase.test.ts +85 -0
  49. package/src/helpers/validators/ChicagoTitleCase.ts +32 -0
  50. package/src/helpers/validators/findURL.test.ts +83 -0
  51. package/src/helpers/validators/findURL.ts +43 -0
  52. package/src/helpers/validators/validURL.test.ts +90 -0
  53. package/src/helpers/validators/validURL.ts +18 -0
  54. package/src/helpers/visitEndpointOnAnotherServer/index.ts +93 -0
  55. package/src/helpers/visitEndpointOnAnotherServer/sendServerToServerRequest.ts +164 -0
  56. package/src/helpers/visitServerEndpoint.tsx +1 -0
  57. package/src/index.ts +8 -0
  58. package/src/server/initLogCollection.ts +5 -0
  59. package/src/server/initServer.ts +11 -5
  60. package/src/types/DropdownItemType.ts +11 -0
  61. package/src/types/ModalType.tsx +1 -0
  62. package/src/types/ReactKitErrorCode.tsx +8 -1
package/dist/cjs/index.js CHANGED
@@ -7,6 +7,7 @@ var freeSolidSvgIcons = require('@fortawesome/free-solid-svg-icons');
7
7
  var reactFontawesome = require('@fortawesome/react-fontawesome');
8
8
  var ReactDOM = require('react-dom');
9
9
  var freeRegularSvgIcons = require('@fortawesome/free-regular-svg-icons');
10
+ var qs = require('qs');
10
11
 
11
12
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
12
13
 
@@ -31,6 +32,7 @@ function _interopNamespace(e) {
31
32
  var React__default = /*#__PURE__*/_interopDefaultLegacy(React);
32
33
  var React__namespace = /*#__PURE__*/_interopNamespace(React);
33
34
  var ReactDOM__default = /*#__PURE__*/_interopDefaultLegacy(ReactDOM);
35
+ var qs__default = /*#__PURE__*/_interopDefaultLegacy(qs);
34
36
 
35
37
  /******************************************************************************
36
38
  Copyright (c) Microsoft Corporation.
@@ -57,7 +59,7 @@ function __awaiter(thisArg, _arguments, P, generator) {
57
59
  });
58
60
  }
59
61
 
60
- // Highest error code = DRK13
62
+ // Highest error code = DRK18
61
63
  /**
62
64
  * List of error codes built into the react kit
63
65
  * @author Gabe Abrams
@@ -69,6 +71,8 @@ var ReactKitErrorCode;
69
71
  ReactKitErrorCode["SessionExpired"] = "DRK3";
70
72
  ReactKitErrorCode["MissingParameter"] = "DRK4";
71
73
  ReactKitErrorCode["InvalidParameter"] = "DRK5";
74
+ ReactKitErrorCode["HostNotAllowed"] = "DRK17";
75
+ ReactKitErrorCode["HostBanned"] = "DRK18";
72
76
  ReactKitErrorCode["WrongCourse"] = "DRK6";
73
77
  ReactKitErrorCode["NoCACCLSendRequestFunction"] = "DRK7";
74
78
  ReactKitErrorCode["NoCACCLGetLaunchInfoFunction"] = "DRK8";
@@ -77,6 +81,10 @@ var ReactKitErrorCode;
77
81
  ReactKitErrorCode["NotAllowedToReviewLogs"] = "DRK11";
78
82
  ReactKitErrorCode["ThemeCheckedBeforeReactKitReady"] = "DRK12";
79
83
  ReactKitErrorCode["SessionExpiredMessageGottenBeforeReactKitReady"] = "DRK13";
84
+ // Server-to-server requests
85
+ ReactKitErrorCode["NotConnected"] = "DRK14";
86
+ ReactKitErrorCode["SelfSigned"] = "DRK15";
87
+ ReactKitErrorCode["ResponseParseError"] = "DRK16";
80
88
  })(ReactKitErrorCode || (ReactKitErrorCode = {}));
81
89
  var ReactKitErrorCode$1 = ReactKitErrorCode;
82
90
 
@@ -178,6 +186,7 @@ var ModalButtonType$1 = ModalButtonType;
178
186
  var ModalType;
179
187
  (function (ModalType) {
180
188
  ModalType["Okay"] = "okay";
189
+ ModalType["Cancel"] = "cancel";
181
190
  ModalType["OkayCancel"] = "okay-cancel";
182
191
  ModalType["YesNo"] = "yes-no";
183
192
  ModalType["YesNoCancel"] = "yes-no-cancel";
@@ -400,11 +409,23 @@ const isDarkModeOn = () => {
400
409
  * @param opts object containing all arguments
401
410
  * @param opts.sendRequest caccl send request functions
402
411
  * @param [opts.sessionExpiredMessage] a custom session expired message
412
+ * @param [opts.darkModeOn] if true, dark mode is enabled
413
+ * @param [opts.noServer] if true, there is no server for this app
403
414
  */
404
415
  const initClient = (opts) => {
405
- // Store values
406
- storedSendRequest = opts.sendRequest;
407
- sessionExpiredMessage = opts.sessionExpiredMessage;
416
+ // Handle separately if there is no server
417
+ if (opts.noServer) {
418
+ // Store values
419
+ storedSendRequest = () => __awaiter(void 0, void 0, void 0, function* () {
420
+ throw new Error('Cannot send requests because there is no server');
421
+ });
422
+ }
423
+ else {
424
+ // Store values
425
+ storedSendRequest = opts.sendRequest;
426
+ sessionExpiredMessage = opts.sessionExpiredMessage;
427
+ }
428
+ // Handle universal parts
408
429
  darkModeOn = !!opts.darkModeOn;
409
430
  // Mark as initialized
410
431
  onInitialized(null);
@@ -425,6 +446,9 @@ const modalTypeToModalButtonTypes = {
425
446
  [ModalType$1.Okay]: [
426
447
  ModalButtonType$1.Okay,
427
448
  ],
449
+ [ModalType$1.Cancel]: [
450
+ ModalButtonType$1.Cancel,
451
+ ],
428
452
  [ModalType$1.OkayCancel]: [
429
453
  ModalButtonType$1.Okay,
430
454
  ModalButtonType$1.Cancel,
@@ -743,7 +767,7 @@ const Modal = (props) => {
743
767
  zIndex: baseZIndex + 2,
744
768
  // Override sizes for even larger for XL
745
769
  width: (size === ModalSize$1.ExtraLarge
746
- ? 'calc(100vw - 3rem)'
770
+ ? 'calc(100vw - 1rem)'
747
771
  : undefined),
748
772
  maxWidth: (size === ModalSize$1.ExtraLarge
749
773
  ? '80rem'
@@ -786,7 +810,7 @@ const Modal = (props) => {
786
810
  backgroundColor: (isDarkModeOn()
787
811
  ? '#444'
788
812
  : undefined),
789
- } }, isLoading && !children ? (React__default["default"].createElement(React__default["default"].Fragment, null,
813
+ } }, isLoading ? (React__default["default"].createElement(React__default["default"].Fragment, null,
790
814
  React__default["default"].createElement(LoadingSpinner, null),
791
815
  React__default["default"].createElement("span", { className: "sr-only" }, "Content loading"))) : (children)),
792
816
  footer && (React__default["default"].createElement("div", { className: "modal-footer pt-1 pb-1", style: {
@@ -880,6 +904,7 @@ const _setStubResponse = (opts) => {
880
904
  */
881
905
  const visitServerEndpoint = (opts) => __awaiter(void 0, void 0, void 0, function* () {
882
906
  var _a, _b, _c;
907
+ // Set default method
883
908
  const method = ((_a = opts.method) !== null && _a !== void 0 ? _a : 'GET');
884
909
  // Handle stubs
885
910
  const stubResponse = (_b = stubResponses[method]) === null || _b === void 0 ? void 0 : _b[opts.path];
@@ -1179,6 +1204,92 @@ const confirm = (title, text, opts) => __awaiter(void 0, void 0, void 0, functio
1179
1204
  });
1180
1205
  });
1181
1206
  /*----------------------------------------*/
1207
+ /* --------------- Prompt -------------- */
1208
+ /*----------------------------------------*/
1209
+ // Stored copies of setters
1210
+ let setPromptInfo;
1211
+ // Function to call when prompt is closed
1212
+ let onPromptClosed;
1213
+ /**
1214
+ * Show a prompt modal asking the user for input
1215
+ * @author Yuen Ler Chow
1216
+ * @param title the title text to display at the top of the prompt
1217
+ * @param [opts={}] additional options for the prompt dialog
1218
+ * @param [opts.textAboveInputField] the text to display in the prompt
1219
+ * @param [opts.defaultText] the default text for the input field
1220
+ * @param [opts.placeholder] the placeholder text for the input field
1221
+ * @param [opts.confirmButtonText=Okay] the text of the confirm button
1222
+ * @param [opts.confirmButtonVariant=Variant.Dark] the variant of the confirm button
1223
+ * @param [opts.cancelButtonText=Cancel] the text of the cancel button
1224
+ * @param [opts.cancelButtonVariant=Variant.Secondary] the variant of the cancel button
1225
+ * @param [opts.minNumChars] the minimum number of characters required for
1226
+ * the input to be valid
1227
+ * @param [opts.findValidationError] a function that takes the input text and
1228
+ * returns an error message if the input is invalid, returns undefined if the
1229
+ * input is valid
1230
+ * @param [opts.ariaLabel] the aria label for the input field
1231
+ * @returns Promise that resolves with the input string or null if canceled
1232
+ */
1233
+ const prompt = (title, opts) => __awaiter(void 0, void 0, void 0, function* () {
1234
+ var _a, _b;
1235
+ // Wait for helper to exist
1236
+ yield waitForHelper(() => {
1237
+ return !!setPromptInfo;
1238
+ });
1239
+ // Fallback if prompt is not available
1240
+ if (!setPromptInfo) {
1241
+ const resultPassesValidation = false;
1242
+ while (!resultPassesValidation) {
1243
+ // eslint-disable-next-line no-alert
1244
+ 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 : '');
1245
+ // Exit loop if user cancels
1246
+ if (result === null) {
1247
+ return null;
1248
+ }
1249
+ // Validate min num chars
1250
+ const minNumCharsValidationError = (((opts === null || opts === void 0 ? void 0 : opts.minNumChars) && result.length < opts.minNumChars)
1251
+ ? `Please enter at least ${opts.minNumChars} characters.`
1252
+ : undefined);
1253
+ // Run custom validation
1254
+ const customValidationError = ((opts === null || opts === void 0 ? void 0 : opts.findValidationError)
1255
+ && opts.findValidationError(result));
1256
+ // Show validation issue
1257
+ if (minNumCharsValidationError || customValidationError) {
1258
+ // Create error message
1259
+ const errorMessage = ([
1260
+ minNumCharsValidationError,
1261
+ customValidationError,
1262
+ ]
1263
+ // Filter out undefined messages
1264
+ .filter((msg) => {
1265
+ return !!msg;
1266
+ })
1267
+ // Join messages with newlines
1268
+ .join('\n'));
1269
+ // Show alert
1270
+ alert('Invalid Input', errorMessage);
1271
+ }
1272
+ else {
1273
+ return result;
1274
+ }
1275
+ }
1276
+ }
1277
+ // Return promise that resolves with result of prompt
1278
+ return new Promise((resolve) => {
1279
+ var _a;
1280
+ // Setup handler
1281
+ onPromptClosed = (result) => {
1282
+ resolve(result);
1283
+ };
1284
+ // Show the prompt
1285
+ setPromptInfo({
1286
+ title,
1287
+ currentInputFieldText: ((_a = opts === null || opts === void 0 ? void 0 : opts.defaultText) !== null && _a !== void 0 ? _a : ''),
1288
+ opts: (opts !== null && opts !== void 0 ? opts : {}),
1289
+ });
1290
+ });
1291
+ });
1292
+ /*----------------------------------------*/
1182
1293
  /* ------------- Fatal Error ------------ */
1183
1294
  /*----------------------------------------*/
1184
1295
  // Stored copies of setters
@@ -1194,14 +1305,14 @@ const fatalErrorHandlers = [];
1194
1305
  * @param [errorTitle] title of the error box
1195
1306
  */
1196
1307
  const showFatalError = (error, errorTitle = 'An Error Occurred') => __awaiter(void 0, void 0, void 0, function* () {
1197
- var _a, _b;
1308
+ var _c, _d;
1198
1309
  // Determine message and code
1199
1310
  const message = (typeof error === 'string'
1200
1311
  ? error.trim()
1201
- : String((_a = error.message) !== null && _a !== void 0 ? _a : 'An unknown error occurred.'));
1312
+ : String((_c = error.message) !== null && _c !== void 0 ? _c : 'An unknown error occurred.'));
1202
1313
  const code = (typeof error === 'string'
1203
1314
  ? ReactKitErrorCode$1.NoCode
1204
- : String((_b = error.code) !== null && _b !== void 0 ? _b : ReactKitErrorCode$1.NoCode));
1315
+ : String((_d = error.code) !== null && _d !== void 0 ? _d : ReactKitErrorCode$1.NoCode));
1205
1316
  // Call all fatal error listeners
1206
1317
  try {
1207
1318
  fatalErrorHandlers.forEach((handler) => {
@@ -1328,6 +1439,9 @@ const AppWrapper = (props) => {
1328
1439
  // Confirm
1329
1440
  const [confirmInfo, setConfirmInfoInner,] = React.useState(undefined);
1330
1441
  setConfirmInfo = setConfirmInfoInner;
1442
+ // Prompt
1443
+ const [promptInfo, setPromptInfoInner] = React.useState(undefined);
1444
+ setPromptInfo = setPromptInfoInner;
1331
1445
  // Session expired
1332
1446
  const [sessionHasExpired, setSessionHasExpiredInner,] = React.useState(false);
1333
1447
  setSessionHasExpired = setSessionHasExpiredInner;
@@ -1358,6 +1472,42 @@ const AppWrapper = (props) => {
1358
1472
  }
1359
1473
  }, onTopOfOtherModals: true, dontAllowBackdropExit: true }, confirmInfo.text));
1360
1474
  }
1475
+ /* ------------- Prompt ------------ */
1476
+ if (promptInfo) {
1477
+ // Run min char validation
1478
+ const minNumCharsValidationError = ((promptInfo.opts.minNumChars
1479
+ && promptInfo.currentInputFieldText.length < promptInfo.opts.minNumChars)
1480
+ ? `Please enter at least ${promptInfo.opts.minNumChars} characters.`
1481
+ : undefined);
1482
+ // Run custom validation
1483
+ const customValidationError = (promptInfo.opts.findValidationError
1484
+ && promptInfo.opts.findValidationError(promptInfo.currentInputFieldText));
1485
+ modal = (React__default["default"].createElement(Modal, { key: `prompt-${promptInfo.title}`, title: promptInfo.title,
1486
+ // Don't show ok button if there is a validation error
1487
+ type: ((customValidationError || minNumCharsValidationError)
1488
+ ? ModalType$1.Cancel
1489
+ : ModalType$1.OkayCancel), okayLabel: promptInfo.opts.confirmButtonText, okayVariant: promptInfo.opts.confirmButtonVariant, cancelLabel: promptInfo.opts.cancelButtonText, cancelVariant: promptInfo.opts.cancelButtonVariant, onClose: (buttonType) => {
1490
+ // Get result
1491
+ const result = (buttonType === ModalButtonType$1.Okay
1492
+ ? promptInfo.currentInputFieldText
1493
+ : null);
1494
+ // Close prompt
1495
+ setPromptInfo(undefined);
1496
+ // Call handler
1497
+ if (onPromptClosed) {
1498
+ onPromptClosed(result);
1499
+ }
1500
+ }, onTopOfOtherModals: true, dontAllowBackdropExit: true },
1501
+ React__default["default"].createElement("div", null,
1502
+ promptInfo.opts.textAboveInputField && (React__default["default"].createElement("div", null, promptInfo.opts.textAboveInputField)),
1503
+ React__default["default"].createElement("input", { type: "text", className: "form-control", "aria-label": promptInfo.opts.ariaLabel, placeholder: promptInfo.opts.placeholder, value: promptInfo.currentInputFieldText, onChange: (e) => {
1504
+ return setPromptInfo(Object.assign(Object.assign({}, promptInfo), { currentInputFieldText: e.target.value }));
1505
+ },
1506
+ // eslint-disable-next-line jsx-a11y/no-autofocus
1507
+ autoFocus: true }),
1508
+ minNumCharsValidationError && (React__default["default"].createElement("div", { className: "text-danger fw-bold mt-2" }, minNumCharsValidationError)),
1509
+ customValidationError && (React__default["default"].createElement("div", { className: "text-danger fw-bold mt-2" }, customValidationError)))));
1510
+ }
1361
1511
  /* ------ Custom Modal Portals ------ */
1362
1512
  // Custom modal portals
1363
1513
  const customModalPortals = [];
@@ -1427,7 +1577,7 @@ const AppWrapper = (props) => {
1427
1577
  ? `
1428
1578
  .tooltip-inner {
1429
1579
  background-color: white;
1430
- color: black;
1580
+ color: black !important;
1431
1581
  border: 0.1rem solid black;
1432
1582
  pointer-events: none;
1433
1583
  }
@@ -1440,7 +1590,7 @@ const AppWrapper = (props) => {
1440
1590
  : `
1441
1591
  .tooltip-inner {
1442
1592
  background-color: black;
1443
- color: white;
1593
+ color: white !important;
1444
1594
  border: 0.1rem solid white;
1445
1595
  pointer-events: none;
1446
1596
  }
@@ -1567,24 +1717,31 @@ const RadioButton = (props) => {
1567
1717
  /* -------------------------------- Setup ------------------------------- */
1568
1718
  /*------------------------------------------------------------------------*/
1569
1719
  /* -------------- Props ------------- */
1570
- const { text, onSelected, ariaLabel, title, selected, id, noMarginOnRight, selectedVariant = (isDarkModeOn()
1720
+ const { text, onSelected, ariaLabel, title, selected, id, className, noMarginOnRight, selectedVariant = (isDarkModeOn()
1571
1721
  ? Variant$1.Light
1572
1722
  : Variant$1.Secondary), unselectedVariant = (isDarkModeOn()
1573
1723
  ? Variant$1.Secondary
1574
- : Variant$1.Light), small, } = props;
1724
+ : Variant$1.Light), small, useComplexFormatting, } = props;
1575
1725
  /*------------------------------------------------------------------------*/
1576
1726
  /* ------------------------------- Render ------------------------------- */
1577
1727
  /*------------------------------------------------------------------------*/
1578
1728
  /*----------------------------------------*/
1579
1729
  /* --------------- Main UI -------------- */
1580
1730
  /*----------------------------------------*/
1581
- 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: () => {
1731
+ 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: () => {
1582
1732
  if (!selected) {
1583
1733
  onSelected();
1584
1734
  }
1585
1735
  } },
1586
- React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: selected ? freeSolidSvgIcons.faDotCircle : freeRegularSvgIcons.faCircle, className: "me-1" }),
1587
- text));
1736
+ React__default["default"].createElement("div", { className: "d-flex flex-row align-items-center" },
1737
+ React__default["default"].createElement("div", { className: "me-1" },
1738
+ React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: selected ? freeSolidSvgIcons.faDotCircle : freeRegularSvgIcons.faCircle })),
1739
+ useComplexFormatting
1740
+ ? (React__default["default"].createElement("pre", { className: "ps-1 text-start text-break m-0", style: {
1741
+ whiteSpace: 'pre-wrap',
1742
+ tabSize: 2,
1743
+ } }, text))
1744
+ : (React__default["default"].createElement("div", { className: "flex-grow-1 text-start text-break" }, text)))));
1588
1745
  };
1589
1746
 
1590
1747
  /**
@@ -1603,7 +1760,7 @@ const CheckboxButton = (props) => {
1603
1760
  ? Variant$1.Light
1604
1761
  : Variant$1.Secondary), uncheckedVariant = (isDarkModeOn()
1605
1762
  ? Variant$1.Secondary
1606
- : Variant$1.Light), small, dashed, } = props;
1763
+ : Variant$1.Light), small, dashed, useComplexFormatting, } = props;
1607
1764
  /*------------------------------------------------------------------------*/
1608
1765
  /* ------------------------------- Render ------------------------------- */
1609
1766
  /*------------------------------------------------------------------------*/
@@ -1624,10 +1781,15 @@ const CheckboxButton = (props) => {
1624
1781
  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: () => {
1625
1782
  onChanged(!checked);
1626
1783
  } },
1627
- React__default["default"].createElement("div", { className: "d-flex" },
1628
- React__default["default"].createElement("div", { className: "align-items-center" },
1629
- React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: icon, className: "me-1" })),
1630
- React__default["default"].createElement("div", { className: "flex-grow-1" }, text))));
1784
+ React__default["default"].createElement("div", { className: "d-flex align-items-center" },
1785
+ React__default["default"].createElement("div", { className: "me-1" },
1786
+ React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: icon })),
1787
+ useComplexFormatting
1788
+ ? (React__default["default"].createElement("pre", { className: "ps-1 text-start text-break m-0", style: {
1789
+ whiteSpace: 'pre-wrap',
1790
+ tabSize: 2,
1791
+ } }, text))
1792
+ : (React__default["default"].createElement("div", { className: "flex-grow-1 text-start text-break" }, text)))));
1631
1793
  };
1632
1794
 
1633
1795
  /**
@@ -1808,7 +1970,13 @@ const SimpleDateChooser = (props) => {
1808
1970
  }
1809
1971
  const monthName = getMonthName(month).full;
1810
1972
  // Year is start year +1 for each 12 months
1811
- const year = (startYear + (Math.floor(unmoddedMonth / 12)));
1973
+ let yearsToAdd = 0;
1974
+ let monthsOfYearsToAdd = unmoddedMonth;
1975
+ while (monthsOfYearsToAdd > 12) {
1976
+ monthsOfYearsToAdd -= 12;
1977
+ yearsToAdd += 1;
1978
+ }
1979
+ const year = startYear + yearsToAdd;
1812
1980
  // Figure out which days are allowed
1813
1981
  const days = [];
1814
1982
  const numDaysInMonth = (new Date(year, month, 0)).getDate();
@@ -1859,7 +2027,7 @@ const SimpleDateChooser = (props) => {
1859
2027
  });
1860
2028
  }
1861
2029
  });
1862
- return (React__default["default"].createElement("div", { className: "SimpleDateChooser d-inline-block", "aria-label": `date chooser with selected date: ${month} ${day}, ${year}` },
2030
+ return (React__default["default"].createElement("div", { className: "SimpleDateChooser d-inline-block", "aria-label": `date chooser with selected date: ${month}/${day}/${year}` },
1863
2031
  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) => {
1864
2032
  const choice = choices[e.target.selectedIndex];
1865
2033
  // Change day, month, and year
@@ -2252,27 +2420,27 @@ const PopPendingMark = (props) => {
2252
2420
  */
2253
2421
  /* ------------- Actions ------------ */
2254
2422
  // Types of actions
2255
- var ActionType$8;
2423
+ var ActionType$9;
2256
2424
  (function (ActionType) {
2257
2425
  // Indicate that the text was recently copied
2258
2426
  ActionType["IndicateRecentlyCopied"] = "indicate-recently-copied";
2259
2427
  // Clear the status
2260
2428
  ActionType["ClearRecentlyCopiedStatus"] = "clear-recently-copied-status";
2261
- })(ActionType$8 || (ActionType$8 = {}));
2429
+ })(ActionType$9 || (ActionType$9 = {}));
2262
2430
  /**
2263
2431
  * Reducer that executes actions
2264
2432
  * @author Gabe Abrams
2265
2433
  * @param state current state
2266
2434
  * @param action action to execute
2267
2435
  */
2268
- const reducer$9 = (state, action) => {
2436
+ const reducer$a = (state, action) => {
2269
2437
  switch (action.type) {
2270
- case ActionType$8.IndicateRecentlyCopied: {
2438
+ case ActionType$9.IndicateRecentlyCopied: {
2271
2439
  return {
2272
2440
  recentlyCopied: true,
2273
2441
  };
2274
2442
  }
2275
- case ActionType$8.ClearRecentlyCopiedStatus: {
2443
+ case ActionType$9.ClearRecentlyCopiedStatus: {
2276
2444
  return {
2277
2445
  recentlyCopied: false,
2278
2446
  };
@@ -2298,7 +2466,7 @@ const CopiableBox = (props) => {
2298
2466
  recentlyCopied: false,
2299
2467
  };
2300
2468
  // Initialize state
2301
- const [state, dispatch] = React.useReducer(reducer$9, initialState);
2469
+ const [state, dispatch] = React.useReducer(reducer$a, initialState);
2302
2470
  // Destructure common state
2303
2471
  const { recentlyCopied, } = state;
2304
2472
  /*------------------------------------------------------------------------*/
@@ -2318,13 +2486,13 @@ const CopiableBox = (props) => {
2318
2486
  }
2319
2487
  // Show copied notice
2320
2488
  dispatch({
2321
- type: ActionType$8.IndicateRecentlyCopied,
2489
+ type: ActionType$9.IndicateRecentlyCopied,
2322
2490
  });
2323
2491
  // Wait a moment
2324
2492
  yield waitMs(4000);
2325
2493
  // Hide copied notice
2326
2494
  dispatch({
2327
- type: ActionType$8.ClearRecentlyCopiedStatus,
2495
+ type: ActionType$9.ClearRecentlyCopiedStatus,
2328
2496
  });
2329
2497
  });
2330
2498
  /*------------------------------------------------------------------------*/
@@ -2380,20 +2548,20 @@ const CopiableBox = (props) => {
2380
2548
  */
2381
2549
  /* ------------- Actions ------------ */
2382
2550
  // Types of actions
2383
- var ActionType$7;
2551
+ var ActionType$8;
2384
2552
  (function (ActionType) {
2385
2553
  // Toggle whether a child are being shown
2386
2554
  ActionType["ToggleChild"] = "toggle-child";
2387
- })(ActionType$7 || (ActionType$7 = {}));
2555
+ })(ActionType$8 || (ActionType$8 = {}));
2388
2556
  /**
2389
2557
  * Reducer that executes actions
2390
2558
  * @author Gabe Abrams
2391
2559
  * @param state current state
2392
2560
  * @param action action to execute
2393
2561
  */
2394
- const reducer$8 = (state, action) => {
2562
+ const reducer$9 = (state, action) => {
2395
2563
  switch (action.type) {
2396
- case ActionType$7.ToggleChild: {
2564
+ case ActionType$8.ToggleChild: {
2397
2565
  return Object.assign(Object.assign({}, state), { childExpanded: Object.assign(Object.assign({}, state.childExpanded), { [String(action.id)]: !state.childExpanded[String(action.id)] }) });
2398
2566
  }
2399
2567
  default: {
@@ -2422,7 +2590,7 @@ const NestableItemList = (props) => {
2422
2590
  childExpanded: initChildExpanded,
2423
2591
  };
2424
2592
  // Initialize state
2425
- const [state, dispatch] = React.useReducer(reducer$8, initialState);
2593
+ const [state, dispatch] = React.useReducer(reducer$9, initialState);
2426
2594
  // Destructure common state
2427
2595
  const { childExpanded, } = state;
2428
2596
  /*------------------------------------------------------------------------*/
@@ -2512,14 +2680,14 @@ const NestableItemList = (props) => {
2512
2680
  backgroundColor: 'transparent',
2513
2681
  }, type: "button", onClick: () => {
2514
2682
  dispatch({
2515
- type: ActionType$7.ToggleChild,
2683
+ type: ActionType$8.ToggleChild,
2516
2684
  id: item.id,
2517
2685
  });
2518
2686
  }, "aria-label": `${childExpanded[item.id] ? 'Hide' : 'Show'} items in ${item.name}` },
2519
2687
  React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: childExpanded[item.id] ? freeSolidSvgIcons.faChevronDown : freeSolidSvgIcons.faChevronRight })))),
2520
2688
  React__default["default"].createElement(CheckboxButton, { className: `NestableItemList-CheckboxButton-${item.id}`, text: item.name, checked: item.isGroup ? allChecked(item.children) : item.checked, dashed: item.isGroup ? !noneChecked(item.children) : false, onChanged: (checked) => {
2521
2689
  onChanged(changeChecked(item.id, checked, items));
2522
- }, ariaLabel: `Select ${item.name}`, checkedVariant: Variant$1.Light }),
2690
+ }, ariaLabel: `Select ${item.name}`, checkedVariant: Variant$1.Light, uncheckedVariant: Variant$1.Light }),
2523
2691
  (item.isGroup && childExpanded[item.id]) && (React__default["default"].createElement("div", { className: "NestableItemList-children-container", style: {
2524
2692
  paddingLeft: '2.2rem',
2525
2693
  } },
@@ -2796,7 +2964,7 @@ var SortType;
2796
2964
  })(SortType || (SortType = {}));
2797
2965
  /* ------------- Actions ------------ */
2798
2966
  // Types of actions
2799
- var ActionType$6;
2967
+ var ActionType$7;
2800
2968
  (function (ActionType) {
2801
2969
  // Toggle sort column param
2802
2970
  ActionType["ToggleSortColumn"] = "toggle-sort-column";
@@ -2808,16 +2976,16 @@ var ActionType$6;
2808
2976
  ActionType["ShowAllColumns"] = "show-all-columns";
2809
2977
  // Hide all columns
2810
2978
  ActionType["HideAllColumns"] = "hide-all-columns";
2811
- })(ActionType$6 || (ActionType$6 = {}));
2979
+ })(ActionType$7 || (ActionType$7 = {}));
2812
2980
  /**
2813
2981
  * Reducer that executes actions
2814
2982
  * @author Gabe Abrams
2815
2983
  * @param state current state
2816
2984
  * @param action action to execute
2817
2985
  */
2818
- const reducer$7 = (state, action) => {
2986
+ const reducer$8 = (state, action) => {
2819
2987
  switch (action.type) {
2820
- case ActionType$6.ToggleSortColumn: {
2988
+ case ActionType$7.ToggleSortColumn: {
2821
2989
  if (action.param !== state.sortColumnParam) {
2822
2990
  // Different column param
2823
2991
  return Object.assign(Object.assign({}, state), { sortColumnParam: action.param, sortType: SortType.Ascending });
@@ -2829,22 +2997,22 @@ const reducer$7 = (state, action) => {
2829
2997
  // Stop sorting by column
2830
2998
  return Object.assign(Object.assign({}, state), { sortColumnParam: undefined, sortType: SortType.Ascending });
2831
2999
  }
2832
- case ActionType$6.UpdateColumnVisibility: {
3000
+ case ActionType$7.UpdateColumnVisibility: {
2833
3001
  const { columnVisibilityMap } = state;
2834
3002
  columnVisibilityMap[action.param] = action.visible;
2835
3003
  return Object.assign(Object.assign({}, state), { columnVisibilityMap });
2836
3004
  }
2837
- case ActionType$6.ToggleColVisCusModalVisibility: {
3005
+ case ActionType$7.ToggleColVisCusModalVisibility: {
2838
3006
  return Object.assign(Object.assign({}, state), { columnVisibilityCustomizationModalVisible: !state.columnVisibilityCustomizationModalVisible });
2839
3007
  }
2840
- case ActionType$6.ShowAllColumns: {
3008
+ case ActionType$7.ShowAllColumns: {
2841
3009
  const { columnVisibilityMap } = state;
2842
3010
  Object.keys(columnVisibilityMap).forEach((param) => {
2843
3011
  columnVisibilityMap[param] = true;
2844
3012
  });
2845
3013
  return Object.assign(Object.assign({}, state), { columnVisibilityMap });
2846
3014
  }
2847
- case ActionType$6.HideAllColumns: {
3015
+ case ActionType$7.HideAllColumns: {
2848
3016
  const { columnVisibilityMap } = state;
2849
3017
  Object.keys(columnVisibilityMap).forEach((param) => {
2850
3018
  columnVisibilityMap[param] = false;
@@ -2891,7 +3059,7 @@ const IntelliTable = (props) => {
2891
3059
  columnVisibilityCustomizationModalVisible: false,
2892
3060
  };
2893
3061
  // Initialize state
2894
- const [state, dispatch] = React.useReducer(reducer$7, initialState);
3062
+ const [state, dispatch] = React.useReducer(reducer$8, initialState);
2895
3063
  // Destructure common state
2896
3064
  const { sortColumnParam, sortType, columnVisibilityMap, columnVisibilityCustomizationModalVisible, } = state;
2897
3065
  /*------------------------------------------------------------------------*/
@@ -2914,27 +3082,27 @@ const IntelliTable = (props) => {
2914
3082
  return alert('Choose at least one column', 'To continue, you have to choose at least one column to show');
2915
3083
  }
2916
3084
  dispatch({
2917
- type: ActionType$6.ToggleColVisCusModalVisibility,
3085
+ type: ActionType$7.ToggleColVisCusModalVisibility,
2918
3086
  });
2919
3087
  }, okayVariant: Variant$1.Light },
2920
3088
  columns.map((column) => {
2921
3089
  return (React__default["default"].createElement(CheckboxButton, { key: column.param, id: `IntelliTable-${id}-toggle-visibility-${column.param}`, className: "mb-2", text: column.title, onChanged: (checked) => {
2922
3090
  dispatch({
2923
- type: ActionType$6.UpdateColumnVisibility,
3091
+ type: ActionType$7.UpdateColumnVisibility,
2924
3092
  param: column.param,
2925
3093
  visible: checked,
2926
3094
  });
2927
- }, checked: columnVisibilityMap[column.param], ariaLabel: `show "${column.title}" column in the ${title} table`, checkedVariant: Variant$1.Light, uncheckedVariant: Variant$1.Secondary }));
3095
+ }, checked: columnVisibilityMap[column.param], ariaLabel: `show "${column.title}" column in the ${title} table`, checkedVariant: Variant$1.Light, uncheckedVariant: Variant$1.Light }));
2928
3096
  }),
2929
3097
  React__default["default"].createElement("div", { className: "mt-3" }, "Or you can:"),
2930
3098
  React__default["default"].createElement("button", { type: "button", id: `IntelliTable-${id}-select-all-columns`, className: "btn btn-secondary me-2", "aria-label": `show all columns in the ${title} table`, onClick: () => {
2931
3099
  dispatch({
2932
- type: ActionType$6.ShowAllColumns,
3100
+ type: ActionType$7.ShowAllColumns,
2933
3101
  });
2934
3102
  } }, "Select All"),
2935
3103
  React__default["default"].createElement("button", { type: "button", id: `IntelliTable-${id}-select-none-columns`, className: "btn btn-secondary", "aria-label": `hide all columns in the ${title} table`, onClick: () => {
2936
3104
  dispatch({
2937
- type: ActionType$6.HideAllColumns,
3105
+ type: ActionType$7.HideAllColumns,
2938
3106
  });
2939
3107
  } }, "Deselect All")));
2940
3108
  }
@@ -2985,7 +3153,7 @@ const IntelliTable = (props) => {
2985
3153
  React__default["default"].createElement("div", null,
2986
3154
  React__default["default"].createElement("button", { type: "button", id: `IntelliTable-${id}-sort-by-${column.param}-button`, className: `btn btn-${sortingByThisColumn ? 'light' : 'secondary'} btn-sm ms-1 ps-1 pe-1 pt-0 pb-0`, "aria-label": sortButtonAriaLabel, onClick: () => {
2987
3155
  dispatch({
2988
- type: ActionType$6.ToggleSortColumn,
3156
+ type: ActionType$7.ToggleSortColumn,
2989
3157
  param: column.param,
2990
3158
  });
2991
3159
  } },
@@ -3174,7 +3342,7 @@ const IntelliTable = (props) => {
3174
3342
  React__default["default"].createElement(CSVDownloadButton, { "aria-label": `download data as csv for ${title}`, id: `IntelliTable-${id}-download-as-csv`, filename: filename, csv: csv }),
3175
3343
  React__default["default"].createElement("button", { type: "button", className: "btn btn-secondary ms-2", "aria-label": `show panel for customizing which columns show in table ${title}`, id: `IntelliTable-${id}-show-column-customization-modal`, onClick: () => {
3176
3344
  dispatch({
3177
- type: ActionType$6.ToggleColVisCusModalVisibility,
3345
+ type: ActionType$7.ToggleColVisCusModalVisibility,
3178
3346
  });
3179
3347
  } },
3180
3348
  "Show/Hide Cols",
@@ -3540,7 +3708,7 @@ const genHumanReadableName = (machineReadableName) => {
3540
3708
  };
3541
3709
  /* ------------- Actions ------------ */
3542
3710
  // Types of actions
3543
- var ActionType$5;
3711
+ var ActionType$6;
3544
3712
  (function (ActionType) {
3545
3713
  // Show the loading bar
3546
3714
  ActionType["StartLoading"] = "start-loading";
@@ -3562,45 +3730,45 @@ var ActionType$5;
3562
3730
  ActionType["UpdateActionErrorFilterState"] = "update-action-error-filter-state";
3563
3731
  // Update the advanced filter state
3564
3732
  ActionType["UpdateAdvancedFilterState"] = "update-advanced-filter-state";
3565
- })(ActionType$5 || (ActionType$5 = {}));
3733
+ })(ActionType$6 || (ActionType$6 = {}));
3566
3734
  /**
3567
3735
  * Reducer that executes actions
3568
3736
  * @author Gabe Abrams
3569
3737
  * @param state current state
3570
3738
  * @param action action to execute
3571
3739
  */
3572
- const reducer$6 = (state, action) => {
3740
+ const reducer$7 = (state, action) => {
3573
3741
  switch (action.type) {
3574
- case ActionType$5.StartLoading: {
3742
+ case ActionType$6.StartLoading: {
3575
3743
  return Object.assign(Object.assign({}, state), { loading: true });
3576
3744
  }
3577
- case ActionType$5.FinishLoading: {
3745
+ case ActionType$6.FinishLoading: {
3578
3746
  return Object.assign(Object.assign({}, state), { loading: false, logMap: action.logMap });
3579
3747
  }
3580
- case ActionType$5.ToggleFilterDrawer: {
3748
+ case ActionType$6.ToggleFilterDrawer: {
3581
3749
  return Object.assign(Object.assign({}, state), { expandedFilterDrawer: (state.expandedFilterDrawer === action.filterDrawer
3582
3750
  ? undefined // hide
3583
3751
  : action.filterDrawer) });
3584
3752
  }
3585
- case ActionType$5.HideFilterDrawer: {
3753
+ case ActionType$6.HideFilterDrawer: {
3586
3754
  return Object.assign(Object.assign({}, state), { expandedFilterDrawer: undefined });
3587
3755
  }
3588
- case ActionType$5.ResetFilters: {
3756
+ case ActionType$6.ResetFilters: {
3589
3757
  return Object.assign(Object.assign({}, state), { dateFilterState: action.initDateFilterState, contextFilterState: action.initContextFilterState, tagFilterState: action.initTagFilterState, actionErrorFilterState: action.initActionErrorFilterState, advancedFilterState: action.initAdvancedFilterState });
3590
3758
  }
3591
- case ActionType$5.UpdateDateFilterState: {
3759
+ case ActionType$6.UpdateDateFilterState: {
3592
3760
  return Object.assign(Object.assign({}, state), { dateFilterState: action.dateFilterState });
3593
3761
  }
3594
- case ActionType$5.UpdateContextFilterState: {
3762
+ case ActionType$6.UpdateContextFilterState: {
3595
3763
  return Object.assign(Object.assign({}, state), { contextFilterState: action.contextFilterState });
3596
3764
  }
3597
- case ActionType$5.UpdateTagFilterState: {
3765
+ case ActionType$6.UpdateTagFilterState: {
3598
3766
  return Object.assign(Object.assign({}, state), { tagFilterState: action.tagFilterState });
3599
3767
  }
3600
- case ActionType$5.UpdateActionErrorFilterState: {
3768
+ case ActionType$6.UpdateActionErrorFilterState: {
3601
3769
  return Object.assign(Object.assign({}, state), { actionErrorFilterState: action.actionErrorFilterState });
3602
3770
  }
3603
- case ActionType$5.UpdateAdvancedFilterState: {
3771
+ case ActionType$6.UpdateAdvancedFilterState: {
3604
3772
  return Object.assign(Object.assign({}, state), { advancedFilterState: action.advancedFilterState });
3605
3773
  }
3606
3774
  default: {
@@ -3729,7 +3897,7 @@ const LogReviewer = (props) => {
3729
3897
  advancedFilterState: initAdvancedFilterState,
3730
3898
  };
3731
3899
  // Initialize state
3732
- const [state, dispatch] = React.useReducer(reducer$6, initialState);
3900
+ const [state, dispatch] = React.useReducer(reducer$7, initialState);
3733
3901
  // Destructure common state
3734
3902
  const { loading, logMap, expandedFilterDrawer, dateFilterState, contextFilterState, tagFilterState, actionErrorFilterState, advancedFilterState, } = state;
3735
3903
  /*------------------------------------------------------------------------*/
@@ -3779,7 +3947,7 @@ const LogReviewer = (props) => {
3779
3947
  const handleDateRangeUpdated = (newDateFilterState) => __awaiter(void 0, void 0, void 0, function* () {
3780
3948
  // Update state
3781
3949
  dispatch({
3782
- type: ActionType$5.UpdateDateFilterState,
3950
+ type: ActionType$6.UpdateDateFilterState,
3783
3951
  dateFilterState: newDateFilterState,
3784
3952
  });
3785
3953
  // Check which year/month combos we need to load
@@ -3790,7 +3958,7 @@ const LogReviewer = (props) => {
3790
3958
  }
3791
3959
  // Start loading
3792
3960
  dispatch({
3793
- type: ActionType$5.StartLoading,
3961
+ type: ActionType$6.StartLoading,
3794
3962
  });
3795
3963
  // Load required months
3796
3964
  try {
@@ -3798,10 +3966,21 @@ const LogReviewer = (props) => {
3798
3966
  // Destructure
3799
3967
  const { year, month } = toLoad[i];
3800
3968
  // Load
3801
- const logs = yield visitServerEndpoint({
3802
- path: `${LOG_REVIEW_ROUTE_PATH_PREFIX}/years/${year}/months/${month}`,
3803
- method: 'GET',
3804
- });
3969
+ let logs = [];
3970
+ let pageNumber = 1;
3971
+ let hasAnotherPage = true;
3972
+ while (hasAnotherPage) {
3973
+ const response = yield visitServerEndpoint({
3974
+ path: `${LOG_REVIEW_ROUTE_PATH_PREFIX}/years/${year}/months/${month}`,
3975
+ method: 'GET',
3976
+ params: {
3977
+ pageNumber,
3978
+ },
3979
+ });
3980
+ logs = logs.concat(response.items);
3981
+ hasAnotherPage = response.hasAnotherPage;
3982
+ pageNumber += 1;
3983
+ }
3805
3984
  // Add to map
3806
3985
  if (!logMap[year]) {
3807
3986
  logMap[year] = {};
@@ -3814,7 +3993,7 @@ const LogReviewer = (props) => {
3814
3993
  }
3815
3994
  // Finish loading
3816
3995
  dispatch({
3817
- type: ActionType$5.FinishLoading,
3996
+ type: ActionType$6.FinishLoading,
3818
3997
  logMap,
3819
3998
  });
3820
3999
  });
@@ -3853,7 +4032,7 @@ const LogReviewer = (props) => {
3853
4032
  React__default["default"].createElement("div", { className: "LogReviewer-filter-toggle-buttons alert alert-secondary p-2 m-0" },
3854
4033
  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: () => {
3855
4034
  dispatch({
3856
- type: ActionType$5.ToggleFilterDrawer,
4035
+ type: ActionType$6.ToggleFilterDrawer,
3857
4036
  filterDrawer: FilterDrawer.Date,
3858
4037
  });
3859
4038
  } },
@@ -3861,7 +4040,7 @@ const LogReviewer = (props) => {
3861
4040
  "Date"),
3862
4041
  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: () => {
3863
4042
  dispatch({
3864
- type: ActionType$5.ToggleFilterDrawer,
4043
+ type: ActionType$6.ToggleFilterDrawer,
3865
4044
  filterDrawer: FilterDrawer.Context,
3866
4045
  });
3867
4046
  } },
@@ -3869,7 +4048,7 @@ const LogReviewer = (props) => {
3869
4048
  "Context"),
3870
4049
  (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: () => {
3871
4050
  dispatch({
3872
- type: ActionType$5.ToggleFilterDrawer,
4051
+ type: ActionType$6.ToggleFilterDrawer,
3873
4052
  filterDrawer: FilterDrawer.Tag,
3874
4053
  });
3875
4054
  } },
@@ -3877,7 +4056,7 @@ const LogReviewer = (props) => {
3877
4056
  "Tag")),
3878
4057
  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: () => {
3879
4058
  dispatch({
3880
- type: ActionType$5.ToggleFilterDrawer,
4059
+ type: ActionType$6.ToggleFilterDrawer,
3881
4060
  filterDrawer: FilterDrawer.Action,
3882
4061
  });
3883
4062
  } },
@@ -3885,7 +4064,7 @@ const LogReviewer = (props) => {
3885
4064
  "Action"),
3886
4065
  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: () => {
3887
4066
  dispatch({
3888
- type: ActionType$5.ToggleFilterDrawer,
4067
+ type: ActionType$6.ToggleFilterDrawer,
3889
4068
  filterDrawer: FilterDrawer.Advanced,
3890
4069
  });
3891
4070
  } },
@@ -3893,7 +4072,7 @@ const LogReviewer = (props) => {
3893
4072
  "Advanced"),
3894
4073
  React__default["default"].createElement("button", { type: "button", id: "LogReviewer-reset-filters-button", className: "btn btn-light", "aria-label": "reset filters", onClick: () => {
3895
4074
  dispatch({
3896
- type: ActionType$5.ResetFilters,
4075
+ type: ActionType$6.ResetFilters,
3897
4076
  initActionErrorFilterState,
3898
4077
  initAdvancedFilterState,
3899
4078
  initContextFilterState,
@@ -4015,7 +4194,7 @@ const LogReviewer = (props) => {
4015
4194
  }
4016
4195
  });
4017
4196
  dispatch({
4018
- type: ActionType$5.UpdateContextFilterState,
4197
+ type: ActionType$6.UpdateContextFilterState,
4019
4198
  contextFilterState,
4020
4199
  });
4021
4200
  } }));
@@ -4030,7 +4209,7 @@ const LogReviewer = (props) => {
4030
4209
  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) => {
4031
4210
  tagFilterState[tag] = checked;
4032
4211
  dispatch({
4033
- type: ActionType$5.UpdateTagFilterState,
4212
+ type: ActionType$6.UpdateTagFilterState,
4034
4213
  tagFilterState,
4035
4214
  });
4036
4215
  } }));
@@ -4043,21 +4222,21 @@ const LogReviewer = (props) => {
4043
4222
  React__default["default"].createElement(RadioButton, { id: "LogReviewer-type-all", text: "All Logs", onSelected: () => {
4044
4223
  actionErrorFilterState.type = undefined;
4045
4224
  dispatch({
4046
- type: ActionType$5.UpdateActionErrorFilterState,
4225
+ type: ActionType$6.UpdateActionErrorFilterState,
4047
4226
  actionErrorFilterState,
4048
4227
  });
4049
4228
  }, ariaLabel: "show logs of all types", selected: actionErrorFilterState.type === undefined }),
4050
4229
  React__default["default"].createElement(RadioButton, { id: "LogReviewer-type-action-only", text: "Action Logs Only", onSelected: () => {
4051
4230
  actionErrorFilterState.type = LogType$1.Action;
4052
4231
  dispatch({
4053
- type: ActionType$5.UpdateActionErrorFilterState,
4232
+ type: ActionType$6.UpdateActionErrorFilterState,
4054
4233
  actionErrorFilterState,
4055
4234
  });
4056
4235
  }, ariaLabel: "only show action logs", selected: actionErrorFilterState.type === LogType$1.Action }),
4057
4236
  React__default["default"].createElement(RadioButton, { id: "LogReviewer-type-error-only", text: "Action Error Only", onSelected: () => {
4058
4237
  actionErrorFilterState.type = LogType$1.Error;
4059
4238
  dispatch({
4060
- type: ActionType$5.UpdateActionErrorFilterState,
4239
+ type: ActionType$6.UpdateActionErrorFilterState,
4061
4240
  actionErrorFilterState,
4062
4241
  });
4063
4242
  }, ariaLabel: "only show error logs", selected: actionErrorFilterState.type === LogType$1.Error, noMarginOnRight: true })),
@@ -4069,7 +4248,7 @@ const LogReviewer = (props) => {
4069
4248
  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) => {
4070
4249
  actionErrorFilterState.action[action] = checked;
4071
4250
  dispatch({
4072
- type: ActionType$5.UpdateActionErrorFilterState,
4251
+ type: ActionType$6.UpdateActionErrorFilterState,
4073
4252
  actionErrorFilterState,
4074
4253
  });
4075
4254
  } }));
@@ -4080,7 +4259,7 @@ const LogReviewer = (props) => {
4080
4259
  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) => {
4081
4260
  actionErrorFilterState.target[target] = checked;
4082
4261
  dispatch({
4083
- type: ActionType$5.UpdateActionErrorFilterState,
4262
+ type: ActionType$6.UpdateActionErrorFilterState,
4084
4263
  actionErrorFilterState,
4085
4264
  });
4086
4265
  } }));
@@ -4092,7 +4271,7 @@ const LogReviewer = (props) => {
4092
4271
  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) => {
4093
4272
  actionErrorFilterState.errorMessage = e.target.value;
4094
4273
  dispatch({
4095
- type: ActionType$5.UpdateActionErrorFilterState,
4274
+ type: ActionType$6.UpdateActionErrorFilterState,
4096
4275
  actionErrorFilterState,
4097
4276
  });
4098
4277
  } })),
@@ -4103,7 +4282,7 @@ const LogReviewer = (props) => {
4103
4282
  .trim()
4104
4283
  .toUpperCase());
4105
4284
  dispatch({
4106
- type: ActionType$5.UpdateActionErrorFilterState,
4285
+ type: ActionType$6.UpdateActionErrorFilterState,
4107
4286
  actionErrorFilterState,
4108
4287
  });
4109
4288
  } }))))));
@@ -4117,7 +4296,7 @@ const LogReviewer = (props) => {
4117
4296
  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) => {
4118
4297
  advancedFilterState.userFirstName = e.target.value;
4119
4298
  dispatch({
4120
- type: ActionType$5.UpdateAdvancedFilterState,
4299
+ type: ActionType$6.UpdateAdvancedFilterState,
4121
4300
  advancedFilterState,
4122
4301
  });
4123
4302
  } })),
@@ -4126,7 +4305,7 @@ const LogReviewer = (props) => {
4126
4305
  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) => {
4127
4306
  advancedFilterState.userLastName = e.target.value;
4128
4307
  dispatch({
4129
- type: ActionType$5.UpdateAdvancedFilterState,
4308
+ type: ActionType$6.UpdateAdvancedFilterState,
4130
4309
  advancedFilterState,
4131
4310
  });
4132
4311
  } })),
@@ -4136,7 +4315,7 @@ const LogReviewer = (props) => {
4136
4315
  advancedFilterState.userEmail = ((e.target.value)
4137
4316
  .trim());
4138
4317
  dispatch({
4139
- type: ActionType$5.UpdateAdvancedFilterState,
4318
+ type: ActionType$6.UpdateAdvancedFilterState,
4140
4319
  advancedFilterState,
4141
4320
  });
4142
4321
  } })),
@@ -4150,7 +4329,7 @@ const LogReviewer = (props) => {
4150
4329
  .trim());
4151
4330
  }
4152
4331
  dispatch({
4153
- type: ActionType$5.UpdateAdvancedFilterState,
4332
+ type: ActionType$6.UpdateAdvancedFilterState,
4154
4333
  advancedFilterState,
4155
4334
  });
4156
4335
  } })),
@@ -4158,21 +4337,21 @@ const LogReviewer = (props) => {
4158
4337
  React__default["default"].createElement(CheckboxButton, { text: "Students", onChanged: (checked) => {
4159
4338
  advancedFilterState.includeLearners = checked;
4160
4339
  dispatch({
4161
- type: ActionType$5.UpdateAdvancedFilterState,
4340
+ type: ActionType$6.UpdateAdvancedFilterState,
4162
4341
  advancedFilterState,
4163
4342
  });
4164
4343
  }, checked: advancedFilterState.includeLearners, ariaLabel: "show logs from students" }),
4165
4344
  React__default["default"].createElement(CheckboxButton, { text: "Teaching Team Members", onChanged: (checked) => {
4166
4345
  advancedFilterState.includeTTMs = checked;
4167
4346
  dispatch({
4168
- type: ActionType$5.UpdateAdvancedFilterState,
4347
+ type: ActionType$6.UpdateAdvancedFilterState,
4169
4348
  advancedFilterState,
4170
4349
  });
4171
4350
  }, checked: advancedFilterState.includeTTMs, ariaLabel: "show logs from teaching team members" }),
4172
4351
  React__default["default"].createElement(CheckboxButton, { text: "Admins", onChanged: (checked) => {
4173
4352
  advancedFilterState.includeAdmins = checked;
4174
4353
  dispatch({
4175
- type: ActionType$5.UpdateAdvancedFilterState,
4354
+ type: ActionType$6.UpdateAdvancedFilterState,
4176
4355
  advancedFilterState,
4177
4356
  });
4178
4357
  }, checked: advancedFilterState.includeAdmins, ariaLabel: "show logs from admins" }))),
@@ -4182,7 +4361,7 @@ const LogReviewer = (props) => {
4182
4361
  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) => {
4183
4362
  advancedFilterState.courseName = e.target.value;
4184
4363
  dispatch({
4185
- type: ActionType$5.UpdateAdvancedFilterState,
4364
+ type: ActionType$6.UpdateAdvancedFilterState,
4186
4365
  advancedFilterState,
4187
4366
  });
4188
4367
  } })),
@@ -4196,7 +4375,7 @@ const LogReviewer = (props) => {
4196
4375
  .trim());
4197
4376
  }
4198
4377
  dispatch({
4199
- type: ActionType$5.UpdateAdvancedFilterState,
4378
+ type: ActionType$6.UpdateAdvancedFilterState,
4200
4379
  advancedFilterState,
4201
4380
  });
4202
4381
  } }))),
@@ -4205,21 +4384,21 @@ const LogReviewer = (props) => {
4205
4384
  React__default["default"].createElement(RadioButton, { text: "All Devices", ariaLabel: "show logs from all devices", selected: advancedFilterState.isMobile === undefined, onSelected: () => {
4206
4385
  advancedFilterState.isMobile = undefined;
4207
4386
  dispatch({
4208
- type: ActionType$5.UpdateAdvancedFilterState,
4387
+ type: ActionType$6.UpdateAdvancedFilterState,
4209
4388
  advancedFilterState,
4210
4389
  });
4211
4390
  } }),
4212
4391
  React__default["default"].createElement(RadioButton, { text: "Mobile Only", ariaLabel: "show logs from mobile devices", selected: advancedFilterState.isMobile === true, onSelected: () => {
4213
4392
  advancedFilterState.isMobile = true;
4214
4393
  dispatch({
4215
- type: ActionType$5.UpdateAdvancedFilterState,
4394
+ type: ActionType$6.UpdateAdvancedFilterState,
4216
4395
  advancedFilterState,
4217
4396
  });
4218
4397
  } }),
4219
4398
  React__default["default"].createElement(RadioButton, { text: "Desktop Only", ariaLabel: "show logs from desktop devices", selected: advancedFilterState.isMobile === false, onSelected: () => {
4220
4399
  advancedFilterState.isMobile = false;
4221
4400
  dispatch({
4222
- type: ActionType$5.UpdateAdvancedFilterState,
4401
+ type: ActionType$6.UpdateAdvancedFilterState,
4223
4402
  advancedFilterState,
4224
4403
  });
4225
4404
  }, noMarginOnRight: true }))),
@@ -4228,21 +4407,21 @@ const LogReviewer = (props) => {
4228
4407
  React__default["default"].createElement(RadioButton, { text: "Both", ariaLabel: "show logs from all sources", selected: advancedFilterState.source === undefined, onSelected: () => {
4229
4408
  advancedFilterState.source = undefined;
4230
4409
  dispatch({
4231
- type: ActionType$5.UpdateAdvancedFilterState,
4410
+ type: ActionType$6.UpdateAdvancedFilterState,
4232
4411
  advancedFilterState,
4233
4412
  });
4234
4413
  } }),
4235
4414
  React__default["default"].createElement(RadioButton, { text: "Client Only", ariaLabel: "show logs from client source", selected: advancedFilterState.source === LogSource$1.Client, onSelected: () => {
4236
4415
  advancedFilterState.source = LogSource$1.Client;
4237
4416
  dispatch({
4238
- type: ActionType$5.UpdateAdvancedFilterState,
4417
+ type: ActionType$6.UpdateAdvancedFilterState,
4239
4418
  advancedFilterState,
4240
4419
  });
4241
4420
  } }),
4242
4421
  React__default["default"].createElement(RadioButton, { text: "Server Only", ariaLabel: "show logs from server source", selected: advancedFilterState.source === LogSource$1.Server, onSelected: () => {
4243
4422
  advancedFilterState.source = LogSource$1.Server;
4244
4423
  dispatch({
4245
- type: ActionType$5.UpdateAdvancedFilterState,
4424
+ type: ActionType$6.UpdateAdvancedFilterState,
4246
4425
  advancedFilterState,
4247
4426
  });
4248
4427
  }, noMarginOnRight: true })),
@@ -4253,7 +4432,7 @@ const LogReviewer = (props) => {
4253
4432
  advancedFilterState.courseName = ((e.target.value)
4254
4433
  .trim());
4255
4434
  dispatch({
4256
- type: ActionType$5.UpdateAdvancedFilterState,
4435
+ type: ActionType$6.UpdateAdvancedFilterState,
4257
4436
  advancedFilterState,
4258
4437
  });
4259
4438
  } })),
@@ -4263,7 +4442,7 @@ const LogReviewer = (props) => {
4263
4442
  advancedFilterState.courseName = ((e.target.value)
4264
4443
  .trim());
4265
4444
  dispatch({
4266
- type: ActionType$5.UpdateAdvancedFilterState,
4445
+ type: ActionType$6.UpdateAdvancedFilterState,
4267
4446
  advancedFilterState,
4268
4447
  });
4269
4448
  } })))))));
@@ -12083,11 +12262,11 @@ var CreatableSelect$1 = CreatableSelect;
12083
12262
  */
12084
12263
  /* ------------- Actions ------------ */
12085
12264
  // Types of actions
12086
- var ActionType$4;
12265
+ var ActionType$5;
12087
12266
  (function (ActionType) {
12088
12267
  // Update the input value
12089
12268
  ActionType["SetInputValue"] = "SetInputValue";
12090
- })(ActionType$4 || (ActionType$4 = {}));
12269
+ })(ActionType$5 || (ActionType$5 = {}));
12091
12270
  /**
12092
12271
  * Reducer that executes actions
12093
12272
  * @author Yuen Ler Chow
@@ -12095,9 +12274,9 @@ var ActionType$4;
12095
12274
  * @param action action to execute
12096
12275
  * @returns updated state
12097
12276
  */
12098
- const reducer$5 = (state, action) => {
12277
+ const reducer$6 = (state, action) => {
12099
12278
  switch (action.type) {
12100
- case ActionType$4.SetInputValue: {
12279
+ case ActionType$5.SetInputValue: {
12101
12280
  return Object.assign(Object.assign({}, state), { inputValue: action.value });
12102
12281
  }
12103
12282
  default: {
@@ -12117,7 +12296,7 @@ const CreatableMultiselect = (props) => {
12117
12296
  inputValue: '',
12118
12297
  };
12119
12298
  // Initialize state
12120
- const [state, dispatch] = React.useReducer(reducer$5, initialState);
12299
+ const [state, dispatch] = React.useReducer(reducer$6, initialState);
12121
12300
  // Destructure common state
12122
12301
  const { inputValue } = state;
12123
12302
  /*------------------------------------------------------------------------*/
@@ -12194,7 +12373,7 @@ const CreatableMultiselect = (props) => {
12194
12373
  }
12195
12374
  // Reset text field to empty because the values have been added
12196
12375
  dispatch({
12197
- type: ActionType$4.SetInputValue,
12376
+ type: ActionType$5.SetInputValue,
12198
12377
  value: '',
12199
12378
  });
12200
12379
  };
@@ -12233,7 +12412,7 @@ const CreatableMultiselect = (props) => {
12233
12412
  else {
12234
12413
  // simply update the input value to the new input value
12235
12414
  dispatch({
12236
- type: ActionType$4.SetInputValue,
12415
+ type: ActionType$5.SetInputValue,
12237
12416
  value: newValue,
12238
12417
  });
12239
12418
  }
@@ -12285,13 +12464,13 @@ const style$1 = `
12285
12464
  `;
12286
12465
  /* ------------- Actions ------------ */
12287
12466
  // Types of actions
12288
- var ActionType$3;
12467
+ var ActionType$4;
12289
12468
  (function (ActionType) {
12290
12469
  // Update the DBEntry
12291
12470
  ActionType["UpdateDBEntry"] = "UpdateDBEntry";
12292
12471
  // Start the save spinner
12293
12472
  ActionType["StartSave"] = "StartSave";
12294
- })(ActionType$3 || (ActionType$3 = {}));
12473
+ })(ActionType$4 || (ActionType$4 = {}));
12295
12474
  /**
12296
12475
  * Reducer that executes actions
12297
12476
  * @author Yuen Ler Chow
@@ -12299,12 +12478,12 @@ var ActionType$3;
12299
12478
  * @param action action to execute
12300
12479
  * @returns updated state
12301
12480
  */
12302
- const reducer$4 = (state, action) => {
12481
+ const reducer$5 = (state, action) => {
12303
12482
  switch (action.type) {
12304
- case ActionType$3.UpdateDBEntry: {
12483
+ case ActionType$4.UpdateDBEntry: {
12305
12484
  return Object.assign(Object.assign({}, state), { entry: action.dbEntry });
12306
12485
  }
12307
- case ActionType$3.StartSave: {
12486
+ case ActionType$4.StartSave: {
12308
12487
  return Object.assign(Object.assign({}, state), { saving: true });
12309
12488
  }
12310
12489
  default: {
@@ -12329,7 +12508,7 @@ const AddOrEditDBEntry = (props) => {
12329
12508
  saving: false,
12330
12509
  };
12331
12510
  // Initialize state
12332
- const [state, dispatch] = React.useReducer(reducer$4, initialState);
12511
+ const [state, dispatch] = React.useReducer(reducer$5, initialState);
12333
12512
  // Destructure common state
12334
12513
  const { entry, saving, } = state;
12335
12514
  /*------------------------------------------------------------------------*/
@@ -12341,7 +12520,7 @@ const AddOrEditDBEntry = (props) => {
12341
12520
  */
12342
12521
  const save = () => __awaiter(void 0, void 0, void 0, function* () {
12343
12522
  // Start the save loading indicator
12344
- dispatch({ type: ActionType$3.StartSave });
12523
+ dispatch({ type: ActionType$4.StartSave });
12345
12524
  // add all default values to the entry
12346
12525
  entryFields.forEach((field) => {
12347
12526
  if (field.defaultValue && !entry[field.objectKey]) {
@@ -12524,7 +12703,7 @@ const AddOrEditDBEntry = (props) => {
12524
12703
  return (React__default["default"].createElement(RadioButton, { key: choice.value, text: choice.title, selected: entry[field.objectKey] === choice.value, onSelected: () => {
12525
12704
  entry[field.objectKey] = choice.value;
12526
12705
  dispatch({
12527
- type: ActionType$3.UpdateDBEntry,
12706
+ type: ActionType$4.UpdateDBEntry,
12528
12707
  dbEntry: entry,
12529
12708
  });
12530
12709
  }, ariaLabel: choice.title }));
@@ -12536,7 +12715,7 @@ const AddOrEditDBEntry = (props) => {
12536
12715
  React__default["default"].createElement("input", { disabled: disabled, type: "text", className: "form-control", placeholder: field.placeholder, "aria-describedby": "AddOrEditDBEntry-form-name-label", value: entry[field.objectKey] || '', onChange: (e) => {
12537
12716
  entry[field.objectKey] = (e.target.value);
12538
12717
  dispatch({
12539
- type: ActionType$3.UpdateDBEntry,
12718
+ type: ActionType$4.UpdateDBEntry,
12540
12719
  dbEntry: entry,
12541
12720
  });
12542
12721
  } }))));
@@ -12549,7 +12728,7 @@ const AddOrEditDBEntry = (props) => {
12549
12728
  entry[field.objectKey] = (e.target.value
12550
12729
  .replace(/[^0-9]/g, ''));
12551
12730
  dispatch({
12552
- type: ActionType$3.UpdateDBEntry,
12731
+ type: ActionType$4.UpdateDBEntry,
12553
12732
  dbEntry: entry,
12554
12733
  });
12555
12734
  } }))));
@@ -12579,7 +12758,7 @@ const AddOrEditDBEntry = (props) => {
12579
12758
  }
12580
12759
  // Save
12581
12760
  dispatch({
12582
- type: ActionType$3.UpdateDBEntry,
12761
+ type: ActionType$4.UpdateDBEntry,
12583
12762
  dbEntry: entry,
12584
12763
  });
12585
12764
  }, ariaLabel: choice.title }));
@@ -12594,7 +12773,7 @@ const AddOrEditDBEntry = (props) => {
12594
12773
  // Update entry and save
12595
12774
  entry[field.objectKey] = values;
12596
12775
  dispatch({
12597
- type: ActionType$3.UpdateDBEntry,
12776
+ type: ActionType$4.UpdateDBEntry,
12598
12777
  dbEntry: entry,
12599
12778
  });
12600
12779
  } })))));
@@ -12607,7 +12786,7 @@ const AddOrEditDBEntry = (props) => {
12607
12786
  React__default["default"].createElement(CreatableMultiselect, { disabled: disabled, type: DBEntryFieldType$1.NumberArray, values: entry[field.objectKey] || [], onChange: (values) => {
12608
12787
  entry[field.objectKey] = values;
12609
12788
  dispatch({
12610
- type: ActionType$3.UpdateDBEntry,
12789
+ type: ActionType$4.UpdateDBEntry,
12611
12790
  dbEntry: entry,
12612
12791
  });
12613
12792
  } })))));
@@ -12681,7 +12860,7 @@ const generateEndpointPath = (collectionName, adminsOnly) => {
12681
12860
  */
12682
12861
  /* ------------- Actions ------------ */
12683
12862
  // Types of actions
12684
- var ActionType$2;
12863
+ var ActionType$3;
12685
12864
  (function (ActionType) {
12686
12865
  // Show adder
12687
12866
  ActionType["ShowAdder"] = "ShowAdder";
@@ -12695,7 +12874,7 @@ var ActionType$2;
12695
12874
  ActionType["StartDelete"] = "StartDelete";
12696
12875
  // Finish deletion process
12697
12876
  ActionType["FinishDelete"] = "FinishDelete";
12698
- })(ActionType$2 || (ActionType$2 = {}));
12877
+ })(ActionType$3 || (ActionType$3 = {}));
12699
12878
  /**
12700
12879
  * Reducer that executes actions
12701
12880
  * @author Yuen Ler Chow
@@ -12703,18 +12882,18 @@ var ActionType$2;
12703
12882
  * @param action action to execute
12704
12883
  * @returns updated state
12705
12884
  */
12706
- const reducer$3 = (state, action) => {
12885
+ const reducer$4 = (state, action) => {
12707
12886
  switch (action.type) {
12708
- case ActionType$2.FinishLoading: {
12887
+ case ActionType$3.FinishLoading: {
12709
12888
  return Object.assign(Object.assign({}, state), { loading: false, dbEntries: action.dbEntries });
12710
12889
  }
12711
- case ActionType$2.ShowAdder: {
12890
+ case ActionType$3.ShowAdder: {
12712
12891
  return Object.assign(Object.assign({}, state), { adding: true, dbEntryToEdit: undefined });
12713
12892
  }
12714
- case ActionType$2.ShowEditor: {
12893
+ case ActionType$3.ShowEditor: {
12715
12894
  return Object.assign(Object.assign({}, state), { adding: false, dbEntryToEdit: action.dbEntry });
12716
12895
  }
12717
- case ActionType$2.FinishAdd: {
12896
+ case ActionType$3.FinishAdd: {
12718
12897
  // Handle cancel
12719
12898
  const finishedEntry = action.dbEntry;
12720
12899
  if (!finishedEntry) {
@@ -12738,10 +12917,10 @@ const reducer$3 = (state, action) => {
12738
12917
  // Update the state
12739
12918
  return Object.assign(Object.assign({}, state), { adding: false, dbEntryToEdit: undefined, dbEntries: updatedDbEntries });
12740
12919
  }
12741
- case ActionType$2.StartDelete: {
12920
+ case ActionType$3.StartDelete: {
12742
12921
  return Object.assign(Object.assign({}, state), { loading: true });
12743
12922
  }
12744
- case ActionType$2.FinishDelete: {
12923
+ case ActionType$3.FinishDelete: {
12745
12924
  return Object.assign(Object.assign({}, state), { loading: false,
12746
12925
  // Remove the deleted entry from the list
12747
12926
  dbEntries: state.dbEntries.filter((entry) => {
@@ -12767,7 +12946,7 @@ const DBEntryManagerPanel = (props) => {
12767
12946
  loading: true,
12768
12947
  };
12769
12948
  // Initialize state
12770
- const [state, dispatch] = React.useReducer(reducer$3, initialState);
12949
+ const [state, dispatch] = React.useReducer(reducer$4, initialState);
12771
12950
  // Destructure common state
12772
12951
  const { adding, dbEntryToEdit, dbEntries, loading, } = state;
12773
12952
  /*------------------------------------------------------------------------*/
@@ -12793,7 +12972,7 @@ const DBEntryManagerPanel = (props) => {
12793
12972
  try {
12794
12973
  // Start loader
12795
12974
  dispatch({
12796
- type: ActionType$2.StartDelete,
12975
+ type: ActionType$3.StartDelete,
12797
12976
  });
12798
12977
  // Perform deletion
12799
12978
  yield visitServerEndpoint({
@@ -12802,7 +12981,7 @@ const DBEntryManagerPanel = (props) => {
12802
12981
  });
12803
12982
  // Finish loader
12804
12983
  dispatch({
12805
- type: ActionType$2.FinishDelete,
12984
+ type: ActionType$3.FinishDelete,
12806
12985
  dbEntry: entry,
12807
12986
  idPropName,
12808
12987
  });
@@ -12831,7 +13010,7 @@ const DBEntryManagerPanel = (props) => {
12831
13010
  });
12832
13011
  // Save loaded data
12833
13012
  dispatch({
12834
- type: ActionType$2.FinishLoading,
13013
+ type: ActionType$3.FinishLoading,
12835
13014
  dbEntries: data,
12836
13015
  });
12837
13016
  }
@@ -12874,7 +13053,7 @@ const DBEntryManagerPanel = (props) => {
12874
13053
  React__default["default"].createElement("span", { className: "d-none d-md-inline ms-1" }, "Remove")),
12875
13054
  !disableEdit && (React__default["default"].createElement("button", { type: "button", id: `DBEntryManagerPanel-edit-with-id-${entry[idPropName]}`, className: "btn btn-primary", "aria-label": `edit db entry: ${entry[titlePropName]}`, onClick: () => {
12876
13055
  dispatch({
12877
- type: ActionType$2.ShowEditor,
13056
+ type: ActionType$3.ShowEditor,
12878
13057
  dbEntry: entry,
12879
13058
  });
12880
13059
  } },
@@ -12884,7 +13063,7 @@ const DBEntryManagerPanel = (props) => {
12884
13063
  React__default["default"].createElement("div", { className: "d-grid" },
12885
13064
  React__default["default"].createElement("button", { type: "button", id: "DBEntryManagerPanel-add-entry", className: "btn btn-lg btn-primary", "aria-label": `add a new ${itemName} entry to the list of entries`, onClick: () => {
12886
13065
  dispatch({
12887
- type: ActionType$2.ShowAdder,
13066
+ type: ActionType$3.ShowAdder,
12888
13067
  });
12889
13068
  } },
12890
13069
  React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.faPlus, className: "me-2" }),
@@ -12896,7 +13075,7 @@ const DBEntryManagerPanel = (props) => {
12896
13075
  if (!loading && (adding || dbEntryToEdit)) {
12897
13076
  body = (React__default["default"].createElement(AddOrEditDBEntry, { saveEndpointPath: endpoint, validateEntry: validateEntry, modifyEntry: modifyEntry, entryFields: entryFields, dbEntryToEdit: dbEntryToEdit, idPropName: idPropName, entries: dbEntries, itemName: itemName, onFinished: (entry) => {
12898
13077
  dispatch({
12899
- type: ActionType$2.FinishAdd,
13078
+ type: ActionType$3.FinishAdd,
12900
13079
  dbEntry: entry,
12901
13080
  idPropName,
12902
13081
  });
@@ -13093,25 +13272,25 @@ const getItemIds = (items) => {
13093
13272
  };
13094
13273
  /* ------------- Actions ------------ */
13095
13274
  // Types of actions
13096
- var ActionType$1;
13275
+ var ActionType$2;
13097
13276
  (function (ActionType) {
13098
13277
  // Hide the "jump to bottom" button
13099
13278
  ActionType["HideJumpToBottomButton"] = "HideJumpToBottomButton";
13100
13279
  // Show the "jump to bottom" button
13101
13280
  ActionType["ShowJumpToBottomButton"] = "ShowJumpToBottomButton";
13102
- })(ActionType$1 || (ActionType$1 = {}));
13281
+ })(ActionType$2 || (ActionType$2 = {}));
13103
13282
  /**
13104
13283
  * Reducer that executes actions
13105
13284
  * @author Gabe Abrams
13106
13285
  * @param state current state
13107
13286
  * @param action action to execute
13108
13287
  */
13109
- const reducer$2 = (state, action) => {
13288
+ const reducer$3 = (state, action) => {
13110
13289
  switch (action.type) {
13111
- case ActionType$1.HideJumpToBottomButton: {
13290
+ case ActionType$2.HideJumpToBottomButton: {
13112
13291
  return Object.assign(Object.assign({}, state), { jumpToBottomButtonVisible: false });
13113
13292
  }
13114
- case ActionType$1.ShowJumpToBottomButton: {
13293
+ case ActionType$2.ShowJumpToBottomButton: {
13115
13294
  return Object.assign(Object.assign({}, state), { jumpToBottomButtonVisible: true });
13116
13295
  }
13117
13296
  default: {
@@ -13135,7 +13314,7 @@ const AutoscrollToBottomContainer = (props) => {
13135
13314
  jumpToBottomButtonVisible: false,
13136
13315
  };
13137
13316
  // Initialize state
13138
- const [state, dispatch] = React.useReducer(reducer$2, initialState);
13317
+ const [state, dispatch] = React.useReducer(reducer$3, initialState);
13139
13318
  // Destructure common state
13140
13319
  const { jumpToBottomButtonVisible, } = state;
13141
13320
  /* -------------- Refs -------------- */
@@ -13181,7 +13360,7 @@ const AutoscrollToBottomContainer = (props) => {
13181
13360
  }
13182
13361
  // Update state
13183
13362
  dispatch({
13184
- type: ActionType$1.HideJumpToBottomButton,
13363
+ type: ActionType$2.HideJumpToBottomButton,
13185
13364
  });
13186
13365
  // Scroll to bottom
13187
13366
  container.current.scrollTop = (container.current.scrollHeight
@@ -13205,7 +13384,7 @@ const AutoscrollToBottomContainer = (props) => {
13205
13384
  if (isScrolledToBottom()) {
13206
13385
  // Hide button
13207
13386
  dispatch({
13208
- type: ActionType$1.HideJumpToBottomButton,
13387
+ type: ActionType$2.HideJumpToBottomButton,
13209
13388
  });
13210
13389
  }
13211
13390
  };
@@ -13247,7 +13426,7 @@ const AutoscrollToBottomContainer = (props) => {
13247
13426
  else {
13248
13427
  // Not scrolled to bottom. Show "jump to bottom" button.
13249
13428
  dispatch({
13250
- type: ActionType$1.ShowJumpToBottomButton,
13429
+ type: ActionType$2.ShowJumpToBottomButton,
13251
13430
  });
13252
13431
  }
13253
13432
  }, [items]);
@@ -13317,25 +13496,25 @@ const combineClassNames = (classNames) => {
13317
13496
  */
13318
13497
  /* ------------- Actions ------------ */
13319
13498
  // Types of actions
13320
- var ActionType;
13499
+ var ActionType$1;
13321
13500
  (function (ActionType) {
13322
13501
  // Start hovering on an option
13323
13502
  ActionType["StartHover"] = "StartHover";
13324
13503
  // Stop hovering on an option
13325
13504
  ActionType["StopHover"] = "StopHover";
13326
- })(ActionType || (ActionType = {}));
13505
+ })(ActionType$1 || (ActionType$1 = {}));
13327
13506
  /**
13328
13507
  * Reducer that executes actions
13329
13508
  * @author Gabe Abrams
13330
13509
  * @param state current state
13331
13510
  * @param action action to execute
13332
13511
  */
13333
- const reducer$1 = (state, action) => {
13512
+ const reducer$2 = (state, action) => {
13334
13513
  switch (action.type) {
13335
- case ActionType.StartHover: {
13514
+ case ActionType$1.StartHover: {
13336
13515
  return Object.assign(Object.assign({}, state), { hoveredOptionId: action.hoveredOptionId });
13337
13516
  }
13338
- case ActionType.StopHover: {
13517
+ case ActionType$1.StopHover: {
13339
13518
  return Object.assign(Object.assign({}, state), { hoveredOptionId: undefined });
13340
13519
  }
13341
13520
  default: {
@@ -13359,7 +13538,7 @@ const MultiSwitch = (props) => {
13359
13538
  hoveredOptionId: undefined,
13360
13539
  };
13361
13540
  // Initialize state
13362
- const [state, dispatch] = React.useReducer(reducer$1, initialState);
13541
+ const [state, dispatch] = React.useReducer(reducer$2, initialState);
13363
13542
  // Destructure common state
13364
13543
  const { hoveredOptionId, } = state;
13365
13544
  /*------------------------------------------------------------------------*/
@@ -13371,7 +13550,7 @@ const MultiSwitch = (props) => {
13371
13550
  });
13372
13551
  // Constants
13373
13552
  const gutterRems = heightRem * 0.1;
13374
- const itemWidthRems = heightRem * 1.3;
13553
+ const itemWidthRems = heightRem * 2.2;
13375
13554
  const textFontSize = heightRem / 3.5;
13376
13555
  const iconFontSize = heightRem / 2;
13377
13556
  const borderWidthRems = 0.05;
@@ -13491,27 +13670,27 @@ const MultiSwitch = (props) => {
13491
13670
  : `click to select option "${option.label}"`), onClick: () => {
13492
13671
  // Remove hover
13493
13672
  dispatch({
13494
- type: ActionType.StopHover,
13673
+ type: ActionType$1.StopHover,
13495
13674
  });
13496
13675
  // Notify parent
13497
13676
  onChange(option.id);
13498
13677
  }, onMouseEnter: () => {
13499
13678
  dispatch({
13500
- type: ActionType.StartHover,
13679
+ type: ActionType$1.StartHover,
13501
13680
  hoveredOptionId: option.id,
13502
13681
  });
13503
13682
  }, onMouseLeave: () => {
13504
13683
  dispatch({
13505
- type: ActionType.StopHover,
13684
+ type: ActionType$1.StopHover,
13506
13685
  });
13507
13686
  }, onFocus: () => {
13508
13687
  dispatch({
13509
- type: ActionType.StartHover,
13688
+ type: ActionType$1.StartHover,
13510
13689
  hoveredOptionId: option.id,
13511
13690
  });
13512
13691
  }, onBlur: () => {
13513
13692
  dispatch({
13514
- type: ActionType.StopHover,
13693
+ type: ActionType$1.StopHover,
13515
13694
  });
13516
13695
  }, style: {
13517
13696
  pointerEvents: ((option.id === selectedOptionId)
@@ -13530,6 +13709,147 @@ const MultiSwitch = (props) => {
13530
13709
  React__default["default"].createElement("div", { className: "MultiSwitch-highlight-container" }, highlight)));
13531
13710
  };
13532
13711
 
13712
+ // Types of dropdown items
13713
+ var DropdownItemType;
13714
+ (function (DropdownItemType) {
13715
+ // Dropdown header
13716
+ DropdownItemType["Header"] = "Header";
13717
+ // Dropdown divider
13718
+ DropdownItemType["Divider"] = "Divider";
13719
+ // Dropdown item
13720
+ DropdownItemType["Item"] = "Item";
13721
+ })(DropdownItemType || (DropdownItemType = {}));
13722
+ var DropdownItemType$1 = DropdownItemType;
13723
+
13724
+ /**
13725
+ * A simple dropdown menu
13726
+ * @author Alessandra De Lucas
13727
+ * @author Yuen Ler Chow
13728
+ * @author Gabe Abrams
13729
+ */
13730
+ /* ------------- Actions ------------ */
13731
+ // Types of actions
13732
+ var ActionType;
13733
+ (function (ActionType) {
13734
+ // Toggle opening the dropdown menu
13735
+ ActionType["ToggleDropdown"] = "ToggleDropdown";
13736
+ // Close the dropdown menu
13737
+ ActionType["CloseDropdown"] = "CloseDropdown";
13738
+ })(ActionType || (ActionType = {}));
13739
+ /**
13740
+ * Reducer that executes actions
13741
+ * @author Alessandra De Lucas
13742
+ * @author Yuen Ler Chow
13743
+ * @param state current state
13744
+ * @param action action to execute
13745
+ */
13746
+ const reducer$1 = (state, action) => {
13747
+ switch (action.type) {
13748
+ case ActionType.ToggleDropdown: {
13749
+ return Object.assign(Object.assign({}, state), { isDropdownOpen: !state.isDropdownOpen });
13750
+ }
13751
+ case ActionType.CloseDropdown: {
13752
+ return Object.assign(Object.assign({}, state), { isDropdownOpen: false });
13753
+ }
13754
+ default: {
13755
+ return state;
13756
+ }
13757
+ }
13758
+ };
13759
+ /*------------------------------------------------------------------------*/
13760
+ /* ------------------------------ Component ----------------------------- */
13761
+ /*------------------------------------------------------------------------*/
13762
+ const Dropdown = (props) => {
13763
+ /*------------------------------------------------------------------------*/
13764
+ /* -------------------------------- Setup ------------------------------- */
13765
+ /*------------------------------------------------------------------------*/
13766
+ /* -------------- Props ------------- */
13767
+ // Destructure all props
13768
+ const { dropdownButton, items, } = props;
13769
+ /* -------------- State ------------- */
13770
+ // Initial state
13771
+ const initialState = {
13772
+ isDropdownOpen: false,
13773
+ };
13774
+ // Initialize state
13775
+ const [state, dispatch] = React.useReducer(reducer$1, initialState);
13776
+ // Destructure common state
13777
+ const { isDropdownOpen, } = state;
13778
+ /* -------------- Refs -------------- */
13779
+ // Initialize refs
13780
+ const dropdownRef = React.useRef(null);
13781
+ /*------------------------------------------------------------------------*/
13782
+ /* ------------------------- Component Functions ------------------------ */
13783
+ /*------------------------------------------------------------------------*/
13784
+ /**
13785
+ * Handle clicking outside of the dropdown menu to close it
13786
+ * @author Yuen Ler Chow
13787
+ * @param event the mouse event
13788
+ */
13789
+ const handleClickOutside = (event) => {
13790
+ if (
13791
+ // Dropdown has been rendered
13792
+ dropdownRef.current
13793
+ // Click occurred outside the dropdown
13794
+ && !dropdownRef.current.contains(event.target)) {
13795
+ dispatch({ type: ActionType.CloseDropdown });
13796
+ }
13797
+ };
13798
+ /*------------------------------------------------------------------------*/
13799
+ /* ------------------------- Lifecycle Functions ------------------------ */
13800
+ /*------------------------------------------------------------------------*/
13801
+ /**
13802
+ * Mount
13803
+ * @author Yuen Ler Chow
13804
+ */
13805
+ React.useEffect(() => {
13806
+ // Add event listener to close dropdown when clicking outside
13807
+ document.addEventListener('mousedown', handleClickOutside);
13808
+ // Cleanup
13809
+ return () => {
13810
+ document.removeEventListener('mousedown', handleClickOutside);
13811
+ };
13812
+ }, []);
13813
+ /*----------------------------------------*/
13814
+ /* --------------- Main UI -------------- */
13815
+ /*----------------------------------------*/
13816
+ return (React__default["default"].createElement("div", { className: "dropdown", ref: dropdownRef, "data-bs-theme": isDarkModeOn() ? 'dark' : undefined },
13817
+ React__default["default"].createElement("button", { className: combineClassNames([
13818
+ 'btn dropdown-toggle border',
13819
+ isDropdownOpen && 'show',
13820
+ `btn-${dropdownButton.variant}`,
13821
+ dropdownButton.variant === Variant$1.Light && 'text-dark',
13822
+ ]), type: "button", id: dropdownButton.id, "aria-expanded": isDropdownOpen, "aria-label": dropdownButton.ariaLabel, onClick: () => {
13823
+ dispatch({
13824
+ type: ActionType.ToggleDropdown,
13825
+ });
13826
+ } }, dropdownButton.content),
13827
+ React__default["default"].createElement("ul", { className: combineClassNames([
13828
+ 'dropdown-menu',
13829
+ isDarkModeOn() && 'dropdown-menu-dark',
13830
+ isDropdownOpen && 'show',
13831
+ ]) }, Object.values(items).map((item) => {
13832
+ if (item.type === DropdownItemType$1.Header) {
13833
+ return (
13834
+ // TODO: Implement header
13835
+ React__default["default"].createElement("span", null));
13836
+ }
13837
+ if (item.type === DropdownItemType$1.Divider) {
13838
+ return (
13839
+ // TODO: Implement divider
13840
+ React__default["default"].createElement("span", null));
13841
+ }
13842
+ return (React__default["default"].createElement("li", { key: item.id },
13843
+ React__default["default"].createElement("button", { type: "button", "aria-label": item.ariaLabel, className: "dropdown-item", onClick: (e) => {
13844
+ e.preventDefault();
13845
+ dispatch({
13846
+ type: ActionType.CloseDropdown,
13847
+ });
13848
+ item.onClick();
13849
+ } }, item.content)));
13850
+ }))));
13851
+ };
13852
+
13533
13853
  /**
13534
13854
  * One minute in ms
13535
13855
  * @author Gabe Abrams
@@ -13920,22 +14240,27 @@ const initServer = (opts) => {
13920
14240
  paramTypes: {
13921
14241
  year: ParamType$1.Int,
13922
14242
  month: ParamType$1.Int,
14243
+ pageNumber: ParamType$1.Int,
13923
14244
  },
13924
14245
  handler: ({ params }) => __awaiter(void 0, void 0, void 0, function* () {
13925
14246
  // Get user info
13926
- const { year, month, userId, isAdmin, } = params;
14247
+ const { year, month, pageNumber, userId, isAdmin, } = params;
13927
14248
  // Validate user
13928
14249
  const canReview = yield canReviewLogs(userId, isAdmin);
13929
14250
  if (!canReview) {
13930
14251
  throw new ErrorWithCode('You cannot access this resource because you do not have the appropriate permissions.', ReactKitErrorCode$1.NotAllowedToReviewLogs);
13931
14252
  }
13932
14253
  // Query for logs
13933
- const logs = yield _logCollection.find({
13934
- year,
13935
- month,
14254
+ const response = yield _logCollection.findPaged({
14255
+ query: {
14256
+ year,
14257
+ month,
14258
+ },
14259
+ perPage: 1000,
14260
+ pageNumber,
13936
14261
  });
13937
- // Return logs
13938
- return logs;
14262
+ // Return response
14263
+ return response;
13939
14264
  }),
13940
14265
  }));
13941
14266
  };
@@ -14337,6 +14662,12 @@ const parseUserAgent = (userAgent) => {
14337
14662
  * @param opts.handler function that processes the request
14338
14663
  * @param [opts.skipSessionCheck] if true, skip the session check (allow users
14339
14664
  * to not be logged in and launched via LTI)
14665
+ * @param [opts.allowedHosts] if included, only allow requests from these hosts
14666
+ * (start a hostname with a "*" to only check the end of the hostname)
14667
+ * you can include just one string instead of an array
14668
+ * @param [opts.bannedHosts] if included, do not allow requests from these hosts
14669
+ * (start a hostname with a "*" to only check the end of the hostname)
14670
+ * you can include just one string instead of an array
14340
14671
  * @param [opts.unhandledErrorMessagePrefix] if included, when an error that
14341
14672
  * is not of type ErrorWithCode is thrown, the client will receive an error
14342
14673
  * where the error message is prefixed with this string. For example,
@@ -14364,10 +14695,76 @@ const parseUserAgent = (userAgent) => {
14364
14695
  const genRouteHandler = (opts) => {
14365
14696
  // Return a route handler
14366
14697
  return (req, res, next) => __awaiter(void 0, void 0, void 0, function* () {
14367
- var _a, _b, _c;
14698
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
14368
14699
  // Output params
14369
14700
  const output = {};
14370
14701
  /*----------------------------------------*/
14702
+ /* ----------- Hostname Check ----------- */
14703
+ /*----------------------------------------*/
14704
+ // Get hostnames
14705
+ const originURL = String(req.get('origin')
14706
+ || req.headers.origin
14707
+ || req.headers.referer);
14708
+ const originHostname = (originURL
14709
+ // Remove protocol
14710
+ .replace(/(^\w+:|^)\/\//, '')
14711
+ // Remove port
14712
+ .replace(/:\d+$/, ''));
14713
+ const serverHostname = String(req.hostname);
14714
+ // Check allowed
14715
+ if (opts.allowedHosts) {
14716
+ // Only accept requests from allowed hosts
14717
+ const allowedArray = (Array.isArray(opts.allowedHosts)
14718
+ ? opts.allowedHosts
14719
+ : [opts.allowedHosts]);
14720
+ // Check if server is localhost
14721
+ if (serverHostname === 'localhost') {
14722
+ // Allow localhost
14723
+ allowedArray.push('localhost');
14724
+ }
14725
+ // Check if current host is allowed
14726
+ const allowed = allowedArray.some((allowedHost) => {
14727
+ if (allowedHost.startsWith('*')) {
14728
+ // Check end of hostname
14729
+ return originHostname.endsWith(allowedHost.substring(1));
14730
+ }
14731
+ // Check full hostname
14732
+ return originHostname.toLowerCase() === allowedHost.toLowerCase();
14733
+ });
14734
+ // If not allowed, return error
14735
+ if (!allowed) {
14736
+ return handleError(res, {
14737
+ message: 'You are not allowed to access this endpoint.',
14738
+ code: ReactKitErrorCode$1.HostNotAllowed,
14739
+ status: 403,
14740
+ });
14741
+ }
14742
+ }
14743
+ // Check banned
14744
+ if (opts.bannedHosts) {
14745
+ // Do not allow requests from banned hosts
14746
+ const bannedArray = (Array.isArray(opts.bannedHosts)
14747
+ ? opts.bannedHosts
14748
+ : [opts.bannedHosts]);
14749
+ // Check if current host is banned
14750
+ const banned = bannedArray.some((bannedHost) => {
14751
+ if (bannedHost.startsWith('*')) {
14752
+ // Check end of hostname
14753
+ return originHostname.endsWith(bannedHost.substring(1));
14754
+ }
14755
+ // Check full hostname
14756
+ return originHostname.toLowerCase() === bannedHost.toLowerCase();
14757
+ });
14758
+ // If banned, return error
14759
+ if (banned) {
14760
+ return handleError(res, {
14761
+ message: 'You are not allowed to access this endpoint.',
14762
+ code: ReactKitErrorCode$1.HostBanned,
14763
+ status: 403,
14764
+ });
14765
+ }
14766
+ }
14767
+ /*----------------------------------------*/
14371
14768
  /* ------------ Parse Params ------------ */
14372
14769
  /*----------------------------------------*/
14373
14770
  // Process items one by one
@@ -14565,34 +14962,34 @@ const genRouteHandler = (opts) => {
14565
14962
  // Add launch info to output
14566
14963
  output.userId = (launchInfo
14567
14964
  ? launchInfo.userId
14568
- : undefined);
14965
+ : ((_b = output.userId) !== null && _b !== void 0 ? _b : undefined));
14569
14966
  output.userFirstName = (launchInfo
14570
14967
  ? launchInfo.userFirstName
14571
- : undefined);
14968
+ : ((_c = output.userFirstName) !== null && _c !== void 0 ? _c : undefined));
14572
14969
  output.userLastName = (launchInfo
14573
14970
  ? launchInfo.userLastName
14574
- : undefined);
14971
+ : ((_d = output.userLastName) !== null && _d !== void 0 ? _d : undefined));
14575
14972
  output.userEmail = (launchInfo
14576
14973
  ? launchInfo.userEmail
14577
- : undefined);
14974
+ : ((_e = output.userEmail) !== null && _e !== void 0 ? _e : undefined));
14578
14975
  output.userAvatarURL = (launchInfo
14579
- ? ((_b = launchInfo.userImage) !== null && _b !== void 0 ? _b : 'http://www.gravatar.com/avatar/?d=identicon')
14580
- : undefined);
14976
+ ? ((_f = launchInfo.userImage) !== null && _f !== void 0 ? _f : 'http://www.gravatar.com/avatar/?d=identicon')
14977
+ : ((_g = output.userAvatarURL) !== null && _g !== void 0 ? _g : undefined));
14581
14978
  output.isLearner = (launchInfo
14582
14979
  ? !!launchInfo.isLearner
14583
- : undefined);
14980
+ : ((_h = output.isLearner) !== null && _h !== void 0 ? _h : undefined));
14584
14981
  output.isTTM = (launchInfo
14585
14982
  ? !!launchInfo.isTTM
14586
- : undefined);
14983
+ : ((_j = output.isTTM) !== null && _j !== void 0 ? _j : undefined));
14587
14984
  output.isAdmin = (launchInfo
14588
14985
  ? !!launchInfo.isAdmin
14589
- : undefined);
14986
+ : ((_k = output.isAdmin) !== null && _k !== void 0 ? _k : undefined));
14590
14987
  output.courseId = (launchInfo
14591
- ? ((_c = output.courseId) !== null && _c !== void 0 ? _c : launchInfo.courseId)
14592
- : undefined);
14988
+ ? ((_l = output.courseId) !== null && _l !== void 0 ? _l : launchInfo.courseId)
14989
+ : ((_m = output.courseId) !== null && _m !== void 0 ? _m : undefined));
14593
14990
  output.courseName = (launchInfo
14594
14991
  ? launchInfo.contextLabel
14595
- : undefined);
14992
+ : ((_o = output.courseName) !== null && _o !== void 0 ? _o : undefined));
14596
14993
  // Add other session variables
14597
14994
  Object.keys(req.session).forEach((propName) => {
14598
14995
  // Skip if prop already in output
@@ -14666,7 +15063,7 @@ const genRouteHandler = (opts) => {
14666
15063
  * @author Gabe Abrams
14667
15064
  */
14668
15065
  const logServerEvent = (logOpts) => __awaiter(void 0, void 0, void 0, function* () {
14669
- var _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r;
15066
+ var _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1;
14670
15067
  // NOTE: internally, we slip through an opts.overrideAsClientEvent boolean
14671
15068
  // that indicates that this is actually a client event, but we don't
14672
15069
  // include that in the LogFunction type because this is internal and
@@ -14678,16 +15075,16 @@ const genRouteHandler = (opts) => {
14678
15075
  const { timestamp, year, month, day, hour, minute, } = getTimeInfoInET();
14679
15076
  // Main log info
14680
15077
  const mainLogInfo = {
14681
- id: `${launchInfo ? 'unknown' : launchInfo.userId}-${Date.now()}-${Math.floor(Math.random() * 100000)}-${Math.floor(Math.random() * 100000)}`,
14682
- userFirstName: (launchInfo ? 'unknown' : launchInfo.userFirstName),
14683
- userLastName: (launchInfo ? 'unknown' : launchInfo.userLastName),
14684
- userEmail: (launchInfo ? 'unknown' : launchInfo.userEmail),
14685
- 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'),
14686
15083
  isLearner: (launchInfo && !!launchInfo.isLearner),
14687
15084
  isAdmin: (launchInfo && !!launchInfo.isAdmin),
14688
15085
  isTTM: (launchInfo && !!launchInfo.isTTM),
14689
- courseId: (launchInfo ? 'unknown' : launchInfo.courseId),
14690
- courseName: (launchInfo ? 'unknown' : launchInfo.contextLabel),
15086
+ courseId: (launchInfo ? launchInfo.courseId : 'unknown'),
15087
+ courseName: (launchInfo ? launchInfo.contextLabel : 'unknown'),
14691
15088
  browser,
14692
15089
  device,
14693
15090
  year,
@@ -14698,24 +15095,24 @@ const genRouteHandler = (opts) => {
14698
15095
  timestamp,
14699
15096
  context: (typeof logOpts.context === 'string'
14700
15097
  ? logOpts.context
14701
- : ((_e = ((_d = logOpts.context) !== null && _d !== void 0 ? _d : {})._) !== null && _e !== void 0 ? _e : LogBuiltInMetadata.Context.Uncategorized)),
14702
- subcontext: ((_f = logOpts.subcontext) !== null && _f !== void 0 ? _f : LogBuiltInMetadata.Context.Uncategorized),
14703
- tags: ((_g = logOpts.tags) !== null && _g !== void 0 ? _g : []),
14704
- level: ((_h = logOpts.level) !== null && _h !== void 0 ? _h : LogLevel$1.Info),
14705
- metadata: ((_j = logOpts.metadata) !== null && _j !== void 0 ? _j : {}),
15098
+ : ((_q = ((_p = logOpts.context) !== null && _p !== void 0 ? _p : {})._) !== null && _q !== void 0 ? _q : LogBuiltInMetadata.Context.Uncategorized)),
15099
+ subcontext: ((_r = logOpts.subcontext) !== null && _r !== void 0 ? _r : LogBuiltInMetadata.Context.Uncategorized),
15100
+ tags: ((_s = logOpts.tags) !== null && _s !== void 0 ? _s : []),
15101
+ level: ((_t = logOpts.level) !== null && _t !== void 0 ? _t : LogLevel$1.Info),
15102
+ metadata: ((_u = logOpts.metadata) !== null && _u !== void 0 ? _u : {}),
14706
15103
  };
14707
15104
  // Type-specific info
14708
15105
  const typeSpecificInfo = (('error' in opts && opts.error)
14709
15106
  ? {
14710
15107
  type: LogType$1.Error,
14711
- errorMessage: (_k = logOpts.error.message) !== null && _k !== void 0 ? _k : 'Unknown message',
14712
- errorCode: (_l = logOpts.error.code) !== null && _l !== void 0 ? _l : ReactKitErrorCode$1.NoCode,
14713
- errorStack: (_m = logOpts.error.stack) !== null && _m !== void 0 ? _m : 'No stack',
15108
+ errorMessage: (_v = logOpts.error.message) !== null && _v !== void 0 ? _v : 'Unknown message',
15109
+ errorCode: (_w = logOpts.error.code) !== null && _w !== void 0 ? _w : ReactKitErrorCode$1.NoCode,
15110
+ errorStack: (_x = logOpts.error.stack) !== null && _x !== void 0 ? _x : 'No stack',
14714
15111
  }
14715
15112
  : {
14716
15113
  type: LogType$1.Action,
14717
- target: ((_o = logOpts.target) !== null && _o !== void 0 ? _o : LogBuiltInMetadata.Target.NoTarget),
14718
- action: ((_p = logOpts.action) !== null && _p !== void 0 ? _p : LogAction$1.Unknown),
15114
+ target: ((_y = logOpts.target) !== null && _y !== void 0 ? _y : LogBuiltInMetadata.Target.NoTarget),
15115
+ action: ((_z = logOpts.action) !== null && _z !== void 0 ? _z : LogAction$1.Unknown),
14719
15116
  });
14720
15117
  // Source-specific info
14721
15118
  const sourceSpecificInfo = (logOpts.overrideAsClientEvent
@@ -14750,7 +15147,7 @@ const genRouteHandler = (opts) => {
14750
15147
  catch (err) {
14751
15148
  // Print because we cannot store the error
14752
15149
  // eslint-disable-next-line no-console
14753
- console.error('Could not log the following:', logOpts, 'due to this error:', ((_q = err) !== null && _q !== void 0 ? _q : {}).message, ((_r = err) !== null && _r !== void 0 ? _r : {}).stack);
15150
+ console.error('Could not log the following:', logOpts, 'due to this error:', ((_0 = err) !== null && _0 !== void 0 ? _0 : {}).message, ((_1 = err) !== null && _1 !== void 0 ? _1 : {}).stack);
14754
15151
  // Create a dummy log to return
14755
15152
  const dummyMainInfo = {
14756
15153
  id: '-1',
@@ -15094,6 +15491,11 @@ const initLogCollection = (Collection) => {
15094
15491
  'context',
15095
15492
  'subcontext',
15096
15493
  'tags',
15494
+ 'year',
15495
+ 'month',
15496
+ 'day',
15497
+ 'hour',
15498
+ 'type',
15097
15499
  ],
15098
15500
  });
15099
15501
  };
@@ -15839,6 +16241,182 @@ const shuffleArray = (arr) => {
15839
16241
  return newArr;
15840
16242
  };
15841
16243
 
16244
+ /**
16245
+ * Sends and retries an http request
16246
+ * @author Gabriel Abrams
16247
+ * @param opts object containing all arguments
16248
+ * @param opts.path path to send request to
16249
+ * @param [opts.host] host to send request to
16250
+ * @param [opts.method=GET] http method to use
16251
+ * @param [opts.params] body/data to include in the request
16252
+ * @param [opts.headers] headers to include in the request
16253
+ * @param [opts.sendCrossDomainCredentials=true if in development mode] if true,
16254
+ * send cross-domain credentials even if not in dev mode
16255
+ * @param [opts.responseType=JSON] expected response type
16256
+ * @returns { body, status, headers } on success
16257
+ */
16258
+ const sendServerToServerRequest = (opts) => __awaiter(void 0, void 0, void 0, function* () {
16259
+ var _a;
16260
+ // Process method
16261
+ const method = (opts.method || 'GET');
16262
+ // Encode objects within params
16263
+ let params;
16264
+ if (opts.params) {
16265
+ params = {};
16266
+ Object.entries(opts.params).forEach(([key, val]) => {
16267
+ if (typeof val === 'object' && !Array.isArray(val)) {
16268
+ params[key] = JSON.stringify(val);
16269
+ }
16270
+ else {
16271
+ params[key] = val;
16272
+ }
16273
+ });
16274
+ }
16275
+ // Stringify parameters
16276
+ const stringifiedParams = qs__default["default"].stringify(params || {}, {
16277
+ encodeValuesOnly: true,
16278
+ arrayFormat: 'brackets',
16279
+ });
16280
+ // Create url (include query if GET)
16281
+ const query = (method === 'GET' ? `?${stringifiedParams}` : '');
16282
+ let url;
16283
+ if (!opts.host) {
16284
+ // No host included at all. Just send to a path
16285
+ url = `${opts.path}${query}`;
16286
+ }
16287
+ else {
16288
+ url = `https://${opts.host}${opts.path}${query}`;
16289
+ }
16290
+ // Update headers
16291
+ const headers = opts.headers || {};
16292
+ let data = null;
16293
+ if (!headers['Content-Type']) {
16294
+ // Form encoded
16295
+ headers['Content-Type'] = 'application/x-www-form-urlencoded';
16296
+ // Add data if applicable
16297
+ data = (method !== 'GET' ? stringifiedParams : null);
16298
+ }
16299
+ else {
16300
+ // JSON encode
16301
+ data = params;
16302
+ }
16303
+ // Encode data
16304
+ let encodedData;
16305
+ if (data) {
16306
+ if (headers['Content-Type'] === 'application/x-www-form-urlencoded') {
16307
+ encodedData = new URLSearchParams(params);
16308
+ }
16309
+ else {
16310
+ encodedData = JSON.stringify(data);
16311
+ }
16312
+ }
16313
+ // Send request
16314
+ try {
16315
+ const response = yield fetch(url, {
16316
+ method,
16317
+ mode: 'cors',
16318
+ headers: headers !== null && headers !== void 0 ? headers : {},
16319
+ body: ((method !== 'GET' && encodedData)
16320
+ ? encodedData
16321
+ : undefined),
16322
+ redirect: 'follow',
16323
+ });
16324
+ // Get headers map
16325
+ const responseHeaders = {};
16326
+ response.headers.forEach((value, key) => {
16327
+ responseHeaders[key] = value;
16328
+ });
16329
+ // Process response based on responseType
16330
+ try {
16331
+ // Parse response
16332
+ let responseBody;
16333
+ if (opts.responseType
16334
+ && opts.responseType === 'Text') {
16335
+ // Response type is text
16336
+ responseBody = yield response.text();
16337
+ }
16338
+ else {
16339
+ // Response type is JSON
16340
+ responseBody = yield response.json();
16341
+ }
16342
+ // Return response
16343
+ return {
16344
+ body: responseBody,
16345
+ status: response.status,
16346
+ headers: responseHeaders,
16347
+ };
16348
+ }
16349
+ catch (err) {
16350
+ throw new ErrorWithCode(`Failed to parse response as ${opts.responseType}: ${err === null || err === void 0 ? void 0 : err.message}`, ReactKitErrorCode$1.ResponseParseError);
16351
+ }
16352
+ }
16353
+ catch (err) {
16354
+ // Self-signed certificate error:
16355
+ if ((_a = err === null || err === void 0 ? void 0 : err.message) === null || _a === void 0 ? void 0 : _a.includes('self signed certificate')) {
16356
+ throw new ErrorWithCode('We refused to send a request because the receiver has self-signed certificates.', ReactKitErrorCode$1.SelfSigned);
16357
+ }
16358
+ // No tries left
16359
+ throw new ErrorWithCode(`We encountered an error when trying to send a network request. If this issue persists, contact an admin. Error: ${err === null || err === void 0 ? void 0 : err.message}`, ReactKitErrorCode$1.NotConnected);
16360
+ }
16361
+ });
16362
+
16363
+ /**
16364
+ * Send a server-to-server request from this sever to another server that uses
16365
+ * dce-reactkit [for server only]
16366
+ * @author Gabe Abrams
16367
+ * @param opts object containing all arguments
16368
+ * @param opts.host - the host of the other server
16369
+ * @param opts.path - the path of the other server's endpoint
16370
+ * @param [opts.method=GET] - the method of the endpoint
16371
+ * @param [opts.params] - query/body parameters to include
16372
+ * @param [opts.headers] - headers to include
16373
+ * @returns response from server
16374
+ */
16375
+ const visitEndpointOnAnotherServer = (opts) => __awaiter(void 0, void 0, void 0, function* () {
16376
+ var _a;
16377
+ // Remove properties with undefined values
16378
+ let params;
16379
+ if (opts.params) {
16380
+ params = Object.fromEntries(Object
16381
+ .entries(opts.params)
16382
+ .filter(([, value]) => {
16383
+ return value !== undefined;
16384
+ }));
16385
+ }
16386
+ // Automatically JSONify arrays and objects
16387
+ if (params) {
16388
+ params = Object.fromEntries(Object
16389
+ .entries(params)
16390
+ .map(([key, value]) => {
16391
+ if (Array.isArray(value) || typeof value === 'object') {
16392
+ return [key, JSON.stringify(value)];
16393
+ }
16394
+ return [key, value];
16395
+ }));
16396
+ }
16397
+ // Send the request
16398
+ const response = yield sendServerToServerRequest({
16399
+ host: opts.host,
16400
+ path: opts.path,
16401
+ method: (_a = opts.method) !== null && _a !== void 0 ? _a : 'GET',
16402
+ params,
16403
+ });
16404
+ // Check for failure
16405
+ if (!response || !response.body) {
16406
+ throw new ErrorWithCode('We didn\'t get a response from the server. Please check your internet connection.', ReactKitErrorCode$1.NoResponse);
16407
+ }
16408
+ if (!response.body.success) {
16409
+ // Other errors
16410
+ throw new ErrorWithCode((response.body.message
16411
+ || 'An unknown error occurred. Please contact an admin.'), (response.body.code
16412
+ || ReactKitErrorCode$1.NoCode));
16413
+ }
16414
+ // Success! Extract the body
16415
+ const { body } = response.body;
16416
+ // Return
16417
+ return body;
16418
+ });
16419
+
15842
16420
  /**
15843
16421
  * Days of the week
15844
16422
  * @author Gabe Abrams
@@ -15866,6 +16444,8 @@ exports.DBEntryFieldType = DBEntryFieldType$1;
15866
16444
  exports.DBEntryManagerPanel = DBEntryManagerPanel;
15867
16445
  exports.DayOfWeek = DayOfWeek$1;
15868
16446
  exports.Drawer = Drawer;
16447
+ exports.Dropdown = Dropdown;
16448
+ exports.DropdownItemType = DropdownItemType$1;
15869
16449
  exports.DynamicWord = DynamicWord;
15870
16450
  exports.ErrorBox = ErrorBox;
15871
16451
  exports.ErrorWithCode = ErrorWithCode;
@@ -15937,6 +16517,7 @@ exports.padDecimalZeros = padDecimalZeros;
15937
16517
  exports.padZerosLeft = padZerosLeft;
15938
16518
  exports.parallelLimit = parallelLimit;
15939
16519
  exports.prefixWithAOrAn = prefixWithAOrAn;
16520
+ exports.prompt = prompt;
15940
16521
  exports.roundToNumDecimals = roundToNumDecimals;
15941
16522
  exports.setClientEventMetadataPopulator = setClientEventMetadataPopulator;
15942
16523
  exports.showFatalError = showFatalError;
@@ -15950,6 +16531,7 @@ exports.useForceRender = useForceRender;
15950
16531
  exports.validateEmail = validateEmail;
15951
16532
  exports.validatePhoneNumber = validatePhoneNumber;
15952
16533
  exports.validateString = validateString;
16534
+ exports.visitEndpointOnAnotherServer = visitEndpointOnAnotherServer;
15953
16535
  exports.visitServerEndpoint = visitServerEndpoint;
15954
16536
  exports.waitMs = waitMs;
15955
16537
  //# sourceMappingURL=index.js.map