dce-reactkit 3.2.2-beta.8 → 3.2.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.
- package/dist/cjs/index.js +743 -440
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/types/components/Drawer.d.ts +1 -0
- package/dist/cjs/types/components/IntelliTable.d.ts +1 -0
- package/dist/cjs/types/components/ItemPicker/NestableItemList.d.ts +1 -0
- package/dist/cjs/types/components/ItemPicker/index.d.ts +1 -0
- package/dist/cjs/types/dynamicConstants/DynamicWord.d.ts +13 -0
- package/dist/cjs/types/index.d.ts +2 -1
- package/dist/cjs/types/types/LogAction.d.ts +16 -16
- package/dist/cjs/types/types/LogBuiltInMetadata.d.ts +1 -1
- package/dist/cjs/types/types/LogMetadataContextMap.d.ts +11 -0
- package/dist/cjs/types/types/LogMetadataTagMap.d.ts +8 -0
- package/dist/cjs/types/types/LogMetadataTargetMap.d.ts +8 -0
- package/dist/cjs/types/types/LogMetadataType.d.ts +6 -12
- package/dist/esm/index.js +744 -442
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/types/components/Drawer.d.ts +1 -0
- package/dist/esm/types/components/IntelliTable.d.ts +1 -0
- package/dist/esm/types/components/ItemPicker/NestableItemList.d.ts +1 -0
- package/dist/esm/types/components/ItemPicker/index.d.ts +1 -0
- package/dist/esm/types/dynamicConstants/DynamicWord.d.ts +13 -0
- package/dist/esm/types/index.d.ts +2 -1
- package/dist/esm/types/types/LogAction.d.ts +16 -16
- package/dist/esm/types/types/LogBuiltInMetadata.d.ts +1 -1
- package/dist/esm/types/types/LogMetadataContextMap.d.ts +11 -0
- package/dist/esm/types/types/LogMetadataTagMap.d.ts +8 -0
- package/dist/esm/types/types/LogMetadataTargetMap.d.ts +8 -0
- package/dist/esm/types/types/LogMetadataType.d.ts +6 -12
- package/dist/index.d.ts +64 -30
- package/package.json +1 -1
- package/src/components/ButtonInputGroup.tsx +1 -1
- package/src/components/CSVDownloadButton.tsx +2 -2
- package/src/components/Drawer.tsx +9 -1
- package/src/components/IntelliTable.tsx +178 -23
- package/src/components/ItemPicker/NestableItemList.tsx +32 -14
- package/src/components/ItemPicker/index.tsx +4 -0
- package/src/components/LogReviewer.tsx +588 -418
- package/src/components/SimpleDateChooser.tsx +30 -25
- package/src/dynamicConstants/DynamicWord.tsx +55 -0
- package/src/helpers/genCSV.ts +22 -4
- package/src/helpers/genRouteHandler.ts +2 -2
- package/src/helpers/logClientEvent.tsx +1 -1
- package/src/index.ts +5 -0
- package/src/server/initServer.ts +10 -3
- package/src/types/LogAction.ts +16 -16
- package/src/types/LogBuiltInMetadata.ts +5 -5
- package/src/types/LogMetadataContextMap.ts +15 -0
- package/src/types/LogMetadataTagMap.ts +9 -0
- package/src/types/LogMetadataTargetMap.ts +9 -0
- package/src/types/LogMetadataType.ts +8 -15
package/dist/cjs/index.js
CHANGED
|
@@ -519,14 +519,14 @@ const LOG_ROUTE_PATH = `${ROUTE_PATH_PREFIX}/log`;
|
|
|
519
519
|
const LogBuiltInMetadata = {
|
|
520
520
|
// Contexts
|
|
521
521
|
Context: {
|
|
522
|
-
Uncategorized: '
|
|
523
|
-
ServerRenderedErrorPage: '
|
|
524
|
-
ServerEndpointError: '
|
|
525
|
-
ClientFatalError: '
|
|
522
|
+
Uncategorized: 'Uncategorized',
|
|
523
|
+
ServerRenderedErrorPage: 'ServerRenderedErrorPage',
|
|
524
|
+
ServerEndpointError: 'ServerEndpointError',
|
|
525
|
+
ClientFatalError: 'ClientFatalError',
|
|
526
526
|
},
|
|
527
527
|
// Targets
|
|
528
528
|
Target: {
|
|
529
|
-
|
|
529
|
+
NoTarget: 'NoTarget',
|
|
530
530
|
},
|
|
531
531
|
};
|
|
532
532
|
|
|
@@ -682,7 +682,7 @@ const logClientEvent = (opts) => __awaiter(void 0, void 0, void 0, function* ()
|
|
|
682
682
|
? opts.error.stack
|
|
683
683
|
: undefined),
|
|
684
684
|
target: (opts.action
|
|
685
|
-
? ((_g = opts.target) !== null && _g !== void 0 ? _g : LogBuiltInMetadata.Target.
|
|
685
|
+
? ((_g = opts.target) !== null && _g !== void 0 ? _g : LogBuiltInMetadata.Target.NoTarget)
|
|
686
686
|
: undefined),
|
|
687
687
|
action: (opts.action
|
|
688
688
|
? opts.action
|
|
@@ -1202,7 +1202,7 @@ const ButtonInputGroup = (props) => {
|
|
|
1202
1202
|
borderTopRightRadius: 0,
|
|
1203
1203
|
borderBottomRightRadius: 0,
|
|
1204
1204
|
} }, label),
|
|
1205
|
-
React__default["default"].createElement("span", { className: "input-group-text flex-grow-1 rounded-right", style: {
|
|
1205
|
+
React__default["default"].createElement("span", { className: "input-group-text flex-grow-1 rounded-right d-flex flex-wrap", style: {
|
|
1206
1206
|
backgroundColor: 'white',
|
|
1207
1207
|
borderTopLeftRadius: 0,
|
|
1208
1208
|
borderBottomLeftRadius: 0,
|
|
@@ -1307,18 +1307,6 @@ const getTimeInfoInET = (dateOrTimestamp) => {
|
|
|
1307
1307
|
};
|
|
1308
1308
|
};
|
|
1309
1309
|
|
|
1310
|
-
/**
|
|
1311
|
-
* Force a number to stay within specific bounds
|
|
1312
|
-
* @author Gabe Abrams
|
|
1313
|
-
* @param num the number to move into the bounds
|
|
1314
|
-
* @param min the minimum number in the bound
|
|
1315
|
-
* @param max the maximum number in the bound
|
|
1316
|
-
* @returns bounded number
|
|
1317
|
-
*/
|
|
1318
|
-
const forceNumIntoBounds = (num, min, max) => {
|
|
1319
|
-
return Math.max(min, Math.min(max, num));
|
|
1320
|
-
};
|
|
1321
|
-
|
|
1322
1310
|
/**
|
|
1323
1311
|
* A very simple, lightweight date chooser
|
|
1324
1312
|
* @author Gabe Abrams
|
|
@@ -1333,7 +1321,7 @@ const SimpleDateChooser = (props) => {
|
|
|
1333
1321
|
var _a;
|
|
1334
1322
|
/* -------------- Props ------------- */
|
|
1335
1323
|
const { ariaLabel, name, month, day, year, onChange, chooseFromPast, } = props;
|
|
1336
|
-
|
|
1324
|
+
const numMonthsToShow = ((_a = props.numMonthsToShow) !== null && _a !== void 0 ? _a : 6);
|
|
1337
1325
|
/*------------------------------------------------------------------------*/
|
|
1338
1326
|
/* Render */
|
|
1339
1327
|
/*------------------------------------------------------------------------*/
|
|
@@ -1347,7 +1335,7 @@ const SimpleDateChooser = (props) => {
|
|
|
1347
1335
|
let startMonth = today.month;
|
|
1348
1336
|
if (chooseFromPast) {
|
|
1349
1337
|
startMonth -= Math.max(0, numMonthsToShow - 1);
|
|
1350
|
-
|
|
1338
|
+
while (startMonth <= 0) {
|
|
1351
1339
|
startMonth += 12;
|
|
1352
1340
|
startYear -= 1;
|
|
1353
1341
|
}
|
|
@@ -1355,22 +1343,35 @@ const SimpleDateChooser = (props) => {
|
|
|
1355
1343
|
for (let i = 0; i < numMonthsToShow; i++) {
|
|
1356
1344
|
// Get month and year info
|
|
1357
1345
|
const unmoddedMonth = (startMonth + i);
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1346
|
+
let month = unmoddedMonth;
|
|
1347
|
+
while (month > 12) {
|
|
1348
|
+
month -= 12;
|
|
1349
|
+
}
|
|
1361
1350
|
const monthName = getMonthName(month).full;
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
: startYear);
|
|
1351
|
+
// Year is start year +1 for each 12 months
|
|
1352
|
+
const year = (startYear + (Math.floor(unmoddedMonth / 12)));
|
|
1365
1353
|
// Figure out which days are allowed
|
|
1366
1354
|
const days = [];
|
|
1367
1355
|
const numDaysInMonth = (new Date(year, month, 0)).getDate();
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1356
|
+
if (chooseFromPast) {
|
|
1357
|
+
// Past selection
|
|
1358
|
+
const numDaysToAdd = ((month === today.month)
|
|
1359
|
+
? today.day // Current month, only add up to today
|
|
1360
|
+
: numDaysInMonth // Past month, add all days
|
|
1361
|
+
);
|
|
1362
|
+
for (let day = 1; day <= numDaysToAdd; day++) {
|
|
1363
|
+
days.push(day);
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
else {
|
|
1367
|
+
// Future selection: add all remaining days of the month
|
|
1368
|
+
const firstDay = (month === today.month
|
|
1369
|
+
? today.day // Current month: start at current date
|
|
1370
|
+
: 1 // Future month: start at beginning of month
|
|
1371
|
+
);
|
|
1372
|
+
for (let day = firstDay; day <= numDaysInMonth; day++) {
|
|
1373
|
+
days.push(day);
|
|
1374
|
+
}
|
|
1374
1375
|
}
|
|
1375
1376
|
choices.push({
|
|
1376
1377
|
choiceName: `${monthName} ${year}`,
|
|
@@ -1384,7 +1385,7 @@ const SimpleDateChooser = (props) => {
|
|
|
1384
1385
|
const dayOptions = [];
|
|
1385
1386
|
choices.forEach((choice) => {
|
|
1386
1387
|
// Create month option
|
|
1387
|
-
monthOptions.push(React__default["default"].createElement("option", { key: choice.month, value: choice.month
|
|
1388
|
+
monthOptions.push(React__default["default"].createElement("option", { key: choice.month, value: `${choice.month}-${choice.year}`, "aria-label": `choose ${choice.choiceName}`, onSelect: () => {
|
|
1388
1389
|
onChange(choice.month, choice.days[0], choice.year);
|
|
1389
1390
|
} }, choice.choiceName));
|
|
1390
1391
|
if (month === choice.month) {
|
|
@@ -1399,7 +1400,7 @@ const SimpleDateChooser = (props) => {
|
|
|
1399
1400
|
}
|
|
1400
1401
|
});
|
|
1401
1402
|
return (React__default["default"].createElement("div", { className: "SimpleDateChooser d-inline-block", "aria-label": `date chooser with selected date: ${month} ${day}, ${year}` },
|
|
1402
|
-
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
|
|
1403
|
+
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) => {
|
|
1403
1404
|
const choice = choices[e.target.selectedIndex];
|
|
1404
1405
|
// Change day, month, and year
|
|
1405
1406
|
onChange(choice.month, choice.days[0], choice.year);
|
|
@@ -1441,14 +1442,16 @@ const Drawer = (props) => {
|
|
|
1441
1442
|
/*------------------------------------------------------------------------*/
|
|
1442
1443
|
/* -------------- Props ------------- */
|
|
1443
1444
|
// Destructure all props
|
|
1444
|
-
const { children, } = props;
|
|
1445
|
+
const { customBackgroundColor, children, } = props;
|
|
1445
1446
|
/*------------------------------------------------------------------------*/
|
|
1446
1447
|
/* Render */
|
|
1447
1448
|
/*------------------------------------------------------------------------*/
|
|
1448
1449
|
/*----------------------------------------*/
|
|
1449
1450
|
/* Main UI */
|
|
1450
1451
|
/*----------------------------------------*/
|
|
1451
|
-
return (React__default["default"].createElement("div", { className: "Drawer-container"
|
|
1452
|
+
return (React__default["default"].createElement("div", { className: "Drawer-container", style: {
|
|
1453
|
+
backgroundColor: (customBackgroundColor !== null && customBackgroundColor !== void 0 ? customBackgroundColor : undefined),
|
|
1454
|
+
} },
|
|
1452
1455
|
React__default["default"].createElement("style", null, style$4),
|
|
1453
1456
|
children));
|
|
1454
1457
|
};
|
|
@@ -1911,24 +1914,25 @@ const CopiableBox = (props) => {
|
|
|
1911
1914
|
/**
|
|
1912
1915
|
* Reusable nested item picker
|
|
1913
1916
|
* @author Yuen Ler Chow
|
|
1917
|
+
* @author Gabe Abrams
|
|
1914
1918
|
*/
|
|
1915
1919
|
/* ------------- Actions ------------ */
|
|
1916
1920
|
// Types of actions
|
|
1917
1921
|
var ActionType$2;
|
|
1918
1922
|
(function (ActionType) {
|
|
1919
|
-
// Toggle whether
|
|
1920
|
-
ActionType["
|
|
1923
|
+
// Toggle whether a child are being shown
|
|
1924
|
+
ActionType["ToggleChild"] = "toggle-child";
|
|
1921
1925
|
})(ActionType$2 || (ActionType$2 = {}));
|
|
1922
1926
|
/**
|
|
1923
1927
|
* Reducer that executes actions
|
|
1924
|
-
* @author
|
|
1928
|
+
* @author Gabe Abrams
|
|
1925
1929
|
* @param state current state
|
|
1926
1930
|
* @param action action to execute
|
|
1927
1931
|
*/
|
|
1928
1932
|
const reducer$2 = (state, action) => {
|
|
1929
1933
|
switch (action.type) {
|
|
1930
|
-
case ActionType$2.
|
|
1931
|
-
return {
|
|
1934
|
+
case ActionType$2.ToggleChild: {
|
|
1935
|
+
return Object.assign(Object.assign({}, state), { childExpanded: Object.assign(Object.assign({}, state.childExpanded), { [String(action.id)]: !state.childExpanded[String(action.id)] }) });
|
|
1932
1936
|
}
|
|
1933
1937
|
default: {
|
|
1934
1938
|
return state;
|
|
@@ -1946,14 +1950,19 @@ const NestableItemList = (props) => {
|
|
|
1946
1950
|
// Destructure all props
|
|
1947
1951
|
const { items, onChanged, } = props;
|
|
1948
1952
|
/* -------------- State ------------- */
|
|
1953
|
+
// Create initial map of child expanded booleans
|
|
1954
|
+
const initChildExpanded = {};
|
|
1955
|
+
items.forEach((item) => {
|
|
1956
|
+
initChildExpanded[String(item.id)] = false;
|
|
1957
|
+
});
|
|
1949
1958
|
// Initial state
|
|
1950
1959
|
const initialState = {
|
|
1951
|
-
|
|
1960
|
+
childExpanded: initChildExpanded,
|
|
1952
1961
|
};
|
|
1953
1962
|
// Initialize state
|
|
1954
1963
|
const [state, dispatch] = React.useReducer(reducer$2, initialState);
|
|
1955
1964
|
// Destructure common state
|
|
1956
|
-
const {
|
|
1965
|
+
const { childExpanded, } = state;
|
|
1957
1966
|
/*------------------------------------------------------------------------*/
|
|
1958
1967
|
/* Component Functions */
|
|
1959
1968
|
/*------------------------------------------------------------------------*/
|
|
@@ -2041,14 +2050,15 @@ const NestableItemList = (props) => {
|
|
|
2041
2050
|
backgroundColor: 'transparent',
|
|
2042
2051
|
}, type: "button", onClick: () => {
|
|
2043
2052
|
dispatch({
|
|
2044
|
-
type: ActionType$2.
|
|
2053
|
+
type: ActionType$2.ToggleChild,
|
|
2054
|
+
id: item.id,
|
|
2045
2055
|
});
|
|
2046
|
-
}, "aria-label": `${
|
|
2047
|
-
React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon:
|
|
2056
|
+
}, "aria-label": `${childExpanded[item.id] ? 'Hide' : 'Show'} items in ${item.name}` },
|
|
2057
|
+
React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: childExpanded[item.id] ? freeSolidSvgIcons.faChevronDown : freeSolidSvgIcons.faChevronRight })))),
|
|
2048
2058
|
React__default["default"].createElement(CheckboxButton, { className: `NestableItemList-CheckboxButton-${item.id}`, text: item.name, checked: item.isGroup ? allChecked(item.children) : item.checked, dashed: item.isGroup ? !noneChecked(item.children) : false, onChanged: (checked) => {
|
|
2049
2059
|
onChanged(changeChecked(item.id, checked, items));
|
|
2050
2060
|
}, ariaLabel: `Select ${item.name}`, checkedVariant: Variant$1.Light }),
|
|
2051
|
-
item.isGroup &&
|
|
2061
|
+
(item.isGroup && childExpanded[item.id]) && (React__default["default"].createElement("div", { className: "NestableItemList-children-container", style: {
|
|
2052
2062
|
paddingLeft: '2.2rem',
|
|
2053
2063
|
} },
|
|
2054
2064
|
React__default["default"].createElement(NestableItemList, { items: item.children, onChanged: (updatedItems) => {
|
|
@@ -2070,7 +2080,7 @@ const ItemPicker = (props) => {
|
|
|
2070
2080
|
/*------------------------------------------------------------------------*/
|
|
2071
2081
|
/* -------------- Props ------------- */
|
|
2072
2082
|
// Destructure all props
|
|
2073
|
-
const { title, items, onChanged, } = props;
|
|
2083
|
+
const { title, items, onChanged, noBottomMargin, } = props;
|
|
2074
2084
|
/*------------------------------------------------------------------------*/
|
|
2075
2085
|
/* Component Functions */
|
|
2076
2086
|
/*------------------------------------------------------------------------*/
|
|
@@ -2080,11 +2090,34 @@ const ItemPicker = (props) => {
|
|
|
2080
2090
|
/*----------------------------------------*/
|
|
2081
2091
|
/* Main UI */
|
|
2082
2092
|
/*----------------------------------------*/
|
|
2083
|
-
return (React__default["default"].createElement(TabBox, { title: title },
|
|
2093
|
+
return (React__default["default"].createElement(TabBox, { title: title, noBottomMargin: noBottomMargin },
|
|
2084
2094
|
React__default["default"].createElement("div", { style: { overflowX: 'auto' } },
|
|
2085
2095
|
React__default["default"].createElement(NestableItemList, { items: items, onChanged: onChanged }))));
|
|
2086
2096
|
};
|
|
2087
2097
|
|
|
2098
|
+
// Import shared helpers
|
|
2099
|
+
/**
|
|
2100
|
+
* Get a human-readable description of a date (all in ET)
|
|
2101
|
+
* @author Gabe Abrams
|
|
2102
|
+
* @param [dateOrTimestamp=today] the date or timestamp for the date to describe
|
|
2103
|
+
* @returns human-readable description of the date
|
|
2104
|
+
*/
|
|
2105
|
+
const getHumanReadableDate = (dateOrTimestamp) => {
|
|
2106
|
+
// Get the date info
|
|
2107
|
+
const { month, day, year, } = getTimeInfoInET(dateOrTimestamp);
|
|
2108
|
+
const currYear = getTimeInfoInET().year;
|
|
2109
|
+
// Get the short month description
|
|
2110
|
+
const monthName = getMonthName(month).short;
|
|
2111
|
+
// Create start of description
|
|
2112
|
+
let description = `${monthName} ${day}${getOrdinal(day)}`;
|
|
2113
|
+
// Add on year if it's different
|
|
2114
|
+
if (year !== currYear) {
|
|
2115
|
+
description += ` ${year}`;
|
|
2116
|
+
}
|
|
2117
|
+
// Return description
|
|
2118
|
+
return description;
|
|
2119
|
+
};
|
|
2120
|
+
|
|
2088
2121
|
/**
|
|
2089
2122
|
* Path of the route for storing client-side logs
|
|
2090
2123
|
* @author Gabe Abrams
|
|
@@ -2124,37 +2157,37 @@ var LogType$1 = LogType;
|
|
|
2124
2157
|
var LogAction;
|
|
2125
2158
|
(function (LogAction) {
|
|
2126
2159
|
// Target was opened by the user (it was not on screen, but now it is)
|
|
2127
|
-
LogAction["Open"] = "
|
|
2160
|
+
LogAction["Open"] = "Open";
|
|
2128
2161
|
// Target was closed by the user (it was on screen, but now it is not)
|
|
2129
|
-
LogAction["Close"] = "
|
|
2162
|
+
LogAction["Close"] = "Close";
|
|
2130
2163
|
// Target was cancelled by the user (it was on closed without saving)
|
|
2131
|
-
LogAction["Cancel"] = "
|
|
2164
|
+
LogAction["Cancel"] = "Cancel";
|
|
2132
2165
|
// Target was expanded by the user (it always remains on screen, but size was changed)
|
|
2133
|
-
LogAction["Expand"] = "
|
|
2166
|
+
LogAction["Expand"] = "Expand";
|
|
2134
2167
|
// Target was collapsed by the user (it always remains on screen, but size was changed)
|
|
2135
|
-
LogAction["Collapse"] = "
|
|
2168
|
+
LogAction["Collapse"] = "Collapse";
|
|
2136
2169
|
// Target was viewed by the user (only for items that are not opened or closed, those must use Open/Close actions)
|
|
2137
|
-
LogAction["View"] = "
|
|
2170
|
+
LogAction["View"] = "View";
|
|
2138
2171
|
// Target interrupted the user (popup, dialog, validation message, etc. appeared without user prompting)
|
|
2139
|
-
LogAction["Interrupt"] = "
|
|
2172
|
+
LogAction["Interrupt"] = "Interrupt";
|
|
2140
2173
|
// Target was created by the user (it did not exist before)
|
|
2141
|
-
LogAction["Create"] = "
|
|
2174
|
+
LogAction["Create"] = "Create";
|
|
2142
2175
|
// Target was edited by the user (it existed and was changed)
|
|
2143
|
-
LogAction["Edit"] = "
|
|
2176
|
+
LogAction["Edit"] = "Edit";
|
|
2144
2177
|
// Target was deleted by the user (it existed and now it doesn't)
|
|
2145
|
-
LogAction["Delete"] = "
|
|
2178
|
+
LogAction["Delete"] = "Delete";
|
|
2146
2179
|
// Target was added by the user (it already existed and was added to another place)
|
|
2147
|
-
LogAction["Add"] = "
|
|
2180
|
+
LogAction["Add"] = "Add";
|
|
2148
2181
|
// Target was removed by the user (it was removed from something but still exists)
|
|
2149
|
-
LogAction["Remove"] = "
|
|
2182
|
+
LogAction["Remove"] = "Remove";
|
|
2150
2183
|
// Target was activated by the user (click, check, tap, keypress, etc.)
|
|
2151
|
-
LogAction["Activate"] = "
|
|
2184
|
+
LogAction["Activate"] = "Activate";
|
|
2152
2185
|
// Target was deactivated by the user (click away, uncheck, tap outside of, tab away, etc.)
|
|
2153
|
-
LogAction["Deactivate"] = "
|
|
2186
|
+
LogAction["Deactivate"] = "Deactivate";
|
|
2154
2187
|
// User showed interest in a target (hover, peek, etc.)
|
|
2155
|
-
LogAction["Peek"] = "
|
|
2188
|
+
LogAction["Peek"] = "Peek";
|
|
2156
2189
|
// Unknown action
|
|
2157
|
-
LogAction["Unknown"] = "
|
|
2190
|
+
LogAction["Unknown"] = "Unknown";
|
|
2158
2191
|
})(LogAction || (LogAction = {}));
|
|
2159
2192
|
var LogAction$1 = LogAction;
|
|
2160
2193
|
|
|
@@ -2196,12 +2229,12 @@ const roundToNumDecimals = (num, numDecimals) => {
|
|
|
2196
2229
|
* @returns escaped cell text
|
|
2197
2230
|
*/
|
|
2198
2231
|
const escapeCellText = (text) => {
|
|
2199
|
-
if (!text.includes(',')) {
|
|
2232
|
+
if (!String(text).includes(',')) {
|
|
2200
2233
|
// No need to escape
|
|
2201
|
-
return text;
|
|
2234
|
+
return String(text);
|
|
2202
2235
|
}
|
|
2203
2236
|
// Perform escape
|
|
2204
|
-
return `"${text.replace(/"/g, '""')}`;
|
|
2237
|
+
return `"${String(text).replace(/"/g, '""')}`;
|
|
2205
2238
|
};
|
|
2206
2239
|
/**
|
|
2207
2240
|
* Generate a CSV file
|
|
@@ -2220,9 +2253,26 @@ const genCSV = (data, columns) => {
|
|
|
2220
2253
|
.join(','));
|
|
2221
2254
|
// Add each row
|
|
2222
2255
|
data.forEach((datum) => {
|
|
2256
|
+
csv += '\n';
|
|
2223
2257
|
csv += (columns
|
|
2224
2258
|
.map((column) => {
|
|
2225
|
-
|
|
2259
|
+
let contents;
|
|
2260
|
+
const cell = datum[column.param];
|
|
2261
|
+
if (typeof cell === 'string'
|
|
2262
|
+
|| typeof cell === 'number') {
|
|
2263
|
+
contents = String(cell);
|
|
2264
|
+
}
|
|
2265
|
+
else if (typeof cell === 'undefined'
|
|
2266
|
+
|| cell === null) {
|
|
2267
|
+
contents = '';
|
|
2268
|
+
}
|
|
2269
|
+
else if (typeof cell === 'object') {
|
|
2270
|
+
contents = JSON.stringify(cell);
|
|
2271
|
+
}
|
|
2272
|
+
else {
|
|
2273
|
+
contents = '';
|
|
2274
|
+
}
|
|
2275
|
+
return escapeCellText(contents);
|
|
2226
2276
|
})
|
|
2227
2277
|
.join(','));
|
|
2228
2278
|
});
|
|
@@ -2251,11 +2301,12 @@ const CSVDownloadButton = (props) => {
|
|
|
2251
2301
|
/* Main UI */
|
|
2252
2302
|
/*----------------------------------------*/
|
|
2253
2303
|
// Render the button
|
|
2254
|
-
return (React__default["default"].createElement("a", { id: id, download: filename, href: `data:application/octet-stream,${csv}`, className: `CSVDownloadButton-button ${className !== null && className !== void 0 ? className : 'btn btn-secondary'}`, "aria-label": (ariaLabel
|
|
2304
|
+
return (React__default["default"].createElement("a", { id: id, download: filename, href: `data:application/octet-stream,${encodeURIComponent(csv)}`, className: `CSVDownloadButton-button ${className !== null && className !== void 0 ? className : 'btn btn-secondary'}`, "aria-label": (ariaLabel
|
|
2255
2305
|
? `Click to download ${filename}`
|
|
2256
2306
|
: ariaLabel), style: style, onClick: onClick },
|
|
2257
2307
|
!children && (React__default["default"].createElement(React__default["default"].Fragment, null,
|
|
2258
|
-
React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.faCloudDownloadAlt
|
|
2308
|
+
React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.faCloudDownloadAlt }),
|
|
2309
|
+
' ',
|
|
2259
2310
|
"Download CSV")),
|
|
2260
2311
|
children));
|
|
2261
2312
|
};
|
|
@@ -2279,9 +2330,13 @@ var ActionType$1;
|
|
|
2279
2330
|
// Toggle sort column param
|
|
2280
2331
|
ActionType["ToggleSortColumn"] = "toggle-sort-column";
|
|
2281
2332
|
// Toggle the visibility of a column
|
|
2282
|
-
ActionType["
|
|
2333
|
+
ActionType["UpdateColumnVisibility"] = "update-column-visibility";
|
|
2283
2334
|
// Toggle the column visibility customization modal
|
|
2284
2335
|
ActionType["ToggleColVisCusModalVisibility"] = "toggle-col-vis-cus-modal-visibility";
|
|
2336
|
+
// Show all columns
|
|
2337
|
+
ActionType["ShowAllColumns"] = "show-all-columns";
|
|
2338
|
+
// Hide all columns
|
|
2339
|
+
ActionType["HideAllColumns"] = "hide-all-columns";
|
|
2285
2340
|
})(ActionType$1 || (ActionType$1 = {}));
|
|
2286
2341
|
/**
|
|
2287
2342
|
* Reducer that executes actions
|
|
@@ -2303,14 +2358,28 @@ const reducer$1 = (state, action) => {
|
|
|
2303
2358
|
// Stop sorting by column
|
|
2304
2359
|
return Object.assign(Object.assign({}, state), { sortColumnParam: undefined, sortType: SortType.Ascending });
|
|
2305
2360
|
}
|
|
2306
|
-
case ActionType$1.
|
|
2361
|
+
case ActionType$1.UpdateColumnVisibility: {
|
|
2307
2362
|
const { columnVisibilityMap } = state;
|
|
2308
|
-
columnVisibilityMap[action.param] =
|
|
2363
|
+
columnVisibilityMap[action.param] = action.visible;
|
|
2309
2364
|
return Object.assign(Object.assign({}, state), { columnVisibilityMap });
|
|
2310
2365
|
}
|
|
2311
2366
|
case ActionType$1.ToggleColVisCusModalVisibility: {
|
|
2312
2367
|
return Object.assign(Object.assign({}, state), { columnVisibilityCustomizationModalVisible: !state.columnVisibilityCustomizationModalVisible });
|
|
2313
2368
|
}
|
|
2369
|
+
case ActionType$1.ShowAllColumns: {
|
|
2370
|
+
const { columnVisibilityMap } = state;
|
|
2371
|
+
Object.keys(columnVisibilityMap).forEach((param) => {
|
|
2372
|
+
columnVisibilityMap[param] = true;
|
|
2373
|
+
});
|
|
2374
|
+
return Object.assign(Object.assign({}, state), { columnVisibilityMap });
|
|
2375
|
+
}
|
|
2376
|
+
case ActionType$1.HideAllColumns: {
|
|
2377
|
+
const { columnVisibilityMap } = state;
|
|
2378
|
+
Object.keys(columnVisibilityMap).forEach((param) => {
|
|
2379
|
+
columnVisibilityMap[param] = false;
|
|
2380
|
+
});
|
|
2381
|
+
return Object.assign(Object.assign({}, state), { columnVisibilityMap });
|
|
2382
|
+
}
|
|
2314
2383
|
default: {
|
|
2315
2384
|
return state;
|
|
2316
2385
|
}
|
|
@@ -2326,7 +2395,18 @@ const IntelliTable = (props) => {
|
|
|
2326
2395
|
var _a;
|
|
2327
2396
|
/* -------------- Props ------------- */
|
|
2328
2397
|
// Destructure all props
|
|
2329
|
-
const { title, id,
|
|
2398
|
+
const { title, id, columns, } = props;
|
|
2399
|
+
// Get data, show empty row if none
|
|
2400
|
+
const data = ((props.data && props.data.length > 0)
|
|
2401
|
+
? props.data
|
|
2402
|
+
: [{ id: 'empty-row' }]);
|
|
2403
|
+
// Get CSV filename
|
|
2404
|
+
let filename = `${title}.csv`;
|
|
2405
|
+
if (props.csvName) {
|
|
2406
|
+
filename = (props.csvName.endsWith('.csv')
|
|
2407
|
+
? props.csvName
|
|
2408
|
+
: `${props.csvName}.csv`);
|
|
2409
|
+
}
|
|
2330
2410
|
/* -------------- State ------------- */
|
|
2331
2411
|
// Initial state
|
|
2332
2412
|
const initColumnVisibilityMap = {};
|
|
@@ -2343,21 +2423,49 @@ const IntelliTable = (props) => {
|
|
|
2343
2423
|
const [state, dispatch] = React.useReducer(reducer$1, initialState);
|
|
2344
2424
|
// Destructure common state
|
|
2345
2425
|
const { sortColumnParam, sortType, columnVisibilityMap, columnVisibilityCustomizationModalVisible, } = state;
|
|
2426
|
+
/*------------------------------------------------------------------------*/
|
|
2427
|
+
/* Render */
|
|
2428
|
+
/*------------------------------------------------------------------------*/
|
|
2429
|
+
/*----------------------------------------*/
|
|
2430
|
+
/* Modal */
|
|
2431
|
+
/*----------------------------------------*/
|
|
2432
|
+
// Modal that may be defined
|
|
2433
|
+
let modal;
|
|
2346
2434
|
/* ------- Col Vis Customization Modal ------ */
|
|
2347
2435
|
if (columnVisibilityCustomizationModalVisible) {
|
|
2348
2436
|
// Create modal
|
|
2349
|
-
(React__default["default"].createElement(Modal, { type: ModalType$1.Okay, title: "Choose columns to show:", onClose: () => {
|
|
2437
|
+
modal = (React__default["default"].createElement(Modal, { type: ModalType$1.Okay, title: "Choose columns to show:", onClose: () => {
|
|
2438
|
+
const noColumnsSelected = (Object.values(columnVisibilityMap)
|
|
2439
|
+
.every((isSelected) => {
|
|
2440
|
+
return !isSelected;
|
|
2441
|
+
}));
|
|
2442
|
+
if (noColumnsSelected) {
|
|
2443
|
+
return alert$1('Choose at least one column', 'To continue, you have to choose at least one column to show');
|
|
2444
|
+
}
|
|
2350
2445
|
dispatch({
|
|
2351
2446
|
type: ActionType$1.ToggleColVisCusModalVisibility,
|
|
2352
2447
|
});
|
|
2353
|
-
}
|
|
2354
|
-
|
|
2448
|
+
}, okayVariant: Variant$1.Light },
|
|
2449
|
+
columns.map((column) => {
|
|
2450
|
+
return (React__default["default"].createElement(CheckboxButton, { key: column.param, id: `IntelliTable-${id}-toggle-visibility-${column.param}`, className: "mb-2", text: column.title, onChanged: (checked) => {
|
|
2451
|
+
dispatch({
|
|
2452
|
+
type: ActionType$1.UpdateColumnVisibility,
|
|
2453
|
+
param: column.param,
|
|
2454
|
+
visible: checked,
|
|
2455
|
+
});
|
|
2456
|
+
}, checked: columnVisibilityMap[column.param], ariaLabel: `show "${column.title}" column in the ${title} table`, checkedVariant: Variant$1.Light, uncheckedVariant: Variant$1.Secondary }));
|
|
2457
|
+
}),
|
|
2458
|
+
React__default["default"].createElement("div", { className: "mt-3" }, "Or you can:"),
|
|
2459
|
+
React__default["default"].createElement("button", { type: "button", id: `IntelliTable-${id}-select-all-columns`, className: "btn btn-secondary me-2", "aria-label": `show all columns in the ${title} table`, onClick: () => {
|
|
2355
2460
|
dispatch({
|
|
2356
|
-
type: ActionType$1.
|
|
2357
|
-
param: column.param,
|
|
2461
|
+
type: ActionType$1.ShowAllColumns,
|
|
2358
2462
|
});
|
|
2359
|
-
}
|
|
2360
|
-
|
|
2463
|
+
} }, "Select All"),
|
|
2464
|
+
React__default["default"].createElement("button", { type: "button", id: `IntelliTable-${id}-select-none-columns`, className: "btn btn-secondary", "aria-label": `hide all columns in the ${title} table`, onClick: () => {
|
|
2465
|
+
dispatch({
|
|
2466
|
+
type: ActionType$1.HideAllColumns,
|
|
2467
|
+
});
|
|
2468
|
+
} }, "Deselect All")));
|
|
2361
2469
|
}
|
|
2362
2470
|
/*----------------------------------------*/
|
|
2363
2471
|
/* Main UI */
|
|
@@ -2371,6 +2479,7 @@ const IntelliTable = (props) => {
|
|
|
2371
2479
|
// Custom info based on current sort type
|
|
2372
2480
|
let sortButtonAriaLabel;
|
|
2373
2481
|
let sortIcon = freeSolidSvgIcons.faSort;
|
|
2482
|
+
let sortingByThisColumn = false;
|
|
2374
2483
|
if (!sortColumnParam) {
|
|
2375
2484
|
// Not being sorted yet
|
|
2376
2485
|
sortButtonAriaLabel = `sort ascending by ${column.title}`;
|
|
@@ -2378,6 +2487,7 @@ const IntelliTable = (props) => {
|
|
|
2378
2487
|
}
|
|
2379
2488
|
else if (column.param === sortColumnParam) {
|
|
2380
2489
|
// Already sorted by this column
|
|
2490
|
+
sortingByThisColumn = true;
|
|
2381
2491
|
if (sortType === SortType.Ascending) {
|
|
2382
2492
|
// Sorted ascending
|
|
2383
2493
|
sortButtonAriaLabel = `sort descending by ${column.title}`;
|
|
@@ -2395,11 +2505,14 @@ const IntelliTable = (props) => {
|
|
|
2395
2505
|
sortIcon = freeSolidSvgIcons.faSort;
|
|
2396
2506
|
}
|
|
2397
2507
|
// Create the cell UI
|
|
2398
|
-
return (React__default["default"].createElement("th", { key: column.param, scope: "col", id: `IntelliTable-${id}-header-${column.param}
|
|
2399
|
-
|
|
2508
|
+
return (React__default["default"].createElement("th", { key: column.param, scope: "col", id: `IntelliTable-${id}-header-${column.param}`, className: "text-start", style: {
|
|
2509
|
+
borderRight: '0.05rem solid #555',
|
|
2510
|
+
borderLeft: '0.05rem solid #555',
|
|
2511
|
+
} },
|
|
2512
|
+
React__default["default"].createElement("div", { className: "d-flex align-items-center justify-content-start flex-row h-100" },
|
|
2400
2513
|
React__default["default"].createElement("span", { className: "text-nowrap" }, column.title),
|
|
2401
2514
|
React__default["default"].createElement("div", null,
|
|
2402
|
-
React__default["default"].createElement("button", { type: "button", className:
|
|
2515
|
+
React__default["default"].createElement("button", { type: "button", id: `IntelliTable-${id}-sort-by-${column.param}-button`, className: `btn btn-${sortingByThisColumn ? 'light' : 'secondary'} btn-sm ms-1 ps-1 pe-1 pt-0 pb-0`, "aria-label": sortButtonAriaLabel, onClick: () => {
|
|
2403
2516
|
dispatch({
|
|
2404
2517
|
type: ActionType$1.ToggleSortColumn,
|
|
2405
2518
|
param: column.param,
|
|
@@ -2417,8 +2530,28 @@ const IntelliTable = (props) => {
|
|
|
2417
2530
|
const descending = (sortType === SortType.Descending);
|
|
2418
2531
|
if (sortColumnParam) {
|
|
2419
2532
|
sortedData.sort((a, b) => {
|
|
2533
|
+
var _a, _b;
|
|
2420
2534
|
const aVal = a[sortColumnParam];
|
|
2421
2535
|
const bVal = b[sortColumnParam];
|
|
2536
|
+
// Tiebreaker sort by timestamp, most recent first (used if tied)
|
|
2537
|
+
const tiebreaker = (((_a = b.timestamp) !== null && _a !== void 0 ? _a : 0)
|
|
2538
|
+
- ((_b = a.timestamp) !== null && _b !== void 0 ? _b : 0));
|
|
2539
|
+
// Auto-sort undefined and null to end of list
|
|
2540
|
+
if ((aVal === undefined || aVal === null)
|
|
2541
|
+
|| (bVal === undefined || bVal === null)) {
|
|
2542
|
+
// At least one was undefined
|
|
2543
|
+
if ((aVal === undefined || aVal === null)
|
|
2544
|
+
&& (bVal === undefined || bVal === null)) {
|
|
2545
|
+
// Both undefined
|
|
2546
|
+
return tiebreaker;
|
|
2547
|
+
}
|
|
2548
|
+
if (aVal === undefined || aVal === null) {
|
|
2549
|
+
// First was undefined
|
|
2550
|
+
return 1;
|
|
2551
|
+
}
|
|
2552
|
+
// Second was undefined
|
|
2553
|
+
return -1;
|
|
2554
|
+
}
|
|
2422
2555
|
// Sort differently based on the data type
|
|
2423
2556
|
// > Boolean
|
|
2424
2557
|
if (paramType === ParamType$1.Boolean) {
|
|
@@ -2428,7 +2561,7 @@ const IntelliTable = (props) => {
|
|
|
2428
2561
|
if (!aVal && bVal) {
|
|
2429
2562
|
return (descending ? 1 : -1);
|
|
2430
2563
|
}
|
|
2431
|
-
return
|
|
2564
|
+
return tiebreaker;
|
|
2432
2565
|
}
|
|
2433
2566
|
// > Number
|
|
2434
2567
|
if (paramType === ParamType$1.Int
|
|
@@ -2440,12 +2573,12 @@ const IntelliTable = (props) => {
|
|
|
2440
2573
|
// > String
|
|
2441
2574
|
if (paramType === ParamType$1.String) {
|
|
2442
2575
|
if (aVal < bVal) {
|
|
2443
|
-
return (descending ?
|
|
2576
|
+
return (descending ? 1 : -1);
|
|
2444
2577
|
}
|
|
2445
2578
|
if (aVal > bVal) {
|
|
2446
|
-
return (descending ? 1 :
|
|
2579
|
+
return (descending ? -1 : 1);
|
|
2447
2580
|
}
|
|
2448
|
-
return
|
|
2581
|
+
return tiebreaker;
|
|
2449
2582
|
}
|
|
2450
2583
|
// > JSON
|
|
2451
2584
|
if (paramType === ParamType$1.JSON) {
|
|
@@ -2460,7 +2593,7 @@ const IntelliTable = (props) => {
|
|
|
2460
2593
|
: (aSize - bSize));
|
|
2461
2594
|
}
|
|
2462
2595
|
// No sort
|
|
2463
|
-
return
|
|
2596
|
+
return tiebreaker;
|
|
2464
2597
|
});
|
|
2465
2598
|
}
|
|
2466
2599
|
// Table body
|
|
@@ -2485,9 +2618,12 @@ const IntelliTable = (props) => {
|
|
|
2485
2618
|
const noValue = (value === undefined
|
|
2486
2619
|
|| value === null);
|
|
2487
2620
|
visibleValue = (noValue
|
|
2488
|
-
? (React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.
|
|
2489
|
-
: (React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.faXmarkCircle })));
|
|
2621
|
+
? (React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.faMinus }))
|
|
2622
|
+
: (React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: fullValue ? freeSolidSvgIcons.faCheckCircle : freeSolidSvgIcons.faXmarkCircle })));
|
|
2490
2623
|
title = (fullValue ? 'True' : 'False');
|
|
2624
|
+
if (noValue) {
|
|
2625
|
+
title = 'Empty Cell';
|
|
2626
|
+
}
|
|
2491
2627
|
}
|
|
2492
2628
|
else if (column.type === ParamType$1.Int) {
|
|
2493
2629
|
fullValue = Number.parseInt(value, 10);
|
|
@@ -2496,6 +2632,9 @@ const IntelliTable = (props) => {
|
|
|
2496
2632
|
? (React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.faMinus }))
|
|
2497
2633
|
: fullValue);
|
|
2498
2634
|
title = String(fullValue);
|
|
2635
|
+
if (noValue) {
|
|
2636
|
+
title = 'Empty Cell';
|
|
2637
|
+
}
|
|
2499
2638
|
}
|
|
2500
2639
|
else if (column.type === ParamType$1.Float) {
|
|
2501
2640
|
fullValue = Number.parseFloat(value);
|
|
@@ -2504,14 +2643,22 @@ const IntelliTable = (props) => {
|
|
|
2504
2643
|
? (React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.faMinus }))
|
|
2505
2644
|
: roundToNumDecimals(fullValue, 2));
|
|
2506
2645
|
title = String(fullValue);
|
|
2646
|
+
if (noValue) {
|
|
2647
|
+
title = 'Empty Cell';
|
|
2648
|
+
}
|
|
2507
2649
|
}
|
|
2508
2650
|
else if (column.type === ParamType$1.String) {
|
|
2509
2651
|
fullValue = String(value).trim();
|
|
2510
|
-
const noValue = (value
|
|
2652
|
+
const noValue = (value === undefined
|
|
2653
|
+
|| value === null
|
|
2654
|
+
|| String(fullValue).trim().length === 0);
|
|
2511
2655
|
visibleValue = (noValue
|
|
2512
2656
|
? (React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.faMinus }))
|
|
2513
2657
|
: fullValue);
|
|
2514
2658
|
title = `"${value}"`;
|
|
2659
|
+
if (noValue) {
|
|
2660
|
+
title = 'Empty Cell';
|
|
2661
|
+
}
|
|
2515
2662
|
}
|
|
2516
2663
|
else if (column.type === ParamType$1.JSON) {
|
|
2517
2664
|
fullValue = JSON.stringify(value);
|
|
@@ -2521,9 +2668,13 @@ const IntelliTable = (props) => {
|
|
|
2521
2668
|
visibleValue = (noValue
|
|
2522
2669
|
? (React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.faMinus }))
|
|
2523
2670
|
: fullValue);
|
|
2671
|
+
title = "JSON Object";
|
|
2524
2672
|
}
|
|
2525
2673
|
// Create UI
|
|
2526
|
-
return (React__default["default"].createElement("td", { key: `${datum.id}-${column.param}`, title: title
|
|
2674
|
+
return (React__default["default"].createElement("td", { key: `${datum.id}-${column.param}`, title: title, style: {
|
|
2675
|
+
borderRight: '0.05rem solid #555',
|
|
2676
|
+
borderLeft: '0.05rem solid #555',
|
|
2677
|
+
} }, visibleValue));
|
|
2527
2678
|
}));
|
|
2528
2679
|
// Add cells to a row
|
|
2529
2680
|
return (React__default["default"].createElement("tr", { key: datum.id }, cells));
|
|
@@ -2540,13 +2691,16 @@ const IntelliTable = (props) => {
|
|
|
2540
2691
|
})
|
|
2541
2692
|
.length);
|
|
2542
2693
|
// Build CSV
|
|
2543
|
-
const csv = genCSV(data, columns)
|
|
2694
|
+
const csv = genCSV(data, columns.filter((column) => {
|
|
2695
|
+
return columnVisibilityMap[column.param];
|
|
2696
|
+
}));
|
|
2544
2697
|
// Build main UI
|
|
2545
2698
|
return (React__default["default"].createElement("div", { className: `IntelliTable-container-${id}` },
|
|
2546
|
-
|
|
2699
|
+
modal,
|
|
2700
|
+
React__default["default"].createElement("div", { className: "d-flex align-items-center justify-content-start" },
|
|
2547
2701
|
React__default["default"].createElement("h3", { className: "m-0" }, title),
|
|
2548
2702
|
React__default["default"].createElement("div", { className: "flex-grow-1 text-end" },
|
|
2549
|
-
React__default["default"].createElement(CSVDownloadButton, { "aria-label": `download data as csv for ${title}`, id: `IntelliTable-${id}-download-as-csv`, filename:
|
|
2703
|
+
React__default["default"].createElement(CSVDownloadButton, { "aria-label": `download data as csv for ${title}`, id: `IntelliTable-${id}-download-as-csv`, filename: filename, csv: csv }),
|
|
2550
2704
|
React__default["default"].createElement("button", { type: "button", className: "btn btn-secondary ms-2", "aria-label": `show panel for customizing which columns show in table ${title}`, id: `IntelliTable-${id}-show-column-customization-modal`, onClick: () => {
|
|
2551
2705
|
dispatch({
|
|
2552
2706
|
type: ActionType$1.ToggleColVisCusModalVisibility,
|
|
@@ -2559,7 +2713,7 @@ const IntelliTable = (props) => {
|
|
|
2559
2713
|
numHiddenCols,
|
|
2560
2714
|
' ',
|
|
2561
2715
|
"hidden)"))))),
|
|
2562
|
-
React__default["default"].createElement("div", { className: `IntelliTable-table-${id}`, style: {
|
|
2716
|
+
React__default["default"].createElement("div", { className: `IntelliTable-table-${id} mt-2`, style: {
|
|
2563
2717
|
overflowX: 'auto',
|
|
2564
2718
|
} }, table)));
|
|
2565
2719
|
};
|
|
@@ -2648,14 +2802,212 @@ const style = `
|
|
|
2648
2802
|
background-color: transparent !important;
|
|
2649
2803
|
padding-top: 0 !important;
|
|
2650
2804
|
padding-bottom: 0 !important;
|
|
2805
|
+
padding-right: 1em !important;
|
|
2651
2806
|
margin: 0 !important;
|
|
2652
|
-
color: #
|
|
2807
|
+
color: #444 !important;
|
|
2808
|
+
|
|
2809
|
+
right: 0 !important;
|
|
2810
|
+
position: absolute !important;
|
|
2653
2811
|
}
|
|
2654
2812
|
.LogReviewer-header-close-button:hover {
|
|
2813
|
+
border: 0 !important;
|
|
2814
|
+
background-color: transparent !important;
|
|
2815
|
+
padding-top: 0 !important;
|
|
2816
|
+
padding-bottom: 0 !important;
|
|
2817
|
+
margin: 0 !important;
|
|
2655
2818
|
color: #000 !important;
|
|
2656
2819
|
}
|
|
2657
2820
|
`;
|
|
2658
2821
|
/*------------------------------------------------------------------------*/
|
|
2822
|
+
/* Constants */
|
|
2823
|
+
/*------------------------------------------------------------------------*/
|
|
2824
|
+
const columns = [
|
|
2825
|
+
{
|
|
2826
|
+
title: 'First Name',
|
|
2827
|
+
param: 'userFirstName',
|
|
2828
|
+
type: ParamType$1.String,
|
|
2829
|
+
},
|
|
2830
|
+
{
|
|
2831
|
+
title: 'Last Name',
|
|
2832
|
+
param: 'userLastName',
|
|
2833
|
+
type: ParamType$1.String,
|
|
2834
|
+
},
|
|
2835
|
+
{
|
|
2836
|
+
title: 'Email',
|
|
2837
|
+
param: 'userEmail',
|
|
2838
|
+
type: ParamType$1.String,
|
|
2839
|
+
},
|
|
2840
|
+
{
|
|
2841
|
+
title: 'Canvas Id',
|
|
2842
|
+
param: 'userId',
|
|
2843
|
+
type: ParamType$1.Int,
|
|
2844
|
+
},
|
|
2845
|
+
{
|
|
2846
|
+
title: 'Student?',
|
|
2847
|
+
param: 'isLearner',
|
|
2848
|
+
type: ParamType$1.Boolean,
|
|
2849
|
+
},
|
|
2850
|
+
{
|
|
2851
|
+
title: 'Teaching Staff?',
|
|
2852
|
+
param: 'isTTM',
|
|
2853
|
+
type: ParamType$1.Boolean,
|
|
2854
|
+
startsHidden: true,
|
|
2855
|
+
},
|
|
2856
|
+
{
|
|
2857
|
+
title: 'Admin?',
|
|
2858
|
+
param: 'isAdmin',
|
|
2859
|
+
type: ParamType$1.Boolean,
|
|
2860
|
+
startsHidden: true,
|
|
2861
|
+
},
|
|
2862
|
+
{
|
|
2863
|
+
title: 'Course Canvas Id',
|
|
2864
|
+
param: 'courseId',
|
|
2865
|
+
type: ParamType$1.Int,
|
|
2866
|
+
startsHidden: true,
|
|
2867
|
+
},
|
|
2868
|
+
{
|
|
2869
|
+
title: 'Course Name',
|
|
2870
|
+
param: 'courseName',
|
|
2871
|
+
type: ParamType$1.String,
|
|
2872
|
+
},
|
|
2873
|
+
{
|
|
2874
|
+
title: 'Browser Name',
|
|
2875
|
+
param: 'browser.name',
|
|
2876
|
+
type: ParamType$1.String,
|
|
2877
|
+
startsHidden: true,
|
|
2878
|
+
},
|
|
2879
|
+
{
|
|
2880
|
+
title: 'Browser Version',
|
|
2881
|
+
param: 'browser.version',
|
|
2882
|
+
type: ParamType$1.String,
|
|
2883
|
+
startsHidden: true,
|
|
2884
|
+
},
|
|
2885
|
+
{
|
|
2886
|
+
title: 'OS',
|
|
2887
|
+
param: 'device.os',
|
|
2888
|
+
type: ParamType$1.String,
|
|
2889
|
+
startsHidden: true,
|
|
2890
|
+
},
|
|
2891
|
+
{
|
|
2892
|
+
title: 'Mobile?',
|
|
2893
|
+
param: 'device.isMobile',
|
|
2894
|
+
type: ParamType$1.Boolean,
|
|
2895
|
+
startsHidden: true,
|
|
2896
|
+
},
|
|
2897
|
+
{
|
|
2898
|
+
title: 'Year',
|
|
2899
|
+
param: 'year',
|
|
2900
|
+
type: ParamType$1.Int,
|
|
2901
|
+
},
|
|
2902
|
+
{
|
|
2903
|
+
title: 'Month',
|
|
2904
|
+
param: 'month',
|
|
2905
|
+
type: ParamType$1.Int,
|
|
2906
|
+
},
|
|
2907
|
+
{
|
|
2908
|
+
title: 'Day',
|
|
2909
|
+
param: 'day',
|
|
2910
|
+
type: ParamType$1.Int,
|
|
2911
|
+
},
|
|
2912
|
+
{
|
|
2913
|
+
title: 'Hour',
|
|
2914
|
+
param: 'hour',
|
|
2915
|
+
type: ParamType$1.Int,
|
|
2916
|
+
},
|
|
2917
|
+
{
|
|
2918
|
+
title: 'Minute',
|
|
2919
|
+
param: 'minute',
|
|
2920
|
+
type: ParamType$1.Int,
|
|
2921
|
+
startsHidden: true,
|
|
2922
|
+
},
|
|
2923
|
+
{
|
|
2924
|
+
title: 'Timestamp',
|
|
2925
|
+
param: 'timestamp',
|
|
2926
|
+
type: ParamType$1.Int,
|
|
2927
|
+
startsHidden: true,
|
|
2928
|
+
},
|
|
2929
|
+
{
|
|
2930
|
+
title: 'Context',
|
|
2931
|
+
param: 'context',
|
|
2932
|
+
type: ParamType$1.String,
|
|
2933
|
+
},
|
|
2934
|
+
{
|
|
2935
|
+
title: 'Subcontext',
|
|
2936
|
+
param: 'subcontext',
|
|
2937
|
+
type: ParamType$1.String,
|
|
2938
|
+
},
|
|
2939
|
+
{
|
|
2940
|
+
title: 'Tags',
|
|
2941
|
+
param: 'tags',
|
|
2942
|
+
type: ParamType$1.JSON,
|
|
2943
|
+
startsHidden: true,
|
|
2944
|
+
},
|
|
2945
|
+
{
|
|
2946
|
+
title: 'Log Level',
|
|
2947
|
+
param: 'level',
|
|
2948
|
+
type: ParamType$1.String,
|
|
2949
|
+
startsHidden: true,
|
|
2950
|
+
},
|
|
2951
|
+
{
|
|
2952
|
+
title: 'Metadata',
|
|
2953
|
+
param: 'metadata',
|
|
2954
|
+
type: ParamType$1.JSON,
|
|
2955
|
+
startsHidden: true,
|
|
2956
|
+
},
|
|
2957
|
+
{
|
|
2958
|
+
title: 'Source',
|
|
2959
|
+
param: 'source',
|
|
2960
|
+
type: ParamType$1.String,
|
|
2961
|
+
},
|
|
2962
|
+
{
|
|
2963
|
+
title: 'Server Route Path',
|
|
2964
|
+
param: 'routePath',
|
|
2965
|
+
type: ParamType$1.String,
|
|
2966
|
+
startsHidden: true,
|
|
2967
|
+
},
|
|
2968
|
+
{
|
|
2969
|
+
title: 'Server Route Template',
|
|
2970
|
+
param: 'routeTemplate',
|
|
2971
|
+
type: ParamType$1.String,
|
|
2972
|
+
startsHidden: true,
|
|
2973
|
+
},
|
|
2974
|
+
{
|
|
2975
|
+
title: 'Type',
|
|
2976
|
+
param: 'type',
|
|
2977
|
+
type: ParamType$1.String,
|
|
2978
|
+
},
|
|
2979
|
+
{
|
|
2980
|
+
title: 'Error Message',
|
|
2981
|
+
param: 'errorMessage',
|
|
2982
|
+
type: ParamType$1.String,
|
|
2983
|
+
startsHidden: true,
|
|
2984
|
+
},
|
|
2985
|
+
{
|
|
2986
|
+
title: 'Error Code',
|
|
2987
|
+
param: 'errorCode',
|
|
2988
|
+
type: ParamType$1.String,
|
|
2989
|
+
startsHidden: true,
|
|
2990
|
+
},
|
|
2991
|
+
{
|
|
2992
|
+
title: 'Error Stack',
|
|
2993
|
+
param: 'errorStack',
|
|
2994
|
+
type: ParamType$1.String,
|
|
2995
|
+
startsHidden: true,
|
|
2996
|
+
},
|
|
2997
|
+
{
|
|
2998
|
+
title: 'Action Target',
|
|
2999
|
+
param: 'target',
|
|
3000
|
+
type: ParamType$1.String,
|
|
3001
|
+
startsHidden: true,
|
|
3002
|
+
},
|
|
3003
|
+
{
|
|
3004
|
+
title: 'Action Type',
|
|
3005
|
+
param: 'action',
|
|
3006
|
+
type: ParamType$1.String,
|
|
3007
|
+
startsHidden: true,
|
|
3008
|
+
},
|
|
3009
|
+
];
|
|
3010
|
+
/*------------------------------------------------------------------------*/
|
|
2659
3011
|
/* Static Functions */
|
|
2660
3012
|
/*------------------------------------------------------------------------*/
|
|
2661
3013
|
/**
|
|
@@ -2755,10 +3107,35 @@ const LogReviewer = (props) => {
|
|
|
2755
3107
|
/*------------------------------------------------------------------------*/
|
|
2756
3108
|
/* Setup */
|
|
2757
3109
|
/*------------------------------------------------------------------------*/
|
|
2758
|
-
var _a, _b, _c, _d
|
|
3110
|
+
var _a, _b, _c, _d;
|
|
2759
3111
|
/* -------------- Props ------------- */
|
|
2760
3112
|
// Destructure props
|
|
2761
3113
|
const { LogMetadata, onClose, } = props;
|
|
3114
|
+
// Create complete map of contexts
|
|
3115
|
+
const contextMap = {};
|
|
3116
|
+
[
|
|
3117
|
+
((_a = LogMetadata.Context) !== null && _a !== void 0 ? _a : {}),
|
|
3118
|
+
LogBuiltInMetadata.Context,
|
|
3119
|
+
].forEach((partialContextMap) => {
|
|
3120
|
+
Object.keys(partialContextMap).forEach((context) => {
|
|
3121
|
+
// Add context
|
|
3122
|
+
contextMap[context] = partialContextMap[context];
|
|
3123
|
+
// If context has children, add an "uncategorized" subcontext
|
|
3124
|
+
if (typeof contextMap[context] !== 'string') {
|
|
3125
|
+
contextMap[context][LogBuiltInMetadata.Context.Uncategorized] = (LogBuiltInMetadata.Context.Uncategorized);
|
|
3126
|
+
}
|
|
3127
|
+
});
|
|
3128
|
+
});
|
|
3129
|
+
// Create complete map of targets
|
|
3130
|
+
const targetMap = {};
|
|
3131
|
+
[
|
|
3132
|
+
((_b = LogMetadata.Target) !== null && _b !== void 0 ? _b : {}),
|
|
3133
|
+
LogBuiltInMetadata.Target,
|
|
3134
|
+
].forEach((partialTargetMap) => {
|
|
3135
|
+
Object.keys(partialTargetMap).forEach((target) => {
|
|
3136
|
+
targetMap[target] = partialTargetMap[target];
|
|
3137
|
+
});
|
|
3138
|
+
});
|
|
2762
3139
|
/* -------------- State ------------- */
|
|
2763
3140
|
// Create initial date filter state
|
|
2764
3141
|
const today = getTimeInfoInET();
|
|
@@ -2778,26 +3155,29 @@ const LogReviewer = (props) => {
|
|
|
2778
3155
|
};
|
|
2779
3156
|
// Create initial context filter state
|
|
2780
3157
|
const initContextFilterState = {};
|
|
2781
|
-
Object.keys(
|
|
2782
|
-
|
|
2783
|
-
const contextValue = ((_a = LogMetadata.Context) !== null && _a !== void 0 ? _a : {})[context];
|
|
3158
|
+
Object.keys(contextMap).forEach((context) => {
|
|
3159
|
+
const contextValue = contextMap[context];
|
|
2784
3160
|
if (typeof contextValue === 'string') {
|
|
2785
|
-
// Case: no subcontexts
|
|
3161
|
+
// Case: no subcontexts, init as checked
|
|
2786
3162
|
initContextFilterState[contextValue] = true;
|
|
2787
3163
|
}
|
|
2788
3164
|
else {
|
|
2789
3165
|
// Case: subcontexts exist
|
|
2790
|
-
initContextFilterState[
|
|
2791
|
-
Object.values(
|
|
2792
|
-
|
|
2793
|
-
|
|
3166
|
+
initContextFilterState[context] = {};
|
|
3167
|
+
Object.values(contextMap[context]).forEach((subcontext) => {
|
|
3168
|
+
// Skip self ("_")
|
|
3169
|
+
if (subcontext === '_') {
|
|
3170
|
+
return;
|
|
3171
|
+
}
|
|
3172
|
+
// Initialize as checked
|
|
3173
|
+
initContextFilterState[context][subcontext] = true;
|
|
2794
3174
|
});
|
|
2795
3175
|
}
|
|
2796
3176
|
});
|
|
2797
3177
|
// Create initial tag filter state
|
|
2798
3178
|
const initTagFilterState = {};
|
|
2799
|
-
Object.
|
|
2800
|
-
initTagFilterState[
|
|
3179
|
+
Object.keys((_c = LogMetadata.Tag) !== null && _c !== void 0 ? _c : {}).forEach((tag) => {
|
|
3180
|
+
initTagFilterState[tag] = false;
|
|
2801
3181
|
});
|
|
2802
3182
|
// Create advanced filter state
|
|
2803
3183
|
const initAdvancedFilterState = {
|
|
@@ -2823,7 +3203,7 @@ const LogReviewer = (props) => {
|
|
|
2823
3203
|
target: {},
|
|
2824
3204
|
action: {},
|
|
2825
3205
|
};
|
|
2826
|
-
Object.values(
|
|
3206
|
+
Object.values(targetMap).forEach((target) => {
|
|
2827
3207
|
initActionErrorFilterState.target[target] = true;
|
|
2828
3208
|
});
|
|
2829
3209
|
Object.values(LogAction$1).forEach((action) => {
|
|
@@ -2862,15 +3242,18 @@ const LogReviewer = (props) => {
|
|
|
2862
3242
|
let month = newDateFilterState.startDate.month;
|
|
2863
3243
|
while (
|
|
2864
3244
|
// Earlier year
|
|
2865
|
-
(year
|
|
3245
|
+
(year < newDateFilterState.endDate.year)
|
|
2866
3246
|
// Current year but included month
|
|
2867
3247
|
|| (year === newDateFilterState.endDate.year
|
|
2868
3248
|
&& month <= newDateFilterState.endDate.month)) {
|
|
2869
|
-
// Add to list
|
|
2870
|
-
|
|
2871
|
-
year
|
|
2872
|
-
|
|
2873
|
-
|
|
3249
|
+
// Add to list if not already loaded
|
|
3250
|
+
if (!logMap[year]
|
|
3251
|
+
|| !logMap[year][month]) {
|
|
3252
|
+
toLoad.push({
|
|
3253
|
+
year,
|
|
3254
|
+
month,
|
|
3255
|
+
});
|
|
3256
|
+
}
|
|
2874
3257
|
// Increment
|
|
2875
3258
|
month += 1;
|
|
2876
3259
|
if (month > 12) {
|
|
@@ -2958,10 +3341,10 @@ const LogReviewer = (props) => {
|
|
|
2958
3341
|
/* Filters */
|
|
2959
3342
|
/*----------------------------------------*/
|
|
2960
3343
|
// Filter toggle
|
|
2961
|
-
const filterToggles = (React__default["default"].createElement("div", { className: "LogReviewer-filter-toggles
|
|
3344
|
+
const filterToggles = (React__default["default"].createElement("div", { className: "LogReviewer-filter-toggles" },
|
|
2962
3345
|
React__default["default"].createElement("h3", { className: "m-0" }, "Filters:"),
|
|
2963
|
-
React__default["default"].createElement("div", { className: "LogReviewer-filter-toggle-buttons" },
|
|
2964
|
-
React__default["default"].createElement("button", { type: "button", id: "LogReviewer-toggle-date-filter-drawer", className: `btn btn-${FilterDrawer.Date === expandedFilterDrawer ? 'warning' : '
|
|
3346
|
+
React__default["default"].createElement("div", { className: "LogReviewer-filter-toggle-buttons alert alert-secondary p-2 m-0" },
|
|
3347
|
+
React__default["default"].createElement("button", { type: "button", id: "LogReviewer-toggle-date-filter-drawer", className: `btn btn-${FilterDrawer.Date === expandedFilterDrawer ? 'warning' : 'light'} me-2`, "aria-label": "toggle date filter drawer", onClick: () => {
|
|
2965
3348
|
dispatch({
|
|
2966
3349
|
type: ActionType.ToggleFilterDrawer,
|
|
2967
3350
|
filterDrawer: FilterDrawer.Date,
|
|
@@ -2969,7 +3352,7 @@ const LogReviewer = (props) => {
|
|
|
2969
3352
|
} },
|
|
2970
3353
|
React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.faCalendar, className: "me-2" }),
|
|
2971
3354
|
"Date"),
|
|
2972
|
-
React__default["default"].createElement("button", { type: "button", id: "LogReviewer-toggle-context-filter-drawer", className: `btn btn-${FilterDrawer.Context === expandedFilterDrawer ? 'warning' : '
|
|
3355
|
+
React__default["default"].createElement("button", { type: "button", id: "LogReviewer-toggle-context-filter-drawer", className: `btn btn-${FilterDrawer.Context === expandedFilterDrawer ? 'warning' : 'light'} me-2`, "aria-label": "toggle context filter drawer", onClick: () => {
|
|
2973
3356
|
dispatch({
|
|
2974
3357
|
type: ActionType.ToggleFilterDrawer,
|
|
2975
3358
|
filterDrawer: FilterDrawer.Context,
|
|
@@ -2977,7 +3360,7 @@ const LogReviewer = (props) => {
|
|
|
2977
3360
|
} },
|
|
2978
3361
|
React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.faCircle, className: "me-2" }),
|
|
2979
3362
|
"Context"),
|
|
2980
|
-
(LogMetadata.Tag && Object.keys(LogMetadata.Tag).length > 0) && (React__default["default"].createElement("button", { type: "button", id: "LogReviewer-toggle-tag-filter-drawer", className: `btn btn-${FilterDrawer.Tag === expandedFilterDrawer ? 'warning' : '
|
|
3363
|
+
(LogMetadata.Tag && Object.keys(LogMetadata.Tag).length > 0) && (React__default["default"].createElement("button", { type: "button", id: "LogReviewer-toggle-tag-filter-drawer", className: `btn btn-${FilterDrawer.Tag === expandedFilterDrawer ? 'warning' : 'light'} me-2`, "aria-label": "toggle tag filter drawer", onClick: () => {
|
|
2981
3364
|
dispatch({
|
|
2982
3365
|
type: ActionType.ToggleFilterDrawer,
|
|
2983
3366
|
filterDrawer: FilterDrawer.Tag,
|
|
@@ -2985,7 +3368,7 @@ const LogReviewer = (props) => {
|
|
|
2985
3368
|
} },
|
|
2986
3369
|
React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.faTag, className: "me-2" }),
|
|
2987
3370
|
"Tag")),
|
|
2988
|
-
React__default["default"].createElement("button", { type: "button", id: "LogReviewer-toggle-action-filter-drawer", className: `btn btn-${FilterDrawer.Action === expandedFilterDrawer ? 'warning' : '
|
|
3371
|
+
React__default["default"].createElement("button", { type: "button", id: "LogReviewer-toggle-action-filter-drawer", className: `btn btn-${FilterDrawer.Action === expandedFilterDrawer ? 'warning' : 'light'} me-2`, "aria-label": "toggle action and error filter drawer", onClick: () => {
|
|
2989
3372
|
dispatch({
|
|
2990
3373
|
type: ActionType.ToggleFilterDrawer,
|
|
2991
3374
|
filterDrawer: FilterDrawer.Action,
|
|
@@ -2993,41 +3376,64 @@ const LogReviewer = (props) => {
|
|
|
2993
3376
|
} },
|
|
2994
3377
|
React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.faHammer, className: "me-2" }),
|
|
2995
3378
|
"Action"),
|
|
2996
|
-
React__default["default"].createElement("button", { type: "button", id: "LogReviewer-toggle-advanced-filter-drawer", className: `btn btn-${FilterDrawer.Advanced === expandedFilterDrawer ? 'warning' : '
|
|
3379
|
+
React__default["default"].createElement("button", { type: "button", id: "LogReviewer-toggle-advanced-filter-drawer", className: `btn btn-${FilterDrawer.Advanced === expandedFilterDrawer ? 'warning' : 'light'} me-2`, "aria-label": "toggle advanced filter drawer", onClick: () => {
|
|
2997
3380
|
dispatch({
|
|
2998
3381
|
type: ActionType.ToggleFilterDrawer,
|
|
2999
3382
|
filterDrawer: FilterDrawer.Advanced,
|
|
3000
3383
|
});
|
|
3001
3384
|
} },
|
|
3002
3385
|
React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.faList, className: "me-2" }),
|
|
3003
|
-
"Advanced")
|
|
3386
|
+
"Advanced"),
|
|
3387
|
+
React__default["default"].createElement("button", { type: "button", id: "LogReviewer-reset-filters-button", className: "btn btn-light", "aria-label": "reset filters", onClick: () => {
|
|
3388
|
+
dispatch({
|
|
3389
|
+
type: ActionType.ResetFilters,
|
|
3390
|
+
initActionErrorFilterState,
|
|
3391
|
+
initAdvancedFilterState,
|
|
3392
|
+
initContextFilterState,
|
|
3393
|
+
initDateFilterState,
|
|
3394
|
+
initTagFilterState,
|
|
3395
|
+
});
|
|
3396
|
+
} },
|
|
3397
|
+
React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.faTimes }),
|
|
3398
|
+
' ',
|
|
3399
|
+
"Reset"))));
|
|
3004
3400
|
// Filter drawer
|
|
3005
3401
|
let filterDrawer;
|
|
3006
3402
|
if (expandedFilterDrawer) {
|
|
3007
3403
|
if (expandedFilterDrawer === FilterDrawer.Date) {
|
|
3008
3404
|
filterDrawer = (React__default["default"].createElement(TabBox, { title: "Date" },
|
|
3009
|
-
React__default["default"].createElement(SimpleDateChooser, { ariaLabel: "filter start date", name: "filter-start-date", year: dateFilterState.startDate.year, month: dateFilterState.startDate.month, day: dateFilterState.startDate.day, onChange: (month, day, year) => {
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
dateFilterState: Object.assign(Object.assign({}, dateFilterState), { startDate: { month, day, year } }),
|
|
3013
|
-
});
|
|
3405
|
+
React__default["default"].createElement(SimpleDateChooser, { ariaLabel: "filter start date", name: "filter-start-date", year: dateFilterState.startDate.year, month: dateFilterState.startDate.month, day: dateFilterState.startDate.day, chooseFromPast: true, numMonthsToShow: 36, onChange: (month, day, year) => {
|
|
3406
|
+
dateFilterState.startDate = { month, day, year };
|
|
3407
|
+
handleDateRangeUpdated(dateFilterState);
|
|
3014
3408
|
} }),
|
|
3015
3409
|
' ',
|
|
3016
3410
|
"to",
|
|
3017
3411
|
' ',
|
|
3018
|
-
React__default["default"].createElement(SimpleDateChooser, { ariaLabel: "filter end date", name: "filter-end-date", year: dateFilterState.endDate.year, month: dateFilterState.endDate.month, day: dateFilterState.endDate.day, onChange: (month, day, year) => {
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3412
|
+
React__default["default"].createElement(SimpleDateChooser, { ariaLabel: "filter end date", name: "filter-end-date", year: dateFilterState.endDate.year, month: dateFilterState.endDate.month, day: dateFilterState.endDate.day, chooseFromPast: true, numMonthsToShow: 12, onChange: (month, day, year) => {
|
|
3413
|
+
if (year < dateFilterState.startDate.year
|
|
3414
|
+
|| (year === dateFilterState.startDate.year
|
|
3415
|
+
&& month < dateFilterState.startDate.month)
|
|
3416
|
+
|| (year === dateFilterState.startDate.year
|
|
3417
|
+
&& month === dateFilterState.startDate.month
|
|
3418
|
+
&& day < dateFilterState.startDate.day)) {
|
|
3419
|
+
return alert$1('Invalid Start Date', 'The start date cannot be before the end date.');
|
|
3420
|
+
}
|
|
3421
|
+
dateFilterState.endDate = { month, day, year };
|
|
3422
|
+
handleDateRangeUpdated(dateFilterState);
|
|
3023
3423
|
} })));
|
|
3024
3424
|
}
|
|
3025
3425
|
else if (expandedFilterDrawer === FilterDrawer.Context) {
|
|
3026
3426
|
// Create item picker items
|
|
3027
|
-
const
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3427
|
+
const builtInPickableItem = {
|
|
3428
|
+
id: 'built-in-contexts',
|
|
3429
|
+
name: 'Auto-logged',
|
|
3430
|
+
isGroup: true,
|
|
3431
|
+
children: [],
|
|
3432
|
+
};
|
|
3433
|
+
const pickableItems = [];
|
|
3434
|
+
Object.keys(contextMap)
|
|
3435
|
+
.forEach((context) => {
|
|
3436
|
+
const value = contextMap[context];
|
|
3031
3437
|
if (typeof value === 'string') {
|
|
3032
3438
|
// No subcategories
|
|
3033
3439
|
const item = {
|
|
@@ -3036,38 +3442,65 @@ const LogReviewer = (props) => {
|
|
|
3036
3442
|
isGroup: false,
|
|
3037
3443
|
checked: !!contextFilterState[context],
|
|
3038
3444
|
};
|
|
3039
|
-
|
|
3445
|
+
// Add built-in items to its own folder
|
|
3446
|
+
const isBuiltIn = context in LogBuiltInMetadata.Context;
|
|
3447
|
+
if (isBuiltIn) {
|
|
3448
|
+
// Add to built-in pickable item
|
|
3449
|
+
builtInPickableItem.children.push(item);
|
|
3450
|
+
}
|
|
3451
|
+
else {
|
|
3452
|
+
// Add to pickable items list
|
|
3453
|
+
pickableItems.push(item);
|
|
3454
|
+
}
|
|
3455
|
+
return;
|
|
3040
3456
|
}
|
|
3041
3457
|
// Has subcategories
|
|
3042
3458
|
const children = (Object.keys(value)
|
|
3459
|
+
// Remove parent name
|
|
3043
3460
|
.filter((subcontext) => {
|
|
3044
3461
|
return subcontext !== '_';
|
|
3045
3462
|
})
|
|
3463
|
+
// Create child pickable items
|
|
3046
3464
|
.map((subcontext) => {
|
|
3047
3465
|
return {
|
|
3048
|
-
id:
|
|
3466
|
+
id: subcontext,
|
|
3049
3467
|
name: genHumanReadableName(subcontext),
|
|
3050
3468
|
isGroup: false,
|
|
3051
|
-
checked:
|
|
3469
|
+
checked: contextFilterState[context][subcontext],
|
|
3052
3470
|
};
|
|
3053
3471
|
}));
|
|
3054
3472
|
const item = {
|
|
3055
3473
|
id: context,
|
|
3056
|
-
name: context,
|
|
3474
|
+
name: genHumanReadableName(context),
|
|
3057
3475
|
isGroup: true,
|
|
3058
3476
|
children,
|
|
3059
3477
|
};
|
|
3060
|
-
|
|
3061
|
-
})
|
|
3478
|
+
pickableItems.push(item);
|
|
3479
|
+
});
|
|
3480
|
+
// Add built-in contexts to end ofl ist
|
|
3481
|
+
pickableItems.push(builtInPickableItem);
|
|
3062
3482
|
// Create filter UI
|
|
3063
3483
|
filterDrawer = (React__default["default"].createElement(ItemPicker, { title: "Context", items: pickableItems, onChanged: (updatedItems) => {
|
|
3064
3484
|
// Update our state
|
|
3065
3485
|
updatedItems.forEach((pickableItem) => {
|
|
3066
3486
|
if (pickableItem.isGroup) {
|
|
3067
3487
|
// Has subcontexts
|
|
3068
|
-
pickableItem.
|
|
3069
|
-
|
|
3070
|
-
|
|
3488
|
+
if (pickableItem.id === 'built-in-contexts') {
|
|
3489
|
+
// Built-in
|
|
3490
|
+
// Treat as if these were top-level contexts
|
|
3491
|
+
pickableItem.children.forEach((subcontextItem) => {
|
|
3492
|
+
contextFilterState[subcontextItem.id] = ('checked' in subcontextItem
|
|
3493
|
+
&& subcontextItem.checked);
|
|
3494
|
+
});
|
|
3495
|
+
}
|
|
3496
|
+
else {
|
|
3497
|
+
// Not built-in
|
|
3498
|
+
pickableItem.children.forEach((subcontextItem) => {
|
|
3499
|
+
if (!subcontextItem.isGroup) {
|
|
3500
|
+
contextFilterState[pickableItem.id][subcontextItem.id] = (subcontextItem.checked);
|
|
3501
|
+
}
|
|
3502
|
+
});
|
|
3503
|
+
}
|
|
3071
3504
|
}
|
|
3072
3505
|
else {
|
|
3073
3506
|
// No subcontexts
|
|
@@ -3082,13 +3515,19 @@ const LogReviewer = (props) => {
|
|
|
3082
3515
|
}
|
|
3083
3516
|
else if (expandedFilterDrawer === FilterDrawer.Tag) {
|
|
3084
3517
|
// Create filter UI
|
|
3085
|
-
filterDrawer = (React__default["default"].createElement(TabBox, { title: "Tags" },
|
|
3086
|
-
.
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
} })
|
|
3091
|
-
|
|
3518
|
+
filterDrawer = (React__default["default"].createElement(TabBox, { title: "Tags" },
|
|
3519
|
+
React__default["default"].createElement("div", null, "If any tags are selected, logs must contain at least one (but not necessarily all) of the selected tags."),
|
|
3520
|
+
React__default["default"].createElement("div", { className: "d-flex gap-1 flex-wrap" }, Object.keys((_d = LogMetadata.Tag) !== null && _d !== void 0 ? _d : {})
|
|
3521
|
+
.map((tag) => {
|
|
3522
|
+
const description = genHumanReadableName(tag);
|
|
3523
|
+
return (React__default["default"].createElement(CheckboxButton, { key: tag, id: `LogReviewer-tag-${tag}-checkbox`, text: description, ariaLabel: `require that logs be tagged with "${description}" or any other selected tag`, checked: tagFilterState[tag], onChanged: (checked) => {
|
|
3524
|
+
tagFilterState[tag] = checked;
|
|
3525
|
+
dispatch({
|
|
3526
|
+
type: ActionType.UpdateTagFilterState,
|
|
3527
|
+
tagFilterState,
|
|
3528
|
+
});
|
|
3529
|
+
} }));
|
|
3530
|
+
}))));
|
|
3092
3531
|
}
|
|
3093
3532
|
else if (expandedFilterDrawer === FilterDrawer.Action) {
|
|
3094
3533
|
// Create filter UI
|
|
@@ -3117,36 +3556,35 @@ const LogReviewer = (props) => {
|
|
|
3117
3556
|
}, ariaLabel: "only show error logs", selected: actionErrorFilterState.type === LogType$1.Error, noMarginOnRight: true })),
|
|
3118
3557
|
(actionErrorFilterState.type === undefined
|
|
3119
3558
|
|| actionErrorFilterState.type === LogType$1.Action) && (React__default["default"].createElement(TabBox, { title: "Action Log Details" },
|
|
3120
|
-
React__default["default"].createElement(ButtonInputGroup, { label: "Action", className: "mb-2" },
|
|
3121
|
-
.
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3559
|
+
React__default["default"].createElement(ButtonInputGroup, { label: "Action", className: "mb-2" },
|
|
3560
|
+
React__default["default"].createElement("div", { className: "d-flex gap-1 flex-wrap" }, Object.keys(LogAction$1)
|
|
3561
|
+
.map((action) => {
|
|
3562
|
+
const description = genHumanReadableName(action);
|
|
3563
|
+
return (React__default["default"].createElement(CheckboxButton, { key: action, id: `LogReviewer-action-${action}-checkbox`, text: description, ariaLabel: `include logs with action type "${description}" in results`, noMarginOnRight: true, checked: actionErrorFilterState.action[action], onChanged: (checked) => {
|
|
3564
|
+
actionErrorFilterState.action[action] = checked;
|
|
3565
|
+
dispatch({
|
|
3566
|
+
type: ActionType.UpdateActionErrorFilterState,
|
|
3567
|
+
actionErrorFilterState,
|
|
3568
|
+
});
|
|
3569
|
+
} }));
|
|
3570
|
+
}))),
|
|
3131
3571
|
React__default["default"].createElement(ButtonInputGroup, { label: "Target" },
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
.map((target, i) => {
|
|
3135
|
-
var _a;
|
|
3572
|
+
React__default["default"].createElement("div", { className: "d-flex gap-1 flex-wrap" }, Object.keys(targetMap)
|
|
3573
|
+
.map((target) => {
|
|
3136
3574
|
const description = genHumanReadableName(target);
|
|
3137
|
-
return (React__default["default"].createElement(CheckboxButton, { id: `LogReviewer-target-${target}-checkbox`, text: description, ariaLabel: `include logs with target "${description}" in results`, onChanged: (checked) => {
|
|
3575
|
+
return (React__default["default"].createElement(CheckboxButton, { key: target, id: `LogReviewer-target-${target}-checkbox`, text: description, ariaLabel: `include logs with target "${description}" in results`, checked: actionErrorFilterState.target[target], noMarginOnRight: true, onChanged: (checked) => {
|
|
3138
3576
|
actionErrorFilterState.target[target] = checked;
|
|
3139
3577
|
dispatch({
|
|
3140
3578
|
type: ActionType.UpdateActionErrorFilterState,
|
|
3141
3579
|
actionErrorFilterState,
|
|
3142
3580
|
});
|
|
3143
|
-
}
|
|
3144
|
-
})))),
|
|
3581
|
+
} }));
|
|
3582
|
+
}))))),
|
|
3145
3583
|
(actionErrorFilterState.type === undefined
|
|
3146
3584
|
|| actionErrorFilterState.type === LogType$1.Error) && (React__default["default"].createElement(TabBox, { title: "Error Log Details" },
|
|
3147
3585
|
React__default["default"].createElement("div", { className: "input-group mb-2" },
|
|
3148
3586
|
React__default["default"].createElement("span", { className: "input-group-text" }, "Error Message"),
|
|
3149
|
-
React__default["default"].createElement("input", { type: "text", className: "form-control", "aria-label": "query for error message", value: actionErrorFilterState.errorMessage, onChange: (e) => {
|
|
3587
|
+
React__default["default"].createElement("input", { type: "text", className: "form-control", "aria-label": "query for error message", value: actionErrorFilterState.errorMessage, placeholder: "e.g. undefined is not a function", onChange: (e) => {
|
|
3150
3588
|
actionErrorFilterState.errorMessage = e.target.value;
|
|
3151
3589
|
dispatch({
|
|
3152
3590
|
type: ActionType.UpdateActionErrorFilterState,
|
|
@@ -3155,7 +3593,7 @@ const LogReviewer = (props) => {
|
|
|
3155
3593
|
} })),
|
|
3156
3594
|
React__default["default"].createElement("div", { className: "input-group mb-2" },
|
|
3157
3595
|
React__default["default"].createElement("span", { className: "input-group-text" }, "Error Code"),
|
|
3158
|
-
React__default["default"].createElement("input", { type: "text", className: "form-control", "aria-label": "query for error code", value: actionErrorFilterState.
|
|
3596
|
+
React__default["default"].createElement("input", { type: "text", className: "form-control", "aria-label": "query for error code", value: actionErrorFilterState.errorCode, placeholder: "e.g. GC22", onChange: (e) => {
|
|
3159
3597
|
actionErrorFilterState.errorCode = ((e.target.value)
|
|
3160
3598
|
.trim()
|
|
3161
3599
|
.toUpperCase());
|
|
@@ -3168,10 +3606,10 @@ const LogReviewer = (props) => {
|
|
|
3168
3606
|
else if (expandedFilterDrawer === FilterDrawer.Advanced) {
|
|
3169
3607
|
// Create advanced filter ui
|
|
3170
3608
|
filterDrawer = (React__default["default"].createElement(React__default["default"].Fragment, null,
|
|
3171
|
-
React__default["default"].createElement(TabBox, { title: "User
|
|
3609
|
+
React__default["default"].createElement(TabBox, { title: "User" },
|
|
3172
3610
|
React__default["default"].createElement("div", { className: "input-group mb-2" },
|
|
3173
3611
|
React__default["default"].createElement("span", { className: "input-group-text" }, "User First Name"),
|
|
3174
|
-
React__default["default"].createElement("input", { type: "text", className: "form-control", "aria-label": "query for user first name", value: advancedFilterState.userFirstName, onChange: (e) => {
|
|
3612
|
+
React__default["default"].createElement("input", { type: "text", className: "form-control", "aria-label": "query for user first name", value: advancedFilterState.userFirstName, placeholder: "e.g. Divardo", onChange: (e) => {
|
|
3175
3613
|
advancedFilterState.userFirstName = e.target.value;
|
|
3176
3614
|
dispatch({
|
|
3177
3615
|
type: ActionType.UpdateAdvancedFilterState,
|
|
@@ -3180,7 +3618,7 @@ const LogReviewer = (props) => {
|
|
|
3180
3618
|
} })),
|
|
3181
3619
|
React__default["default"].createElement("div", { className: "input-group mb-2" },
|
|
3182
3620
|
React__default["default"].createElement("span", { className: "input-group-text" }, "User Last Name"),
|
|
3183
|
-
React__default["default"].createElement("input", { type: "text", className: "form-control", "aria-label": "query for user last name", value: advancedFilterState.userLastName, onChange: (e) => {
|
|
3621
|
+
React__default["default"].createElement("input", { type: "text", className: "form-control", "aria-label": "query for user last name", value: advancedFilterState.userLastName, placeholder: "e.g. Calicci", onChange: (e) => {
|
|
3184
3622
|
advancedFilterState.userLastName = e.target.value;
|
|
3185
3623
|
dispatch({
|
|
3186
3624
|
type: ActionType.UpdateAdvancedFilterState,
|
|
@@ -3189,7 +3627,7 @@ const LogReviewer = (props) => {
|
|
|
3189
3627
|
} })),
|
|
3190
3628
|
React__default["default"].createElement("div", { className: "input-group mb-2" },
|
|
3191
3629
|
React__default["default"].createElement("span", { className: "input-group-text" }, "User Email"),
|
|
3192
|
-
React__default["default"].createElement("input", { type: "text", className: "form-control", "aria-label": "query for user email", value: advancedFilterState.userEmail, onChange: (e) => {
|
|
3630
|
+
React__default["default"].createElement("input", { type: "text", className: "form-control", "aria-label": "query for user email", value: advancedFilterState.userEmail, placeholder: "e.g. calicci@fas.harvard.edu", onChange: (e) => {
|
|
3193
3631
|
advancedFilterState.userEmail = ((e.target.value)
|
|
3194
3632
|
.trim());
|
|
3195
3633
|
dispatch({
|
|
@@ -3199,7 +3637,7 @@ const LogReviewer = (props) => {
|
|
|
3199
3637
|
} })),
|
|
3200
3638
|
React__default["default"].createElement("div", { className: "input-group mb-2" },
|
|
3201
3639
|
React__default["default"].createElement("span", { className: "input-group-text" }, "User Canvas Id"),
|
|
3202
|
-
React__default["default"].createElement("input", { type: "text", className: "form-control", "aria-label": "query for user canvas id", value: advancedFilterState.userId, onChange: (e) => {
|
|
3640
|
+
React__default["default"].createElement("input", { type: "text", className: "form-control", "aria-label": "query for user canvas id", value: advancedFilterState.userId, placeholder: "e.g. 104985", onChange: (e) => {
|
|
3203
3641
|
const { value } = e.target;
|
|
3204
3642
|
// Only update if value contains only numbers
|
|
3205
3643
|
if (/^\d+$/.test(value)) {
|
|
@@ -3233,10 +3671,10 @@ const LogReviewer = (props) => {
|
|
|
3233
3671
|
advancedFilterState,
|
|
3234
3672
|
});
|
|
3235
3673
|
}, checked: advancedFilterState.includeAdmins, ariaLabel: "show logs from admins" }))),
|
|
3236
|
-
React__default["default"].createElement(TabBox, { title: "Course
|
|
3674
|
+
React__default["default"].createElement(TabBox, { title: "Course" },
|
|
3237
3675
|
React__default["default"].createElement("div", { className: "input-group mb-2" },
|
|
3238
3676
|
React__default["default"].createElement("span", { className: "input-group-text" }, "Course Name"),
|
|
3239
|
-
React__default["default"].createElement("input", { type: "text", className: "form-control", "aria-label": "query for course name", value: advancedFilterState.courseName, onChange: (e) => {
|
|
3677
|
+
React__default["default"].createElement("input", { type: "text", className: "form-control", "aria-label": "query for course name", value: advancedFilterState.courseName, placeholder: "e.g. GLC 200", onChange: (e) => {
|
|
3240
3678
|
advancedFilterState.courseName = e.target.value;
|
|
3241
3679
|
dispatch({
|
|
3242
3680
|
type: ActionType.UpdateAdvancedFilterState,
|
|
@@ -3245,7 +3683,7 @@ const LogReviewer = (props) => {
|
|
|
3245
3683
|
} })),
|
|
3246
3684
|
React__default["default"].createElement("div", { className: "input-group mb-2" },
|
|
3247
3685
|
React__default["default"].createElement("span", { className: "input-group-text" }, "Course Canvas Id"),
|
|
3248
|
-
React__default["default"].createElement("input", { type: "text", className: "form-control", "aria-label": "query for course canvas id", value: advancedFilterState.courseId, onChange: (e) => {
|
|
3686
|
+
React__default["default"].createElement("input", { type: "text", className: "form-control", "aria-label": "query for course canvas id", value: advancedFilterState.courseId, placeholder: "e.g. 15948", onChange: (e) => {
|
|
3249
3687
|
const { value } = e.target;
|
|
3250
3688
|
// Only update if value contains only numbers
|
|
3251
3689
|
if (/^\d+$/.test(value)) {
|
|
@@ -3257,7 +3695,7 @@ const LogReviewer = (props) => {
|
|
|
3257
3695
|
advancedFilterState,
|
|
3258
3696
|
});
|
|
3259
3697
|
} }))),
|
|
3260
|
-
React__default["default"].createElement(TabBox, { title: "Device
|
|
3698
|
+
React__default["default"].createElement(TabBox, { title: "Device" },
|
|
3261
3699
|
React__default["default"].createElement(ButtonInputGroup, { label: "Device Type" },
|
|
3262
3700
|
React__default["default"].createElement(RadioButton, { text: "All Devices", ariaLabel: "show logs from all devices", selected: advancedFilterState.isMobile === undefined, onSelected: () => {
|
|
3263
3701
|
advancedFilterState.isMobile = undefined;
|
|
@@ -3303,32 +3741,33 @@ const LogReviewer = (props) => {
|
|
|
3303
3741
|
advancedFilterState,
|
|
3304
3742
|
});
|
|
3305
3743
|
}, noMarginOnRight: true })),
|
|
3306
|
-
React__default["default"].createElement("div", { className: "
|
|
3307
|
-
React__default["default"].createElement("
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
.
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
React__default["default"].createElement("
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
.
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
|
|
3744
|
+
advancedFilterState.source !== LogSource$1.Client && (React__default["default"].createElement("div", { className: "mt-2" },
|
|
3745
|
+
React__default["default"].createElement("div", { className: "input-group mb-2" },
|
|
3746
|
+
React__default["default"].createElement("span", { className: "input-group-text" }, "Server Route Path"),
|
|
3747
|
+
React__default["default"].createElement("input", { type: "text", className: "form-control", "aria-label": "query for server route path", value: advancedFilterState.routePath, placeholder: "e.g. /api/ttm/courses/12345", onChange: (e) => {
|
|
3748
|
+
advancedFilterState.courseName = ((e.target.value)
|
|
3749
|
+
.trim());
|
|
3750
|
+
dispatch({
|
|
3751
|
+
type: ActionType.UpdateAdvancedFilterState,
|
|
3752
|
+
advancedFilterState,
|
|
3753
|
+
});
|
|
3754
|
+
} })),
|
|
3755
|
+
React__default["default"].createElement("div", { className: "input-group mb-2" },
|
|
3756
|
+
React__default["default"].createElement("span", { className: "input-group-text" }, "Server Route Template"),
|
|
3757
|
+
React__default["default"].createElement("input", { type: "text", className: "form-control", "aria-label": "query for server route template", value: advancedFilterState.routeTemplate, placeholder: "e.g. /api/ttm/courses/:courseId", onChange: (e) => {
|
|
3758
|
+
advancedFilterState.courseName = ((e.target.value)
|
|
3759
|
+
.trim());
|
|
3760
|
+
dispatch({
|
|
3761
|
+
type: ActionType.UpdateAdvancedFilterState,
|
|
3762
|
+
advancedFilterState,
|
|
3763
|
+
});
|
|
3764
|
+
} })))))));
|
|
3326
3765
|
}
|
|
3327
3766
|
}
|
|
3328
3767
|
// Filters UI
|
|
3329
3768
|
const filters = (React__default["default"].createElement(React__default["default"].Fragment, null,
|
|
3330
3769
|
filterToggles,
|
|
3331
|
-
filterDrawer && (React__default["default"].createElement(Drawer,
|
|
3770
|
+
filterDrawer && (React__default["default"].createElement(Drawer, { customBackgroundColor: "#eee" }, filterDrawer))));
|
|
3332
3771
|
// Actually filter the logs
|
|
3333
3772
|
// > Perform filters
|
|
3334
3773
|
const logs = [];
|
|
@@ -3369,16 +3808,22 @@ const LogReviewer = (props) => {
|
|
|
3369
3808
|
// Whole context is deselected
|
|
3370
3809
|
contextFilterState[log.context] === false
|
|
3371
3810
|
// None of the subcontexts are selected
|
|
3372
|
-
|| (
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3811
|
+
|| (
|
|
3812
|
+
// Has subcontexts
|
|
3813
|
+
typeof contextFilterState[log.context] !== 'boolean'
|
|
3814
|
+
// None of the subcontexts are selected
|
|
3815
|
+
&& Object.values((_a = contextFilterState[log.context]) !== null && _a !== void 0 ? _a : {})
|
|
3816
|
+
.every((isSelected) => {
|
|
3817
|
+
return !isSelected;
|
|
3818
|
+
}))) {
|
|
3376
3819
|
return;
|
|
3377
3820
|
}
|
|
3378
3821
|
// Subcontext doesn't match
|
|
3379
3822
|
if (
|
|
3380
|
-
// Log
|
|
3381
|
-
log.
|
|
3823
|
+
// Log context is not "uncategorized" (no point in further filters)
|
|
3824
|
+
log.context !== LogBuiltInMetadata.Context.Uncategorized
|
|
3825
|
+
// Log has a subcontext
|
|
3826
|
+
&& log.subcontext
|
|
3382
3827
|
// Context has subcontexts
|
|
3383
3828
|
&& (contextFilterState[log.context]
|
|
3384
3829
|
&& contextFilterState[log.context] !== false
|
|
@@ -3390,12 +3835,16 @@ const LogReviewer = (props) => {
|
|
|
3390
3835
|
/* -------------- Tags -------------- */
|
|
3391
3836
|
// No tags match
|
|
3392
3837
|
if (
|
|
3393
|
-
//
|
|
3394
|
-
|
|
3838
|
+
// At least one tag is required
|
|
3839
|
+
Object.values(tagFilterState)
|
|
3840
|
+
.filter((isSelected) => {
|
|
3841
|
+
return isSelected;
|
|
3842
|
+
})
|
|
3843
|
+
.length > 0
|
|
3395
3844
|
// No tags match
|
|
3396
|
-
&&
|
|
3845
|
+
&& log.tags.every((tag) => {
|
|
3397
3846
|
return !tagFilterState[tag];
|
|
3398
|
-
}))
|
|
3847
|
+
})) {
|
|
3399
3848
|
return;
|
|
3400
3849
|
}
|
|
3401
3850
|
/* ------- Actions and Errors ------- */
|
|
@@ -3529,7 +3978,7 @@ const LogReviewer = (props) => {
|
|
|
3529
3978
|
// Device info exists
|
|
3530
3979
|
&& log.device
|
|
3531
3980
|
// Mobile filter doesn't match
|
|
3532
|
-
&& (advancedFilterState.isMobile
|
|
3981
|
+
&& (advancedFilterState.isMobile !== log.device.isMobile)) {
|
|
3533
3982
|
return;
|
|
3534
3983
|
}
|
|
3535
3984
|
// Log source doesn't match
|
|
@@ -3571,203 +4020,20 @@ const LogReviewer = (props) => {
|
|
|
3571
4020
|
/*----------------------------------------*/
|
|
3572
4021
|
/* Data */
|
|
3573
4022
|
/*----------------------------------------*/
|
|
3574
|
-
//
|
|
3575
|
-
const
|
|
3576
|
-
{
|
|
3577
|
-
title: 'First Name',
|
|
3578
|
-
param: 'userFirstName',
|
|
3579
|
-
type: ParamType$1.String,
|
|
3580
|
-
},
|
|
3581
|
-
{
|
|
3582
|
-
title: 'Last Name',
|
|
3583
|
-
param: 'userLastName',
|
|
3584
|
-
type: ParamType$1.String,
|
|
3585
|
-
},
|
|
3586
|
-
{
|
|
3587
|
-
title: 'Email',
|
|
3588
|
-
param: 'userEmail',
|
|
3589
|
-
type: ParamType$1.String,
|
|
3590
|
-
},
|
|
3591
|
-
{
|
|
3592
|
-
title: 'Canvas Id',
|
|
3593
|
-
param: 'userId',
|
|
3594
|
-
type: ParamType$1.Int,
|
|
3595
|
-
},
|
|
3596
|
-
{
|
|
3597
|
-
title: 'Student',
|
|
3598
|
-
param: 'isLearner',
|
|
3599
|
-
type: ParamType$1.Boolean,
|
|
3600
|
-
},
|
|
3601
|
-
{
|
|
3602
|
-
title: 'Teaching Staff',
|
|
3603
|
-
param: 'isTTM',
|
|
3604
|
-
type: ParamType$1.Boolean,
|
|
3605
|
-
startsHidden: true,
|
|
3606
|
-
},
|
|
3607
|
-
{
|
|
3608
|
-
title: 'Admin',
|
|
3609
|
-
param: 'isAdmin',
|
|
3610
|
-
type: ParamType$1.Boolean,
|
|
3611
|
-
startsHidden: true,
|
|
3612
|
-
},
|
|
3613
|
-
{
|
|
3614
|
-
title: 'Course Canvas Id',
|
|
3615
|
-
param: 'courseId',
|
|
3616
|
-
type: ParamType$1.Int,
|
|
3617
|
-
startsHidden: true,
|
|
3618
|
-
},
|
|
3619
|
-
{
|
|
3620
|
-
title: 'Course Name',
|
|
3621
|
-
param: 'courseName',
|
|
3622
|
-
type: ParamType$1.String,
|
|
3623
|
-
},
|
|
3624
|
-
{
|
|
3625
|
-
title: 'Browser Name',
|
|
3626
|
-
param: 'browser.name',
|
|
3627
|
-
type: ParamType$1.String,
|
|
3628
|
-
startsHidden: true,
|
|
3629
|
-
},
|
|
3630
|
-
{
|
|
3631
|
-
title: 'Browser Version',
|
|
3632
|
-
param: 'browser.version',
|
|
3633
|
-
type: ParamType$1.String,
|
|
3634
|
-
startsHidden: true,
|
|
3635
|
-
},
|
|
3636
|
-
{
|
|
3637
|
-
title: 'OS',
|
|
3638
|
-
param: 'device.os',
|
|
3639
|
-
type: ParamType$1.String,
|
|
3640
|
-
startsHidden: true,
|
|
3641
|
-
},
|
|
3642
|
-
{
|
|
3643
|
-
title: 'Mobile',
|
|
3644
|
-
param: 'device.isMobile',
|
|
3645
|
-
type: ParamType$1.Boolean,
|
|
3646
|
-
startsHidden: true,
|
|
3647
|
-
},
|
|
3648
|
-
{
|
|
3649
|
-
title: 'Year',
|
|
3650
|
-
param: 'year',
|
|
3651
|
-
type: ParamType$1.Int,
|
|
3652
|
-
},
|
|
3653
|
-
{
|
|
3654
|
-
title: 'Month',
|
|
3655
|
-
param: 'month',
|
|
3656
|
-
type: ParamType$1.Int,
|
|
3657
|
-
},
|
|
3658
|
-
{
|
|
3659
|
-
title: 'Day',
|
|
3660
|
-
param: 'day',
|
|
3661
|
-
type: ParamType$1.Int,
|
|
3662
|
-
},
|
|
3663
|
-
{
|
|
3664
|
-
title: 'Hour',
|
|
3665
|
-
param: 'hour',
|
|
3666
|
-
type: ParamType$1.Int,
|
|
3667
|
-
},
|
|
3668
|
-
{
|
|
3669
|
-
title: 'Minute',
|
|
3670
|
-
param: 'minute',
|
|
3671
|
-
type: ParamType$1.Int,
|
|
3672
|
-
startsHidden: true,
|
|
3673
|
-
},
|
|
3674
|
-
{
|
|
3675
|
-
title: 'Timestamp',
|
|
3676
|
-
param: 'timestamp',
|
|
3677
|
-
type: ParamType$1.Int,
|
|
3678
|
-
startsHidden: true,
|
|
3679
|
-
},
|
|
3680
|
-
{
|
|
3681
|
-
title: 'Context',
|
|
3682
|
-
param: 'context',
|
|
3683
|
-
type: ParamType$1.String,
|
|
3684
|
-
},
|
|
3685
|
-
{
|
|
3686
|
-
title: 'Subcontext',
|
|
3687
|
-
param: 'subcontext',
|
|
3688
|
-
type: ParamType$1.String,
|
|
3689
|
-
},
|
|
3690
|
-
{
|
|
3691
|
-
title: 'Tags',
|
|
3692
|
-
param: 'tags',
|
|
3693
|
-
type: ParamType$1.JSON,
|
|
3694
|
-
startsHidden: true,
|
|
3695
|
-
},
|
|
3696
|
-
{
|
|
3697
|
-
title: 'Log Level',
|
|
3698
|
-
param: 'level',
|
|
3699
|
-
type: ParamType$1.String,
|
|
3700
|
-
startsHidden: true,
|
|
3701
|
-
},
|
|
3702
|
-
{
|
|
3703
|
-
title: 'Metadata',
|
|
3704
|
-
param: 'metadata',
|
|
3705
|
-
type: ParamType$1.JSON,
|
|
3706
|
-
startsHidden: true,
|
|
3707
|
-
},
|
|
3708
|
-
{
|
|
3709
|
-
title: 'Source',
|
|
3710
|
-
param: 'source',
|
|
3711
|
-
type: ParamType$1.String,
|
|
3712
|
-
},
|
|
3713
|
-
{
|
|
3714
|
-
title: 'Server Route Path',
|
|
3715
|
-
param: 'routePath',
|
|
3716
|
-
type: ParamType$1.String,
|
|
3717
|
-
startsHidden: true,
|
|
3718
|
-
},
|
|
3719
|
-
{
|
|
3720
|
-
title: 'Server Route Template',
|
|
3721
|
-
param: 'routeTemplate',
|
|
3722
|
-
type: ParamType$1.String,
|
|
3723
|
-
startsHidden: true,
|
|
3724
|
-
},
|
|
3725
|
-
{
|
|
3726
|
-
title: 'Type',
|
|
3727
|
-
param: 'type',
|
|
3728
|
-
type: ParamType$1.String,
|
|
3729
|
-
},
|
|
3730
|
-
{
|
|
3731
|
-
title: 'Error Message',
|
|
3732
|
-
param: 'errorMessage',
|
|
3733
|
-
type: ParamType$1.String,
|
|
3734
|
-
startsHidden: true,
|
|
3735
|
-
},
|
|
3736
|
-
{
|
|
3737
|
-
title: 'Error Code',
|
|
3738
|
-
param: 'errorCode',
|
|
3739
|
-
type: ParamType$1.String,
|
|
3740
|
-
startsHidden: true,
|
|
3741
|
-
},
|
|
3742
|
-
{
|
|
3743
|
-
title: 'Error Stack',
|
|
3744
|
-
param: 'errorStack',
|
|
3745
|
-
type: ParamType$1.String,
|
|
3746
|
-
startsHidden: true,
|
|
3747
|
-
},
|
|
3748
|
-
{
|
|
3749
|
-
title: 'Action Target',
|
|
3750
|
-
param: 'target',
|
|
3751
|
-
type: ParamType$1.String,
|
|
3752
|
-
startsHidden: true,
|
|
3753
|
-
},
|
|
3754
|
-
{
|
|
3755
|
-
title: 'Action Type',
|
|
3756
|
-
param: 'action',
|
|
3757
|
-
type: ParamType$1.String,
|
|
3758
|
-
startsHidden: true,
|
|
3759
|
-
},
|
|
3760
|
-
];
|
|
3761
|
-
// Create intelliTable
|
|
3762
|
-
const dataTable = (logs.length === 0
|
|
3763
|
-
? (React__default["default"].createElement("div", { className: "alert alert-warning" },
|
|
4023
|
+
// Nothing to show notice
|
|
4024
|
+
const noLogsNotice = (logs.length === 0
|
|
4025
|
+
? (React__default["default"].createElement("div", { className: "alert alert-warning text-center mt-2" },
|
|
3764
4026
|
React__default["default"].createElement("h4", { className: "m-1" }, "No Logs to Show"),
|
|
3765
4027
|
React__default["default"].createElement("div", null, "Either your filters are too strict or no matching logs have been created yet.")))
|
|
3766
|
-
:
|
|
4028
|
+
: undefined);
|
|
4029
|
+
// Create intelliTable
|
|
4030
|
+
const dataTable = (React__default["default"].createElement(IntelliTable, { title: "Matching Logs:", csvName: `Logs from ${getHumanReadableDate()}`, id: "logs", data: logs, columns: columns }));
|
|
3767
4031
|
// Main body
|
|
3768
4032
|
body = (React__default["default"].createElement(React__default["default"].Fragment, null,
|
|
3769
4033
|
filters,
|
|
3770
|
-
|
|
4034
|
+
React__default["default"].createElement("div", { className: "mt-2" },
|
|
4035
|
+
dataTable,
|
|
4036
|
+
noLogsNotice)));
|
|
3771
4037
|
}
|
|
3772
4038
|
/* ---------- Wrap in Modal --------- */
|
|
3773
4039
|
return (React__default["default"].createElement("div", { className: "LogReviewer-outer-container" },
|
|
@@ -3775,9 +4041,10 @@ const LogReviewer = (props) => {
|
|
|
3775
4041
|
React__default["default"].createElement("div", { className: "LogReviewer-inner-container" },
|
|
3776
4042
|
React__default["default"].createElement("div", { className: "LogReviewer-header" },
|
|
3777
4043
|
React__default["default"].createElement("div", { className: "LogReviewer-header-title" },
|
|
3778
|
-
React__default["default"].createElement("h3", { className: "m-0" }, "Log Review Dashboard")),
|
|
3779
|
-
React__default["default"].createElement("
|
|
3780
|
-
React__default["default"].createElement(
|
|
4044
|
+
React__default["default"].createElement("h3", { className: "text-center m-0" }, "Log Review Dashboard")),
|
|
4045
|
+
React__default["default"].createElement("div", { style: { width: 0 } },
|
|
4046
|
+
React__default["default"].createElement("button", { type: "button", className: "LogReviewer-header-close-button btn btn-dark btn-lg pe-0", "aria-label": "close log reviewer panel", onClick: onClose },
|
|
4047
|
+
React__default["default"].createElement(reactFontawesome.FontAwesomeIcon, { icon: freeSolidSvgIcons.faTimes })))),
|
|
3781
4048
|
React__default["default"].createElement("div", { className: "LogReviewer-contents" }, body))));
|
|
3782
4049
|
};
|
|
3783
4050
|
|
|
@@ -3799,6 +4066,50 @@ const HOUR_IN_MS = 3600000;
|
|
|
3799
4066
|
*/
|
|
3800
4067
|
const DAY_IN_MS = 86400000;
|
|
3801
4068
|
|
|
4069
|
+
/**
|
|
4070
|
+
* Check if the user is on mobile
|
|
4071
|
+
* from https://stackoverflow.com/questions/11381673/detecting-a-mobile-browser
|
|
4072
|
+
* @returns true if user is on a mobile device
|
|
4073
|
+
*/
|
|
4074
|
+
const checkIfMobileOrTablet = () => {
|
|
4075
|
+
var _a;
|
|
4076
|
+
try {
|
|
4077
|
+
let check = false;
|
|
4078
|
+
((a) => { if (/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino|android|ipad|playbook|silk/i.test(a) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0, 4)))
|
|
4079
|
+
check = true; })((navigator !== null && navigator !== void 0 ? navigator : {}).userAgent || (navigator !== null && navigator !== void 0 ? navigator : {}).vendor || ((_a = window) !== null && _a !== void 0 ? _a : {}).opera);
|
|
4080
|
+
return check;
|
|
4081
|
+
}
|
|
4082
|
+
catch (err) {
|
|
4083
|
+
// Probably this code is being executed on a server
|
|
4084
|
+
return false;
|
|
4085
|
+
}
|
|
4086
|
+
};
|
|
4087
|
+
const isMobileOrTablet = checkIfMobileOrTablet();
|
|
4088
|
+
/**
|
|
4089
|
+
* Dynamic words determined by the user's platform
|
|
4090
|
+
* @author Gabe Abrams
|
|
4091
|
+
*/
|
|
4092
|
+
const DynamicWord = {
|
|
4093
|
+
Click: (isMobileOrTablet
|
|
4094
|
+
? 'tap'
|
|
4095
|
+
: 'click'),
|
|
4096
|
+
ClickCapitalized: (isMobileOrTablet
|
|
4097
|
+
? 'Tap'
|
|
4098
|
+
: 'Click'),
|
|
4099
|
+
App: (isMobileOrTablet
|
|
4100
|
+
? 'app'
|
|
4101
|
+
: 'application'),
|
|
4102
|
+
AppCapitalized: (isMobileOrTablet
|
|
4103
|
+
? 'App'
|
|
4104
|
+
: 'Application'),
|
|
4105
|
+
Device: (isMobileOrTablet
|
|
4106
|
+
? 'device'
|
|
4107
|
+
: 'computer'),
|
|
4108
|
+
DeviceCapitalized: (isMobileOrTablet
|
|
4109
|
+
? 'Device'
|
|
4110
|
+
: 'Computer'),
|
|
4111
|
+
};
|
|
4112
|
+
|
|
3802
4113
|
/**
|
|
3803
4114
|
* Shorten text so it fits into a certain number of chars
|
|
3804
4115
|
* @author Gabe Abrams
|
|
@@ -3873,6 +4184,18 @@ const floorToNumDecimals = (num, numDecimals) => {
|
|
|
3873
4184
|
return (Math.floor(num * rounder) / rounder);
|
|
3874
4185
|
};
|
|
3875
4186
|
|
|
4187
|
+
/**
|
|
4188
|
+
* Force a number to stay within specific bounds
|
|
4189
|
+
* @author Gabe Abrams
|
|
4190
|
+
* @param num the number to move into the bounds
|
|
4191
|
+
* @param min the minimum number in the bound
|
|
4192
|
+
* @param max the maximum number in the bound
|
|
4193
|
+
* @returns bounded number
|
|
4194
|
+
*/
|
|
4195
|
+
const forceNumIntoBounds = (num, min, max) => {
|
|
4196
|
+
return Math.max(min, Math.min(max, num));
|
|
4197
|
+
};
|
|
4198
|
+
|
|
3876
4199
|
/**
|
|
3877
4200
|
* Pad a number's decimal with zeros on the right
|
|
3878
4201
|
* (e.g. 5.2 becomes 5.20 with 2 digit padding)
|
|
@@ -4108,15 +4431,17 @@ const initServer = (opts) => {
|
|
|
4108
4431
|
},
|
|
4109
4432
|
handler: ({ params }) => __awaiter(void 0, void 0, void 0, function* () {
|
|
4110
4433
|
// Get user info
|
|
4111
|
-
const { userId, isAdmin } = params;
|
|
4434
|
+
const { year, month, userId, isAdmin, } = params;
|
|
4112
4435
|
// Validate user
|
|
4113
|
-
// isAdmin is already checked because path starts with '/admin'
|
|
4114
4436
|
const canReview = yield canReviewLogs(userId, isAdmin);
|
|
4115
4437
|
if (!canReview) {
|
|
4116
4438
|
throw new ErrorWithCode('You cannot access this resource because you do not have the appropriate permissions.', ReactKitErrorCode$1.NotAllowedToReviewLogs);
|
|
4117
4439
|
}
|
|
4118
4440
|
// Query for logs
|
|
4119
|
-
const logs = yield _logCollection.find({
|
|
4441
|
+
const logs = yield _logCollection.find({
|
|
4442
|
+
year,
|
|
4443
|
+
month,
|
|
4444
|
+
});
|
|
4120
4445
|
// Return logs
|
|
4121
4446
|
return logs;
|
|
4122
4447
|
}),
|
|
@@ -4765,7 +5090,7 @@ const genRouteHandler = (opts) => {
|
|
|
4765
5090
|
isAdmin: !!launchInfo.isAdmin,
|
|
4766
5091
|
isTTM: !!launchInfo.isTTM,
|
|
4767
5092
|
courseId: launchInfo.courseId,
|
|
4768
|
-
courseName: launchInfo.
|
|
5093
|
+
courseName: launchInfo.contextLabel,
|
|
4769
5094
|
browser,
|
|
4770
5095
|
device,
|
|
4771
5096
|
year,
|
|
@@ -4792,7 +5117,7 @@ const genRouteHandler = (opts) => {
|
|
|
4792
5117
|
}
|
|
4793
5118
|
: {
|
|
4794
5119
|
type: LogType$1.Action,
|
|
4795
|
-
target: ((_m = opts.target) !== null && _m !== void 0 ? _m : LogBuiltInMetadata.Target.
|
|
5120
|
+
target: ((_m = opts.target) !== null && _m !== void 0 ? _m : LogBuiltInMetadata.Target.NoTarget),
|
|
4796
5121
|
action: ((_o = opts.action) !== null && _o !== void 0 ? _o : LogAction$1.Unknown),
|
|
4797
5122
|
});
|
|
4798
5123
|
// Source-specific info
|
|
@@ -5005,29 +5330,6 @@ const startMinWait = (minWaitMs) => {
|
|
|
5005
5330
|
});
|
|
5006
5331
|
};
|
|
5007
5332
|
|
|
5008
|
-
// Import shared helpers
|
|
5009
|
-
/**
|
|
5010
|
-
* Get a human-readable description of a date (all in ET)
|
|
5011
|
-
* @author Gabe Abrams
|
|
5012
|
-
* @param [dateOrTimestamp=today] the date or timestamp for the date to describe
|
|
5013
|
-
* @returns human-readable description of the date
|
|
5014
|
-
*/
|
|
5015
|
-
const getHumanReadableDate = (dateOrTimestamp) => {
|
|
5016
|
-
// Get the date info
|
|
5017
|
-
const { month, day, year, } = getTimeInfoInET(dateOrTimestamp);
|
|
5018
|
-
const currYear = getTimeInfoInET().year;
|
|
5019
|
-
// Get the short month description
|
|
5020
|
-
const monthName = getMonthName(month).short;
|
|
5021
|
-
// Create start of description
|
|
5022
|
-
let description = `${monthName} ${day}${getOrdinal(day)}`;
|
|
5023
|
-
// Add on year if it's different
|
|
5024
|
-
if (year !== currYear) {
|
|
5025
|
-
description += ` ${year}`;
|
|
5026
|
-
}
|
|
5027
|
-
// Return description
|
|
5028
|
-
return description;
|
|
5029
|
-
};
|
|
5030
|
-
|
|
5031
5333
|
/**
|
|
5032
5334
|
* Get the current part of day (morning, evening, etc.)
|
|
5033
5335
|
* @author Gabe Abrams
|
|
@@ -5208,6 +5510,7 @@ exports.CopiableBox = CopiableBox;
|
|
|
5208
5510
|
exports.DAY_IN_MS = DAY_IN_MS;
|
|
5209
5511
|
exports.DayOfWeek = DayOfWeek$1;
|
|
5210
5512
|
exports.Drawer = Drawer;
|
|
5513
|
+
exports.DynamicWord = DynamicWord;
|
|
5211
5514
|
exports.ErrorBox = ErrorBox;
|
|
5212
5515
|
exports.ErrorWithCode = ErrorWithCode;
|
|
5213
5516
|
exports.HOUR_IN_MS = HOUR_IN_MS;
|