dce-reactkit 3.9.0-beta-loading-modal.2 → 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 +783 -201
  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 +779 -202
  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 +4 -1
  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/esm/index.js CHANGED
@@ -4,6 +4,7 @@ import { faExclamationTriangle, faCircle, faHourglassEnd, faDotCircle, faCheckSq
4
4
  import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
5
5
  import ReactDOM, { createPortal } from 'react-dom';
6
6
  import { faCircle as faCircle$1, faSquareMinus, faSquare } from '@fortawesome/free-regular-svg-icons';
7
+ import qs from 'qs';
7
8
 
8
9
  /******************************************************************************
9
10
  Copyright (c) Microsoft Corporation.
@@ -30,7 +31,7 @@ function __awaiter(thisArg, _arguments, P, generator) {
30
31
  });
31
32
  }
32
33
 
33
- // Highest error code = DRK13
34
+ // Highest error code = DRK18
34
35
  /**
35
36
  * List of error codes built into the react kit
36
37
  * @author Gabe Abrams
@@ -42,6 +43,8 @@ var ReactKitErrorCode;
42
43
  ReactKitErrorCode["SessionExpired"] = "DRK3";
43
44
  ReactKitErrorCode["MissingParameter"] = "DRK4";
44
45
  ReactKitErrorCode["InvalidParameter"] = "DRK5";
46
+ ReactKitErrorCode["HostNotAllowed"] = "DRK17";
47
+ ReactKitErrorCode["HostBanned"] = "DRK18";
45
48
  ReactKitErrorCode["WrongCourse"] = "DRK6";
46
49
  ReactKitErrorCode["NoCACCLSendRequestFunction"] = "DRK7";
47
50
  ReactKitErrorCode["NoCACCLGetLaunchInfoFunction"] = "DRK8";
@@ -50,6 +53,10 @@ var ReactKitErrorCode;
50
53
  ReactKitErrorCode["NotAllowedToReviewLogs"] = "DRK11";
51
54
  ReactKitErrorCode["ThemeCheckedBeforeReactKitReady"] = "DRK12";
52
55
  ReactKitErrorCode["SessionExpiredMessageGottenBeforeReactKitReady"] = "DRK13";
56
+ // Server-to-server requests
57
+ ReactKitErrorCode["NotConnected"] = "DRK14";
58
+ ReactKitErrorCode["SelfSigned"] = "DRK15";
59
+ ReactKitErrorCode["ResponseParseError"] = "DRK16";
53
60
  })(ReactKitErrorCode || (ReactKitErrorCode = {}));
54
61
  var ReactKitErrorCode$1 = ReactKitErrorCode;
55
62
 
@@ -151,6 +158,7 @@ var ModalButtonType$1 = ModalButtonType;
151
158
  var ModalType;
152
159
  (function (ModalType) {
153
160
  ModalType["Okay"] = "okay";
161
+ ModalType["Cancel"] = "cancel";
154
162
  ModalType["OkayCancel"] = "okay-cancel";
155
163
  ModalType["YesNo"] = "yes-no";
156
164
  ModalType["YesNoCancel"] = "yes-no-cancel";
@@ -373,11 +381,23 @@ const isDarkModeOn = () => {
373
381
  * @param opts object containing all arguments
374
382
  * @param opts.sendRequest caccl send request functions
375
383
  * @param [opts.sessionExpiredMessage] a custom session expired message
384
+ * @param [opts.darkModeOn] if true, dark mode is enabled
385
+ * @param [opts.noServer] if true, there is no server for this app
376
386
  */
377
387
  const initClient = (opts) => {
378
- // Store values
379
- storedSendRequest = opts.sendRequest;
380
- sessionExpiredMessage = opts.sessionExpiredMessage;
388
+ // Handle separately if there is no server
389
+ if (opts.noServer) {
390
+ // Store values
391
+ storedSendRequest = () => __awaiter(void 0, void 0, void 0, function* () {
392
+ throw new Error('Cannot send requests because there is no server');
393
+ });
394
+ }
395
+ else {
396
+ // Store values
397
+ storedSendRequest = opts.sendRequest;
398
+ sessionExpiredMessage = opts.sessionExpiredMessage;
399
+ }
400
+ // Handle universal parts
381
401
  darkModeOn = !!opts.darkModeOn;
382
402
  // Mark as initialized
383
403
  onInitialized(null);
@@ -398,6 +418,9 @@ const modalTypeToModalButtonTypes = {
398
418
  [ModalType$1.Okay]: [
399
419
  ModalButtonType$1.Okay,
400
420
  ],
421
+ [ModalType$1.Cancel]: [
422
+ ModalButtonType$1.Cancel,
423
+ ],
401
424
  [ModalType$1.OkayCancel]: [
402
425
  ModalButtonType$1.Okay,
403
426
  ModalButtonType$1.Cancel,
@@ -716,7 +739,7 @@ const Modal = (props) => {
716
739
  zIndex: baseZIndex + 2,
717
740
  // Override sizes for even larger for XL
718
741
  width: (size === ModalSize$1.ExtraLarge
719
- ? 'calc(100vw - 3rem)'
742
+ ? 'calc(100vw - 1rem)'
720
743
  : undefined),
721
744
  maxWidth: (size === ModalSize$1.ExtraLarge
722
745
  ? '80rem'
@@ -853,6 +876,7 @@ const _setStubResponse = (opts) => {
853
876
  */
854
877
  const visitServerEndpoint = (opts) => __awaiter(void 0, void 0, void 0, function* () {
855
878
  var _a, _b, _c;
879
+ // Set default method
856
880
  const method = ((_a = opts.method) !== null && _a !== void 0 ? _a : 'GET');
857
881
  // Handle stubs
858
882
  const stubResponse = (_b = stubResponses[method]) === null || _b === void 0 ? void 0 : _b[opts.path];
@@ -1152,6 +1176,92 @@ const confirm = (title, text, opts) => __awaiter(void 0, void 0, void 0, functio
1152
1176
  });
1153
1177
  });
1154
1178
  /*----------------------------------------*/
1179
+ /* --------------- Prompt -------------- */
1180
+ /*----------------------------------------*/
1181
+ // Stored copies of setters
1182
+ let setPromptInfo;
1183
+ // Function to call when prompt is closed
1184
+ let onPromptClosed;
1185
+ /**
1186
+ * Show a prompt modal asking the user for input
1187
+ * @author Yuen Ler Chow
1188
+ * @param title the title text to display at the top of the prompt
1189
+ * @param [opts={}] additional options for the prompt dialog
1190
+ * @param [opts.textAboveInputField] the text to display in the prompt
1191
+ * @param [opts.defaultText] the default text for the input field
1192
+ * @param [opts.placeholder] the placeholder text for the input field
1193
+ * @param [opts.confirmButtonText=Okay] the text of the confirm button
1194
+ * @param [opts.confirmButtonVariant=Variant.Dark] the variant of the confirm button
1195
+ * @param [opts.cancelButtonText=Cancel] the text of the cancel button
1196
+ * @param [opts.cancelButtonVariant=Variant.Secondary] the variant of the cancel button
1197
+ * @param [opts.minNumChars] the minimum number of characters required for
1198
+ * the input to be valid
1199
+ * @param [opts.findValidationError] a function that takes the input text and
1200
+ * returns an error message if the input is invalid, returns undefined if the
1201
+ * input is valid
1202
+ * @param [opts.ariaLabel] the aria label for the input field
1203
+ * @returns Promise that resolves with the input string or null if canceled
1204
+ */
1205
+ const prompt = (title, opts) => __awaiter(void 0, void 0, void 0, function* () {
1206
+ var _a, _b;
1207
+ // Wait for helper to exist
1208
+ yield waitForHelper(() => {
1209
+ return !!setPromptInfo;
1210
+ });
1211
+ // Fallback if prompt is not available
1212
+ if (!setPromptInfo) {
1213
+ const resultPassesValidation = false;
1214
+ while (!resultPassesValidation) {
1215
+ // eslint-disable-next-line no-alert
1216
+ 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 : '');
1217
+ // Exit loop if user cancels
1218
+ if (result === null) {
1219
+ return null;
1220
+ }
1221
+ // Validate min num chars
1222
+ const minNumCharsValidationError = (((opts === null || opts === void 0 ? void 0 : opts.minNumChars) && result.length < opts.minNumChars)
1223
+ ? `Please enter at least ${opts.minNumChars} characters.`
1224
+ : undefined);
1225
+ // Run custom validation
1226
+ const customValidationError = ((opts === null || opts === void 0 ? void 0 : opts.findValidationError)
1227
+ && opts.findValidationError(result));
1228
+ // Show validation issue
1229
+ if (minNumCharsValidationError || customValidationError) {
1230
+ // Create error message
1231
+ const errorMessage = ([
1232
+ minNumCharsValidationError,
1233
+ customValidationError,
1234
+ ]
1235
+ // Filter out undefined messages
1236
+ .filter((msg) => {
1237
+ return !!msg;
1238
+ })
1239
+ // Join messages with newlines
1240
+ .join('\n'));
1241
+ // Show alert
1242
+ alert('Invalid Input', errorMessage);
1243
+ }
1244
+ else {
1245
+ return result;
1246
+ }
1247
+ }
1248
+ }
1249
+ // Return promise that resolves with result of prompt
1250
+ return new Promise((resolve) => {
1251
+ var _a;
1252
+ // Setup handler
1253
+ onPromptClosed = (result) => {
1254
+ resolve(result);
1255
+ };
1256
+ // Show the prompt
1257
+ setPromptInfo({
1258
+ title,
1259
+ currentInputFieldText: ((_a = opts === null || opts === void 0 ? void 0 : opts.defaultText) !== null && _a !== void 0 ? _a : ''),
1260
+ opts: (opts !== null && opts !== void 0 ? opts : {}),
1261
+ });
1262
+ });
1263
+ });
1264
+ /*----------------------------------------*/
1155
1265
  /* ------------- Fatal Error ------------ */
1156
1266
  /*----------------------------------------*/
1157
1267
  // Stored copies of setters
@@ -1167,14 +1277,14 @@ const fatalErrorHandlers = [];
1167
1277
  * @param [errorTitle] title of the error box
1168
1278
  */
1169
1279
  const showFatalError = (error, errorTitle = 'An Error Occurred') => __awaiter(void 0, void 0, void 0, function* () {
1170
- var _a, _b;
1280
+ var _c, _d;
1171
1281
  // Determine message and code
1172
1282
  const message = (typeof error === 'string'
1173
1283
  ? error.trim()
1174
- : String((_a = error.message) !== null && _a !== void 0 ? _a : 'An unknown error occurred.'));
1284
+ : String((_c = error.message) !== null && _c !== void 0 ? _c : 'An unknown error occurred.'));
1175
1285
  const code = (typeof error === 'string'
1176
1286
  ? ReactKitErrorCode$1.NoCode
1177
- : String((_b = error.code) !== null && _b !== void 0 ? _b : ReactKitErrorCode$1.NoCode));
1287
+ : String((_d = error.code) !== null && _d !== void 0 ? _d : ReactKitErrorCode$1.NoCode));
1178
1288
  // Call all fatal error listeners
1179
1289
  try {
1180
1290
  fatalErrorHandlers.forEach((handler) => {
@@ -1301,6 +1411,9 @@ const AppWrapper = (props) => {
1301
1411
  // Confirm
1302
1412
  const [confirmInfo, setConfirmInfoInner,] = useState(undefined);
1303
1413
  setConfirmInfo = setConfirmInfoInner;
1414
+ // Prompt
1415
+ const [promptInfo, setPromptInfoInner] = useState(undefined);
1416
+ setPromptInfo = setPromptInfoInner;
1304
1417
  // Session expired
1305
1418
  const [sessionHasExpired, setSessionHasExpiredInner,] = useState(false);
1306
1419
  setSessionHasExpired = setSessionHasExpiredInner;
@@ -1331,6 +1444,42 @@ const AppWrapper = (props) => {
1331
1444
  }
1332
1445
  }, onTopOfOtherModals: true, dontAllowBackdropExit: true }, confirmInfo.text));
1333
1446
  }
1447
+ /* ------------- Prompt ------------ */
1448
+ if (promptInfo) {
1449
+ // Run min char validation
1450
+ const minNumCharsValidationError = ((promptInfo.opts.minNumChars
1451
+ && promptInfo.currentInputFieldText.length < promptInfo.opts.minNumChars)
1452
+ ? `Please enter at least ${promptInfo.opts.minNumChars} characters.`
1453
+ : undefined);
1454
+ // Run custom validation
1455
+ const customValidationError = (promptInfo.opts.findValidationError
1456
+ && promptInfo.opts.findValidationError(promptInfo.currentInputFieldText));
1457
+ modal = (React__default.createElement(Modal, { key: `prompt-${promptInfo.title}`, title: promptInfo.title,
1458
+ // Don't show ok button if there is a validation error
1459
+ type: ((customValidationError || minNumCharsValidationError)
1460
+ ? ModalType$1.Cancel
1461
+ : ModalType$1.OkayCancel), okayLabel: promptInfo.opts.confirmButtonText, okayVariant: promptInfo.opts.confirmButtonVariant, cancelLabel: promptInfo.opts.cancelButtonText, cancelVariant: promptInfo.opts.cancelButtonVariant, onClose: (buttonType) => {
1462
+ // Get result
1463
+ const result = (buttonType === ModalButtonType$1.Okay
1464
+ ? promptInfo.currentInputFieldText
1465
+ : null);
1466
+ // Close prompt
1467
+ setPromptInfo(undefined);
1468
+ // Call handler
1469
+ if (onPromptClosed) {
1470
+ onPromptClosed(result);
1471
+ }
1472
+ }, onTopOfOtherModals: true, dontAllowBackdropExit: true },
1473
+ React__default.createElement("div", null,
1474
+ promptInfo.opts.textAboveInputField && (React__default.createElement("div", null, promptInfo.opts.textAboveInputField)),
1475
+ React__default.createElement("input", { type: "text", className: "form-control", "aria-label": promptInfo.opts.ariaLabel, placeholder: promptInfo.opts.placeholder, value: promptInfo.currentInputFieldText, onChange: (e) => {
1476
+ return setPromptInfo(Object.assign(Object.assign({}, promptInfo), { currentInputFieldText: e.target.value }));
1477
+ },
1478
+ // eslint-disable-next-line jsx-a11y/no-autofocus
1479
+ autoFocus: true }),
1480
+ minNumCharsValidationError && (React__default.createElement("div", { className: "text-danger fw-bold mt-2" }, minNumCharsValidationError)),
1481
+ customValidationError && (React__default.createElement("div", { className: "text-danger fw-bold mt-2" }, customValidationError)))));
1482
+ }
1334
1483
  /* ------ Custom Modal Portals ------ */
1335
1484
  // Custom modal portals
1336
1485
  const customModalPortals = [];
@@ -1400,7 +1549,7 @@ const AppWrapper = (props) => {
1400
1549
  ? `
1401
1550
  .tooltip-inner {
1402
1551
  background-color: white;
1403
- color: black;
1552
+ color: black !important;
1404
1553
  border: 0.1rem solid black;
1405
1554
  pointer-events: none;
1406
1555
  }
@@ -1413,7 +1562,7 @@ const AppWrapper = (props) => {
1413
1562
  : `
1414
1563
  .tooltip-inner {
1415
1564
  background-color: black;
1416
- color: white;
1565
+ color: white !important;
1417
1566
  border: 0.1rem solid white;
1418
1567
  pointer-events: none;
1419
1568
  }
@@ -1540,24 +1689,31 @@ const RadioButton = (props) => {
1540
1689
  /* -------------------------------- Setup ------------------------------- */
1541
1690
  /*------------------------------------------------------------------------*/
1542
1691
  /* -------------- Props ------------- */
1543
- const { text, onSelected, ariaLabel, title, selected, id, noMarginOnRight, selectedVariant = (isDarkModeOn()
1692
+ const { text, onSelected, ariaLabel, title, selected, id, className, noMarginOnRight, selectedVariant = (isDarkModeOn()
1544
1693
  ? Variant$1.Light
1545
1694
  : Variant$1.Secondary), unselectedVariant = (isDarkModeOn()
1546
1695
  ? Variant$1.Secondary
1547
- : Variant$1.Light), small, } = props;
1696
+ : Variant$1.Light), small, useComplexFormatting, } = props;
1548
1697
  /*------------------------------------------------------------------------*/
1549
1698
  /* ------------------------------- Render ------------------------------- */
1550
1699
  /*------------------------------------------------------------------------*/
1551
1700
  /*----------------------------------------*/
1552
1701
  /* --------------- Main UI -------------- */
1553
1702
  /*----------------------------------------*/
1554
- return (React__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: () => {
1703
+ return (React__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: () => {
1555
1704
  if (!selected) {
1556
1705
  onSelected();
1557
1706
  }
1558
1707
  } },
1559
- React__default.createElement(FontAwesomeIcon, { icon: selected ? faDotCircle : faCircle$1, className: "me-1" }),
1560
- text));
1708
+ React__default.createElement("div", { className: "d-flex flex-row align-items-center" },
1709
+ React__default.createElement("div", { className: "me-1" },
1710
+ React__default.createElement(FontAwesomeIcon, { icon: selected ? faDotCircle : faCircle$1 })),
1711
+ useComplexFormatting
1712
+ ? (React__default.createElement("pre", { className: "ps-1 text-start text-break m-0", style: {
1713
+ whiteSpace: 'pre-wrap',
1714
+ tabSize: 2,
1715
+ } }, text))
1716
+ : (React__default.createElement("div", { className: "flex-grow-1 text-start text-break" }, text)))));
1561
1717
  };
1562
1718
 
1563
1719
  /**
@@ -1576,7 +1732,7 @@ const CheckboxButton = (props) => {
1576
1732
  ? Variant$1.Light
1577
1733
  : Variant$1.Secondary), uncheckedVariant = (isDarkModeOn()
1578
1734
  ? Variant$1.Secondary
1579
- : Variant$1.Light), small, dashed, } = props;
1735
+ : Variant$1.Light), small, dashed, useComplexFormatting, } = props;
1580
1736
  /*------------------------------------------------------------------------*/
1581
1737
  /* ------------------------------- Render ------------------------------- */
1582
1738
  /*------------------------------------------------------------------------*/
@@ -1597,10 +1753,15 @@ const CheckboxButton = (props) => {
1597
1753
  return (React__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: () => {
1598
1754
  onChanged(!checked);
1599
1755
  } },
1600
- React__default.createElement("div", { className: "d-flex" },
1601
- React__default.createElement("div", { className: "align-items-center" },
1602
- React__default.createElement(FontAwesomeIcon, { icon: icon, className: "me-1" })),
1603
- React__default.createElement("div", { className: "flex-grow-1" }, text))));
1756
+ React__default.createElement("div", { className: "d-flex align-items-center" },
1757
+ React__default.createElement("div", { className: "me-1" },
1758
+ React__default.createElement(FontAwesomeIcon, { icon: icon })),
1759
+ useComplexFormatting
1760
+ ? (React__default.createElement("pre", { className: "ps-1 text-start text-break m-0", style: {
1761
+ whiteSpace: 'pre-wrap',
1762
+ tabSize: 2,
1763
+ } }, text))
1764
+ : (React__default.createElement("div", { className: "flex-grow-1 text-start text-break" }, text)))));
1604
1765
  };
1605
1766
 
1606
1767
  /**
@@ -1781,7 +1942,13 @@ const SimpleDateChooser = (props) => {
1781
1942
  }
1782
1943
  const monthName = getMonthName(month).full;
1783
1944
  // Year is start year +1 for each 12 months
1784
- const year = (startYear + (Math.floor(unmoddedMonth / 12)));
1945
+ let yearsToAdd = 0;
1946
+ let monthsOfYearsToAdd = unmoddedMonth;
1947
+ while (monthsOfYearsToAdd > 12) {
1948
+ monthsOfYearsToAdd -= 12;
1949
+ yearsToAdd += 1;
1950
+ }
1951
+ const year = startYear + yearsToAdd;
1785
1952
  // Figure out which days are allowed
1786
1953
  const days = [];
1787
1954
  const numDaysInMonth = (new Date(year, month, 0)).getDate();
@@ -1832,7 +1999,7 @@ const SimpleDateChooser = (props) => {
1832
1999
  });
1833
2000
  }
1834
2001
  });
1835
- return (React__default.createElement("div", { className: "SimpleDateChooser d-inline-block", "aria-label": `date chooser with selected date: ${month} ${day}, ${year}` },
2002
+ return (React__default.createElement("div", { className: "SimpleDateChooser d-inline-block", "aria-label": `date chooser with selected date: ${month}/${day}/${year}` },
1836
2003
  React__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) => {
1837
2004
  const choice = choices[e.target.selectedIndex];
1838
2005
  // Change day, month, and year
@@ -2225,27 +2392,27 @@ const PopPendingMark = (props) => {
2225
2392
  */
2226
2393
  /* ------------- Actions ------------ */
2227
2394
  // Types of actions
2228
- var ActionType$8;
2395
+ var ActionType$9;
2229
2396
  (function (ActionType) {
2230
2397
  // Indicate that the text was recently copied
2231
2398
  ActionType["IndicateRecentlyCopied"] = "indicate-recently-copied";
2232
2399
  // Clear the status
2233
2400
  ActionType["ClearRecentlyCopiedStatus"] = "clear-recently-copied-status";
2234
- })(ActionType$8 || (ActionType$8 = {}));
2401
+ })(ActionType$9 || (ActionType$9 = {}));
2235
2402
  /**
2236
2403
  * Reducer that executes actions
2237
2404
  * @author Gabe Abrams
2238
2405
  * @param state current state
2239
2406
  * @param action action to execute
2240
2407
  */
2241
- const reducer$9 = (state, action) => {
2408
+ const reducer$a = (state, action) => {
2242
2409
  switch (action.type) {
2243
- case ActionType$8.IndicateRecentlyCopied: {
2410
+ case ActionType$9.IndicateRecentlyCopied: {
2244
2411
  return {
2245
2412
  recentlyCopied: true,
2246
2413
  };
2247
2414
  }
2248
- case ActionType$8.ClearRecentlyCopiedStatus: {
2415
+ case ActionType$9.ClearRecentlyCopiedStatus: {
2249
2416
  return {
2250
2417
  recentlyCopied: false,
2251
2418
  };
@@ -2271,7 +2438,7 @@ const CopiableBox = (props) => {
2271
2438
  recentlyCopied: false,
2272
2439
  };
2273
2440
  // Initialize state
2274
- const [state, dispatch] = useReducer(reducer$9, initialState);
2441
+ const [state, dispatch] = useReducer(reducer$a, initialState);
2275
2442
  // Destructure common state
2276
2443
  const { recentlyCopied, } = state;
2277
2444
  /*------------------------------------------------------------------------*/
@@ -2291,13 +2458,13 @@ const CopiableBox = (props) => {
2291
2458
  }
2292
2459
  // Show copied notice
2293
2460
  dispatch({
2294
- type: ActionType$8.IndicateRecentlyCopied,
2461
+ type: ActionType$9.IndicateRecentlyCopied,
2295
2462
  });
2296
2463
  // Wait a moment
2297
2464
  yield waitMs(4000);
2298
2465
  // Hide copied notice
2299
2466
  dispatch({
2300
- type: ActionType$8.ClearRecentlyCopiedStatus,
2467
+ type: ActionType$9.ClearRecentlyCopiedStatus,
2301
2468
  });
2302
2469
  });
2303
2470
  /*------------------------------------------------------------------------*/
@@ -2353,20 +2520,20 @@ const CopiableBox = (props) => {
2353
2520
  */
2354
2521
  /* ------------- Actions ------------ */
2355
2522
  // Types of actions
2356
- var ActionType$7;
2523
+ var ActionType$8;
2357
2524
  (function (ActionType) {
2358
2525
  // Toggle whether a child are being shown
2359
2526
  ActionType["ToggleChild"] = "toggle-child";
2360
- })(ActionType$7 || (ActionType$7 = {}));
2527
+ })(ActionType$8 || (ActionType$8 = {}));
2361
2528
  /**
2362
2529
  * Reducer that executes actions
2363
2530
  * @author Gabe Abrams
2364
2531
  * @param state current state
2365
2532
  * @param action action to execute
2366
2533
  */
2367
- const reducer$8 = (state, action) => {
2534
+ const reducer$9 = (state, action) => {
2368
2535
  switch (action.type) {
2369
- case ActionType$7.ToggleChild: {
2536
+ case ActionType$8.ToggleChild: {
2370
2537
  return Object.assign(Object.assign({}, state), { childExpanded: Object.assign(Object.assign({}, state.childExpanded), { [String(action.id)]: !state.childExpanded[String(action.id)] }) });
2371
2538
  }
2372
2539
  default: {
@@ -2395,7 +2562,7 @@ const NestableItemList = (props) => {
2395
2562
  childExpanded: initChildExpanded,
2396
2563
  };
2397
2564
  // Initialize state
2398
- const [state, dispatch] = useReducer(reducer$8, initialState);
2565
+ const [state, dispatch] = useReducer(reducer$9, initialState);
2399
2566
  // Destructure common state
2400
2567
  const { childExpanded, } = state;
2401
2568
  /*------------------------------------------------------------------------*/
@@ -2485,14 +2652,14 @@ const NestableItemList = (props) => {
2485
2652
  backgroundColor: 'transparent',
2486
2653
  }, type: "button", onClick: () => {
2487
2654
  dispatch({
2488
- type: ActionType$7.ToggleChild,
2655
+ type: ActionType$8.ToggleChild,
2489
2656
  id: item.id,
2490
2657
  });
2491
2658
  }, "aria-label": `${childExpanded[item.id] ? 'Hide' : 'Show'} items in ${item.name}` },
2492
2659
  React__default.createElement(FontAwesomeIcon, { icon: childExpanded[item.id] ? faChevronDown : faChevronRight })))),
2493
2660
  React__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) => {
2494
2661
  onChanged(changeChecked(item.id, checked, items));
2495
- }, ariaLabel: `Select ${item.name}`, checkedVariant: Variant$1.Light }),
2662
+ }, ariaLabel: `Select ${item.name}`, checkedVariant: Variant$1.Light, uncheckedVariant: Variant$1.Light }),
2496
2663
  (item.isGroup && childExpanded[item.id]) && (React__default.createElement("div", { className: "NestableItemList-children-container", style: {
2497
2664
  paddingLeft: '2.2rem',
2498
2665
  } },
@@ -2769,7 +2936,7 @@ var SortType;
2769
2936
  })(SortType || (SortType = {}));
2770
2937
  /* ------------- Actions ------------ */
2771
2938
  // Types of actions
2772
- var ActionType$6;
2939
+ var ActionType$7;
2773
2940
  (function (ActionType) {
2774
2941
  // Toggle sort column param
2775
2942
  ActionType["ToggleSortColumn"] = "toggle-sort-column";
@@ -2781,16 +2948,16 @@ var ActionType$6;
2781
2948
  ActionType["ShowAllColumns"] = "show-all-columns";
2782
2949
  // Hide all columns
2783
2950
  ActionType["HideAllColumns"] = "hide-all-columns";
2784
- })(ActionType$6 || (ActionType$6 = {}));
2951
+ })(ActionType$7 || (ActionType$7 = {}));
2785
2952
  /**
2786
2953
  * Reducer that executes actions
2787
2954
  * @author Gabe Abrams
2788
2955
  * @param state current state
2789
2956
  * @param action action to execute
2790
2957
  */
2791
- const reducer$7 = (state, action) => {
2958
+ const reducer$8 = (state, action) => {
2792
2959
  switch (action.type) {
2793
- case ActionType$6.ToggleSortColumn: {
2960
+ case ActionType$7.ToggleSortColumn: {
2794
2961
  if (action.param !== state.sortColumnParam) {
2795
2962
  // Different column param
2796
2963
  return Object.assign(Object.assign({}, state), { sortColumnParam: action.param, sortType: SortType.Ascending });
@@ -2802,22 +2969,22 @@ const reducer$7 = (state, action) => {
2802
2969
  // Stop sorting by column
2803
2970
  return Object.assign(Object.assign({}, state), { sortColumnParam: undefined, sortType: SortType.Ascending });
2804
2971
  }
2805
- case ActionType$6.UpdateColumnVisibility: {
2972
+ case ActionType$7.UpdateColumnVisibility: {
2806
2973
  const { columnVisibilityMap } = state;
2807
2974
  columnVisibilityMap[action.param] = action.visible;
2808
2975
  return Object.assign(Object.assign({}, state), { columnVisibilityMap });
2809
2976
  }
2810
- case ActionType$6.ToggleColVisCusModalVisibility: {
2977
+ case ActionType$7.ToggleColVisCusModalVisibility: {
2811
2978
  return Object.assign(Object.assign({}, state), { columnVisibilityCustomizationModalVisible: !state.columnVisibilityCustomizationModalVisible });
2812
2979
  }
2813
- case ActionType$6.ShowAllColumns: {
2980
+ case ActionType$7.ShowAllColumns: {
2814
2981
  const { columnVisibilityMap } = state;
2815
2982
  Object.keys(columnVisibilityMap).forEach((param) => {
2816
2983
  columnVisibilityMap[param] = true;
2817
2984
  });
2818
2985
  return Object.assign(Object.assign({}, state), { columnVisibilityMap });
2819
2986
  }
2820
- case ActionType$6.HideAllColumns: {
2987
+ case ActionType$7.HideAllColumns: {
2821
2988
  const { columnVisibilityMap } = state;
2822
2989
  Object.keys(columnVisibilityMap).forEach((param) => {
2823
2990
  columnVisibilityMap[param] = false;
@@ -2864,7 +3031,7 @@ const IntelliTable = (props) => {
2864
3031
  columnVisibilityCustomizationModalVisible: false,
2865
3032
  };
2866
3033
  // Initialize state
2867
- const [state, dispatch] = useReducer(reducer$7, initialState);
3034
+ const [state, dispatch] = useReducer(reducer$8, initialState);
2868
3035
  // Destructure common state
2869
3036
  const { sortColumnParam, sortType, columnVisibilityMap, columnVisibilityCustomizationModalVisible, } = state;
2870
3037
  /*------------------------------------------------------------------------*/
@@ -2887,27 +3054,27 @@ const IntelliTable = (props) => {
2887
3054
  return alert('Choose at least one column', 'To continue, you have to choose at least one column to show');
2888
3055
  }
2889
3056
  dispatch({
2890
- type: ActionType$6.ToggleColVisCusModalVisibility,
3057
+ type: ActionType$7.ToggleColVisCusModalVisibility,
2891
3058
  });
2892
3059
  }, okayVariant: Variant$1.Light },
2893
3060
  columns.map((column) => {
2894
3061
  return (React__default.createElement(CheckboxButton, { key: column.param, id: `IntelliTable-${id}-toggle-visibility-${column.param}`, className: "mb-2", text: column.title, onChanged: (checked) => {
2895
3062
  dispatch({
2896
- type: ActionType$6.UpdateColumnVisibility,
3063
+ type: ActionType$7.UpdateColumnVisibility,
2897
3064
  param: column.param,
2898
3065
  visible: checked,
2899
3066
  });
2900
- }, checked: columnVisibilityMap[column.param], ariaLabel: `show "${column.title}" column in the ${title} table`, checkedVariant: Variant$1.Light, uncheckedVariant: Variant$1.Secondary }));
3067
+ }, checked: columnVisibilityMap[column.param], ariaLabel: `show "${column.title}" column in the ${title} table`, checkedVariant: Variant$1.Light, uncheckedVariant: Variant$1.Light }));
2901
3068
  }),
2902
3069
  React__default.createElement("div", { className: "mt-3" }, "Or you can:"),
2903
3070
  React__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: () => {
2904
3071
  dispatch({
2905
- type: ActionType$6.ShowAllColumns,
3072
+ type: ActionType$7.ShowAllColumns,
2906
3073
  });
2907
3074
  } }, "Select All"),
2908
3075
  React__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: () => {
2909
3076
  dispatch({
2910
- type: ActionType$6.HideAllColumns,
3077
+ type: ActionType$7.HideAllColumns,
2911
3078
  });
2912
3079
  } }, "Deselect All")));
2913
3080
  }
@@ -2958,7 +3125,7 @@ const IntelliTable = (props) => {
2958
3125
  React__default.createElement("div", null,
2959
3126
  React__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: () => {
2960
3127
  dispatch({
2961
- type: ActionType$6.ToggleSortColumn,
3128
+ type: ActionType$7.ToggleSortColumn,
2962
3129
  param: column.param,
2963
3130
  });
2964
3131
  } },
@@ -3147,7 +3314,7 @@ const IntelliTable = (props) => {
3147
3314
  React__default.createElement(CSVDownloadButton, { "aria-label": `download data as csv for ${title}`, id: `IntelliTable-${id}-download-as-csv`, filename: filename, csv: csv }),
3148
3315
  React__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: () => {
3149
3316
  dispatch({
3150
- type: ActionType$6.ToggleColVisCusModalVisibility,
3317
+ type: ActionType$7.ToggleColVisCusModalVisibility,
3151
3318
  });
3152
3319
  } },
3153
3320
  "Show/Hide Cols",
@@ -3513,7 +3680,7 @@ const genHumanReadableName = (machineReadableName) => {
3513
3680
  };
3514
3681
  /* ------------- Actions ------------ */
3515
3682
  // Types of actions
3516
- var ActionType$5;
3683
+ var ActionType$6;
3517
3684
  (function (ActionType) {
3518
3685
  // Show the loading bar
3519
3686
  ActionType["StartLoading"] = "start-loading";
@@ -3535,45 +3702,45 @@ var ActionType$5;
3535
3702
  ActionType["UpdateActionErrorFilterState"] = "update-action-error-filter-state";
3536
3703
  // Update the advanced filter state
3537
3704
  ActionType["UpdateAdvancedFilterState"] = "update-advanced-filter-state";
3538
- })(ActionType$5 || (ActionType$5 = {}));
3705
+ })(ActionType$6 || (ActionType$6 = {}));
3539
3706
  /**
3540
3707
  * Reducer that executes actions
3541
3708
  * @author Gabe Abrams
3542
3709
  * @param state current state
3543
3710
  * @param action action to execute
3544
3711
  */
3545
- const reducer$6 = (state, action) => {
3712
+ const reducer$7 = (state, action) => {
3546
3713
  switch (action.type) {
3547
- case ActionType$5.StartLoading: {
3714
+ case ActionType$6.StartLoading: {
3548
3715
  return Object.assign(Object.assign({}, state), { loading: true });
3549
3716
  }
3550
- case ActionType$5.FinishLoading: {
3717
+ case ActionType$6.FinishLoading: {
3551
3718
  return Object.assign(Object.assign({}, state), { loading: false, logMap: action.logMap });
3552
3719
  }
3553
- case ActionType$5.ToggleFilterDrawer: {
3720
+ case ActionType$6.ToggleFilterDrawer: {
3554
3721
  return Object.assign(Object.assign({}, state), { expandedFilterDrawer: (state.expandedFilterDrawer === action.filterDrawer
3555
3722
  ? undefined // hide
3556
3723
  : action.filterDrawer) });
3557
3724
  }
3558
- case ActionType$5.HideFilterDrawer: {
3725
+ case ActionType$6.HideFilterDrawer: {
3559
3726
  return Object.assign(Object.assign({}, state), { expandedFilterDrawer: undefined });
3560
3727
  }
3561
- case ActionType$5.ResetFilters: {
3728
+ case ActionType$6.ResetFilters: {
3562
3729
  return Object.assign(Object.assign({}, state), { dateFilterState: action.initDateFilterState, contextFilterState: action.initContextFilterState, tagFilterState: action.initTagFilterState, actionErrorFilterState: action.initActionErrorFilterState, advancedFilterState: action.initAdvancedFilterState });
3563
3730
  }
3564
- case ActionType$5.UpdateDateFilterState: {
3731
+ case ActionType$6.UpdateDateFilterState: {
3565
3732
  return Object.assign(Object.assign({}, state), { dateFilterState: action.dateFilterState });
3566
3733
  }
3567
- case ActionType$5.UpdateContextFilterState: {
3734
+ case ActionType$6.UpdateContextFilterState: {
3568
3735
  return Object.assign(Object.assign({}, state), { contextFilterState: action.contextFilterState });
3569
3736
  }
3570
- case ActionType$5.UpdateTagFilterState: {
3737
+ case ActionType$6.UpdateTagFilterState: {
3571
3738
  return Object.assign(Object.assign({}, state), { tagFilterState: action.tagFilterState });
3572
3739
  }
3573
- case ActionType$5.UpdateActionErrorFilterState: {
3740
+ case ActionType$6.UpdateActionErrorFilterState: {
3574
3741
  return Object.assign(Object.assign({}, state), { actionErrorFilterState: action.actionErrorFilterState });
3575
3742
  }
3576
- case ActionType$5.UpdateAdvancedFilterState: {
3743
+ case ActionType$6.UpdateAdvancedFilterState: {
3577
3744
  return Object.assign(Object.assign({}, state), { advancedFilterState: action.advancedFilterState });
3578
3745
  }
3579
3746
  default: {
@@ -3702,7 +3869,7 @@ const LogReviewer = (props) => {
3702
3869
  advancedFilterState: initAdvancedFilterState,
3703
3870
  };
3704
3871
  // Initialize state
3705
- const [state, dispatch] = useReducer(reducer$6, initialState);
3872
+ const [state, dispatch] = useReducer(reducer$7, initialState);
3706
3873
  // Destructure common state
3707
3874
  const { loading, logMap, expandedFilterDrawer, dateFilterState, contextFilterState, tagFilterState, actionErrorFilterState, advancedFilterState, } = state;
3708
3875
  /*------------------------------------------------------------------------*/
@@ -3752,7 +3919,7 @@ const LogReviewer = (props) => {
3752
3919
  const handleDateRangeUpdated = (newDateFilterState) => __awaiter(void 0, void 0, void 0, function* () {
3753
3920
  // Update state
3754
3921
  dispatch({
3755
- type: ActionType$5.UpdateDateFilterState,
3922
+ type: ActionType$6.UpdateDateFilterState,
3756
3923
  dateFilterState: newDateFilterState,
3757
3924
  });
3758
3925
  // Check which year/month combos we need to load
@@ -3763,7 +3930,7 @@ const LogReviewer = (props) => {
3763
3930
  }
3764
3931
  // Start loading
3765
3932
  dispatch({
3766
- type: ActionType$5.StartLoading,
3933
+ type: ActionType$6.StartLoading,
3767
3934
  });
3768
3935
  // Load required months
3769
3936
  try {
@@ -3771,10 +3938,21 @@ const LogReviewer = (props) => {
3771
3938
  // Destructure
3772
3939
  const { year, month } = toLoad[i];
3773
3940
  // Load
3774
- const logs = yield visitServerEndpoint({
3775
- path: `${LOG_REVIEW_ROUTE_PATH_PREFIX}/years/${year}/months/${month}`,
3776
- method: 'GET',
3777
- });
3941
+ let logs = [];
3942
+ let pageNumber = 1;
3943
+ let hasAnotherPage = true;
3944
+ while (hasAnotherPage) {
3945
+ const response = yield visitServerEndpoint({
3946
+ path: `${LOG_REVIEW_ROUTE_PATH_PREFIX}/years/${year}/months/${month}`,
3947
+ method: 'GET',
3948
+ params: {
3949
+ pageNumber,
3950
+ },
3951
+ });
3952
+ logs = logs.concat(response.items);
3953
+ hasAnotherPage = response.hasAnotherPage;
3954
+ pageNumber += 1;
3955
+ }
3778
3956
  // Add to map
3779
3957
  if (!logMap[year]) {
3780
3958
  logMap[year] = {};
@@ -3787,7 +3965,7 @@ const LogReviewer = (props) => {
3787
3965
  }
3788
3966
  // Finish loading
3789
3967
  dispatch({
3790
- type: ActionType$5.FinishLoading,
3968
+ type: ActionType$6.FinishLoading,
3791
3969
  logMap,
3792
3970
  });
3793
3971
  });
@@ -3826,7 +4004,7 @@ const LogReviewer = (props) => {
3826
4004
  React__default.createElement("div", { className: "LogReviewer-filter-toggle-buttons alert alert-secondary p-2 m-0" },
3827
4005
  React__default.createElement("button", { type: "button", id: "LogReviewer-toggle-date-filter-drawer", className: `btn btn-${FilterDrawer.Date === expandedFilterDrawer ? 'warning' : 'light'} me-2`, "aria-label": "toggle date filter drawer", onClick: () => {
3828
4006
  dispatch({
3829
- type: ActionType$5.ToggleFilterDrawer,
4007
+ type: ActionType$6.ToggleFilterDrawer,
3830
4008
  filterDrawer: FilterDrawer.Date,
3831
4009
  });
3832
4010
  } },
@@ -3834,7 +4012,7 @@ const LogReviewer = (props) => {
3834
4012
  "Date"),
3835
4013
  React__default.createElement("button", { type: "button", id: "LogReviewer-toggle-context-filter-drawer", className: `btn btn-${FilterDrawer.Context === expandedFilterDrawer ? 'warning' : 'light'} me-2`, "aria-label": "toggle context filter drawer", onClick: () => {
3836
4014
  dispatch({
3837
- type: ActionType$5.ToggleFilterDrawer,
4015
+ type: ActionType$6.ToggleFilterDrawer,
3838
4016
  filterDrawer: FilterDrawer.Context,
3839
4017
  });
3840
4018
  } },
@@ -3842,7 +4020,7 @@ const LogReviewer = (props) => {
3842
4020
  "Context"),
3843
4021
  (LogMetadata.Tag && Object.keys(LogMetadata.Tag).length > 0) && (React__default.createElement("button", { type: "button", id: "LogReviewer-toggle-tag-filter-drawer", className: `btn btn-${FilterDrawer.Tag === expandedFilterDrawer ? 'warning' : 'light'} me-2`, "aria-label": "toggle tag filter drawer", onClick: () => {
3844
4022
  dispatch({
3845
- type: ActionType$5.ToggleFilterDrawer,
4023
+ type: ActionType$6.ToggleFilterDrawer,
3846
4024
  filterDrawer: FilterDrawer.Tag,
3847
4025
  });
3848
4026
  } },
@@ -3850,7 +4028,7 @@ const LogReviewer = (props) => {
3850
4028
  "Tag")),
3851
4029
  React__default.createElement("button", { type: "button", id: "LogReviewer-toggle-action-filter-drawer", className: `btn btn-${FilterDrawer.Action === expandedFilterDrawer ? 'warning' : 'light'} me-2`, "aria-label": "toggle action and error filter drawer", onClick: () => {
3852
4030
  dispatch({
3853
- type: ActionType$5.ToggleFilterDrawer,
4031
+ type: ActionType$6.ToggleFilterDrawer,
3854
4032
  filterDrawer: FilterDrawer.Action,
3855
4033
  });
3856
4034
  } },
@@ -3858,7 +4036,7 @@ const LogReviewer = (props) => {
3858
4036
  "Action"),
3859
4037
  React__default.createElement("button", { type: "button", id: "LogReviewer-toggle-advanced-filter-drawer", className: `btn btn-${FilterDrawer.Advanced === expandedFilterDrawer ? 'warning' : 'light'} me-2`, "aria-label": "toggle advanced filter drawer", onClick: () => {
3860
4038
  dispatch({
3861
- type: ActionType$5.ToggleFilterDrawer,
4039
+ type: ActionType$6.ToggleFilterDrawer,
3862
4040
  filterDrawer: FilterDrawer.Advanced,
3863
4041
  });
3864
4042
  } },
@@ -3866,7 +4044,7 @@ const LogReviewer = (props) => {
3866
4044
  "Advanced"),
3867
4045
  React__default.createElement("button", { type: "button", id: "LogReviewer-reset-filters-button", className: "btn btn-light", "aria-label": "reset filters", onClick: () => {
3868
4046
  dispatch({
3869
- type: ActionType$5.ResetFilters,
4047
+ type: ActionType$6.ResetFilters,
3870
4048
  initActionErrorFilterState,
3871
4049
  initAdvancedFilterState,
3872
4050
  initContextFilterState,
@@ -3988,7 +4166,7 @@ const LogReviewer = (props) => {
3988
4166
  }
3989
4167
  });
3990
4168
  dispatch({
3991
- type: ActionType$5.UpdateContextFilterState,
4169
+ type: ActionType$6.UpdateContextFilterState,
3992
4170
  contextFilterState,
3993
4171
  });
3994
4172
  } }));
@@ -4003,7 +4181,7 @@ const LogReviewer = (props) => {
4003
4181
  return (React__default.createElement(CheckboxButton, { key: tag, id: `LogReviewer-tag-${tag}-checkbox`, text: description, ariaLabel: `require that logs be tagged with "${description}" or any other selected tag`, checked: tagFilterState[tag], onChanged: (checked) => {
4004
4182
  tagFilterState[tag] = checked;
4005
4183
  dispatch({
4006
- type: ActionType$5.UpdateTagFilterState,
4184
+ type: ActionType$6.UpdateTagFilterState,
4007
4185
  tagFilterState,
4008
4186
  });
4009
4187
  } }));
@@ -4016,21 +4194,21 @@ const LogReviewer = (props) => {
4016
4194
  React__default.createElement(RadioButton, { id: "LogReviewer-type-all", text: "All Logs", onSelected: () => {
4017
4195
  actionErrorFilterState.type = undefined;
4018
4196
  dispatch({
4019
- type: ActionType$5.UpdateActionErrorFilterState,
4197
+ type: ActionType$6.UpdateActionErrorFilterState,
4020
4198
  actionErrorFilterState,
4021
4199
  });
4022
4200
  }, ariaLabel: "show logs of all types", selected: actionErrorFilterState.type === undefined }),
4023
4201
  React__default.createElement(RadioButton, { id: "LogReviewer-type-action-only", text: "Action Logs Only", onSelected: () => {
4024
4202
  actionErrorFilterState.type = LogType$1.Action;
4025
4203
  dispatch({
4026
- type: ActionType$5.UpdateActionErrorFilterState,
4204
+ type: ActionType$6.UpdateActionErrorFilterState,
4027
4205
  actionErrorFilterState,
4028
4206
  });
4029
4207
  }, ariaLabel: "only show action logs", selected: actionErrorFilterState.type === LogType$1.Action }),
4030
4208
  React__default.createElement(RadioButton, { id: "LogReviewer-type-error-only", text: "Action Error Only", onSelected: () => {
4031
4209
  actionErrorFilterState.type = LogType$1.Error;
4032
4210
  dispatch({
4033
- type: ActionType$5.UpdateActionErrorFilterState,
4211
+ type: ActionType$6.UpdateActionErrorFilterState,
4034
4212
  actionErrorFilterState,
4035
4213
  });
4036
4214
  }, ariaLabel: "only show error logs", selected: actionErrorFilterState.type === LogType$1.Error, noMarginOnRight: true })),
@@ -4042,7 +4220,7 @@ const LogReviewer = (props) => {
4042
4220
  return (React__default.createElement(CheckboxButton, { key: action, id: `LogReviewer-action-${action}-checkbox`, text: description, ariaLabel: `include logs with action type "${description}" in results`, noMarginOnRight: true, checked: actionErrorFilterState.action[action], onChanged: (checked) => {
4043
4221
  actionErrorFilterState.action[action] = checked;
4044
4222
  dispatch({
4045
- type: ActionType$5.UpdateActionErrorFilterState,
4223
+ type: ActionType$6.UpdateActionErrorFilterState,
4046
4224
  actionErrorFilterState,
4047
4225
  });
4048
4226
  } }));
@@ -4053,7 +4231,7 @@ const LogReviewer = (props) => {
4053
4231
  return (React__default.createElement(CheckboxButton, { key: target, id: `LogReviewer-target-${target}-checkbox`, text: description, ariaLabel: `include logs with target "${description}" in results`, checked: actionErrorFilterState.target[target], noMarginOnRight: true, onChanged: (checked) => {
4054
4232
  actionErrorFilterState.target[target] = checked;
4055
4233
  dispatch({
4056
- type: ActionType$5.UpdateActionErrorFilterState,
4234
+ type: ActionType$6.UpdateActionErrorFilterState,
4057
4235
  actionErrorFilterState,
4058
4236
  });
4059
4237
  } }));
@@ -4065,7 +4243,7 @@ const LogReviewer = (props) => {
4065
4243
  React__default.createElement("input", { type: "text", className: "form-control", "aria-label": "query for error message", value: actionErrorFilterState.errorMessage, placeholder: "e.g. undefined is not a function", onChange: (e) => {
4066
4244
  actionErrorFilterState.errorMessage = e.target.value;
4067
4245
  dispatch({
4068
- type: ActionType$5.UpdateActionErrorFilterState,
4246
+ type: ActionType$6.UpdateActionErrorFilterState,
4069
4247
  actionErrorFilterState,
4070
4248
  });
4071
4249
  } })),
@@ -4076,7 +4254,7 @@ const LogReviewer = (props) => {
4076
4254
  .trim()
4077
4255
  .toUpperCase());
4078
4256
  dispatch({
4079
- type: ActionType$5.UpdateActionErrorFilterState,
4257
+ type: ActionType$6.UpdateActionErrorFilterState,
4080
4258
  actionErrorFilterState,
4081
4259
  });
4082
4260
  } }))))));
@@ -4090,7 +4268,7 @@ const LogReviewer = (props) => {
4090
4268
  React__default.createElement("input", { type: "text", className: "form-control", "aria-label": "query for user first name", value: advancedFilterState.userFirstName, placeholder: "e.g. Divardo", onChange: (e) => {
4091
4269
  advancedFilterState.userFirstName = e.target.value;
4092
4270
  dispatch({
4093
- type: ActionType$5.UpdateAdvancedFilterState,
4271
+ type: ActionType$6.UpdateAdvancedFilterState,
4094
4272
  advancedFilterState,
4095
4273
  });
4096
4274
  } })),
@@ -4099,7 +4277,7 @@ const LogReviewer = (props) => {
4099
4277
  React__default.createElement("input", { type: "text", className: "form-control", "aria-label": "query for user last name", value: advancedFilterState.userLastName, placeholder: "e.g. Calicci", onChange: (e) => {
4100
4278
  advancedFilterState.userLastName = e.target.value;
4101
4279
  dispatch({
4102
- type: ActionType$5.UpdateAdvancedFilterState,
4280
+ type: ActionType$6.UpdateAdvancedFilterState,
4103
4281
  advancedFilterState,
4104
4282
  });
4105
4283
  } })),
@@ -4109,7 +4287,7 @@ const LogReviewer = (props) => {
4109
4287
  advancedFilterState.userEmail = ((e.target.value)
4110
4288
  .trim());
4111
4289
  dispatch({
4112
- type: ActionType$5.UpdateAdvancedFilterState,
4290
+ type: ActionType$6.UpdateAdvancedFilterState,
4113
4291
  advancedFilterState,
4114
4292
  });
4115
4293
  } })),
@@ -4123,7 +4301,7 @@ const LogReviewer = (props) => {
4123
4301
  .trim());
4124
4302
  }
4125
4303
  dispatch({
4126
- type: ActionType$5.UpdateAdvancedFilterState,
4304
+ type: ActionType$6.UpdateAdvancedFilterState,
4127
4305
  advancedFilterState,
4128
4306
  });
4129
4307
  } })),
@@ -4131,21 +4309,21 @@ const LogReviewer = (props) => {
4131
4309
  React__default.createElement(CheckboxButton, { text: "Students", onChanged: (checked) => {
4132
4310
  advancedFilterState.includeLearners = checked;
4133
4311
  dispatch({
4134
- type: ActionType$5.UpdateAdvancedFilterState,
4312
+ type: ActionType$6.UpdateAdvancedFilterState,
4135
4313
  advancedFilterState,
4136
4314
  });
4137
4315
  }, checked: advancedFilterState.includeLearners, ariaLabel: "show logs from students" }),
4138
4316
  React__default.createElement(CheckboxButton, { text: "Teaching Team Members", onChanged: (checked) => {
4139
4317
  advancedFilterState.includeTTMs = checked;
4140
4318
  dispatch({
4141
- type: ActionType$5.UpdateAdvancedFilterState,
4319
+ type: ActionType$6.UpdateAdvancedFilterState,
4142
4320
  advancedFilterState,
4143
4321
  });
4144
4322
  }, checked: advancedFilterState.includeTTMs, ariaLabel: "show logs from teaching team members" }),
4145
4323
  React__default.createElement(CheckboxButton, { text: "Admins", onChanged: (checked) => {
4146
4324
  advancedFilterState.includeAdmins = checked;
4147
4325
  dispatch({
4148
- type: ActionType$5.UpdateAdvancedFilterState,
4326
+ type: ActionType$6.UpdateAdvancedFilterState,
4149
4327
  advancedFilterState,
4150
4328
  });
4151
4329
  }, checked: advancedFilterState.includeAdmins, ariaLabel: "show logs from admins" }))),
@@ -4155,7 +4333,7 @@ const LogReviewer = (props) => {
4155
4333
  React__default.createElement("input", { type: "text", className: "form-control", "aria-label": "query for course name", value: advancedFilterState.courseName, placeholder: "e.g. GLC 200", onChange: (e) => {
4156
4334
  advancedFilterState.courseName = e.target.value;
4157
4335
  dispatch({
4158
- type: ActionType$5.UpdateAdvancedFilterState,
4336
+ type: ActionType$6.UpdateAdvancedFilterState,
4159
4337
  advancedFilterState,
4160
4338
  });
4161
4339
  } })),
@@ -4169,7 +4347,7 @@ const LogReviewer = (props) => {
4169
4347
  .trim());
4170
4348
  }
4171
4349
  dispatch({
4172
- type: ActionType$5.UpdateAdvancedFilterState,
4350
+ type: ActionType$6.UpdateAdvancedFilterState,
4173
4351
  advancedFilterState,
4174
4352
  });
4175
4353
  } }))),
@@ -4178,21 +4356,21 @@ const LogReviewer = (props) => {
4178
4356
  React__default.createElement(RadioButton, { text: "All Devices", ariaLabel: "show logs from all devices", selected: advancedFilterState.isMobile === undefined, onSelected: () => {
4179
4357
  advancedFilterState.isMobile = undefined;
4180
4358
  dispatch({
4181
- type: ActionType$5.UpdateAdvancedFilterState,
4359
+ type: ActionType$6.UpdateAdvancedFilterState,
4182
4360
  advancedFilterState,
4183
4361
  });
4184
4362
  } }),
4185
4363
  React__default.createElement(RadioButton, { text: "Mobile Only", ariaLabel: "show logs from mobile devices", selected: advancedFilterState.isMobile === true, onSelected: () => {
4186
4364
  advancedFilterState.isMobile = true;
4187
4365
  dispatch({
4188
- type: ActionType$5.UpdateAdvancedFilterState,
4366
+ type: ActionType$6.UpdateAdvancedFilterState,
4189
4367
  advancedFilterState,
4190
4368
  });
4191
4369
  } }),
4192
4370
  React__default.createElement(RadioButton, { text: "Desktop Only", ariaLabel: "show logs from desktop devices", selected: advancedFilterState.isMobile === false, onSelected: () => {
4193
4371
  advancedFilterState.isMobile = false;
4194
4372
  dispatch({
4195
- type: ActionType$5.UpdateAdvancedFilterState,
4373
+ type: ActionType$6.UpdateAdvancedFilterState,
4196
4374
  advancedFilterState,
4197
4375
  });
4198
4376
  }, noMarginOnRight: true }))),
@@ -4201,21 +4379,21 @@ const LogReviewer = (props) => {
4201
4379
  React__default.createElement(RadioButton, { text: "Both", ariaLabel: "show logs from all sources", selected: advancedFilterState.source === undefined, onSelected: () => {
4202
4380
  advancedFilterState.source = undefined;
4203
4381
  dispatch({
4204
- type: ActionType$5.UpdateAdvancedFilterState,
4382
+ type: ActionType$6.UpdateAdvancedFilterState,
4205
4383
  advancedFilterState,
4206
4384
  });
4207
4385
  } }),
4208
4386
  React__default.createElement(RadioButton, { text: "Client Only", ariaLabel: "show logs from client source", selected: advancedFilterState.source === LogSource$1.Client, onSelected: () => {
4209
4387
  advancedFilterState.source = LogSource$1.Client;
4210
4388
  dispatch({
4211
- type: ActionType$5.UpdateAdvancedFilterState,
4389
+ type: ActionType$6.UpdateAdvancedFilterState,
4212
4390
  advancedFilterState,
4213
4391
  });
4214
4392
  } }),
4215
4393
  React__default.createElement(RadioButton, { text: "Server Only", ariaLabel: "show logs from server source", selected: advancedFilterState.source === LogSource$1.Server, onSelected: () => {
4216
4394
  advancedFilterState.source = LogSource$1.Server;
4217
4395
  dispatch({
4218
- type: ActionType$5.UpdateAdvancedFilterState,
4396
+ type: ActionType$6.UpdateAdvancedFilterState,
4219
4397
  advancedFilterState,
4220
4398
  });
4221
4399
  }, noMarginOnRight: true })),
@@ -4226,7 +4404,7 @@ const LogReviewer = (props) => {
4226
4404
  advancedFilterState.courseName = ((e.target.value)
4227
4405
  .trim());
4228
4406
  dispatch({
4229
- type: ActionType$5.UpdateAdvancedFilterState,
4407
+ type: ActionType$6.UpdateAdvancedFilterState,
4230
4408
  advancedFilterState,
4231
4409
  });
4232
4410
  } })),
@@ -4236,7 +4414,7 @@ const LogReviewer = (props) => {
4236
4414
  advancedFilterState.courseName = ((e.target.value)
4237
4415
  .trim());
4238
4416
  dispatch({
4239
- type: ActionType$5.UpdateAdvancedFilterState,
4417
+ type: ActionType$6.UpdateAdvancedFilterState,
4240
4418
  advancedFilterState,
4241
4419
  });
4242
4420
  } })))))));
@@ -12056,11 +12234,11 @@ var CreatableSelect$1 = CreatableSelect;
12056
12234
  */
12057
12235
  /* ------------- Actions ------------ */
12058
12236
  // Types of actions
12059
- var ActionType$4;
12237
+ var ActionType$5;
12060
12238
  (function (ActionType) {
12061
12239
  // Update the input value
12062
12240
  ActionType["SetInputValue"] = "SetInputValue";
12063
- })(ActionType$4 || (ActionType$4 = {}));
12241
+ })(ActionType$5 || (ActionType$5 = {}));
12064
12242
  /**
12065
12243
  * Reducer that executes actions
12066
12244
  * @author Yuen Ler Chow
@@ -12068,9 +12246,9 @@ var ActionType$4;
12068
12246
  * @param action action to execute
12069
12247
  * @returns updated state
12070
12248
  */
12071
- const reducer$5 = (state, action) => {
12249
+ const reducer$6 = (state, action) => {
12072
12250
  switch (action.type) {
12073
- case ActionType$4.SetInputValue: {
12251
+ case ActionType$5.SetInputValue: {
12074
12252
  return Object.assign(Object.assign({}, state), { inputValue: action.value });
12075
12253
  }
12076
12254
  default: {
@@ -12090,7 +12268,7 @@ const CreatableMultiselect = (props) => {
12090
12268
  inputValue: '',
12091
12269
  };
12092
12270
  // Initialize state
12093
- const [state, dispatch] = useReducer(reducer$5, initialState);
12271
+ const [state, dispatch] = useReducer(reducer$6, initialState);
12094
12272
  // Destructure common state
12095
12273
  const { inputValue } = state;
12096
12274
  /*------------------------------------------------------------------------*/
@@ -12167,7 +12345,7 @@ const CreatableMultiselect = (props) => {
12167
12345
  }
12168
12346
  // Reset text field to empty because the values have been added
12169
12347
  dispatch({
12170
- type: ActionType$4.SetInputValue,
12348
+ type: ActionType$5.SetInputValue,
12171
12349
  value: '',
12172
12350
  });
12173
12351
  };
@@ -12206,7 +12384,7 @@ const CreatableMultiselect = (props) => {
12206
12384
  else {
12207
12385
  // simply update the input value to the new input value
12208
12386
  dispatch({
12209
- type: ActionType$4.SetInputValue,
12387
+ type: ActionType$5.SetInputValue,
12210
12388
  value: newValue,
12211
12389
  });
12212
12390
  }
@@ -12258,13 +12436,13 @@ const style$1 = `
12258
12436
  `;
12259
12437
  /* ------------- Actions ------------ */
12260
12438
  // Types of actions
12261
- var ActionType$3;
12439
+ var ActionType$4;
12262
12440
  (function (ActionType) {
12263
12441
  // Update the DBEntry
12264
12442
  ActionType["UpdateDBEntry"] = "UpdateDBEntry";
12265
12443
  // Start the save spinner
12266
12444
  ActionType["StartSave"] = "StartSave";
12267
- })(ActionType$3 || (ActionType$3 = {}));
12445
+ })(ActionType$4 || (ActionType$4 = {}));
12268
12446
  /**
12269
12447
  * Reducer that executes actions
12270
12448
  * @author Yuen Ler Chow
@@ -12272,12 +12450,12 @@ var ActionType$3;
12272
12450
  * @param action action to execute
12273
12451
  * @returns updated state
12274
12452
  */
12275
- const reducer$4 = (state, action) => {
12453
+ const reducer$5 = (state, action) => {
12276
12454
  switch (action.type) {
12277
- case ActionType$3.UpdateDBEntry: {
12455
+ case ActionType$4.UpdateDBEntry: {
12278
12456
  return Object.assign(Object.assign({}, state), { entry: action.dbEntry });
12279
12457
  }
12280
- case ActionType$3.StartSave: {
12458
+ case ActionType$4.StartSave: {
12281
12459
  return Object.assign(Object.assign({}, state), { saving: true });
12282
12460
  }
12283
12461
  default: {
@@ -12302,7 +12480,7 @@ const AddOrEditDBEntry = (props) => {
12302
12480
  saving: false,
12303
12481
  };
12304
12482
  // Initialize state
12305
- const [state, dispatch] = useReducer(reducer$4, initialState);
12483
+ const [state, dispatch] = useReducer(reducer$5, initialState);
12306
12484
  // Destructure common state
12307
12485
  const { entry, saving, } = state;
12308
12486
  /*------------------------------------------------------------------------*/
@@ -12314,7 +12492,7 @@ const AddOrEditDBEntry = (props) => {
12314
12492
  */
12315
12493
  const save = () => __awaiter(void 0, void 0, void 0, function* () {
12316
12494
  // Start the save loading indicator
12317
- dispatch({ type: ActionType$3.StartSave });
12495
+ dispatch({ type: ActionType$4.StartSave });
12318
12496
  // add all default values to the entry
12319
12497
  entryFields.forEach((field) => {
12320
12498
  if (field.defaultValue && !entry[field.objectKey]) {
@@ -12497,7 +12675,7 @@ const AddOrEditDBEntry = (props) => {
12497
12675
  return (React__default.createElement(RadioButton, { key: choice.value, text: choice.title, selected: entry[field.objectKey] === choice.value, onSelected: () => {
12498
12676
  entry[field.objectKey] = choice.value;
12499
12677
  dispatch({
12500
- type: ActionType$3.UpdateDBEntry,
12678
+ type: ActionType$4.UpdateDBEntry,
12501
12679
  dbEntry: entry,
12502
12680
  });
12503
12681
  }, ariaLabel: choice.title }));
@@ -12509,7 +12687,7 @@ const AddOrEditDBEntry = (props) => {
12509
12687
  React__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) => {
12510
12688
  entry[field.objectKey] = (e.target.value);
12511
12689
  dispatch({
12512
- type: ActionType$3.UpdateDBEntry,
12690
+ type: ActionType$4.UpdateDBEntry,
12513
12691
  dbEntry: entry,
12514
12692
  });
12515
12693
  } }))));
@@ -12522,7 +12700,7 @@ const AddOrEditDBEntry = (props) => {
12522
12700
  entry[field.objectKey] = (e.target.value
12523
12701
  .replace(/[^0-9]/g, ''));
12524
12702
  dispatch({
12525
- type: ActionType$3.UpdateDBEntry,
12703
+ type: ActionType$4.UpdateDBEntry,
12526
12704
  dbEntry: entry,
12527
12705
  });
12528
12706
  } }))));
@@ -12552,7 +12730,7 @@ const AddOrEditDBEntry = (props) => {
12552
12730
  }
12553
12731
  // Save
12554
12732
  dispatch({
12555
- type: ActionType$3.UpdateDBEntry,
12733
+ type: ActionType$4.UpdateDBEntry,
12556
12734
  dbEntry: entry,
12557
12735
  });
12558
12736
  }, ariaLabel: choice.title }));
@@ -12567,7 +12745,7 @@ const AddOrEditDBEntry = (props) => {
12567
12745
  // Update entry and save
12568
12746
  entry[field.objectKey] = values;
12569
12747
  dispatch({
12570
- type: ActionType$3.UpdateDBEntry,
12748
+ type: ActionType$4.UpdateDBEntry,
12571
12749
  dbEntry: entry,
12572
12750
  });
12573
12751
  } })))));
@@ -12580,7 +12758,7 @@ const AddOrEditDBEntry = (props) => {
12580
12758
  React__default.createElement(CreatableMultiselect, { disabled: disabled, type: DBEntryFieldType$1.NumberArray, values: entry[field.objectKey] || [], onChange: (values) => {
12581
12759
  entry[field.objectKey] = values;
12582
12760
  dispatch({
12583
- type: ActionType$3.UpdateDBEntry,
12761
+ type: ActionType$4.UpdateDBEntry,
12584
12762
  dbEntry: entry,
12585
12763
  });
12586
12764
  } })))));
@@ -12654,7 +12832,7 @@ const generateEndpointPath = (collectionName, adminsOnly) => {
12654
12832
  */
12655
12833
  /* ------------- Actions ------------ */
12656
12834
  // Types of actions
12657
- var ActionType$2;
12835
+ var ActionType$3;
12658
12836
  (function (ActionType) {
12659
12837
  // Show adder
12660
12838
  ActionType["ShowAdder"] = "ShowAdder";
@@ -12668,7 +12846,7 @@ var ActionType$2;
12668
12846
  ActionType["StartDelete"] = "StartDelete";
12669
12847
  // Finish deletion process
12670
12848
  ActionType["FinishDelete"] = "FinishDelete";
12671
- })(ActionType$2 || (ActionType$2 = {}));
12849
+ })(ActionType$3 || (ActionType$3 = {}));
12672
12850
  /**
12673
12851
  * Reducer that executes actions
12674
12852
  * @author Yuen Ler Chow
@@ -12676,18 +12854,18 @@ var ActionType$2;
12676
12854
  * @param action action to execute
12677
12855
  * @returns updated state
12678
12856
  */
12679
- const reducer$3 = (state, action) => {
12857
+ const reducer$4 = (state, action) => {
12680
12858
  switch (action.type) {
12681
- case ActionType$2.FinishLoading: {
12859
+ case ActionType$3.FinishLoading: {
12682
12860
  return Object.assign(Object.assign({}, state), { loading: false, dbEntries: action.dbEntries });
12683
12861
  }
12684
- case ActionType$2.ShowAdder: {
12862
+ case ActionType$3.ShowAdder: {
12685
12863
  return Object.assign(Object.assign({}, state), { adding: true, dbEntryToEdit: undefined });
12686
12864
  }
12687
- case ActionType$2.ShowEditor: {
12865
+ case ActionType$3.ShowEditor: {
12688
12866
  return Object.assign(Object.assign({}, state), { adding: false, dbEntryToEdit: action.dbEntry });
12689
12867
  }
12690
- case ActionType$2.FinishAdd: {
12868
+ case ActionType$3.FinishAdd: {
12691
12869
  // Handle cancel
12692
12870
  const finishedEntry = action.dbEntry;
12693
12871
  if (!finishedEntry) {
@@ -12711,10 +12889,10 @@ const reducer$3 = (state, action) => {
12711
12889
  // Update the state
12712
12890
  return Object.assign(Object.assign({}, state), { adding: false, dbEntryToEdit: undefined, dbEntries: updatedDbEntries });
12713
12891
  }
12714
- case ActionType$2.StartDelete: {
12892
+ case ActionType$3.StartDelete: {
12715
12893
  return Object.assign(Object.assign({}, state), { loading: true });
12716
12894
  }
12717
- case ActionType$2.FinishDelete: {
12895
+ case ActionType$3.FinishDelete: {
12718
12896
  return Object.assign(Object.assign({}, state), { loading: false,
12719
12897
  // Remove the deleted entry from the list
12720
12898
  dbEntries: state.dbEntries.filter((entry) => {
@@ -12740,7 +12918,7 @@ const DBEntryManagerPanel = (props) => {
12740
12918
  loading: true,
12741
12919
  };
12742
12920
  // Initialize state
12743
- const [state, dispatch] = useReducer(reducer$3, initialState);
12921
+ const [state, dispatch] = useReducer(reducer$4, initialState);
12744
12922
  // Destructure common state
12745
12923
  const { adding, dbEntryToEdit, dbEntries, loading, } = state;
12746
12924
  /*------------------------------------------------------------------------*/
@@ -12766,7 +12944,7 @@ const DBEntryManagerPanel = (props) => {
12766
12944
  try {
12767
12945
  // Start loader
12768
12946
  dispatch({
12769
- type: ActionType$2.StartDelete,
12947
+ type: ActionType$3.StartDelete,
12770
12948
  });
12771
12949
  // Perform deletion
12772
12950
  yield visitServerEndpoint({
@@ -12775,7 +12953,7 @@ const DBEntryManagerPanel = (props) => {
12775
12953
  });
12776
12954
  // Finish loader
12777
12955
  dispatch({
12778
- type: ActionType$2.FinishDelete,
12956
+ type: ActionType$3.FinishDelete,
12779
12957
  dbEntry: entry,
12780
12958
  idPropName,
12781
12959
  });
@@ -12804,7 +12982,7 @@ const DBEntryManagerPanel = (props) => {
12804
12982
  });
12805
12983
  // Save loaded data
12806
12984
  dispatch({
12807
- type: ActionType$2.FinishLoading,
12985
+ type: ActionType$3.FinishLoading,
12808
12986
  dbEntries: data,
12809
12987
  });
12810
12988
  }
@@ -12847,7 +13025,7 @@ const DBEntryManagerPanel = (props) => {
12847
13025
  React__default.createElement("span", { className: "d-none d-md-inline ms-1" }, "Remove")),
12848
13026
  !disableEdit && (React__default.createElement("button", { type: "button", id: `DBEntryManagerPanel-edit-with-id-${entry[idPropName]}`, className: "btn btn-primary", "aria-label": `edit db entry: ${entry[titlePropName]}`, onClick: () => {
12849
13027
  dispatch({
12850
- type: ActionType$2.ShowEditor,
13028
+ type: ActionType$3.ShowEditor,
12851
13029
  dbEntry: entry,
12852
13030
  });
12853
13031
  } },
@@ -12857,7 +13035,7 @@ const DBEntryManagerPanel = (props) => {
12857
13035
  React__default.createElement("div", { className: "d-grid" },
12858
13036
  React__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: () => {
12859
13037
  dispatch({
12860
- type: ActionType$2.ShowAdder,
13038
+ type: ActionType$3.ShowAdder,
12861
13039
  });
12862
13040
  } },
12863
13041
  React__default.createElement(FontAwesomeIcon, { icon: faPlus, className: "me-2" }),
@@ -12869,7 +13047,7 @@ const DBEntryManagerPanel = (props) => {
12869
13047
  if (!loading && (adding || dbEntryToEdit)) {
12870
13048
  body = (React__default.createElement(AddOrEditDBEntry, { saveEndpointPath: endpoint, validateEntry: validateEntry, modifyEntry: modifyEntry, entryFields: entryFields, dbEntryToEdit: dbEntryToEdit, idPropName: idPropName, entries: dbEntries, itemName: itemName, onFinished: (entry) => {
12871
13049
  dispatch({
12872
- type: ActionType$2.FinishAdd,
13050
+ type: ActionType$3.FinishAdd,
12873
13051
  dbEntry: entry,
12874
13052
  idPropName,
12875
13053
  });
@@ -13066,25 +13244,25 @@ const getItemIds = (items) => {
13066
13244
  };
13067
13245
  /* ------------- Actions ------------ */
13068
13246
  // Types of actions
13069
- var ActionType$1;
13247
+ var ActionType$2;
13070
13248
  (function (ActionType) {
13071
13249
  // Hide the "jump to bottom" button
13072
13250
  ActionType["HideJumpToBottomButton"] = "HideJumpToBottomButton";
13073
13251
  // Show the "jump to bottom" button
13074
13252
  ActionType["ShowJumpToBottomButton"] = "ShowJumpToBottomButton";
13075
- })(ActionType$1 || (ActionType$1 = {}));
13253
+ })(ActionType$2 || (ActionType$2 = {}));
13076
13254
  /**
13077
13255
  * Reducer that executes actions
13078
13256
  * @author Gabe Abrams
13079
13257
  * @param state current state
13080
13258
  * @param action action to execute
13081
13259
  */
13082
- const reducer$2 = (state, action) => {
13260
+ const reducer$3 = (state, action) => {
13083
13261
  switch (action.type) {
13084
- case ActionType$1.HideJumpToBottomButton: {
13262
+ case ActionType$2.HideJumpToBottomButton: {
13085
13263
  return Object.assign(Object.assign({}, state), { jumpToBottomButtonVisible: false });
13086
13264
  }
13087
- case ActionType$1.ShowJumpToBottomButton: {
13265
+ case ActionType$2.ShowJumpToBottomButton: {
13088
13266
  return Object.assign(Object.assign({}, state), { jumpToBottomButtonVisible: true });
13089
13267
  }
13090
13268
  default: {
@@ -13108,7 +13286,7 @@ const AutoscrollToBottomContainer = (props) => {
13108
13286
  jumpToBottomButtonVisible: false,
13109
13287
  };
13110
13288
  // Initialize state
13111
- const [state, dispatch] = useReducer(reducer$2, initialState);
13289
+ const [state, dispatch] = useReducer(reducer$3, initialState);
13112
13290
  // Destructure common state
13113
13291
  const { jumpToBottomButtonVisible, } = state;
13114
13292
  /* -------------- Refs -------------- */
@@ -13154,7 +13332,7 @@ const AutoscrollToBottomContainer = (props) => {
13154
13332
  }
13155
13333
  // Update state
13156
13334
  dispatch({
13157
- type: ActionType$1.HideJumpToBottomButton,
13335
+ type: ActionType$2.HideJumpToBottomButton,
13158
13336
  });
13159
13337
  // Scroll to bottom
13160
13338
  container.current.scrollTop = (container.current.scrollHeight
@@ -13178,7 +13356,7 @@ const AutoscrollToBottomContainer = (props) => {
13178
13356
  if (isScrolledToBottom()) {
13179
13357
  // Hide button
13180
13358
  dispatch({
13181
- type: ActionType$1.HideJumpToBottomButton,
13359
+ type: ActionType$2.HideJumpToBottomButton,
13182
13360
  });
13183
13361
  }
13184
13362
  };
@@ -13220,7 +13398,7 @@ const AutoscrollToBottomContainer = (props) => {
13220
13398
  else {
13221
13399
  // Not scrolled to bottom. Show "jump to bottom" button.
13222
13400
  dispatch({
13223
- type: ActionType$1.ShowJumpToBottomButton,
13401
+ type: ActionType$2.ShowJumpToBottomButton,
13224
13402
  });
13225
13403
  }
13226
13404
  }, [items]);
@@ -13290,25 +13468,25 @@ const combineClassNames = (classNames) => {
13290
13468
  */
13291
13469
  /* ------------- Actions ------------ */
13292
13470
  // Types of actions
13293
- var ActionType;
13471
+ var ActionType$1;
13294
13472
  (function (ActionType) {
13295
13473
  // Start hovering on an option
13296
13474
  ActionType["StartHover"] = "StartHover";
13297
13475
  // Stop hovering on an option
13298
13476
  ActionType["StopHover"] = "StopHover";
13299
- })(ActionType || (ActionType = {}));
13477
+ })(ActionType$1 || (ActionType$1 = {}));
13300
13478
  /**
13301
13479
  * Reducer that executes actions
13302
13480
  * @author Gabe Abrams
13303
13481
  * @param state current state
13304
13482
  * @param action action to execute
13305
13483
  */
13306
- const reducer$1 = (state, action) => {
13484
+ const reducer$2 = (state, action) => {
13307
13485
  switch (action.type) {
13308
- case ActionType.StartHover: {
13486
+ case ActionType$1.StartHover: {
13309
13487
  return Object.assign(Object.assign({}, state), { hoveredOptionId: action.hoveredOptionId });
13310
13488
  }
13311
- case ActionType.StopHover: {
13489
+ case ActionType$1.StopHover: {
13312
13490
  return Object.assign(Object.assign({}, state), { hoveredOptionId: undefined });
13313
13491
  }
13314
13492
  default: {
@@ -13332,7 +13510,7 @@ const MultiSwitch = (props) => {
13332
13510
  hoveredOptionId: undefined,
13333
13511
  };
13334
13512
  // Initialize state
13335
- const [state, dispatch] = useReducer(reducer$1, initialState);
13513
+ const [state, dispatch] = useReducer(reducer$2, initialState);
13336
13514
  // Destructure common state
13337
13515
  const { hoveredOptionId, } = state;
13338
13516
  /*------------------------------------------------------------------------*/
@@ -13344,7 +13522,7 @@ const MultiSwitch = (props) => {
13344
13522
  });
13345
13523
  // Constants
13346
13524
  const gutterRems = heightRem * 0.1;
13347
- const itemWidthRems = heightRem * 1.3;
13525
+ const itemWidthRems = heightRem * 2.2;
13348
13526
  const textFontSize = heightRem / 3.5;
13349
13527
  const iconFontSize = heightRem / 2;
13350
13528
  const borderWidthRems = 0.05;
@@ -13464,27 +13642,27 @@ const MultiSwitch = (props) => {
13464
13642
  : `click to select option "${option.label}"`), onClick: () => {
13465
13643
  // Remove hover
13466
13644
  dispatch({
13467
- type: ActionType.StopHover,
13645
+ type: ActionType$1.StopHover,
13468
13646
  });
13469
13647
  // Notify parent
13470
13648
  onChange(option.id);
13471
13649
  }, onMouseEnter: () => {
13472
13650
  dispatch({
13473
- type: ActionType.StartHover,
13651
+ type: ActionType$1.StartHover,
13474
13652
  hoveredOptionId: option.id,
13475
13653
  });
13476
13654
  }, onMouseLeave: () => {
13477
13655
  dispatch({
13478
- type: ActionType.StopHover,
13656
+ type: ActionType$1.StopHover,
13479
13657
  });
13480
13658
  }, onFocus: () => {
13481
13659
  dispatch({
13482
- type: ActionType.StartHover,
13660
+ type: ActionType$1.StartHover,
13483
13661
  hoveredOptionId: option.id,
13484
13662
  });
13485
13663
  }, onBlur: () => {
13486
13664
  dispatch({
13487
- type: ActionType.StopHover,
13665
+ type: ActionType$1.StopHover,
13488
13666
  });
13489
13667
  }, style: {
13490
13668
  pointerEvents: ((option.id === selectedOptionId)
@@ -13503,6 +13681,147 @@ const MultiSwitch = (props) => {
13503
13681
  React__default.createElement("div", { className: "MultiSwitch-highlight-container" }, highlight)));
13504
13682
  };
13505
13683
 
13684
+ // Types of dropdown items
13685
+ var DropdownItemType;
13686
+ (function (DropdownItemType) {
13687
+ // Dropdown header
13688
+ DropdownItemType["Header"] = "Header";
13689
+ // Dropdown divider
13690
+ DropdownItemType["Divider"] = "Divider";
13691
+ // Dropdown item
13692
+ DropdownItemType["Item"] = "Item";
13693
+ })(DropdownItemType || (DropdownItemType = {}));
13694
+ var DropdownItemType$1 = DropdownItemType;
13695
+
13696
+ /**
13697
+ * A simple dropdown menu
13698
+ * @author Alessandra De Lucas
13699
+ * @author Yuen Ler Chow
13700
+ * @author Gabe Abrams
13701
+ */
13702
+ /* ------------- Actions ------------ */
13703
+ // Types of actions
13704
+ var ActionType;
13705
+ (function (ActionType) {
13706
+ // Toggle opening the dropdown menu
13707
+ ActionType["ToggleDropdown"] = "ToggleDropdown";
13708
+ // Close the dropdown menu
13709
+ ActionType["CloseDropdown"] = "CloseDropdown";
13710
+ })(ActionType || (ActionType = {}));
13711
+ /**
13712
+ * Reducer that executes actions
13713
+ * @author Alessandra De Lucas
13714
+ * @author Yuen Ler Chow
13715
+ * @param state current state
13716
+ * @param action action to execute
13717
+ */
13718
+ const reducer$1 = (state, action) => {
13719
+ switch (action.type) {
13720
+ case ActionType.ToggleDropdown: {
13721
+ return Object.assign(Object.assign({}, state), { isDropdownOpen: !state.isDropdownOpen });
13722
+ }
13723
+ case ActionType.CloseDropdown: {
13724
+ return Object.assign(Object.assign({}, state), { isDropdownOpen: false });
13725
+ }
13726
+ default: {
13727
+ return state;
13728
+ }
13729
+ }
13730
+ };
13731
+ /*------------------------------------------------------------------------*/
13732
+ /* ------------------------------ Component ----------------------------- */
13733
+ /*------------------------------------------------------------------------*/
13734
+ const Dropdown = (props) => {
13735
+ /*------------------------------------------------------------------------*/
13736
+ /* -------------------------------- Setup ------------------------------- */
13737
+ /*------------------------------------------------------------------------*/
13738
+ /* -------------- Props ------------- */
13739
+ // Destructure all props
13740
+ const { dropdownButton, items, } = props;
13741
+ /* -------------- State ------------- */
13742
+ // Initial state
13743
+ const initialState = {
13744
+ isDropdownOpen: false,
13745
+ };
13746
+ // Initialize state
13747
+ const [state, dispatch] = useReducer(reducer$1, initialState);
13748
+ // Destructure common state
13749
+ const { isDropdownOpen, } = state;
13750
+ /* -------------- Refs -------------- */
13751
+ // Initialize refs
13752
+ const dropdownRef = useRef(null);
13753
+ /*------------------------------------------------------------------------*/
13754
+ /* ------------------------- Component Functions ------------------------ */
13755
+ /*------------------------------------------------------------------------*/
13756
+ /**
13757
+ * Handle clicking outside of the dropdown menu to close it
13758
+ * @author Yuen Ler Chow
13759
+ * @param event the mouse event
13760
+ */
13761
+ const handleClickOutside = (event) => {
13762
+ if (
13763
+ // Dropdown has been rendered
13764
+ dropdownRef.current
13765
+ // Click occurred outside the dropdown
13766
+ && !dropdownRef.current.contains(event.target)) {
13767
+ dispatch({ type: ActionType.CloseDropdown });
13768
+ }
13769
+ };
13770
+ /*------------------------------------------------------------------------*/
13771
+ /* ------------------------- Lifecycle Functions ------------------------ */
13772
+ /*------------------------------------------------------------------------*/
13773
+ /**
13774
+ * Mount
13775
+ * @author Yuen Ler Chow
13776
+ */
13777
+ useEffect(() => {
13778
+ // Add event listener to close dropdown when clicking outside
13779
+ document.addEventListener('mousedown', handleClickOutside);
13780
+ // Cleanup
13781
+ return () => {
13782
+ document.removeEventListener('mousedown', handleClickOutside);
13783
+ };
13784
+ }, []);
13785
+ /*----------------------------------------*/
13786
+ /* --------------- Main UI -------------- */
13787
+ /*----------------------------------------*/
13788
+ return (React__default.createElement("div", { className: "dropdown", ref: dropdownRef, "data-bs-theme": isDarkModeOn() ? 'dark' : undefined },
13789
+ React__default.createElement("button", { className: combineClassNames([
13790
+ 'btn dropdown-toggle border',
13791
+ isDropdownOpen && 'show',
13792
+ `btn-${dropdownButton.variant}`,
13793
+ dropdownButton.variant === Variant$1.Light && 'text-dark',
13794
+ ]), type: "button", id: dropdownButton.id, "aria-expanded": isDropdownOpen, "aria-label": dropdownButton.ariaLabel, onClick: () => {
13795
+ dispatch({
13796
+ type: ActionType.ToggleDropdown,
13797
+ });
13798
+ } }, dropdownButton.content),
13799
+ React__default.createElement("ul", { className: combineClassNames([
13800
+ 'dropdown-menu',
13801
+ isDarkModeOn() && 'dropdown-menu-dark',
13802
+ isDropdownOpen && 'show',
13803
+ ]) }, Object.values(items).map((item) => {
13804
+ if (item.type === DropdownItemType$1.Header) {
13805
+ return (
13806
+ // TODO: Implement header
13807
+ React__default.createElement("span", null));
13808
+ }
13809
+ if (item.type === DropdownItemType$1.Divider) {
13810
+ return (
13811
+ // TODO: Implement divider
13812
+ React__default.createElement("span", null));
13813
+ }
13814
+ return (React__default.createElement("li", { key: item.id },
13815
+ React__default.createElement("button", { type: "button", "aria-label": item.ariaLabel, className: "dropdown-item", onClick: (e) => {
13816
+ e.preventDefault();
13817
+ dispatch({
13818
+ type: ActionType.CloseDropdown,
13819
+ });
13820
+ item.onClick();
13821
+ } }, item.content)));
13822
+ }))));
13823
+ };
13824
+
13506
13825
  /**
13507
13826
  * One minute in ms
13508
13827
  * @author Gabe Abrams
@@ -13893,22 +14212,27 @@ const initServer = (opts) => {
13893
14212
  paramTypes: {
13894
14213
  year: ParamType$1.Int,
13895
14214
  month: ParamType$1.Int,
14215
+ pageNumber: ParamType$1.Int,
13896
14216
  },
13897
14217
  handler: ({ params }) => __awaiter(void 0, void 0, void 0, function* () {
13898
14218
  // Get user info
13899
- const { year, month, userId, isAdmin, } = params;
14219
+ const { year, month, pageNumber, userId, isAdmin, } = params;
13900
14220
  // Validate user
13901
14221
  const canReview = yield canReviewLogs(userId, isAdmin);
13902
14222
  if (!canReview) {
13903
14223
  throw new ErrorWithCode('You cannot access this resource because you do not have the appropriate permissions.', ReactKitErrorCode$1.NotAllowedToReviewLogs);
13904
14224
  }
13905
14225
  // Query for logs
13906
- const logs = yield _logCollection.find({
13907
- year,
13908
- month,
14226
+ const response = yield _logCollection.findPaged({
14227
+ query: {
14228
+ year,
14229
+ month,
14230
+ },
14231
+ perPage: 1000,
14232
+ pageNumber,
13909
14233
  });
13910
- // Return logs
13911
- return logs;
14234
+ // Return response
14235
+ return response;
13912
14236
  }),
13913
14237
  }));
13914
14238
  };
@@ -14310,6 +14634,12 @@ const parseUserAgent = (userAgent) => {
14310
14634
  * @param opts.handler function that processes the request
14311
14635
  * @param [opts.skipSessionCheck] if true, skip the session check (allow users
14312
14636
  * to not be logged in and launched via LTI)
14637
+ * @param [opts.allowedHosts] if included, only allow requests from these hosts
14638
+ * (start a hostname with a "*" to only check the end of the hostname)
14639
+ * you can include just one string instead of an array
14640
+ * @param [opts.bannedHosts] if included, do not allow requests from these hosts
14641
+ * (start a hostname with a "*" to only check the end of the hostname)
14642
+ * you can include just one string instead of an array
14313
14643
  * @param [opts.unhandledErrorMessagePrefix] if included, when an error that
14314
14644
  * is not of type ErrorWithCode is thrown, the client will receive an error
14315
14645
  * where the error message is prefixed with this string. For example,
@@ -14337,10 +14667,76 @@ const parseUserAgent = (userAgent) => {
14337
14667
  const genRouteHandler = (opts) => {
14338
14668
  // Return a route handler
14339
14669
  return (req, res, next) => __awaiter(void 0, void 0, void 0, function* () {
14340
- var _a, _b, _c;
14670
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
14341
14671
  // Output params
14342
14672
  const output = {};
14343
14673
  /*----------------------------------------*/
14674
+ /* ----------- Hostname Check ----------- */
14675
+ /*----------------------------------------*/
14676
+ // Get hostnames
14677
+ const originURL = String(req.get('origin')
14678
+ || req.headers.origin
14679
+ || req.headers.referer);
14680
+ const originHostname = (originURL
14681
+ // Remove protocol
14682
+ .replace(/(^\w+:|^)\/\//, '')
14683
+ // Remove port
14684
+ .replace(/:\d+$/, ''));
14685
+ const serverHostname = String(req.hostname);
14686
+ // Check allowed
14687
+ if (opts.allowedHosts) {
14688
+ // Only accept requests from allowed hosts
14689
+ const allowedArray = (Array.isArray(opts.allowedHosts)
14690
+ ? opts.allowedHosts
14691
+ : [opts.allowedHosts]);
14692
+ // Check if server is localhost
14693
+ if (serverHostname === 'localhost') {
14694
+ // Allow localhost
14695
+ allowedArray.push('localhost');
14696
+ }
14697
+ // Check if current host is allowed
14698
+ const allowed = allowedArray.some((allowedHost) => {
14699
+ if (allowedHost.startsWith('*')) {
14700
+ // Check end of hostname
14701
+ return originHostname.endsWith(allowedHost.substring(1));
14702
+ }
14703
+ // Check full hostname
14704
+ return originHostname.toLowerCase() === allowedHost.toLowerCase();
14705
+ });
14706
+ // If not allowed, return error
14707
+ if (!allowed) {
14708
+ return handleError(res, {
14709
+ message: 'You are not allowed to access this endpoint.',
14710
+ code: ReactKitErrorCode$1.HostNotAllowed,
14711
+ status: 403,
14712
+ });
14713
+ }
14714
+ }
14715
+ // Check banned
14716
+ if (opts.bannedHosts) {
14717
+ // Do not allow requests from banned hosts
14718
+ const bannedArray = (Array.isArray(opts.bannedHosts)
14719
+ ? opts.bannedHosts
14720
+ : [opts.bannedHosts]);
14721
+ // Check if current host is banned
14722
+ const banned = bannedArray.some((bannedHost) => {
14723
+ if (bannedHost.startsWith('*')) {
14724
+ // Check end of hostname
14725
+ return originHostname.endsWith(bannedHost.substring(1));
14726
+ }
14727
+ // Check full hostname
14728
+ return originHostname.toLowerCase() === bannedHost.toLowerCase();
14729
+ });
14730
+ // If banned, return error
14731
+ if (banned) {
14732
+ return handleError(res, {
14733
+ message: 'You are not allowed to access this endpoint.',
14734
+ code: ReactKitErrorCode$1.HostBanned,
14735
+ status: 403,
14736
+ });
14737
+ }
14738
+ }
14739
+ /*----------------------------------------*/
14344
14740
  /* ------------ Parse Params ------------ */
14345
14741
  /*----------------------------------------*/
14346
14742
  // Process items one by one
@@ -14538,34 +14934,34 @@ const genRouteHandler = (opts) => {
14538
14934
  // Add launch info to output
14539
14935
  output.userId = (launchInfo
14540
14936
  ? launchInfo.userId
14541
- : undefined);
14937
+ : ((_b = output.userId) !== null && _b !== void 0 ? _b : undefined));
14542
14938
  output.userFirstName = (launchInfo
14543
14939
  ? launchInfo.userFirstName
14544
- : undefined);
14940
+ : ((_c = output.userFirstName) !== null && _c !== void 0 ? _c : undefined));
14545
14941
  output.userLastName = (launchInfo
14546
14942
  ? launchInfo.userLastName
14547
- : undefined);
14943
+ : ((_d = output.userLastName) !== null && _d !== void 0 ? _d : undefined));
14548
14944
  output.userEmail = (launchInfo
14549
14945
  ? launchInfo.userEmail
14550
- : undefined);
14946
+ : ((_e = output.userEmail) !== null && _e !== void 0 ? _e : undefined));
14551
14947
  output.userAvatarURL = (launchInfo
14552
- ? ((_b = launchInfo.userImage) !== null && _b !== void 0 ? _b : 'http://www.gravatar.com/avatar/?d=identicon')
14553
- : undefined);
14948
+ ? ((_f = launchInfo.userImage) !== null && _f !== void 0 ? _f : 'http://www.gravatar.com/avatar/?d=identicon')
14949
+ : ((_g = output.userAvatarURL) !== null && _g !== void 0 ? _g : undefined));
14554
14950
  output.isLearner = (launchInfo
14555
14951
  ? !!launchInfo.isLearner
14556
- : undefined);
14952
+ : ((_h = output.isLearner) !== null && _h !== void 0 ? _h : undefined));
14557
14953
  output.isTTM = (launchInfo
14558
14954
  ? !!launchInfo.isTTM
14559
- : undefined);
14955
+ : ((_j = output.isTTM) !== null && _j !== void 0 ? _j : undefined));
14560
14956
  output.isAdmin = (launchInfo
14561
14957
  ? !!launchInfo.isAdmin
14562
- : undefined);
14958
+ : ((_k = output.isAdmin) !== null && _k !== void 0 ? _k : undefined));
14563
14959
  output.courseId = (launchInfo
14564
- ? ((_c = output.courseId) !== null && _c !== void 0 ? _c : launchInfo.courseId)
14565
- : undefined);
14960
+ ? ((_l = output.courseId) !== null && _l !== void 0 ? _l : launchInfo.courseId)
14961
+ : ((_m = output.courseId) !== null && _m !== void 0 ? _m : undefined));
14566
14962
  output.courseName = (launchInfo
14567
14963
  ? launchInfo.contextLabel
14568
- : undefined);
14964
+ : ((_o = output.courseName) !== null && _o !== void 0 ? _o : undefined));
14569
14965
  // Add other session variables
14570
14966
  Object.keys(req.session).forEach((propName) => {
14571
14967
  // Skip if prop already in output
@@ -14639,7 +15035,7 @@ const genRouteHandler = (opts) => {
14639
15035
  * @author Gabe Abrams
14640
15036
  */
14641
15037
  const logServerEvent = (logOpts) => __awaiter(void 0, void 0, void 0, function* () {
14642
- var _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r;
15038
+ var _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1;
14643
15039
  // NOTE: internally, we slip through an opts.overrideAsClientEvent boolean
14644
15040
  // that indicates that this is actually a client event, but we don't
14645
15041
  // include that in the LogFunction type because this is internal and
@@ -14651,16 +15047,16 @@ const genRouteHandler = (opts) => {
14651
15047
  const { timestamp, year, month, day, hour, minute, } = getTimeInfoInET();
14652
15048
  // Main log info
14653
15049
  const mainLogInfo = {
14654
- id: `${launchInfo ? 'unknown' : launchInfo.userId}-${Date.now()}-${Math.floor(Math.random() * 100000)}-${Math.floor(Math.random() * 100000)}`,
14655
- userFirstName: (launchInfo ? 'unknown' : launchInfo.userFirstName),
14656
- userLastName: (launchInfo ? 'unknown' : launchInfo.userLastName),
14657
- userEmail: (launchInfo ? 'unknown' : launchInfo.userEmail),
14658
- userId: (launchInfo ? 'unknown' : launchInfo.userId),
15050
+ id: `${launchInfo ? launchInfo.userId : 'unknown'}-${Date.now()}-${Math.floor(Math.random() * 100000)}-${Math.floor(Math.random() * 100000)}`,
15051
+ userFirstName: (launchInfo ? launchInfo.userFirstName : 'unknown'),
15052
+ userLastName: (launchInfo ? launchInfo.userLastName : 'unknown'),
15053
+ userEmail: (launchInfo ? launchInfo.userEmail : 'unknown'),
15054
+ userId: (launchInfo ? launchInfo.userId : 'unknown'),
14659
15055
  isLearner: (launchInfo && !!launchInfo.isLearner),
14660
15056
  isAdmin: (launchInfo && !!launchInfo.isAdmin),
14661
15057
  isTTM: (launchInfo && !!launchInfo.isTTM),
14662
- courseId: (launchInfo ? 'unknown' : launchInfo.courseId),
14663
- courseName: (launchInfo ? 'unknown' : launchInfo.contextLabel),
15058
+ courseId: (launchInfo ? launchInfo.courseId : 'unknown'),
15059
+ courseName: (launchInfo ? launchInfo.contextLabel : 'unknown'),
14664
15060
  browser,
14665
15061
  device,
14666
15062
  year,
@@ -14671,24 +15067,24 @@ const genRouteHandler = (opts) => {
14671
15067
  timestamp,
14672
15068
  context: (typeof logOpts.context === 'string'
14673
15069
  ? logOpts.context
14674
- : ((_e = ((_d = logOpts.context) !== null && _d !== void 0 ? _d : {})._) !== null && _e !== void 0 ? _e : LogBuiltInMetadata.Context.Uncategorized)),
14675
- subcontext: ((_f = logOpts.subcontext) !== null && _f !== void 0 ? _f : LogBuiltInMetadata.Context.Uncategorized),
14676
- tags: ((_g = logOpts.tags) !== null && _g !== void 0 ? _g : []),
14677
- level: ((_h = logOpts.level) !== null && _h !== void 0 ? _h : LogLevel$1.Info),
14678
- metadata: ((_j = logOpts.metadata) !== null && _j !== void 0 ? _j : {}),
15070
+ : ((_q = ((_p = logOpts.context) !== null && _p !== void 0 ? _p : {})._) !== null && _q !== void 0 ? _q : LogBuiltInMetadata.Context.Uncategorized)),
15071
+ subcontext: ((_r = logOpts.subcontext) !== null && _r !== void 0 ? _r : LogBuiltInMetadata.Context.Uncategorized),
15072
+ tags: ((_s = logOpts.tags) !== null && _s !== void 0 ? _s : []),
15073
+ level: ((_t = logOpts.level) !== null && _t !== void 0 ? _t : LogLevel$1.Info),
15074
+ metadata: ((_u = logOpts.metadata) !== null && _u !== void 0 ? _u : {}),
14679
15075
  };
14680
15076
  // Type-specific info
14681
15077
  const typeSpecificInfo = (('error' in opts && opts.error)
14682
15078
  ? {
14683
15079
  type: LogType$1.Error,
14684
- errorMessage: (_k = logOpts.error.message) !== null && _k !== void 0 ? _k : 'Unknown message',
14685
- errorCode: (_l = logOpts.error.code) !== null && _l !== void 0 ? _l : ReactKitErrorCode$1.NoCode,
14686
- errorStack: (_m = logOpts.error.stack) !== null && _m !== void 0 ? _m : 'No stack',
15080
+ errorMessage: (_v = logOpts.error.message) !== null && _v !== void 0 ? _v : 'Unknown message',
15081
+ errorCode: (_w = logOpts.error.code) !== null && _w !== void 0 ? _w : ReactKitErrorCode$1.NoCode,
15082
+ errorStack: (_x = logOpts.error.stack) !== null && _x !== void 0 ? _x : 'No stack',
14687
15083
  }
14688
15084
  : {
14689
15085
  type: LogType$1.Action,
14690
- target: ((_o = logOpts.target) !== null && _o !== void 0 ? _o : LogBuiltInMetadata.Target.NoTarget),
14691
- action: ((_p = logOpts.action) !== null && _p !== void 0 ? _p : LogAction$1.Unknown),
15086
+ target: ((_y = logOpts.target) !== null && _y !== void 0 ? _y : LogBuiltInMetadata.Target.NoTarget),
15087
+ action: ((_z = logOpts.action) !== null && _z !== void 0 ? _z : LogAction$1.Unknown),
14692
15088
  });
14693
15089
  // Source-specific info
14694
15090
  const sourceSpecificInfo = (logOpts.overrideAsClientEvent
@@ -14723,7 +15119,7 @@ const genRouteHandler = (opts) => {
14723
15119
  catch (err) {
14724
15120
  // Print because we cannot store the error
14725
15121
  // eslint-disable-next-line no-console
14726
- 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);
15122
+ 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);
14727
15123
  // Create a dummy log to return
14728
15124
  const dummyMainInfo = {
14729
15125
  id: '-1',
@@ -15067,6 +15463,11 @@ const initLogCollection = (Collection) => {
15067
15463
  'context',
15068
15464
  'subcontext',
15069
15465
  'tags',
15466
+ 'year',
15467
+ 'month',
15468
+ 'day',
15469
+ 'hour',
15470
+ 'type',
15070
15471
  ],
15071
15472
  });
15072
15473
  };
@@ -15812,6 +16213,182 @@ const shuffleArray = (arr) => {
15812
16213
  return newArr;
15813
16214
  };
15814
16215
 
16216
+ /**
16217
+ * Sends and retries an http request
16218
+ * @author Gabriel Abrams
16219
+ * @param opts object containing all arguments
16220
+ * @param opts.path path to send request to
16221
+ * @param [opts.host] host to send request to
16222
+ * @param [opts.method=GET] http method to use
16223
+ * @param [opts.params] body/data to include in the request
16224
+ * @param [opts.headers] headers to include in the request
16225
+ * @param [opts.sendCrossDomainCredentials=true if in development mode] if true,
16226
+ * send cross-domain credentials even if not in dev mode
16227
+ * @param [opts.responseType=JSON] expected response type
16228
+ * @returns { body, status, headers } on success
16229
+ */
16230
+ const sendServerToServerRequest = (opts) => __awaiter(void 0, void 0, void 0, function* () {
16231
+ var _a;
16232
+ // Process method
16233
+ const method = (opts.method || 'GET');
16234
+ // Encode objects within params
16235
+ let params;
16236
+ if (opts.params) {
16237
+ params = {};
16238
+ Object.entries(opts.params).forEach(([key, val]) => {
16239
+ if (typeof val === 'object' && !Array.isArray(val)) {
16240
+ params[key] = JSON.stringify(val);
16241
+ }
16242
+ else {
16243
+ params[key] = val;
16244
+ }
16245
+ });
16246
+ }
16247
+ // Stringify parameters
16248
+ const stringifiedParams = qs.stringify(params || {}, {
16249
+ encodeValuesOnly: true,
16250
+ arrayFormat: 'brackets',
16251
+ });
16252
+ // Create url (include query if GET)
16253
+ const query = (method === 'GET' ? `?${stringifiedParams}` : '');
16254
+ let url;
16255
+ if (!opts.host) {
16256
+ // No host included at all. Just send to a path
16257
+ url = `${opts.path}${query}`;
16258
+ }
16259
+ else {
16260
+ url = `https://${opts.host}${opts.path}${query}`;
16261
+ }
16262
+ // Update headers
16263
+ const headers = opts.headers || {};
16264
+ let data = null;
16265
+ if (!headers['Content-Type']) {
16266
+ // Form encoded
16267
+ headers['Content-Type'] = 'application/x-www-form-urlencoded';
16268
+ // Add data if applicable
16269
+ data = (method !== 'GET' ? stringifiedParams : null);
16270
+ }
16271
+ else {
16272
+ // JSON encode
16273
+ data = params;
16274
+ }
16275
+ // Encode data
16276
+ let encodedData;
16277
+ if (data) {
16278
+ if (headers['Content-Type'] === 'application/x-www-form-urlencoded') {
16279
+ encodedData = new URLSearchParams(params);
16280
+ }
16281
+ else {
16282
+ encodedData = JSON.stringify(data);
16283
+ }
16284
+ }
16285
+ // Send request
16286
+ try {
16287
+ const response = yield fetch(url, {
16288
+ method,
16289
+ mode: 'cors',
16290
+ headers: headers !== null && headers !== void 0 ? headers : {},
16291
+ body: ((method !== 'GET' && encodedData)
16292
+ ? encodedData
16293
+ : undefined),
16294
+ redirect: 'follow',
16295
+ });
16296
+ // Get headers map
16297
+ const responseHeaders = {};
16298
+ response.headers.forEach((value, key) => {
16299
+ responseHeaders[key] = value;
16300
+ });
16301
+ // Process response based on responseType
16302
+ try {
16303
+ // Parse response
16304
+ let responseBody;
16305
+ if (opts.responseType
16306
+ && opts.responseType === 'Text') {
16307
+ // Response type is text
16308
+ responseBody = yield response.text();
16309
+ }
16310
+ else {
16311
+ // Response type is JSON
16312
+ responseBody = yield response.json();
16313
+ }
16314
+ // Return response
16315
+ return {
16316
+ body: responseBody,
16317
+ status: response.status,
16318
+ headers: responseHeaders,
16319
+ };
16320
+ }
16321
+ catch (err) {
16322
+ throw new ErrorWithCode(`Failed to parse response as ${opts.responseType}: ${err === null || err === void 0 ? void 0 : err.message}`, ReactKitErrorCode$1.ResponseParseError);
16323
+ }
16324
+ }
16325
+ catch (err) {
16326
+ // Self-signed certificate error:
16327
+ if ((_a = err === null || err === void 0 ? void 0 : err.message) === null || _a === void 0 ? void 0 : _a.includes('self signed certificate')) {
16328
+ throw new ErrorWithCode('We refused to send a request because the receiver has self-signed certificates.', ReactKitErrorCode$1.SelfSigned);
16329
+ }
16330
+ // No tries left
16331
+ 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);
16332
+ }
16333
+ });
16334
+
16335
+ /**
16336
+ * Send a server-to-server request from this sever to another server that uses
16337
+ * dce-reactkit [for server only]
16338
+ * @author Gabe Abrams
16339
+ * @param opts object containing all arguments
16340
+ * @param opts.host - the host of the other server
16341
+ * @param opts.path - the path of the other server's endpoint
16342
+ * @param [opts.method=GET] - the method of the endpoint
16343
+ * @param [opts.params] - query/body parameters to include
16344
+ * @param [opts.headers] - headers to include
16345
+ * @returns response from server
16346
+ */
16347
+ const visitEndpointOnAnotherServer = (opts) => __awaiter(void 0, void 0, void 0, function* () {
16348
+ var _a;
16349
+ // Remove properties with undefined values
16350
+ let params;
16351
+ if (opts.params) {
16352
+ params = Object.fromEntries(Object
16353
+ .entries(opts.params)
16354
+ .filter(([, value]) => {
16355
+ return value !== undefined;
16356
+ }));
16357
+ }
16358
+ // Automatically JSONify arrays and objects
16359
+ if (params) {
16360
+ params = Object.fromEntries(Object
16361
+ .entries(params)
16362
+ .map(([key, value]) => {
16363
+ if (Array.isArray(value) || typeof value === 'object') {
16364
+ return [key, JSON.stringify(value)];
16365
+ }
16366
+ return [key, value];
16367
+ }));
16368
+ }
16369
+ // Send the request
16370
+ const response = yield sendServerToServerRequest({
16371
+ host: opts.host,
16372
+ path: opts.path,
16373
+ method: (_a = opts.method) !== null && _a !== void 0 ? _a : 'GET',
16374
+ params,
16375
+ });
16376
+ // Check for failure
16377
+ if (!response || !response.body) {
16378
+ throw new ErrorWithCode('We didn\'t get a response from the server. Please check your internet connection.', ReactKitErrorCode$1.NoResponse);
16379
+ }
16380
+ if (!response.body.success) {
16381
+ // Other errors
16382
+ throw new ErrorWithCode((response.body.message
16383
+ || 'An unknown error occurred. Please contact an admin.'), (response.body.code
16384
+ || ReactKitErrorCode$1.NoCode));
16385
+ }
16386
+ // Success! Extract the body
16387
+ const { body } = response.body;
16388
+ // Return
16389
+ return body;
16390
+ });
16391
+
15815
16392
  /**
15816
16393
  * Days of the week
15817
16394
  * @author Gabe Abrams
@@ -15828,5 +16405,5 @@ var DayOfWeek;
15828
16405
  })(DayOfWeek || (DayOfWeek = {}));
15829
16406
  var DayOfWeek$1 = DayOfWeek;
15830
16407
 
15831
- export { AppWrapper, AutoscrollToBottomContainer, ButtonInputGroup, CSVDownloadButton, CheckboxButton, CopiableBox, DAY_IN_MS, DBEntryFieldType$1 as DBEntryFieldType, DBEntryManagerPanel, DayOfWeek$1 as DayOfWeek, Drawer, DynamicWord, ErrorBox, ErrorWithCode, HOUR_IN_MS, IntelliTable, ItemPicker, LoadingSpinner, LogAction$1 as LogAction, LogBuiltInMetadata, LogReviewer, LogSource$1 as LogSource, LogType$1 as LogType, MINUTE_IN_MS, Modal, ModalButtonType$1 as ModalButtonType, ModalSize$1 as ModalSize, ModalType$1 as ModalType, MultiSwitch, ParamType$1 as ParamType, PopFailureMark, PopPendingMark, PopSuccessMark, RadioButton, ReactKitErrorCode$1 as ReactKitErrorCode, SimpleDateChooser, TabBox, ToggleSwitch, Tooltip, Variant$1 as Variant, abbreviate, addDBEditorEndpoints, addFatalErrorHandler, alert, avg, canReviewLogs, capitalize, ceilToNumDecimals, combineClassNames, compareArraysByProp, confirm, everyAsync, extractProp, filterAsync, floorToNumDecimals, forEachAsync, forceNumIntoBounds, genCSV, genCommaList, genRouteHandler, getHumanReadableDate, getLocalTimeInfo, getMonthName, getOrdinal, getPartOfDay, getTimeInfoInET, handleError, handleSuccess, idify, initClient, initLogCollection, initServer, isMobileOrTablet, leaveToURL, logClientEvent, makeLinksClickable, mapAsync, onlyKeepLetters, padDecimalZeros, padZerosLeft, parallelLimit, prefixWithAOrAn, roundToNumDecimals, setClientEventMetadataPopulator, showFatalError, shuffleArray, someAsync, startMinWait, stringsToHumanReadableList, stubServerEndpoint, sum, useForceRender, validateEmail, validatePhoneNumber, validateString, visitServerEndpoint, waitMs };
16408
+ export { AppWrapper, AutoscrollToBottomContainer, ButtonInputGroup, CSVDownloadButton, CheckboxButton, CopiableBox, DAY_IN_MS, DBEntryFieldType$1 as DBEntryFieldType, DBEntryManagerPanel, DayOfWeek$1 as DayOfWeek, Drawer, Dropdown, DropdownItemType$1 as DropdownItemType, DynamicWord, ErrorBox, ErrorWithCode, HOUR_IN_MS, IntelliTable, ItemPicker, LoadingSpinner, LogAction$1 as LogAction, LogBuiltInMetadata, LogReviewer, LogSource$1 as LogSource, LogType$1 as LogType, MINUTE_IN_MS, Modal, ModalButtonType$1 as ModalButtonType, ModalSize$1 as ModalSize, ModalType$1 as ModalType, MultiSwitch, ParamType$1 as ParamType, PopFailureMark, PopPendingMark, PopSuccessMark, RadioButton, ReactKitErrorCode$1 as ReactKitErrorCode, SimpleDateChooser, TabBox, ToggleSwitch, Tooltip, Variant$1 as Variant, abbreviate, addDBEditorEndpoints, addFatalErrorHandler, alert, avg, canReviewLogs, capitalize, ceilToNumDecimals, combineClassNames, compareArraysByProp, confirm, everyAsync, extractProp, filterAsync, floorToNumDecimals, forEachAsync, forceNumIntoBounds, genCSV, genCommaList, genRouteHandler, getHumanReadableDate, getLocalTimeInfo, getMonthName, getOrdinal, getPartOfDay, getTimeInfoInET, handleError, handleSuccess, idify, initClient, initLogCollection, initServer, isMobileOrTablet, leaveToURL, logClientEvent, makeLinksClickable, mapAsync, onlyKeepLetters, padDecimalZeros, padZerosLeft, parallelLimit, prefixWithAOrAn, prompt, roundToNumDecimals, setClientEventMetadataPopulator, showFatalError, shuffleArray, someAsync, startMinWait, stringsToHumanReadableList, stubServerEndpoint, sum, useForceRender, validateEmail, validatePhoneNumber, validateString, visitEndpointOnAnotherServer, visitServerEndpoint, waitMs };
15832
16409
  //# sourceMappingURL=index.js.map