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

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 (34) hide show
  1. package/dist/cjs/index.js +201 -29
  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/RadioButton.d.ts +2 -0
  7. package/dist/cjs/types/constants/LOG_REVIEW_PAGE_SIZE.d.ts +6 -0
  8. package/dist/cjs/types/index.d.ts +2 -2
  9. package/dist/cjs/types/types/ModalType.d.ts +1 -0
  10. package/dist/esm/index.js +201 -30
  11. package/dist/esm/index.js.map +1 -1
  12. package/dist/esm/types/client/initClient.d.ts +16 -2
  13. package/dist/esm/types/components/AppWrapper.d.ts +32 -0
  14. package/dist/esm/types/components/CheckboxButton.d.ts +1 -0
  15. package/dist/esm/types/components/RadioButton.d.ts +2 -0
  16. package/dist/esm/types/constants/LOG_REVIEW_PAGE_SIZE.d.ts +6 -0
  17. package/dist/esm/types/index.d.ts +2 -2
  18. package/dist/esm/types/types/ModalType.d.ts +1 -0
  19. package/dist/index.d.ts +47 -3
  20. package/package.json +1 -1
  21. package/src/client/initClient.tsx +49 -11
  22. package/src/components/AppWrapper.tsx +258 -2
  23. package/src/components/CheckboxButton.tsx +24 -6
  24. package/src/components/Modal/index.tsx +3 -0
  25. package/src/components/RadioButton.tsx +33 -6
  26. package/src/components/SimpleDateChooser.tsx +8 -2
  27. package/src/constants/LOG_REVIEW_PAGE_SIZE.ts +7 -0
  28. package/src/helpers/genRouteHandler.ts +7 -7
  29. package/src/helpers/logClientEvent.tsx +8 -0
  30. package/src/helpers/visitServerEndpoint.tsx +1 -0
  31. package/src/index.ts +2 -0
  32. package/src/server/initLogCollection.ts +5 -0
  33. package/src/server/initServer.ts +3 -2
  34. package/src/types/ModalType.tsx +1 -0
package/dist/cjs/index.js CHANGED
@@ -186,6 +186,7 @@ var ModalButtonType$1 = ModalButtonType;
186
186
  var ModalType;
187
187
  (function (ModalType) {
188
188
  ModalType["Okay"] = "okay";
189
+ ModalType["Cancel"] = "cancel";
189
190
  ModalType["OkayCancel"] = "okay-cancel";
190
191
  ModalType["YesNo"] = "yes-no";
191
192
  ModalType["YesNoCancel"] = "yes-no-cancel";
@@ -316,11 +317,23 @@ const isDarkModeOn = () => {
316
317
  * @param opts object containing all arguments
317
318
  * @param opts.sendRequest caccl send request functions
318
319
  * @param [opts.sessionExpiredMessage] a custom session expired message
320
+ * @param [opts.darkModeOn] if true, dark mode is enabled
321
+ * @param [opts.noServer] if true, there is no server for this app
319
322
  */
320
323
  const initClient = (opts) => {
321
- // Store values
322
- storedSendRequest = opts.sendRequest;
323
- sessionExpiredMessage = opts.sessionExpiredMessage;
324
+ // Handle separately if there is no server
325
+ if (opts.noServer) {
326
+ // Store values
327
+ storedSendRequest = () => __awaiter(void 0, void 0, void 0, function* () {
328
+ throw new Error('Cannot send requests because there is no server');
329
+ });
330
+ }
331
+ else {
332
+ // Store values
333
+ storedSendRequest = opts.sendRequest;
334
+ sessionExpiredMessage = opts.sessionExpiredMessage;
335
+ }
336
+ // Handle universal parts
324
337
  darkModeOn = !!opts.darkModeOn;
325
338
  // Mark as initialized
326
339
  onInitialized(null);
@@ -340,6 +353,9 @@ const modalTypeToModalButtonTypes = {
340
353
  [ModalType$1.Okay]: [
341
354
  ModalButtonType$1.Okay,
342
355
  ],
356
+ [ModalType$1.Cancel]: [
357
+ ModalButtonType$1.Cancel,
358
+ ],
343
359
  [ModalType$1.OkayCancel]: [
344
360
  ModalButtonType$1.Okay,
345
361
  ModalButtonType$1.Cancel,
@@ -781,6 +797,7 @@ const _setStubResponse = (opts) => {
781
797
  */
782
798
  const visitServerEndpoint = (opts) => __awaiter(void 0, void 0, void 0, function* () {
783
799
  var _a, _b, _c;
800
+ // Set default method
784
801
  const method = ((_a = opts.method) !== null && _a !== void 0 ? _a : 'GET');
785
802
  // Handle stubs
786
803
  const stubResponse = (_b = stubResponses[method]) === null || _b === void 0 ? void 0 : _b[opts.path];
@@ -1080,6 +1097,92 @@ const confirm = (title, text, opts) => __awaiter(void 0, void 0, void 0, functio
1080
1097
  });
1081
1098
  });
1082
1099
  /*----------------------------------------*/
1100
+ /* --------------- Prompt -------------- */
1101
+ /*----------------------------------------*/
1102
+ // Stored copies of setters
1103
+ let setPromptInfo;
1104
+ // Function to call when prompt is closed
1105
+ let onPromptClosed;
1106
+ /**
1107
+ * Show a prompt modal asking the user for input
1108
+ * @author Yuen Ler Chow
1109
+ * @param title the title text to display at the top of the prompt
1110
+ * @param [opts={}] additional options for the prompt dialog
1111
+ * @param [opts.textAboveInputField] the text to display in the prompt
1112
+ * @param [opts.defaultText] the default text for the input field
1113
+ * @param [opts.placeholder] the placeholder text for the input field
1114
+ * @param [opts.confirmButtonText=Okay] the text of the confirm button
1115
+ * @param [opts.confirmButtonVariant=Variant.Dark] the variant of the confirm button
1116
+ * @param [opts.cancelButtonText=Cancel] the text of the cancel button
1117
+ * @param [opts.cancelButtonVariant=Variant.Secondary] the variant of the cancel button
1118
+ * @param [opts.minNumChars] the minimum number of characters required for
1119
+ * the input to be valid
1120
+ * @param [opts.findValidationError] a function that takes the input text and
1121
+ * returns an error message if the input is invalid, returns undefined if the
1122
+ * input is valid
1123
+ * @param [opts.ariaLabel] the aria label for the input field
1124
+ * @returns Promise that resolves with the input string or null if canceled
1125
+ */
1126
+ const prompt = (title, opts) => __awaiter(void 0, void 0, void 0, function* () {
1127
+ var _a, _b;
1128
+ // Wait for helper to exist
1129
+ yield waitForHelper(() => {
1130
+ return !!setPromptInfo;
1131
+ });
1132
+ // Fallback if prompt is not available
1133
+ if (!setPromptInfo) {
1134
+ const resultPassesValidation = false;
1135
+ while (!resultPassesValidation) {
1136
+ // eslint-disable-next-line no-alert
1137
+ const result = window.prompt(`${title}\n\n${(_a = opts === null || opts === void 0 ? void 0 : opts.textAboveInputField) !== null && _a !== void 0 ? _a : ''}`, (_b = opts === null || opts === void 0 ? void 0 : opts.defaultText) !== null && _b !== void 0 ? _b : '');
1138
+ // Exit loop if user cancels
1139
+ if (result === null) {
1140
+ return null;
1141
+ }
1142
+ // Validate min num chars
1143
+ const minNumCharsValidationError = (((opts === null || opts === void 0 ? void 0 : opts.minNumChars) && result.length < opts.minNumChars)
1144
+ ? `Please enter at least ${opts.minNumChars} characters.`
1145
+ : undefined);
1146
+ // Run custom validation
1147
+ const customValidationError = ((opts === null || opts === void 0 ? void 0 : opts.findValidationError)
1148
+ && opts.findValidationError(result));
1149
+ // Show validation issue
1150
+ if (minNumCharsValidationError || customValidationError) {
1151
+ // Create error message
1152
+ const errorMessage = ([
1153
+ minNumCharsValidationError,
1154
+ customValidationError,
1155
+ ]
1156
+ // Filter out undefined messages
1157
+ .filter((msg) => {
1158
+ return !!msg;
1159
+ })
1160
+ // Join messages with newlines
1161
+ .join('\n'));
1162
+ // Show alert
1163
+ alert('Invalid Input', errorMessage);
1164
+ }
1165
+ else {
1166
+ return result;
1167
+ }
1168
+ }
1169
+ }
1170
+ // Return promise that resolves with result of prompt
1171
+ return new Promise((resolve) => {
1172
+ var _a;
1173
+ // Setup handler
1174
+ onPromptClosed = (result) => {
1175
+ resolve(result);
1176
+ };
1177
+ // Show the prompt
1178
+ setPromptInfo({
1179
+ title,
1180
+ currentInputFieldText: ((_a = opts === null || opts === void 0 ? void 0 : opts.defaultText) !== null && _a !== void 0 ? _a : ''),
1181
+ opts: (opts !== null && opts !== void 0 ? opts : {}),
1182
+ });
1183
+ });
1184
+ });
1185
+ /*----------------------------------------*/
1083
1186
  /* ------------- Fatal Error ------------ */
1084
1187
  /*----------------------------------------*/
1085
1188
  // Stored copies of setters
@@ -1095,14 +1198,14 @@ const fatalErrorHandlers = [];
1095
1198
  * @param [errorTitle] title of the error box
1096
1199
  */
1097
1200
  const showFatalError = (error, errorTitle = 'An Error Occurred') => __awaiter(void 0, void 0, void 0, function* () {
1098
- var _a, _b;
1201
+ var _c, _d;
1099
1202
  // Determine message and code
1100
1203
  const message = (typeof error === 'string'
1101
1204
  ? error.trim()
1102
- : String((_a = error.message) !== null && _a !== void 0 ? _a : 'An unknown error occurred.'));
1205
+ : String((_c = error.message) !== null && _c !== void 0 ? _c : 'An unknown error occurred.'));
1103
1206
  const code = (typeof error === 'string'
1104
1207
  ? ReactKitErrorCode$1.NoCode
1105
- : String((_b = error.code) !== null && _b !== void 0 ? _b : ReactKitErrorCode$1.NoCode));
1208
+ : String((_d = error.code) !== null && _d !== void 0 ? _d : ReactKitErrorCode$1.NoCode));
1106
1209
  // Call all fatal error listeners
1107
1210
  try {
1108
1211
  fatalErrorHandlers.forEach((handler) => {
@@ -1229,6 +1332,9 @@ const AppWrapper = (props) => {
1229
1332
  // Confirm
1230
1333
  const [confirmInfo, setConfirmInfoInner,] = React.useState(undefined);
1231
1334
  setConfirmInfo = setConfirmInfoInner;
1335
+ // Prompt
1336
+ const [promptInfo, setPromptInfoInner] = React.useState(undefined);
1337
+ setPromptInfo = setPromptInfoInner;
1232
1338
  // Session expired
1233
1339
  const [sessionHasExpired, setSessionHasExpiredInner,] = React.useState(false);
1234
1340
  setSessionHasExpired = setSessionHasExpiredInner;
@@ -1259,6 +1365,42 @@ const AppWrapper = (props) => {
1259
1365
  }
1260
1366
  }, onTopOfOtherModals: true, dontAllowBackdropExit: true }, confirmInfo.text));
1261
1367
  }
1368
+ /* ------------- Prompt ------------ */
1369
+ if (promptInfo) {
1370
+ // Run min char validation
1371
+ const minNumCharsValidationError = ((promptInfo.opts.minNumChars
1372
+ && promptInfo.currentInputFieldText.length < promptInfo.opts.minNumChars)
1373
+ ? `Please enter at least ${promptInfo.opts.minNumChars} characters.`
1374
+ : undefined);
1375
+ // Run custom validation
1376
+ const customValidationError = (promptInfo.opts.findValidationError
1377
+ && promptInfo.opts.findValidationError(promptInfo.currentInputFieldText));
1378
+ modal = (React__default["default"].createElement(Modal, { key: `prompt-${promptInfo.title}`, title: promptInfo.title,
1379
+ // Don't show ok button if there is a validation error
1380
+ type: ((customValidationError || minNumCharsValidationError)
1381
+ ? ModalType$1.Cancel
1382
+ : ModalType$1.OkayCancel), okayLabel: promptInfo.opts.confirmButtonText, okayVariant: promptInfo.opts.confirmButtonVariant, cancelLabel: promptInfo.opts.cancelButtonText, cancelVariant: promptInfo.opts.cancelButtonVariant, onClose: (buttonType) => {
1383
+ // Get result
1384
+ const result = (buttonType === ModalButtonType$1.Okay
1385
+ ? promptInfo.currentInputFieldText
1386
+ : null);
1387
+ // Close prompt
1388
+ setPromptInfo(undefined);
1389
+ // Call handler
1390
+ if (onPromptClosed) {
1391
+ onPromptClosed(result);
1392
+ }
1393
+ }, onTopOfOtherModals: true, dontAllowBackdropExit: true },
1394
+ React__default["default"].createElement("div", null,
1395
+ promptInfo.opts.textAboveInputField && (React__default["default"].createElement("div", null, promptInfo.opts.textAboveInputField)),
1396
+ React__default["default"].createElement("input", { type: "text", className: "form-control", "aria-label": promptInfo.opts.ariaLabel, placeholder: promptInfo.opts.placeholder, value: promptInfo.currentInputFieldText, onChange: (e) => {
1397
+ return setPromptInfo(Object.assign(Object.assign({}, promptInfo), { currentInputFieldText: e.target.value }));
1398
+ },
1399
+ // eslint-disable-next-line jsx-a11y/no-autofocus
1400
+ autoFocus: true }),
1401
+ minNumCharsValidationError && (React__default["default"].createElement("div", { className: "text-danger fw-bold mt-2" }, minNumCharsValidationError)),
1402
+ customValidationError && (React__default["default"].createElement("div", { className: "text-danger fw-bold mt-2" }, customValidationError)))));
1403
+ }
1262
1404
  /* ------ Custom Modal Portals ------ */
1263
1405
  // Custom modal portals
1264
1406
  const customModalPortals = [];
@@ -1328,7 +1470,7 @@ const AppWrapper = (props) => {
1328
1470
  ? `
1329
1471
  .tooltip-inner {
1330
1472
  background-color: white;
1331
- color: black;
1473
+ color: black !important;
1332
1474
  border: 0.1rem solid black;
1333
1475
  pointer-events: none;
1334
1476
  }
@@ -1341,7 +1483,7 @@ const AppWrapper = (props) => {
1341
1483
  : `
1342
1484
  .tooltip-inner {
1343
1485
  background-color: black;
1344
- color: white;
1486
+ color: white !important;
1345
1487
  border: 0.1rem solid white;
1346
1488
  pointer-events: none;
1347
1489
  }
@@ -1560,24 +1702,31 @@ const RadioButton = (props) => {
1560
1702
  /* -------------------------------- Setup ------------------------------- */
1561
1703
  /*------------------------------------------------------------------------*/
1562
1704
  /* -------------- Props ------------- */
1563
- const { text, onSelected, ariaLabel, title, selected, id, noMarginOnRight, selectedVariant = (isDarkModeOn()
1705
+ const { text, onSelected, ariaLabel, title, selected, id, className, noMarginOnRight, selectedVariant = (isDarkModeOn()
1564
1706
  ? Variant$1.Light
1565
1707
  : Variant$1.Secondary), unselectedVariant = (isDarkModeOn()
1566
1708
  ? Variant$1.Secondary
1567
- : Variant$1.Light), small, } = props;
1709
+ : Variant$1.Light), small, useComplexFormatting, } = props;
1568
1710
  /*------------------------------------------------------------------------*/
1569
1711
  /* ------------------------------- Render ------------------------------- */
1570
1712
  /*------------------------------------------------------------------------*/
1571
1713
  /*----------------------------------------*/
1572
1714
  /* --------------- Main UI -------------- */
1573
1715
  /*----------------------------------------*/
1574
- return (React__default["default"].createElement("button", { type: "button", id: id, title: title, className: `btn btn-${selected ? selectedVariant : unselectedVariant}${selected ? ' selected' : ''}${small ? ' btn-sm' : ''} m-0${noMarginOnRight ? '' : ' me-1'}`, "aria-label": `${ariaLabel}${selected ? ': currently selected' : ''}`, onClick: () => {
1716
+ return (React__default["default"].createElement("button", { type: "button", id: id, title: title, className: `btn btn-${selected ? selectedVariant : unselectedVariant}${selected ? ' selected' : ''}${small ? ' btn-sm' : ''} m-0${noMarginOnRight ? '' : ' me-1'} ${className !== null && className !== void 0 ? className : ''}`, "aria-label": `${ariaLabel}${selected ? ': currently selected' : ''}`, onClick: () => {
1575
1717
  if (!selected) {
1576
1718
  onSelected();
1577
1719
  }
1578
1720
  } },
1579
- React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: selected ? freeSolidSvgIcons.faDotCircle : freeRegularSvgIcons.faCircle, className: "me-1" }),
1580
- text));
1721
+ React__default["default"].createElement("div", { className: "d-flex flex-row align-items-center" },
1722
+ React__default["default"].createElement("div", { className: "me-1" },
1723
+ React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: selected ? freeSolidSvgIcons.faDotCircle : freeRegularSvgIcons.faCircle })),
1724
+ useComplexFormatting
1725
+ ? (React__default["default"].createElement("pre", { className: "ps-1 text-start text-break", style: {
1726
+ whiteSpace: 'pre-wrap',
1727
+ tabSize: 2,
1728
+ } }, text))
1729
+ : (React__default["default"].createElement("div", { className: "flex-grow-1 text-start text-break" }, text)))));
1581
1730
  };
1582
1731
 
1583
1732
  /**
@@ -1596,7 +1745,7 @@ const CheckboxButton = (props) => {
1596
1745
  ? Variant$1.Light
1597
1746
  : Variant$1.Secondary), uncheckedVariant = (isDarkModeOn()
1598
1747
  ? Variant$1.Secondary
1599
- : Variant$1.Light), small, dashed, } = props;
1748
+ : Variant$1.Light), small, dashed, useComplexFormatting, } = props;
1600
1749
  /*------------------------------------------------------------------------*/
1601
1750
  /* ------------------------------- Render ------------------------------- */
1602
1751
  /*------------------------------------------------------------------------*/
@@ -1617,10 +1766,15 @@ const CheckboxButton = (props) => {
1617
1766
  return (React__default["default"].createElement("button", { type: "button", id: id, title: title, className: `CheckboxButton-status-${checked ? 'checked' : 'unchecked'} ${dashed ? 'CheckboxButton-dashed ' : ''}btn btn-${checked ? checkedVariant : uncheckedVariant}${checked ? ' selected' : ''}${small ? ' btn-sm' : ''} m-0${noMarginOnRight ? '' : ' me-1'} ${className !== null && className !== void 0 ? className : ''}`, "aria-label": `${ariaLabel}${checked ? ': currently checked' : ''}`, onClick: () => {
1618
1767
  onChanged(!checked);
1619
1768
  } },
1620
- React__default["default"].createElement("div", { className: "d-flex" },
1621
- React__default["default"].createElement("div", { className: "align-items-center" },
1622
- React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: icon, className: "me-1" })),
1623
- React__default["default"].createElement("div", { className: "flex-grow-1" }, text))));
1769
+ React__default["default"].createElement("div", { className: "d-flex align-items-center" },
1770
+ React__default["default"].createElement("div", { className: "me-1" },
1771
+ React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: icon })),
1772
+ useComplexFormatting
1773
+ ? (React__default["default"].createElement("pre", { className: "ps-1 text-start text-break", style: {
1774
+ whiteSpace: 'pre-wrap',
1775
+ tabSize: 2,
1776
+ } }, text))
1777
+ : (React__default["default"].createElement("div", { className: "flex-grow-1 text-start text-break" }, text)))));
1624
1778
  };
1625
1779
 
1626
1780
  /**
@@ -1801,7 +1955,13 @@ const SimpleDateChooser = (props) => {
1801
1955
  }
1802
1956
  const monthName = getMonthName(month).full;
1803
1957
  // Year is start year +1 for each 12 months
1804
- const year = (startYear + (Math.floor(unmoddedMonth / 12)));
1958
+ let yearsToAdd = 0;
1959
+ let monthsOfYearsToAdd = unmoddedMonth;
1960
+ while (monthsOfYearsToAdd > 12) {
1961
+ monthsOfYearsToAdd -= 12;
1962
+ yearsToAdd += 1;
1963
+ }
1964
+ const year = startYear + yearsToAdd;
1805
1965
  // Figure out which days are allowed
1806
1966
  const days = [];
1807
1967
  const numDaysInMonth = (new Date(year, month, 0)).getDate();
@@ -1852,7 +2012,7 @@ const SimpleDateChooser = (props) => {
1852
2012
  });
1853
2013
  }
1854
2014
  });
1855
- return (React__default["default"].createElement("div", { className: "SimpleDateChooser d-inline-block", "aria-label": `date chooser with selected date: ${month} ${day}, ${year}` },
2015
+ return (React__default["default"].createElement("div", { className: "SimpleDateChooser d-inline-block", "aria-label": `date chooser with selected date: ${month}/${day}/${year}` },
1856
2016
  React__default["default"].createElement("select", { "aria-label": `month for ${ariaLabel}`, className: "custom-select d-inline-block mr-1", style: { width: 'auto' }, id: `SimpleDateChooser-${name}-month`, value: `${month}-${year}`, onChange: (e) => {
1857
2017
  const choice = choices[e.target.selectedIndex];
1858
2018
  // Change day, month, and year
@@ -13688,6 +13848,12 @@ const padZerosLeft = (num, numDigits) => {
13688
13848
  */
13689
13849
  const LOG_REVIEW_STATUS_ROUTE = `${ROUTE_PATH_PREFIX}/logs/access_allowed`;
13690
13850
 
13851
+ /**
13852
+ * Log reviewer page size
13853
+ * @author Yuen Ler Chow
13854
+ */
13855
+ const LOG_REVIEW_PAGE_SIZE = 100;
13856
+
13691
13857
  // Stored copy of caccl functions
13692
13858
  let _cacclGetLaunchInfo;
13693
13859
  // Stored copy of dce-mango log collection
@@ -14036,12 +14202,12 @@ const initServer = (opts) => {
14036
14202
  // Query for logs
14037
14203
  const response = yield _logCollection.findPaged({
14038
14204
  query,
14039
- perPage: 50,
14205
+ perPage: LOG_REVIEW_PAGE_SIZE,
14040
14206
  pageNumber,
14041
14207
  });
14042
14208
  // Count documents if requested
14043
14209
  if (countDocuments) {
14044
- response.numPages = Math.ceil((yield _logCollection.count(query)) / 50);
14210
+ response.numPages = Math.ceil((yield _logCollection.count(query)) / LOG_REVIEW_PAGE_SIZE);
14045
14211
  }
14046
14212
  // Return response
14047
14213
  return response;
@@ -14859,16 +15025,16 @@ const genRouteHandler = (opts) => {
14859
15025
  const { timestamp, year, month, day, hour, minute, } = getTimeInfoInET();
14860
15026
  // Main log info
14861
15027
  const mainLogInfo = {
14862
- id: `${launchInfo ? 'unknown' : launchInfo.userId}-${Date.now()}-${Math.floor(Math.random() * 100000)}-${Math.floor(Math.random() * 100000)}`,
14863
- userFirstName: (launchInfo ? 'unknown' : launchInfo.userFirstName),
14864
- userLastName: (launchInfo ? 'unknown' : launchInfo.userLastName),
14865
- userEmail: (launchInfo ? 'unknown' : launchInfo.userEmail),
14866
- userId: (launchInfo ? 'unknown' : launchInfo.userId),
15028
+ id: `${launchInfo ? launchInfo.userId : 'unknown'}-${Date.now()}-${Math.floor(Math.random() * 100000)}-${Math.floor(Math.random() * 100000)}`,
15029
+ userFirstName: (launchInfo ? launchInfo.userFirstName : 'unknown'),
15030
+ userLastName: (launchInfo ? launchInfo.userLastName : 'unknown'),
15031
+ userEmail: (launchInfo ? launchInfo.userEmail : 'unknown'),
15032
+ userId: (launchInfo ? launchInfo.userId : 'unknown'),
14867
15033
  isLearner: (launchInfo && !!launchInfo.isLearner),
14868
15034
  isAdmin: (launchInfo && !!launchInfo.isAdmin),
14869
15035
  isTTM: (launchInfo && !!launchInfo.isTTM),
14870
- courseId: (launchInfo ? 'unknown' : launchInfo.courseId),
14871
- courseName: (launchInfo ? 'unknown' : launchInfo.contextLabel),
15036
+ courseId: (launchInfo ? launchInfo.courseId : 'unknown'),
15037
+ courseName: (launchInfo ? launchInfo.contextLabel : 'unknown'),
14872
15038
  browser,
14873
15039
  device,
14874
15040
  year,
@@ -15275,6 +15441,11 @@ const initLogCollection = (Collection) => {
15275
15441
  'context',
15276
15442
  'subcontext',
15277
15443
  'tags',
15444
+ 'year',
15445
+ 'month',
15446
+ 'day',
15447
+ 'hour',
15448
+ 'type',
15278
15449
  ],
15279
15450
  });
15280
15451
  };
@@ -16296,6 +16467,7 @@ exports.padDecimalZeros = padDecimalZeros;
16296
16467
  exports.padZerosLeft = padZerosLeft;
16297
16468
  exports.parallelLimit = parallelLimit;
16298
16469
  exports.prefixWithAOrAn = prefixWithAOrAn;
16470
+ exports.prompt = prompt;
16299
16471
  exports.roundToNumDecimals = roundToNumDecimals;
16300
16472
  exports.setClientEventMetadataPopulator = setClientEventMetadataPopulator;
16301
16473
  exports.showFatalError = showFatalError;