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/esm/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { useState, useRef, useEffect, useReducer } from 'react';
|
|
2
2
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
|
3
|
-
import { faExclamationTriangle, faCircle, faDotCircle, faCheckSquare, faHourglass, faClipboard, faChevronDown, faChevronRight, faCloudDownloadAlt, faCheckCircle, faXmarkCircle,
|
|
3
|
+
import { faExclamationTriangle, faCircle, faDotCircle, faCheckSquare, faHourglass, faClipboard, faChevronDown, faChevronRight, faCloudDownloadAlt, faMinus, faCheckCircle, faXmarkCircle, faSort, faSortDown, faSortUp, faCalendar, faTag, faHammer, faList, faTimes } from '@fortawesome/free-solid-svg-icons';
|
|
4
4
|
import { faCircle as faCircle$1, faSquareMinus, faSquare } from '@fortawesome/free-regular-svg-icons';
|
|
5
5
|
|
|
6
6
|
/******************************************************************************
|
|
@@ -511,14 +511,14 @@ const LOG_ROUTE_PATH = `${ROUTE_PATH_PREFIX}/log`;
|
|
|
511
511
|
const LogBuiltInMetadata = {
|
|
512
512
|
// Contexts
|
|
513
513
|
Context: {
|
|
514
|
-
Uncategorized: '
|
|
515
|
-
ServerRenderedErrorPage: '
|
|
516
|
-
ServerEndpointError: '
|
|
517
|
-
ClientFatalError: '
|
|
514
|
+
Uncategorized: 'Uncategorized',
|
|
515
|
+
ServerRenderedErrorPage: 'ServerRenderedErrorPage',
|
|
516
|
+
ServerEndpointError: 'ServerEndpointError',
|
|
517
|
+
ClientFatalError: 'ClientFatalError',
|
|
518
518
|
},
|
|
519
519
|
// Targets
|
|
520
520
|
Target: {
|
|
521
|
-
|
|
521
|
+
NoTarget: 'NoTarget',
|
|
522
522
|
},
|
|
523
523
|
};
|
|
524
524
|
|
|
@@ -674,7 +674,7 @@ const logClientEvent = (opts) => __awaiter(void 0, void 0, void 0, function* ()
|
|
|
674
674
|
? opts.error.stack
|
|
675
675
|
: undefined),
|
|
676
676
|
target: (opts.action
|
|
677
|
-
? ((_g = opts.target) !== null && _g !== void 0 ? _g : LogBuiltInMetadata.Target.
|
|
677
|
+
? ((_g = opts.target) !== null && _g !== void 0 ? _g : LogBuiltInMetadata.Target.NoTarget)
|
|
678
678
|
: undefined),
|
|
679
679
|
action: (opts.action
|
|
680
680
|
? opts.action
|
|
@@ -1194,7 +1194,7 @@ const ButtonInputGroup = (props) => {
|
|
|
1194
1194
|
borderTopRightRadius: 0,
|
|
1195
1195
|
borderBottomRightRadius: 0,
|
|
1196
1196
|
} }, label),
|
|
1197
|
-
React.createElement("span", { className: "input-group-text flex-grow-1 rounded-right", style: {
|
|
1197
|
+
React.createElement("span", { className: "input-group-text flex-grow-1 rounded-right d-flex flex-wrap", style: {
|
|
1198
1198
|
backgroundColor: 'white',
|
|
1199
1199
|
borderTopLeftRadius: 0,
|
|
1200
1200
|
borderBottomLeftRadius: 0,
|
|
@@ -1299,18 +1299,6 @@ const getTimeInfoInET = (dateOrTimestamp) => {
|
|
|
1299
1299
|
};
|
|
1300
1300
|
};
|
|
1301
1301
|
|
|
1302
|
-
/**
|
|
1303
|
-
* Force a number to stay within specific bounds
|
|
1304
|
-
* @author Gabe Abrams
|
|
1305
|
-
* @param num the number to move into the bounds
|
|
1306
|
-
* @param min the minimum number in the bound
|
|
1307
|
-
* @param max the maximum number in the bound
|
|
1308
|
-
* @returns bounded number
|
|
1309
|
-
*/
|
|
1310
|
-
const forceNumIntoBounds = (num, min, max) => {
|
|
1311
|
-
return Math.max(min, Math.min(max, num));
|
|
1312
|
-
};
|
|
1313
|
-
|
|
1314
1302
|
/**
|
|
1315
1303
|
* A very simple, lightweight date chooser
|
|
1316
1304
|
* @author Gabe Abrams
|
|
@@ -1325,7 +1313,7 @@ const SimpleDateChooser = (props) => {
|
|
|
1325
1313
|
var _a;
|
|
1326
1314
|
/* -------------- Props ------------- */
|
|
1327
1315
|
const { ariaLabel, name, month, day, year, onChange, chooseFromPast, } = props;
|
|
1328
|
-
|
|
1316
|
+
const numMonthsToShow = ((_a = props.numMonthsToShow) !== null && _a !== void 0 ? _a : 6);
|
|
1329
1317
|
/*------------------------------------------------------------------------*/
|
|
1330
1318
|
/* Render */
|
|
1331
1319
|
/*------------------------------------------------------------------------*/
|
|
@@ -1339,7 +1327,7 @@ const SimpleDateChooser = (props) => {
|
|
|
1339
1327
|
let startMonth = today.month;
|
|
1340
1328
|
if (chooseFromPast) {
|
|
1341
1329
|
startMonth -= Math.max(0, numMonthsToShow - 1);
|
|
1342
|
-
|
|
1330
|
+
while (startMonth <= 0) {
|
|
1343
1331
|
startMonth += 12;
|
|
1344
1332
|
startYear -= 1;
|
|
1345
1333
|
}
|
|
@@ -1347,22 +1335,35 @@ const SimpleDateChooser = (props) => {
|
|
|
1347
1335
|
for (let i = 0; i < numMonthsToShow; i++) {
|
|
1348
1336
|
// Get month and year info
|
|
1349
1337
|
const unmoddedMonth = (startMonth + i);
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1338
|
+
let month = unmoddedMonth;
|
|
1339
|
+
while (month > 12) {
|
|
1340
|
+
month -= 12;
|
|
1341
|
+
}
|
|
1353
1342
|
const monthName = getMonthName(month).full;
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
: startYear);
|
|
1343
|
+
// Year is start year +1 for each 12 months
|
|
1344
|
+
const year = (startYear + (Math.floor(unmoddedMonth / 12)));
|
|
1357
1345
|
// Figure out which days are allowed
|
|
1358
1346
|
const days = [];
|
|
1359
1347
|
const numDaysInMonth = (new Date(year, month, 0)).getDate();
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1348
|
+
if (chooseFromPast) {
|
|
1349
|
+
// Past selection
|
|
1350
|
+
const numDaysToAdd = ((month === today.month)
|
|
1351
|
+
? today.day // Current month, only add up to today
|
|
1352
|
+
: numDaysInMonth // Past month, add all days
|
|
1353
|
+
);
|
|
1354
|
+
for (let day = 1; day <= numDaysToAdd; day++) {
|
|
1355
|
+
days.push(day);
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
else {
|
|
1359
|
+
// Future selection: add all remaining days of the month
|
|
1360
|
+
const firstDay = (month === today.month
|
|
1361
|
+
? today.day // Current month: start at current date
|
|
1362
|
+
: 1 // Future month: start at beginning of month
|
|
1363
|
+
);
|
|
1364
|
+
for (let day = firstDay; day <= numDaysInMonth; day++) {
|
|
1365
|
+
days.push(day);
|
|
1366
|
+
}
|
|
1366
1367
|
}
|
|
1367
1368
|
choices.push({
|
|
1368
1369
|
choiceName: `${monthName} ${year}`,
|
|
@@ -1376,7 +1377,7 @@ const SimpleDateChooser = (props) => {
|
|
|
1376
1377
|
const dayOptions = [];
|
|
1377
1378
|
choices.forEach((choice) => {
|
|
1378
1379
|
// Create month option
|
|
1379
|
-
monthOptions.push(React.createElement("option", { key: choice.month, value: choice.month
|
|
1380
|
+
monthOptions.push(React.createElement("option", { key: choice.month, value: `${choice.month}-${choice.year}`, "aria-label": `choose ${choice.choiceName}`, onSelect: () => {
|
|
1380
1381
|
onChange(choice.month, choice.days[0], choice.year);
|
|
1381
1382
|
} }, choice.choiceName));
|
|
1382
1383
|
if (month === choice.month) {
|
|
@@ -1391,7 +1392,7 @@ const SimpleDateChooser = (props) => {
|
|
|
1391
1392
|
}
|
|
1392
1393
|
});
|
|
1393
1394
|
return (React.createElement("div", { className: "SimpleDateChooser d-inline-block", "aria-label": `date chooser with selected date: ${month} ${day}, ${year}` },
|
|
1394
|
-
React.createElement("select", { "aria-label": `month for ${ariaLabel}`, className: "custom-select d-inline-block mr-1", style: { width: 'auto' }, id: `SimpleDateChooser-${name}-month`, value: month
|
|
1395
|
+
React.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) => {
|
|
1395
1396
|
const choice = choices[e.target.selectedIndex];
|
|
1396
1397
|
// Change day, month, and year
|
|
1397
1398
|
onChange(choice.month, choice.days[0], choice.year);
|
|
@@ -1433,14 +1434,16 @@ const Drawer = (props) => {
|
|
|
1433
1434
|
/*------------------------------------------------------------------------*/
|
|
1434
1435
|
/* -------------- Props ------------- */
|
|
1435
1436
|
// Destructure all props
|
|
1436
|
-
const { children, } = props;
|
|
1437
|
+
const { customBackgroundColor, children, } = props;
|
|
1437
1438
|
/*------------------------------------------------------------------------*/
|
|
1438
1439
|
/* Render */
|
|
1439
1440
|
/*------------------------------------------------------------------------*/
|
|
1440
1441
|
/*----------------------------------------*/
|
|
1441
1442
|
/* Main UI */
|
|
1442
1443
|
/*----------------------------------------*/
|
|
1443
|
-
return (React.createElement("div", { className: "Drawer-container"
|
|
1444
|
+
return (React.createElement("div", { className: "Drawer-container", style: {
|
|
1445
|
+
backgroundColor: (customBackgroundColor !== null && customBackgroundColor !== void 0 ? customBackgroundColor : undefined),
|
|
1446
|
+
} },
|
|
1444
1447
|
React.createElement("style", null, style$4),
|
|
1445
1448
|
children));
|
|
1446
1449
|
};
|
|
@@ -1903,24 +1906,25 @@ const CopiableBox = (props) => {
|
|
|
1903
1906
|
/**
|
|
1904
1907
|
* Reusable nested item picker
|
|
1905
1908
|
* @author Yuen Ler Chow
|
|
1909
|
+
* @author Gabe Abrams
|
|
1906
1910
|
*/
|
|
1907
1911
|
/* ------------- Actions ------------ */
|
|
1908
1912
|
// Types of actions
|
|
1909
1913
|
var ActionType$2;
|
|
1910
1914
|
(function (ActionType) {
|
|
1911
|
-
// Toggle whether
|
|
1912
|
-
ActionType["
|
|
1915
|
+
// Toggle whether a child are being shown
|
|
1916
|
+
ActionType["ToggleChild"] = "toggle-child";
|
|
1913
1917
|
})(ActionType$2 || (ActionType$2 = {}));
|
|
1914
1918
|
/**
|
|
1915
1919
|
* Reducer that executes actions
|
|
1916
|
-
* @author
|
|
1920
|
+
* @author Gabe Abrams
|
|
1917
1921
|
* @param state current state
|
|
1918
1922
|
* @param action action to execute
|
|
1919
1923
|
*/
|
|
1920
1924
|
const reducer$2 = (state, action) => {
|
|
1921
1925
|
switch (action.type) {
|
|
1922
|
-
case ActionType$2.
|
|
1923
|
-
return {
|
|
1926
|
+
case ActionType$2.ToggleChild: {
|
|
1927
|
+
return Object.assign(Object.assign({}, state), { childExpanded: Object.assign(Object.assign({}, state.childExpanded), { [String(action.id)]: !state.childExpanded[String(action.id)] }) });
|
|
1924
1928
|
}
|
|
1925
1929
|
default: {
|
|
1926
1930
|
return state;
|
|
@@ -1938,14 +1942,19 @@ const NestableItemList = (props) => {
|
|
|
1938
1942
|
// Destructure all props
|
|
1939
1943
|
const { items, onChanged, } = props;
|
|
1940
1944
|
/* -------------- State ------------- */
|
|
1945
|
+
// Create initial map of child expanded booleans
|
|
1946
|
+
const initChildExpanded = {};
|
|
1947
|
+
items.forEach((item) => {
|
|
1948
|
+
initChildExpanded[String(item.id)] = false;
|
|
1949
|
+
});
|
|
1941
1950
|
// Initial state
|
|
1942
1951
|
const initialState = {
|
|
1943
|
-
|
|
1952
|
+
childExpanded: initChildExpanded,
|
|
1944
1953
|
};
|
|
1945
1954
|
// Initialize state
|
|
1946
1955
|
const [state, dispatch] = useReducer(reducer$2, initialState);
|
|
1947
1956
|
// Destructure common state
|
|
1948
|
-
const {
|
|
1957
|
+
const { childExpanded, } = state;
|
|
1949
1958
|
/*------------------------------------------------------------------------*/
|
|
1950
1959
|
/* Component Functions */
|
|
1951
1960
|
/*------------------------------------------------------------------------*/
|
|
@@ -2033,14 +2042,15 @@ const NestableItemList = (props) => {
|
|
|
2033
2042
|
backgroundColor: 'transparent',
|
|
2034
2043
|
}, type: "button", onClick: () => {
|
|
2035
2044
|
dispatch({
|
|
2036
|
-
type: ActionType$2.
|
|
2045
|
+
type: ActionType$2.ToggleChild,
|
|
2046
|
+
id: item.id,
|
|
2037
2047
|
});
|
|
2038
|
-
}, "aria-label": `${
|
|
2039
|
-
React.createElement(FontAwesomeIcon, { icon:
|
|
2048
|
+
}, "aria-label": `${childExpanded[item.id] ? 'Hide' : 'Show'} items in ${item.name}` },
|
|
2049
|
+
React.createElement(FontAwesomeIcon, { icon: childExpanded[item.id] ? faChevronDown : faChevronRight })))),
|
|
2040
2050
|
React.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) => {
|
|
2041
2051
|
onChanged(changeChecked(item.id, checked, items));
|
|
2042
2052
|
}, ariaLabel: `Select ${item.name}`, checkedVariant: Variant$1.Light }),
|
|
2043
|
-
item.isGroup &&
|
|
2053
|
+
(item.isGroup && childExpanded[item.id]) && (React.createElement("div", { className: "NestableItemList-children-container", style: {
|
|
2044
2054
|
paddingLeft: '2.2rem',
|
|
2045
2055
|
} },
|
|
2046
2056
|
React.createElement(NestableItemList, { items: item.children, onChanged: (updatedItems) => {
|
|
@@ -2062,7 +2072,7 @@ const ItemPicker = (props) => {
|
|
|
2062
2072
|
/*------------------------------------------------------------------------*/
|
|
2063
2073
|
/* -------------- Props ------------- */
|
|
2064
2074
|
// Destructure all props
|
|
2065
|
-
const { title, items, onChanged, } = props;
|
|
2075
|
+
const { title, items, onChanged, noBottomMargin, } = props;
|
|
2066
2076
|
/*------------------------------------------------------------------------*/
|
|
2067
2077
|
/* Component Functions */
|
|
2068
2078
|
/*------------------------------------------------------------------------*/
|
|
@@ -2072,11 +2082,34 @@ const ItemPicker = (props) => {
|
|
|
2072
2082
|
/*----------------------------------------*/
|
|
2073
2083
|
/* Main UI */
|
|
2074
2084
|
/*----------------------------------------*/
|
|
2075
|
-
return (React.createElement(TabBox, { title: title },
|
|
2085
|
+
return (React.createElement(TabBox, { title: title, noBottomMargin: noBottomMargin },
|
|
2076
2086
|
React.createElement("div", { style: { overflowX: 'auto' } },
|
|
2077
2087
|
React.createElement(NestableItemList, { items: items, onChanged: onChanged }))));
|
|
2078
2088
|
};
|
|
2079
2089
|
|
|
2090
|
+
// Import shared helpers
|
|
2091
|
+
/**
|
|
2092
|
+
* Get a human-readable description of a date (all in ET)
|
|
2093
|
+
* @author Gabe Abrams
|
|
2094
|
+
* @param [dateOrTimestamp=today] the date or timestamp for the date to describe
|
|
2095
|
+
* @returns human-readable description of the date
|
|
2096
|
+
*/
|
|
2097
|
+
const getHumanReadableDate = (dateOrTimestamp) => {
|
|
2098
|
+
// Get the date info
|
|
2099
|
+
const { month, day, year, } = getTimeInfoInET(dateOrTimestamp);
|
|
2100
|
+
const currYear = getTimeInfoInET().year;
|
|
2101
|
+
// Get the short month description
|
|
2102
|
+
const monthName = getMonthName(month).short;
|
|
2103
|
+
// Create start of description
|
|
2104
|
+
let description = `${monthName} ${day}${getOrdinal(day)}`;
|
|
2105
|
+
// Add on year if it's different
|
|
2106
|
+
if (year !== currYear) {
|
|
2107
|
+
description += ` ${year}`;
|
|
2108
|
+
}
|
|
2109
|
+
// Return description
|
|
2110
|
+
return description;
|
|
2111
|
+
};
|
|
2112
|
+
|
|
2080
2113
|
/**
|
|
2081
2114
|
* Path of the route for storing client-side logs
|
|
2082
2115
|
* @author Gabe Abrams
|
|
@@ -2116,37 +2149,37 @@ var LogType$1 = LogType;
|
|
|
2116
2149
|
var LogAction;
|
|
2117
2150
|
(function (LogAction) {
|
|
2118
2151
|
// Target was opened by the user (it was not on screen, but now it is)
|
|
2119
|
-
LogAction["Open"] = "
|
|
2152
|
+
LogAction["Open"] = "Open";
|
|
2120
2153
|
// Target was closed by the user (it was on screen, but now it is not)
|
|
2121
|
-
LogAction["Close"] = "
|
|
2154
|
+
LogAction["Close"] = "Close";
|
|
2122
2155
|
// Target was cancelled by the user (it was on closed without saving)
|
|
2123
|
-
LogAction["Cancel"] = "
|
|
2156
|
+
LogAction["Cancel"] = "Cancel";
|
|
2124
2157
|
// Target was expanded by the user (it always remains on screen, but size was changed)
|
|
2125
|
-
LogAction["Expand"] = "
|
|
2158
|
+
LogAction["Expand"] = "Expand";
|
|
2126
2159
|
// Target was collapsed by the user (it always remains on screen, but size was changed)
|
|
2127
|
-
LogAction["Collapse"] = "
|
|
2160
|
+
LogAction["Collapse"] = "Collapse";
|
|
2128
2161
|
// Target was viewed by the user (only for items that are not opened or closed, those must use Open/Close actions)
|
|
2129
|
-
LogAction["View"] = "
|
|
2162
|
+
LogAction["View"] = "View";
|
|
2130
2163
|
// Target interrupted the user (popup, dialog, validation message, etc. appeared without user prompting)
|
|
2131
|
-
LogAction["Interrupt"] = "
|
|
2164
|
+
LogAction["Interrupt"] = "Interrupt";
|
|
2132
2165
|
// Target was created by the user (it did not exist before)
|
|
2133
|
-
LogAction["Create"] = "
|
|
2166
|
+
LogAction["Create"] = "Create";
|
|
2134
2167
|
// Target was edited by the user (it existed and was changed)
|
|
2135
|
-
LogAction["Edit"] = "
|
|
2168
|
+
LogAction["Edit"] = "Edit";
|
|
2136
2169
|
// Target was deleted by the user (it existed and now it doesn't)
|
|
2137
|
-
LogAction["Delete"] = "
|
|
2170
|
+
LogAction["Delete"] = "Delete";
|
|
2138
2171
|
// Target was added by the user (it already existed and was added to another place)
|
|
2139
|
-
LogAction["Add"] = "
|
|
2172
|
+
LogAction["Add"] = "Add";
|
|
2140
2173
|
// Target was removed by the user (it was removed from something but still exists)
|
|
2141
|
-
LogAction["Remove"] = "
|
|
2174
|
+
LogAction["Remove"] = "Remove";
|
|
2142
2175
|
// Target was activated by the user (click, check, tap, keypress, etc.)
|
|
2143
|
-
LogAction["Activate"] = "
|
|
2176
|
+
LogAction["Activate"] = "Activate";
|
|
2144
2177
|
// Target was deactivated by the user (click away, uncheck, tap outside of, tab away, etc.)
|
|
2145
|
-
LogAction["Deactivate"] = "
|
|
2178
|
+
LogAction["Deactivate"] = "Deactivate";
|
|
2146
2179
|
// User showed interest in a target (hover, peek, etc.)
|
|
2147
|
-
LogAction["Peek"] = "
|
|
2180
|
+
LogAction["Peek"] = "Peek";
|
|
2148
2181
|
// Unknown action
|
|
2149
|
-
LogAction["Unknown"] = "
|
|
2182
|
+
LogAction["Unknown"] = "Unknown";
|
|
2150
2183
|
})(LogAction || (LogAction = {}));
|
|
2151
2184
|
var LogAction$1 = LogAction;
|
|
2152
2185
|
|
|
@@ -2188,12 +2221,12 @@ const roundToNumDecimals = (num, numDecimals) => {
|
|
|
2188
2221
|
* @returns escaped cell text
|
|
2189
2222
|
*/
|
|
2190
2223
|
const escapeCellText = (text) => {
|
|
2191
|
-
if (!text.includes(',')) {
|
|
2224
|
+
if (!String(text).includes(',')) {
|
|
2192
2225
|
// No need to escape
|
|
2193
|
-
return text;
|
|
2226
|
+
return String(text);
|
|
2194
2227
|
}
|
|
2195
2228
|
// Perform escape
|
|
2196
|
-
return `"${text.replace(/"/g, '""')}`;
|
|
2229
|
+
return `"${String(text).replace(/"/g, '""')}`;
|
|
2197
2230
|
};
|
|
2198
2231
|
/**
|
|
2199
2232
|
* Generate a CSV file
|
|
@@ -2212,9 +2245,26 @@ const genCSV = (data, columns) => {
|
|
|
2212
2245
|
.join(','));
|
|
2213
2246
|
// Add each row
|
|
2214
2247
|
data.forEach((datum) => {
|
|
2248
|
+
csv += '\n';
|
|
2215
2249
|
csv += (columns
|
|
2216
2250
|
.map((column) => {
|
|
2217
|
-
|
|
2251
|
+
let contents;
|
|
2252
|
+
const cell = datum[column.param];
|
|
2253
|
+
if (typeof cell === 'string'
|
|
2254
|
+
|| typeof cell === 'number') {
|
|
2255
|
+
contents = String(cell);
|
|
2256
|
+
}
|
|
2257
|
+
else if (typeof cell === 'undefined'
|
|
2258
|
+
|| cell === null) {
|
|
2259
|
+
contents = '';
|
|
2260
|
+
}
|
|
2261
|
+
else if (typeof cell === 'object') {
|
|
2262
|
+
contents = JSON.stringify(cell);
|
|
2263
|
+
}
|
|
2264
|
+
else {
|
|
2265
|
+
contents = '';
|
|
2266
|
+
}
|
|
2267
|
+
return escapeCellText(contents);
|
|
2218
2268
|
})
|
|
2219
2269
|
.join(','));
|
|
2220
2270
|
});
|
|
@@ -2243,11 +2293,12 @@ const CSVDownloadButton = (props) => {
|
|
|
2243
2293
|
/* Main UI */
|
|
2244
2294
|
/*----------------------------------------*/
|
|
2245
2295
|
// Render the button
|
|
2246
|
-
return (React.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
|
|
2296
|
+
return (React.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
|
|
2247
2297
|
? `Click to download ${filename}`
|
|
2248
2298
|
: ariaLabel), style: style, onClick: onClick },
|
|
2249
2299
|
!children && (React.createElement(React.Fragment, null,
|
|
2250
|
-
React.createElement(FontAwesomeIcon, { icon: faCloudDownloadAlt
|
|
2300
|
+
React.createElement(FontAwesomeIcon, { icon: faCloudDownloadAlt }),
|
|
2301
|
+
' ',
|
|
2251
2302
|
"Download CSV")),
|
|
2252
2303
|
children));
|
|
2253
2304
|
};
|
|
@@ -2271,9 +2322,13 @@ var ActionType$1;
|
|
|
2271
2322
|
// Toggle sort column param
|
|
2272
2323
|
ActionType["ToggleSortColumn"] = "toggle-sort-column";
|
|
2273
2324
|
// Toggle the visibility of a column
|
|
2274
|
-
ActionType["
|
|
2325
|
+
ActionType["UpdateColumnVisibility"] = "update-column-visibility";
|
|
2275
2326
|
// Toggle the column visibility customization modal
|
|
2276
2327
|
ActionType["ToggleColVisCusModalVisibility"] = "toggle-col-vis-cus-modal-visibility";
|
|
2328
|
+
// Show all columns
|
|
2329
|
+
ActionType["ShowAllColumns"] = "show-all-columns";
|
|
2330
|
+
// Hide all columns
|
|
2331
|
+
ActionType["HideAllColumns"] = "hide-all-columns";
|
|
2277
2332
|
})(ActionType$1 || (ActionType$1 = {}));
|
|
2278
2333
|
/**
|
|
2279
2334
|
* Reducer that executes actions
|
|
@@ -2295,14 +2350,28 @@ const reducer$1 = (state, action) => {
|
|
|
2295
2350
|
// Stop sorting by column
|
|
2296
2351
|
return Object.assign(Object.assign({}, state), { sortColumnParam: undefined, sortType: SortType.Ascending });
|
|
2297
2352
|
}
|
|
2298
|
-
case ActionType$1.
|
|
2353
|
+
case ActionType$1.UpdateColumnVisibility: {
|
|
2299
2354
|
const { columnVisibilityMap } = state;
|
|
2300
|
-
columnVisibilityMap[action.param] =
|
|
2355
|
+
columnVisibilityMap[action.param] = action.visible;
|
|
2301
2356
|
return Object.assign(Object.assign({}, state), { columnVisibilityMap });
|
|
2302
2357
|
}
|
|
2303
2358
|
case ActionType$1.ToggleColVisCusModalVisibility: {
|
|
2304
2359
|
return Object.assign(Object.assign({}, state), { columnVisibilityCustomizationModalVisible: !state.columnVisibilityCustomizationModalVisible });
|
|
2305
2360
|
}
|
|
2361
|
+
case ActionType$1.ShowAllColumns: {
|
|
2362
|
+
const { columnVisibilityMap } = state;
|
|
2363
|
+
Object.keys(columnVisibilityMap).forEach((param) => {
|
|
2364
|
+
columnVisibilityMap[param] = true;
|
|
2365
|
+
});
|
|
2366
|
+
return Object.assign(Object.assign({}, state), { columnVisibilityMap });
|
|
2367
|
+
}
|
|
2368
|
+
case ActionType$1.HideAllColumns: {
|
|
2369
|
+
const { columnVisibilityMap } = state;
|
|
2370
|
+
Object.keys(columnVisibilityMap).forEach((param) => {
|
|
2371
|
+
columnVisibilityMap[param] = false;
|
|
2372
|
+
});
|
|
2373
|
+
return Object.assign(Object.assign({}, state), { columnVisibilityMap });
|
|
2374
|
+
}
|
|
2306
2375
|
default: {
|
|
2307
2376
|
return state;
|
|
2308
2377
|
}
|
|
@@ -2318,7 +2387,18 @@ const IntelliTable = (props) => {
|
|
|
2318
2387
|
var _a;
|
|
2319
2388
|
/* -------------- Props ------------- */
|
|
2320
2389
|
// Destructure all props
|
|
2321
|
-
const { title, id,
|
|
2390
|
+
const { title, id, columns, } = props;
|
|
2391
|
+
// Get data, show empty row if none
|
|
2392
|
+
const data = ((props.data && props.data.length > 0)
|
|
2393
|
+
? props.data
|
|
2394
|
+
: [{ id: 'empty-row' }]);
|
|
2395
|
+
// Get CSV filename
|
|
2396
|
+
let filename = `${title}.csv`;
|
|
2397
|
+
if (props.csvName) {
|
|
2398
|
+
filename = (props.csvName.endsWith('.csv')
|
|
2399
|
+
? props.csvName
|
|
2400
|
+
: `${props.csvName}.csv`);
|
|
2401
|
+
}
|
|
2322
2402
|
/* -------------- State ------------- */
|
|
2323
2403
|
// Initial state
|
|
2324
2404
|
const initColumnVisibilityMap = {};
|
|
@@ -2335,21 +2415,49 @@ const IntelliTable = (props) => {
|
|
|
2335
2415
|
const [state, dispatch] = useReducer(reducer$1, initialState);
|
|
2336
2416
|
// Destructure common state
|
|
2337
2417
|
const { sortColumnParam, sortType, columnVisibilityMap, columnVisibilityCustomizationModalVisible, } = state;
|
|
2418
|
+
/*------------------------------------------------------------------------*/
|
|
2419
|
+
/* Render */
|
|
2420
|
+
/*------------------------------------------------------------------------*/
|
|
2421
|
+
/*----------------------------------------*/
|
|
2422
|
+
/* Modal */
|
|
2423
|
+
/*----------------------------------------*/
|
|
2424
|
+
// Modal that may be defined
|
|
2425
|
+
let modal;
|
|
2338
2426
|
/* ------- Col Vis Customization Modal ------ */
|
|
2339
2427
|
if (columnVisibilityCustomizationModalVisible) {
|
|
2340
2428
|
// Create modal
|
|
2341
|
-
(React.createElement(Modal, { type: ModalType$1.Okay, title: "Choose columns to show:", onClose: () => {
|
|
2429
|
+
modal = (React.createElement(Modal, { type: ModalType$1.Okay, title: "Choose columns to show:", onClose: () => {
|
|
2430
|
+
const noColumnsSelected = (Object.values(columnVisibilityMap)
|
|
2431
|
+
.every((isSelected) => {
|
|
2432
|
+
return !isSelected;
|
|
2433
|
+
}));
|
|
2434
|
+
if (noColumnsSelected) {
|
|
2435
|
+
return alert$1('Choose at least one column', 'To continue, you have to choose at least one column to show');
|
|
2436
|
+
}
|
|
2342
2437
|
dispatch({
|
|
2343
2438
|
type: ActionType$1.ToggleColVisCusModalVisibility,
|
|
2344
2439
|
});
|
|
2345
|
-
}
|
|
2346
|
-
|
|
2440
|
+
}, okayVariant: Variant$1.Light },
|
|
2441
|
+
columns.map((column) => {
|
|
2442
|
+
return (React.createElement(CheckboxButton, { key: column.param, id: `IntelliTable-${id}-toggle-visibility-${column.param}`, className: "mb-2", text: column.title, onChanged: (checked) => {
|
|
2443
|
+
dispatch({
|
|
2444
|
+
type: ActionType$1.UpdateColumnVisibility,
|
|
2445
|
+
param: column.param,
|
|
2446
|
+
visible: checked,
|
|
2447
|
+
});
|
|
2448
|
+
}, checked: columnVisibilityMap[column.param], ariaLabel: `show "${column.title}" column in the ${title} table`, checkedVariant: Variant$1.Light, uncheckedVariant: Variant$1.Secondary }));
|
|
2449
|
+
}),
|
|
2450
|
+
React.createElement("div", { className: "mt-3" }, "Or you can:"),
|
|
2451
|
+
React.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: () => {
|
|
2347
2452
|
dispatch({
|
|
2348
|
-
type: ActionType$1.
|
|
2349
|
-
param: column.param,
|
|
2453
|
+
type: ActionType$1.ShowAllColumns,
|
|
2350
2454
|
});
|
|
2351
|
-
}
|
|
2352
|
-
|
|
2455
|
+
} }, "Select All"),
|
|
2456
|
+
React.createElement("button", { type: "button", id: `IntelliTable-${id}-select-none-columns`, className: "btn btn-secondary", "aria-label": `hide all columns in the ${title} table`, onClick: () => {
|
|
2457
|
+
dispatch({
|
|
2458
|
+
type: ActionType$1.HideAllColumns,
|
|
2459
|
+
});
|
|
2460
|
+
} }, "Deselect All")));
|
|
2353
2461
|
}
|
|
2354
2462
|
/*----------------------------------------*/
|
|
2355
2463
|
/* Main UI */
|
|
@@ -2363,6 +2471,7 @@ const IntelliTable = (props) => {
|
|
|
2363
2471
|
// Custom info based on current sort type
|
|
2364
2472
|
let sortButtonAriaLabel;
|
|
2365
2473
|
let sortIcon = faSort;
|
|
2474
|
+
let sortingByThisColumn = false;
|
|
2366
2475
|
if (!sortColumnParam) {
|
|
2367
2476
|
// Not being sorted yet
|
|
2368
2477
|
sortButtonAriaLabel = `sort ascending by ${column.title}`;
|
|
@@ -2370,6 +2479,7 @@ const IntelliTable = (props) => {
|
|
|
2370
2479
|
}
|
|
2371
2480
|
else if (column.param === sortColumnParam) {
|
|
2372
2481
|
// Already sorted by this column
|
|
2482
|
+
sortingByThisColumn = true;
|
|
2373
2483
|
if (sortType === SortType.Ascending) {
|
|
2374
2484
|
// Sorted ascending
|
|
2375
2485
|
sortButtonAriaLabel = `sort descending by ${column.title}`;
|
|
@@ -2387,11 +2497,14 @@ const IntelliTable = (props) => {
|
|
|
2387
2497
|
sortIcon = faSort;
|
|
2388
2498
|
}
|
|
2389
2499
|
// Create the cell UI
|
|
2390
|
-
return (React.createElement("th", { key: column.param, scope: "col", id: `IntelliTable-${id}-header-${column.param}
|
|
2391
|
-
|
|
2500
|
+
return (React.createElement("th", { key: column.param, scope: "col", id: `IntelliTable-${id}-header-${column.param}`, className: "text-start", style: {
|
|
2501
|
+
borderRight: '0.05rem solid #555',
|
|
2502
|
+
borderLeft: '0.05rem solid #555',
|
|
2503
|
+
} },
|
|
2504
|
+
React.createElement("div", { className: "d-flex align-items-center justify-content-start flex-row h-100" },
|
|
2392
2505
|
React.createElement("span", { className: "text-nowrap" }, column.title),
|
|
2393
2506
|
React.createElement("div", null,
|
|
2394
|
-
React.createElement("button", { type: "button", className:
|
|
2507
|
+
React.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: () => {
|
|
2395
2508
|
dispatch({
|
|
2396
2509
|
type: ActionType$1.ToggleSortColumn,
|
|
2397
2510
|
param: column.param,
|
|
@@ -2409,8 +2522,28 @@ const IntelliTable = (props) => {
|
|
|
2409
2522
|
const descending = (sortType === SortType.Descending);
|
|
2410
2523
|
if (sortColumnParam) {
|
|
2411
2524
|
sortedData.sort((a, b) => {
|
|
2525
|
+
var _a, _b;
|
|
2412
2526
|
const aVal = a[sortColumnParam];
|
|
2413
2527
|
const bVal = b[sortColumnParam];
|
|
2528
|
+
// Tiebreaker sort by timestamp, most recent first (used if tied)
|
|
2529
|
+
const tiebreaker = (((_a = b.timestamp) !== null && _a !== void 0 ? _a : 0)
|
|
2530
|
+
- ((_b = a.timestamp) !== null && _b !== void 0 ? _b : 0));
|
|
2531
|
+
// Auto-sort undefined and null to end of list
|
|
2532
|
+
if ((aVal === undefined || aVal === null)
|
|
2533
|
+
|| (bVal === undefined || bVal === null)) {
|
|
2534
|
+
// At least one was undefined
|
|
2535
|
+
if ((aVal === undefined || aVal === null)
|
|
2536
|
+
&& (bVal === undefined || bVal === null)) {
|
|
2537
|
+
// Both undefined
|
|
2538
|
+
return tiebreaker;
|
|
2539
|
+
}
|
|
2540
|
+
if (aVal === undefined || aVal === null) {
|
|
2541
|
+
// First was undefined
|
|
2542
|
+
return 1;
|
|
2543
|
+
}
|
|
2544
|
+
// Second was undefined
|
|
2545
|
+
return -1;
|
|
2546
|
+
}
|
|
2414
2547
|
// Sort differently based on the data type
|
|
2415
2548
|
// > Boolean
|
|
2416
2549
|
if (paramType === ParamType$1.Boolean) {
|
|
@@ -2420,7 +2553,7 @@ const IntelliTable = (props) => {
|
|
|
2420
2553
|
if (!aVal && bVal) {
|
|
2421
2554
|
return (descending ? 1 : -1);
|
|
2422
2555
|
}
|
|
2423
|
-
return
|
|
2556
|
+
return tiebreaker;
|
|
2424
2557
|
}
|
|
2425
2558
|
// > Number
|
|
2426
2559
|
if (paramType === ParamType$1.Int
|
|
@@ -2432,12 +2565,12 @@ const IntelliTable = (props) => {
|
|
|
2432
2565
|
// > String
|
|
2433
2566
|
if (paramType === ParamType$1.String) {
|
|
2434
2567
|
if (aVal < bVal) {
|
|
2435
|
-
return (descending ?
|
|
2568
|
+
return (descending ? 1 : -1);
|
|
2436
2569
|
}
|
|
2437
2570
|
if (aVal > bVal) {
|
|
2438
|
-
return (descending ? 1 :
|
|
2571
|
+
return (descending ? -1 : 1);
|
|
2439
2572
|
}
|
|
2440
|
-
return
|
|
2573
|
+
return tiebreaker;
|
|
2441
2574
|
}
|
|
2442
2575
|
// > JSON
|
|
2443
2576
|
if (paramType === ParamType$1.JSON) {
|
|
@@ -2452,7 +2585,7 @@ const IntelliTable = (props) => {
|
|
|
2452
2585
|
: (aSize - bSize));
|
|
2453
2586
|
}
|
|
2454
2587
|
// No sort
|
|
2455
|
-
return
|
|
2588
|
+
return tiebreaker;
|
|
2456
2589
|
});
|
|
2457
2590
|
}
|
|
2458
2591
|
// Table body
|
|
@@ -2477,9 +2610,12 @@ const IntelliTable = (props) => {
|
|
|
2477
2610
|
const noValue = (value === undefined
|
|
2478
2611
|
|| value === null);
|
|
2479
2612
|
visibleValue = (noValue
|
|
2480
|
-
? (React.createElement(FontAwesomeIcon, { icon:
|
|
2481
|
-
: (React.createElement(FontAwesomeIcon, { icon: faXmarkCircle })));
|
|
2613
|
+
? (React.createElement(FontAwesomeIcon, { icon: faMinus }))
|
|
2614
|
+
: (React.createElement(FontAwesomeIcon, { icon: fullValue ? faCheckCircle : faXmarkCircle })));
|
|
2482
2615
|
title = (fullValue ? 'True' : 'False');
|
|
2616
|
+
if (noValue) {
|
|
2617
|
+
title = 'Empty Cell';
|
|
2618
|
+
}
|
|
2483
2619
|
}
|
|
2484
2620
|
else if (column.type === ParamType$1.Int) {
|
|
2485
2621
|
fullValue = Number.parseInt(value, 10);
|
|
@@ -2488,6 +2624,9 @@ const IntelliTable = (props) => {
|
|
|
2488
2624
|
? (React.createElement(FontAwesomeIcon, { icon: faMinus }))
|
|
2489
2625
|
: fullValue);
|
|
2490
2626
|
title = String(fullValue);
|
|
2627
|
+
if (noValue) {
|
|
2628
|
+
title = 'Empty Cell';
|
|
2629
|
+
}
|
|
2491
2630
|
}
|
|
2492
2631
|
else if (column.type === ParamType$1.Float) {
|
|
2493
2632
|
fullValue = Number.parseFloat(value);
|
|
@@ -2496,14 +2635,22 @@ const IntelliTable = (props) => {
|
|
|
2496
2635
|
? (React.createElement(FontAwesomeIcon, { icon: faMinus }))
|
|
2497
2636
|
: roundToNumDecimals(fullValue, 2));
|
|
2498
2637
|
title = String(fullValue);
|
|
2638
|
+
if (noValue) {
|
|
2639
|
+
title = 'Empty Cell';
|
|
2640
|
+
}
|
|
2499
2641
|
}
|
|
2500
2642
|
else if (column.type === ParamType$1.String) {
|
|
2501
2643
|
fullValue = String(value).trim();
|
|
2502
|
-
const noValue = (value
|
|
2644
|
+
const noValue = (value === undefined
|
|
2645
|
+
|| value === null
|
|
2646
|
+
|| String(fullValue).trim().length === 0);
|
|
2503
2647
|
visibleValue = (noValue
|
|
2504
2648
|
? (React.createElement(FontAwesomeIcon, { icon: faMinus }))
|
|
2505
2649
|
: fullValue);
|
|
2506
2650
|
title = `"${value}"`;
|
|
2651
|
+
if (noValue) {
|
|
2652
|
+
title = 'Empty Cell';
|
|
2653
|
+
}
|
|
2507
2654
|
}
|
|
2508
2655
|
else if (column.type === ParamType$1.JSON) {
|
|
2509
2656
|
fullValue = JSON.stringify(value);
|
|
@@ -2513,9 +2660,13 @@ const IntelliTable = (props) => {
|
|
|
2513
2660
|
visibleValue = (noValue
|
|
2514
2661
|
? (React.createElement(FontAwesomeIcon, { icon: faMinus }))
|
|
2515
2662
|
: fullValue);
|
|
2663
|
+
title = "JSON Object";
|
|
2516
2664
|
}
|
|
2517
2665
|
// Create UI
|
|
2518
|
-
return (React.createElement("td", { key: `${datum.id}-${column.param}`, title: title
|
|
2666
|
+
return (React.createElement("td", { key: `${datum.id}-${column.param}`, title: title, style: {
|
|
2667
|
+
borderRight: '0.05rem solid #555',
|
|
2668
|
+
borderLeft: '0.05rem solid #555',
|
|
2669
|
+
} }, visibleValue));
|
|
2519
2670
|
}));
|
|
2520
2671
|
// Add cells to a row
|
|
2521
2672
|
return (React.createElement("tr", { key: datum.id }, cells));
|
|
@@ -2532,13 +2683,16 @@ const IntelliTable = (props) => {
|
|
|
2532
2683
|
})
|
|
2533
2684
|
.length);
|
|
2534
2685
|
// Build CSV
|
|
2535
|
-
const csv = genCSV(data, columns)
|
|
2686
|
+
const csv = genCSV(data, columns.filter((column) => {
|
|
2687
|
+
return columnVisibilityMap[column.param];
|
|
2688
|
+
}));
|
|
2536
2689
|
// Build main UI
|
|
2537
2690
|
return (React.createElement("div", { className: `IntelliTable-container-${id}` },
|
|
2538
|
-
|
|
2691
|
+
modal,
|
|
2692
|
+
React.createElement("div", { className: "d-flex align-items-center justify-content-start" },
|
|
2539
2693
|
React.createElement("h3", { className: "m-0" }, title),
|
|
2540
2694
|
React.createElement("div", { className: "flex-grow-1 text-end" },
|
|
2541
|
-
React.createElement(CSVDownloadButton, { "aria-label": `download data as csv for ${title}`, id: `IntelliTable-${id}-download-as-csv`, filename:
|
|
2695
|
+
React.createElement(CSVDownloadButton, { "aria-label": `download data as csv for ${title}`, id: `IntelliTable-${id}-download-as-csv`, filename: filename, csv: csv }),
|
|
2542
2696
|
React.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: () => {
|
|
2543
2697
|
dispatch({
|
|
2544
2698
|
type: ActionType$1.ToggleColVisCusModalVisibility,
|
|
@@ -2551,7 +2705,7 @@ const IntelliTable = (props) => {
|
|
|
2551
2705
|
numHiddenCols,
|
|
2552
2706
|
' ',
|
|
2553
2707
|
"hidden)"))))),
|
|
2554
|
-
React.createElement("div", { className: `IntelliTable-table-${id}`, style: {
|
|
2708
|
+
React.createElement("div", { className: `IntelliTable-table-${id} mt-2`, style: {
|
|
2555
2709
|
overflowX: 'auto',
|
|
2556
2710
|
} }, table)));
|
|
2557
2711
|
};
|
|
@@ -2640,14 +2794,212 @@ const style = `
|
|
|
2640
2794
|
background-color: transparent !important;
|
|
2641
2795
|
padding-top: 0 !important;
|
|
2642
2796
|
padding-bottom: 0 !important;
|
|
2797
|
+
padding-right: 1em !important;
|
|
2643
2798
|
margin: 0 !important;
|
|
2644
|
-
color: #
|
|
2799
|
+
color: #444 !important;
|
|
2800
|
+
|
|
2801
|
+
right: 0 !important;
|
|
2802
|
+
position: absolute !important;
|
|
2645
2803
|
}
|
|
2646
2804
|
.LogReviewer-header-close-button:hover {
|
|
2805
|
+
border: 0 !important;
|
|
2806
|
+
background-color: transparent !important;
|
|
2807
|
+
padding-top: 0 !important;
|
|
2808
|
+
padding-bottom: 0 !important;
|
|
2809
|
+
margin: 0 !important;
|
|
2647
2810
|
color: #000 !important;
|
|
2648
2811
|
}
|
|
2649
2812
|
`;
|
|
2650
2813
|
/*------------------------------------------------------------------------*/
|
|
2814
|
+
/* Constants */
|
|
2815
|
+
/*------------------------------------------------------------------------*/
|
|
2816
|
+
const columns = [
|
|
2817
|
+
{
|
|
2818
|
+
title: 'First Name',
|
|
2819
|
+
param: 'userFirstName',
|
|
2820
|
+
type: ParamType$1.String,
|
|
2821
|
+
},
|
|
2822
|
+
{
|
|
2823
|
+
title: 'Last Name',
|
|
2824
|
+
param: 'userLastName',
|
|
2825
|
+
type: ParamType$1.String,
|
|
2826
|
+
},
|
|
2827
|
+
{
|
|
2828
|
+
title: 'Email',
|
|
2829
|
+
param: 'userEmail',
|
|
2830
|
+
type: ParamType$1.String,
|
|
2831
|
+
},
|
|
2832
|
+
{
|
|
2833
|
+
title: 'Canvas Id',
|
|
2834
|
+
param: 'userId',
|
|
2835
|
+
type: ParamType$1.Int,
|
|
2836
|
+
},
|
|
2837
|
+
{
|
|
2838
|
+
title: 'Student?',
|
|
2839
|
+
param: 'isLearner',
|
|
2840
|
+
type: ParamType$1.Boolean,
|
|
2841
|
+
},
|
|
2842
|
+
{
|
|
2843
|
+
title: 'Teaching Staff?',
|
|
2844
|
+
param: 'isTTM',
|
|
2845
|
+
type: ParamType$1.Boolean,
|
|
2846
|
+
startsHidden: true,
|
|
2847
|
+
},
|
|
2848
|
+
{
|
|
2849
|
+
title: 'Admin?',
|
|
2850
|
+
param: 'isAdmin',
|
|
2851
|
+
type: ParamType$1.Boolean,
|
|
2852
|
+
startsHidden: true,
|
|
2853
|
+
},
|
|
2854
|
+
{
|
|
2855
|
+
title: 'Course Canvas Id',
|
|
2856
|
+
param: 'courseId',
|
|
2857
|
+
type: ParamType$1.Int,
|
|
2858
|
+
startsHidden: true,
|
|
2859
|
+
},
|
|
2860
|
+
{
|
|
2861
|
+
title: 'Course Name',
|
|
2862
|
+
param: 'courseName',
|
|
2863
|
+
type: ParamType$1.String,
|
|
2864
|
+
},
|
|
2865
|
+
{
|
|
2866
|
+
title: 'Browser Name',
|
|
2867
|
+
param: 'browser.name',
|
|
2868
|
+
type: ParamType$1.String,
|
|
2869
|
+
startsHidden: true,
|
|
2870
|
+
},
|
|
2871
|
+
{
|
|
2872
|
+
title: 'Browser Version',
|
|
2873
|
+
param: 'browser.version',
|
|
2874
|
+
type: ParamType$1.String,
|
|
2875
|
+
startsHidden: true,
|
|
2876
|
+
},
|
|
2877
|
+
{
|
|
2878
|
+
title: 'OS',
|
|
2879
|
+
param: 'device.os',
|
|
2880
|
+
type: ParamType$1.String,
|
|
2881
|
+
startsHidden: true,
|
|
2882
|
+
},
|
|
2883
|
+
{
|
|
2884
|
+
title: 'Mobile?',
|
|
2885
|
+
param: 'device.isMobile',
|
|
2886
|
+
type: ParamType$1.Boolean,
|
|
2887
|
+
startsHidden: true,
|
|
2888
|
+
},
|
|
2889
|
+
{
|
|
2890
|
+
title: 'Year',
|
|
2891
|
+
param: 'year',
|
|
2892
|
+
type: ParamType$1.Int,
|
|
2893
|
+
},
|
|
2894
|
+
{
|
|
2895
|
+
title: 'Month',
|
|
2896
|
+
param: 'month',
|
|
2897
|
+
type: ParamType$1.Int,
|
|
2898
|
+
},
|
|
2899
|
+
{
|
|
2900
|
+
title: 'Day',
|
|
2901
|
+
param: 'day',
|
|
2902
|
+
type: ParamType$1.Int,
|
|
2903
|
+
},
|
|
2904
|
+
{
|
|
2905
|
+
title: 'Hour',
|
|
2906
|
+
param: 'hour',
|
|
2907
|
+
type: ParamType$1.Int,
|
|
2908
|
+
},
|
|
2909
|
+
{
|
|
2910
|
+
title: 'Minute',
|
|
2911
|
+
param: 'minute',
|
|
2912
|
+
type: ParamType$1.Int,
|
|
2913
|
+
startsHidden: true,
|
|
2914
|
+
},
|
|
2915
|
+
{
|
|
2916
|
+
title: 'Timestamp',
|
|
2917
|
+
param: 'timestamp',
|
|
2918
|
+
type: ParamType$1.Int,
|
|
2919
|
+
startsHidden: true,
|
|
2920
|
+
},
|
|
2921
|
+
{
|
|
2922
|
+
title: 'Context',
|
|
2923
|
+
param: 'context',
|
|
2924
|
+
type: ParamType$1.String,
|
|
2925
|
+
},
|
|
2926
|
+
{
|
|
2927
|
+
title: 'Subcontext',
|
|
2928
|
+
param: 'subcontext',
|
|
2929
|
+
type: ParamType$1.String,
|
|
2930
|
+
},
|
|
2931
|
+
{
|
|
2932
|
+
title: 'Tags',
|
|
2933
|
+
param: 'tags',
|
|
2934
|
+
type: ParamType$1.JSON,
|
|
2935
|
+
startsHidden: true,
|
|
2936
|
+
},
|
|
2937
|
+
{
|
|
2938
|
+
title: 'Log Level',
|
|
2939
|
+
param: 'level',
|
|
2940
|
+
type: ParamType$1.String,
|
|
2941
|
+
startsHidden: true,
|
|
2942
|
+
},
|
|
2943
|
+
{
|
|
2944
|
+
title: 'Metadata',
|
|
2945
|
+
param: 'metadata',
|
|
2946
|
+
type: ParamType$1.JSON,
|
|
2947
|
+
startsHidden: true,
|
|
2948
|
+
},
|
|
2949
|
+
{
|
|
2950
|
+
title: 'Source',
|
|
2951
|
+
param: 'source',
|
|
2952
|
+
type: ParamType$1.String,
|
|
2953
|
+
},
|
|
2954
|
+
{
|
|
2955
|
+
title: 'Server Route Path',
|
|
2956
|
+
param: 'routePath',
|
|
2957
|
+
type: ParamType$1.String,
|
|
2958
|
+
startsHidden: true,
|
|
2959
|
+
},
|
|
2960
|
+
{
|
|
2961
|
+
title: 'Server Route Template',
|
|
2962
|
+
param: 'routeTemplate',
|
|
2963
|
+
type: ParamType$1.String,
|
|
2964
|
+
startsHidden: true,
|
|
2965
|
+
},
|
|
2966
|
+
{
|
|
2967
|
+
title: 'Type',
|
|
2968
|
+
param: 'type',
|
|
2969
|
+
type: ParamType$1.String,
|
|
2970
|
+
},
|
|
2971
|
+
{
|
|
2972
|
+
title: 'Error Message',
|
|
2973
|
+
param: 'errorMessage',
|
|
2974
|
+
type: ParamType$1.String,
|
|
2975
|
+
startsHidden: true,
|
|
2976
|
+
},
|
|
2977
|
+
{
|
|
2978
|
+
title: 'Error Code',
|
|
2979
|
+
param: 'errorCode',
|
|
2980
|
+
type: ParamType$1.String,
|
|
2981
|
+
startsHidden: true,
|
|
2982
|
+
},
|
|
2983
|
+
{
|
|
2984
|
+
title: 'Error Stack',
|
|
2985
|
+
param: 'errorStack',
|
|
2986
|
+
type: ParamType$1.String,
|
|
2987
|
+
startsHidden: true,
|
|
2988
|
+
},
|
|
2989
|
+
{
|
|
2990
|
+
title: 'Action Target',
|
|
2991
|
+
param: 'target',
|
|
2992
|
+
type: ParamType$1.String,
|
|
2993
|
+
startsHidden: true,
|
|
2994
|
+
},
|
|
2995
|
+
{
|
|
2996
|
+
title: 'Action Type',
|
|
2997
|
+
param: 'action',
|
|
2998
|
+
type: ParamType$1.String,
|
|
2999
|
+
startsHidden: true,
|
|
3000
|
+
},
|
|
3001
|
+
];
|
|
3002
|
+
/*------------------------------------------------------------------------*/
|
|
2651
3003
|
/* Static Functions */
|
|
2652
3004
|
/*------------------------------------------------------------------------*/
|
|
2653
3005
|
/**
|
|
@@ -2747,10 +3099,35 @@ const LogReviewer = (props) => {
|
|
|
2747
3099
|
/*------------------------------------------------------------------------*/
|
|
2748
3100
|
/* Setup */
|
|
2749
3101
|
/*------------------------------------------------------------------------*/
|
|
2750
|
-
var _a, _b, _c, _d
|
|
3102
|
+
var _a, _b, _c, _d;
|
|
2751
3103
|
/* -------------- Props ------------- */
|
|
2752
3104
|
// Destructure props
|
|
2753
3105
|
const { LogMetadata, onClose, } = props;
|
|
3106
|
+
// Create complete map of contexts
|
|
3107
|
+
const contextMap = {};
|
|
3108
|
+
[
|
|
3109
|
+
((_a = LogMetadata.Context) !== null && _a !== void 0 ? _a : {}),
|
|
3110
|
+
LogBuiltInMetadata.Context,
|
|
3111
|
+
].forEach((partialContextMap) => {
|
|
3112
|
+
Object.keys(partialContextMap).forEach((context) => {
|
|
3113
|
+
// Add context
|
|
3114
|
+
contextMap[context] = partialContextMap[context];
|
|
3115
|
+
// If context has children, add an "uncategorized" subcontext
|
|
3116
|
+
if (typeof contextMap[context] !== 'string') {
|
|
3117
|
+
contextMap[context][LogBuiltInMetadata.Context.Uncategorized] = (LogBuiltInMetadata.Context.Uncategorized);
|
|
3118
|
+
}
|
|
3119
|
+
});
|
|
3120
|
+
});
|
|
3121
|
+
// Create complete map of targets
|
|
3122
|
+
const targetMap = {};
|
|
3123
|
+
[
|
|
3124
|
+
((_b = LogMetadata.Target) !== null && _b !== void 0 ? _b : {}),
|
|
3125
|
+
LogBuiltInMetadata.Target,
|
|
3126
|
+
].forEach((partialTargetMap) => {
|
|
3127
|
+
Object.keys(partialTargetMap).forEach((target) => {
|
|
3128
|
+
targetMap[target] = partialTargetMap[target];
|
|
3129
|
+
});
|
|
3130
|
+
});
|
|
2754
3131
|
/* -------------- State ------------- */
|
|
2755
3132
|
// Create initial date filter state
|
|
2756
3133
|
const today = getTimeInfoInET();
|
|
@@ -2770,26 +3147,29 @@ const LogReviewer = (props) => {
|
|
|
2770
3147
|
};
|
|
2771
3148
|
// Create initial context filter state
|
|
2772
3149
|
const initContextFilterState = {};
|
|
2773
|
-
Object.keys(
|
|
2774
|
-
|
|
2775
|
-
const contextValue = ((_a = LogMetadata.Context) !== null && _a !== void 0 ? _a : {})[context];
|
|
3150
|
+
Object.keys(contextMap).forEach((context) => {
|
|
3151
|
+
const contextValue = contextMap[context];
|
|
2776
3152
|
if (typeof contextValue === 'string') {
|
|
2777
|
-
// Case: no subcontexts
|
|
3153
|
+
// Case: no subcontexts, init as checked
|
|
2778
3154
|
initContextFilterState[contextValue] = true;
|
|
2779
3155
|
}
|
|
2780
3156
|
else {
|
|
2781
3157
|
// Case: subcontexts exist
|
|
2782
|
-
initContextFilterState[
|
|
2783
|
-
Object.values(
|
|
2784
|
-
|
|
2785
|
-
|
|
3158
|
+
initContextFilterState[context] = {};
|
|
3159
|
+
Object.values(contextMap[context]).forEach((subcontext) => {
|
|
3160
|
+
// Skip self ("_")
|
|
3161
|
+
if (subcontext === '_') {
|
|
3162
|
+
return;
|
|
3163
|
+
}
|
|
3164
|
+
// Initialize as checked
|
|
3165
|
+
initContextFilterState[context][subcontext] = true;
|
|
2786
3166
|
});
|
|
2787
3167
|
}
|
|
2788
3168
|
});
|
|
2789
3169
|
// Create initial tag filter state
|
|
2790
3170
|
const initTagFilterState = {};
|
|
2791
|
-
Object.
|
|
2792
|
-
initTagFilterState[
|
|
3171
|
+
Object.keys((_c = LogMetadata.Tag) !== null && _c !== void 0 ? _c : {}).forEach((tag) => {
|
|
3172
|
+
initTagFilterState[tag] = false;
|
|
2793
3173
|
});
|
|
2794
3174
|
// Create advanced filter state
|
|
2795
3175
|
const initAdvancedFilterState = {
|
|
@@ -2815,7 +3195,7 @@ const LogReviewer = (props) => {
|
|
|
2815
3195
|
target: {},
|
|
2816
3196
|
action: {},
|
|
2817
3197
|
};
|
|
2818
|
-
Object.values(
|
|
3198
|
+
Object.values(targetMap).forEach((target) => {
|
|
2819
3199
|
initActionErrorFilterState.target[target] = true;
|
|
2820
3200
|
});
|
|
2821
3201
|
Object.values(LogAction$1).forEach((action) => {
|
|
@@ -2854,15 +3234,18 @@ const LogReviewer = (props) => {
|
|
|
2854
3234
|
let month = newDateFilterState.startDate.month;
|
|
2855
3235
|
while (
|
|
2856
3236
|
// Earlier year
|
|
2857
|
-
(year
|
|
3237
|
+
(year < newDateFilterState.endDate.year)
|
|
2858
3238
|
// Current year but included month
|
|
2859
3239
|
|| (year === newDateFilterState.endDate.year
|
|
2860
3240
|
&& month <= newDateFilterState.endDate.month)) {
|
|
2861
|
-
// Add to list
|
|
2862
|
-
|
|
2863
|
-
year
|
|
2864
|
-
|
|
2865
|
-
|
|
3241
|
+
// Add to list if not already loaded
|
|
3242
|
+
if (!logMap[year]
|
|
3243
|
+
|| !logMap[year][month]) {
|
|
3244
|
+
toLoad.push({
|
|
3245
|
+
year,
|
|
3246
|
+
month,
|
|
3247
|
+
});
|
|
3248
|
+
}
|
|
2866
3249
|
// Increment
|
|
2867
3250
|
month += 1;
|
|
2868
3251
|
if (month > 12) {
|
|
@@ -2950,10 +3333,10 @@ const LogReviewer = (props) => {
|
|
|
2950
3333
|
/* Filters */
|
|
2951
3334
|
/*----------------------------------------*/
|
|
2952
3335
|
// Filter toggle
|
|
2953
|
-
const filterToggles = (React.createElement("div", { className: "LogReviewer-filter-toggles
|
|
3336
|
+
const filterToggles = (React.createElement("div", { className: "LogReviewer-filter-toggles" },
|
|
2954
3337
|
React.createElement("h3", { className: "m-0" }, "Filters:"),
|
|
2955
|
-
React.createElement("div", { className: "LogReviewer-filter-toggle-buttons" },
|
|
2956
|
-
React.createElement("button", { type: "button", id: "LogReviewer-toggle-date-filter-drawer", className: `btn btn-${FilterDrawer.Date === expandedFilterDrawer ? 'warning' : '
|
|
3338
|
+
React.createElement("div", { className: "LogReviewer-filter-toggle-buttons alert alert-secondary p-2 m-0" },
|
|
3339
|
+
React.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: () => {
|
|
2957
3340
|
dispatch({
|
|
2958
3341
|
type: ActionType.ToggleFilterDrawer,
|
|
2959
3342
|
filterDrawer: FilterDrawer.Date,
|
|
@@ -2961,7 +3344,7 @@ const LogReviewer = (props) => {
|
|
|
2961
3344
|
} },
|
|
2962
3345
|
React.createElement(FontAwesomeIcon, { icon: faCalendar, className: "me-2" }),
|
|
2963
3346
|
"Date"),
|
|
2964
|
-
React.createElement("button", { type: "button", id: "LogReviewer-toggle-context-filter-drawer", className: `btn btn-${FilterDrawer.Context === expandedFilterDrawer ? 'warning' : '
|
|
3347
|
+
React.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: () => {
|
|
2965
3348
|
dispatch({
|
|
2966
3349
|
type: ActionType.ToggleFilterDrawer,
|
|
2967
3350
|
filterDrawer: FilterDrawer.Context,
|
|
@@ -2969,7 +3352,7 @@ const LogReviewer = (props) => {
|
|
|
2969
3352
|
} },
|
|
2970
3353
|
React.createElement(FontAwesomeIcon, { icon: faCircle, className: "me-2" }),
|
|
2971
3354
|
"Context"),
|
|
2972
|
-
(LogMetadata.Tag && Object.keys(LogMetadata.Tag).length > 0) && (React.createElement("button", { type: "button", id: "LogReviewer-toggle-tag-filter-drawer", className: `btn btn-${FilterDrawer.Tag === expandedFilterDrawer ? 'warning' : '
|
|
3355
|
+
(LogMetadata.Tag && Object.keys(LogMetadata.Tag).length > 0) && (React.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: () => {
|
|
2973
3356
|
dispatch({
|
|
2974
3357
|
type: ActionType.ToggleFilterDrawer,
|
|
2975
3358
|
filterDrawer: FilterDrawer.Tag,
|
|
@@ -2977,7 +3360,7 @@ const LogReviewer = (props) => {
|
|
|
2977
3360
|
} },
|
|
2978
3361
|
React.createElement(FontAwesomeIcon, { icon: faTag, className: "me-2" }),
|
|
2979
3362
|
"Tag")),
|
|
2980
|
-
React.createElement("button", { type: "button", id: "LogReviewer-toggle-action-filter-drawer", className: `btn btn-${FilterDrawer.Action === expandedFilterDrawer ? 'warning' : '
|
|
3363
|
+
React.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: () => {
|
|
2981
3364
|
dispatch({
|
|
2982
3365
|
type: ActionType.ToggleFilterDrawer,
|
|
2983
3366
|
filterDrawer: FilterDrawer.Action,
|
|
@@ -2985,41 +3368,64 @@ const LogReviewer = (props) => {
|
|
|
2985
3368
|
} },
|
|
2986
3369
|
React.createElement(FontAwesomeIcon, { icon: faHammer, className: "me-2" }),
|
|
2987
3370
|
"Action"),
|
|
2988
|
-
React.createElement("button", { type: "button", id: "LogReviewer-toggle-advanced-filter-drawer", className: `btn btn-${FilterDrawer.Advanced === expandedFilterDrawer ? 'warning' : '
|
|
3371
|
+
React.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: () => {
|
|
2989
3372
|
dispatch({
|
|
2990
3373
|
type: ActionType.ToggleFilterDrawer,
|
|
2991
3374
|
filterDrawer: FilterDrawer.Advanced,
|
|
2992
3375
|
});
|
|
2993
3376
|
} },
|
|
2994
3377
|
React.createElement(FontAwesomeIcon, { icon: faList, className: "me-2" }),
|
|
2995
|
-
"Advanced")
|
|
3378
|
+
"Advanced"),
|
|
3379
|
+
React.createElement("button", { type: "button", id: "LogReviewer-reset-filters-button", className: "btn btn-light", "aria-label": "reset filters", onClick: () => {
|
|
3380
|
+
dispatch({
|
|
3381
|
+
type: ActionType.ResetFilters,
|
|
3382
|
+
initActionErrorFilterState,
|
|
3383
|
+
initAdvancedFilterState,
|
|
3384
|
+
initContextFilterState,
|
|
3385
|
+
initDateFilterState,
|
|
3386
|
+
initTagFilterState,
|
|
3387
|
+
});
|
|
3388
|
+
} },
|
|
3389
|
+
React.createElement(FontAwesomeIcon, { icon: faTimes }),
|
|
3390
|
+
' ',
|
|
3391
|
+
"Reset"))));
|
|
2996
3392
|
// Filter drawer
|
|
2997
3393
|
let filterDrawer;
|
|
2998
3394
|
if (expandedFilterDrawer) {
|
|
2999
3395
|
if (expandedFilterDrawer === FilterDrawer.Date) {
|
|
3000
3396
|
filterDrawer = (React.createElement(TabBox, { title: "Date" },
|
|
3001
|
-
React.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) => {
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
dateFilterState: Object.assign(Object.assign({}, dateFilterState), { startDate: { month, day, year } }),
|
|
3005
|
-
});
|
|
3397
|
+
React.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) => {
|
|
3398
|
+
dateFilterState.startDate = { month, day, year };
|
|
3399
|
+
handleDateRangeUpdated(dateFilterState);
|
|
3006
3400
|
} }),
|
|
3007
3401
|
' ',
|
|
3008
3402
|
"to",
|
|
3009
3403
|
' ',
|
|
3010
|
-
React.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) => {
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3404
|
+
React.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) => {
|
|
3405
|
+
if (year < dateFilterState.startDate.year
|
|
3406
|
+
|| (year === dateFilterState.startDate.year
|
|
3407
|
+
&& month < dateFilterState.startDate.month)
|
|
3408
|
+
|| (year === dateFilterState.startDate.year
|
|
3409
|
+
&& month === dateFilterState.startDate.month
|
|
3410
|
+
&& day < dateFilterState.startDate.day)) {
|
|
3411
|
+
return alert$1('Invalid Start Date', 'The start date cannot be before the end date.');
|
|
3412
|
+
}
|
|
3413
|
+
dateFilterState.endDate = { month, day, year };
|
|
3414
|
+
handleDateRangeUpdated(dateFilterState);
|
|
3015
3415
|
} })));
|
|
3016
3416
|
}
|
|
3017
3417
|
else if (expandedFilterDrawer === FilterDrawer.Context) {
|
|
3018
3418
|
// Create item picker items
|
|
3019
|
-
const
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3419
|
+
const builtInPickableItem = {
|
|
3420
|
+
id: 'built-in-contexts',
|
|
3421
|
+
name: 'Auto-logged',
|
|
3422
|
+
isGroup: true,
|
|
3423
|
+
children: [],
|
|
3424
|
+
};
|
|
3425
|
+
const pickableItems = [];
|
|
3426
|
+
Object.keys(contextMap)
|
|
3427
|
+
.forEach((context) => {
|
|
3428
|
+
const value = contextMap[context];
|
|
3023
3429
|
if (typeof value === 'string') {
|
|
3024
3430
|
// No subcategories
|
|
3025
3431
|
const item = {
|
|
@@ -3028,38 +3434,65 @@ const LogReviewer = (props) => {
|
|
|
3028
3434
|
isGroup: false,
|
|
3029
3435
|
checked: !!contextFilterState[context],
|
|
3030
3436
|
};
|
|
3031
|
-
|
|
3437
|
+
// Add built-in items to its own folder
|
|
3438
|
+
const isBuiltIn = context in LogBuiltInMetadata.Context;
|
|
3439
|
+
if (isBuiltIn) {
|
|
3440
|
+
// Add to built-in pickable item
|
|
3441
|
+
builtInPickableItem.children.push(item);
|
|
3442
|
+
}
|
|
3443
|
+
else {
|
|
3444
|
+
// Add to pickable items list
|
|
3445
|
+
pickableItems.push(item);
|
|
3446
|
+
}
|
|
3447
|
+
return;
|
|
3032
3448
|
}
|
|
3033
3449
|
// Has subcategories
|
|
3034
3450
|
const children = (Object.keys(value)
|
|
3451
|
+
// Remove parent name
|
|
3035
3452
|
.filter((subcontext) => {
|
|
3036
3453
|
return subcontext !== '_';
|
|
3037
3454
|
})
|
|
3455
|
+
// Create child pickable items
|
|
3038
3456
|
.map((subcontext) => {
|
|
3039
3457
|
return {
|
|
3040
|
-
id:
|
|
3458
|
+
id: subcontext,
|
|
3041
3459
|
name: genHumanReadableName(subcontext),
|
|
3042
3460
|
isGroup: false,
|
|
3043
|
-
checked:
|
|
3461
|
+
checked: contextFilterState[context][subcontext],
|
|
3044
3462
|
};
|
|
3045
3463
|
}));
|
|
3046
3464
|
const item = {
|
|
3047
3465
|
id: context,
|
|
3048
|
-
name: context,
|
|
3466
|
+
name: genHumanReadableName(context),
|
|
3049
3467
|
isGroup: true,
|
|
3050
3468
|
children,
|
|
3051
3469
|
};
|
|
3052
|
-
|
|
3053
|
-
})
|
|
3470
|
+
pickableItems.push(item);
|
|
3471
|
+
});
|
|
3472
|
+
// Add built-in contexts to end ofl ist
|
|
3473
|
+
pickableItems.push(builtInPickableItem);
|
|
3054
3474
|
// Create filter UI
|
|
3055
3475
|
filterDrawer = (React.createElement(ItemPicker, { title: "Context", items: pickableItems, onChanged: (updatedItems) => {
|
|
3056
3476
|
// Update our state
|
|
3057
3477
|
updatedItems.forEach((pickableItem) => {
|
|
3058
3478
|
if (pickableItem.isGroup) {
|
|
3059
3479
|
// Has subcontexts
|
|
3060
|
-
pickableItem.
|
|
3061
|
-
|
|
3062
|
-
|
|
3480
|
+
if (pickableItem.id === 'built-in-contexts') {
|
|
3481
|
+
// Built-in
|
|
3482
|
+
// Treat as if these were top-level contexts
|
|
3483
|
+
pickableItem.children.forEach((subcontextItem) => {
|
|
3484
|
+
contextFilterState[subcontextItem.id] = ('checked' in subcontextItem
|
|
3485
|
+
&& subcontextItem.checked);
|
|
3486
|
+
});
|
|
3487
|
+
}
|
|
3488
|
+
else {
|
|
3489
|
+
// Not built-in
|
|
3490
|
+
pickableItem.children.forEach((subcontextItem) => {
|
|
3491
|
+
if (!subcontextItem.isGroup) {
|
|
3492
|
+
contextFilterState[pickableItem.id][subcontextItem.id] = (subcontextItem.checked);
|
|
3493
|
+
}
|
|
3494
|
+
});
|
|
3495
|
+
}
|
|
3063
3496
|
}
|
|
3064
3497
|
else {
|
|
3065
3498
|
// No subcontexts
|
|
@@ -3074,13 +3507,19 @@ const LogReviewer = (props) => {
|
|
|
3074
3507
|
}
|
|
3075
3508
|
else if (expandedFilterDrawer === FilterDrawer.Tag) {
|
|
3076
3509
|
// Create filter UI
|
|
3077
|
-
filterDrawer = (React.createElement(TabBox, { title: "Tags" },
|
|
3078
|
-
.
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
} })
|
|
3083
|
-
|
|
3510
|
+
filterDrawer = (React.createElement(TabBox, { title: "Tags" },
|
|
3511
|
+
React.createElement("div", null, "If any tags are selected, logs must contain at least one (but not necessarily all) of the selected tags."),
|
|
3512
|
+
React.createElement("div", { className: "d-flex gap-1 flex-wrap" }, Object.keys((_d = LogMetadata.Tag) !== null && _d !== void 0 ? _d : {})
|
|
3513
|
+
.map((tag) => {
|
|
3514
|
+
const description = genHumanReadableName(tag);
|
|
3515
|
+
return (React.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) => {
|
|
3516
|
+
tagFilterState[tag] = checked;
|
|
3517
|
+
dispatch({
|
|
3518
|
+
type: ActionType.UpdateTagFilterState,
|
|
3519
|
+
tagFilterState,
|
|
3520
|
+
});
|
|
3521
|
+
} }));
|
|
3522
|
+
}))));
|
|
3084
3523
|
}
|
|
3085
3524
|
else if (expandedFilterDrawer === FilterDrawer.Action) {
|
|
3086
3525
|
// Create filter UI
|
|
@@ -3109,36 +3548,35 @@ const LogReviewer = (props) => {
|
|
|
3109
3548
|
}, ariaLabel: "only show error logs", selected: actionErrorFilterState.type === LogType$1.Error, noMarginOnRight: true })),
|
|
3110
3549
|
(actionErrorFilterState.type === undefined
|
|
3111
3550
|
|| actionErrorFilterState.type === LogType$1.Action) && (React.createElement(TabBox, { title: "Action Log Details" },
|
|
3112
|
-
React.createElement(ButtonInputGroup, { label: "Action", className: "mb-2" },
|
|
3113
|
-
.
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3551
|
+
React.createElement(ButtonInputGroup, { label: "Action", className: "mb-2" },
|
|
3552
|
+
React.createElement("div", { className: "d-flex gap-1 flex-wrap" }, Object.keys(LogAction$1)
|
|
3553
|
+
.map((action) => {
|
|
3554
|
+
const description = genHumanReadableName(action);
|
|
3555
|
+
return (React.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) => {
|
|
3556
|
+
actionErrorFilterState.action[action] = checked;
|
|
3557
|
+
dispatch({
|
|
3558
|
+
type: ActionType.UpdateActionErrorFilterState,
|
|
3559
|
+
actionErrorFilterState,
|
|
3560
|
+
});
|
|
3561
|
+
} }));
|
|
3562
|
+
}))),
|
|
3123
3563
|
React.createElement(ButtonInputGroup, { label: "Target" },
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
.map((target, i) => {
|
|
3127
|
-
var _a;
|
|
3564
|
+
React.createElement("div", { className: "d-flex gap-1 flex-wrap" }, Object.keys(targetMap)
|
|
3565
|
+
.map((target) => {
|
|
3128
3566
|
const description = genHumanReadableName(target);
|
|
3129
|
-
return (React.createElement(CheckboxButton, { id: `LogReviewer-target-${target}-checkbox`, text: description, ariaLabel: `include logs with target "${description}" in results`, onChanged: (checked) => {
|
|
3567
|
+
return (React.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) => {
|
|
3130
3568
|
actionErrorFilterState.target[target] = checked;
|
|
3131
3569
|
dispatch({
|
|
3132
3570
|
type: ActionType.UpdateActionErrorFilterState,
|
|
3133
3571
|
actionErrorFilterState,
|
|
3134
3572
|
});
|
|
3135
|
-
}
|
|
3136
|
-
})))),
|
|
3573
|
+
} }));
|
|
3574
|
+
}))))),
|
|
3137
3575
|
(actionErrorFilterState.type === undefined
|
|
3138
3576
|
|| actionErrorFilterState.type === LogType$1.Error) && (React.createElement(TabBox, { title: "Error Log Details" },
|
|
3139
3577
|
React.createElement("div", { className: "input-group mb-2" },
|
|
3140
3578
|
React.createElement("span", { className: "input-group-text" }, "Error Message"),
|
|
3141
|
-
React.createElement("input", { type: "text", className: "form-control", "aria-label": "query for error message", value: actionErrorFilterState.errorMessage, onChange: (e) => {
|
|
3579
|
+
React.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) => {
|
|
3142
3580
|
actionErrorFilterState.errorMessage = e.target.value;
|
|
3143
3581
|
dispatch({
|
|
3144
3582
|
type: ActionType.UpdateActionErrorFilterState,
|
|
@@ -3147,7 +3585,7 @@ const LogReviewer = (props) => {
|
|
|
3147
3585
|
} })),
|
|
3148
3586
|
React.createElement("div", { className: "input-group mb-2" },
|
|
3149
3587
|
React.createElement("span", { className: "input-group-text" }, "Error Code"),
|
|
3150
|
-
React.createElement("input", { type: "text", className: "form-control", "aria-label": "query for error code", value: actionErrorFilterState.
|
|
3588
|
+
React.createElement("input", { type: "text", className: "form-control", "aria-label": "query for error code", value: actionErrorFilterState.errorCode, placeholder: "e.g. GC22", onChange: (e) => {
|
|
3151
3589
|
actionErrorFilterState.errorCode = ((e.target.value)
|
|
3152
3590
|
.trim()
|
|
3153
3591
|
.toUpperCase());
|
|
@@ -3160,10 +3598,10 @@ const LogReviewer = (props) => {
|
|
|
3160
3598
|
else if (expandedFilterDrawer === FilterDrawer.Advanced) {
|
|
3161
3599
|
// Create advanced filter ui
|
|
3162
3600
|
filterDrawer = (React.createElement(React.Fragment, null,
|
|
3163
|
-
React.createElement(TabBox, { title: "User
|
|
3601
|
+
React.createElement(TabBox, { title: "User" },
|
|
3164
3602
|
React.createElement("div", { className: "input-group mb-2" },
|
|
3165
3603
|
React.createElement("span", { className: "input-group-text" }, "User First Name"),
|
|
3166
|
-
React.createElement("input", { type: "text", className: "form-control", "aria-label": "query for user first name", value: advancedFilterState.userFirstName, onChange: (e) => {
|
|
3604
|
+
React.createElement("input", { type: "text", className: "form-control", "aria-label": "query for user first name", value: advancedFilterState.userFirstName, placeholder: "e.g. Divardo", onChange: (e) => {
|
|
3167
3605
|
advancedFilterState.userFirstName = e.target.value;
|
|
3168
3606
|
dispatch({
|
|
3169
3607
|
type: ActionType.UpdateAdvancedFilterState,
|
|
@@ -3172,7 +3610,7 @@ const LogReviewer = (props) => {
|
|
|
3172
3610
|
} })),
|
|
3173
3611
|
React.createElement("div", { className: "input-group mb-2" },
|
|
3174
3612
|
React.createElement("span", { className: "input-group-text" }, "User Last Name"),
|
|
3175
|
-
React.createElement("input", { type: "text", className: "form-control", "aria-label": "query for user last name", value: advancedFilterState.userLastName, onChange: (e) => {
|
|
3613
|
+
React.createElement("input", { type: "text", className: "form-control", "aria-label": "query for user last name", value: advancedFilterState.userLastName, placeholder: "e.g. Calicci", onChange: (e) => {
|
|
3176
3614
|
advancedFilterState.userLastName = e.target.value;
|
|
3177
3615
|
dispatch({
|
|
3178
3616
|
type: ActionType.UpdateAdvancedFilterState,
|
|
@@ -3181,7 +3619,7 @@ const LogReviewer = (props) => {
|
|
|
3181
3619
|
} })),
|
|
3182
3620
|
React.createElement("div", { className: "input-group mb-2" },
|
|
3183
3621
|
React.createElement("span", { className: "input-group-text" }, "User Email"),
|
|
3184
|
-
React.createElement("input", { type: "text", className: "form-control", "aria-label": "query for user email", value: advancedFilterState.userEmail, onChange: (e) => {
|
|
3622
|
+
React.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) => {
|
|
3185
3623
|
advancedFilterState.userEmail = ((e.target.value)
|
|
3186
3624
|
.trim());
|
|
3187
3625
|
dispatch({
|
|
@@ -3191,7 +3629,7 @@ const LogReviewer = (props) => {
|
|
|
3191
3629
|
} })),
|
|
3192
3630
|
React.createElement("div", { className: "input-group mb-2" },
|
|
3193
3631
|
React.createElement("span", { className: "input-group-text" }, "User Canvas Id"),
|
|
3194
|
-
React.createElement("input", { type: "text", className: "form-control", "aria-label": "query for user canvas id", value: advancedFilterState.userId, onChange: (e) => {
|
|
3632
|
+
React.createElement("input", { type: "text", className: "form-control", "aria-label": "query for user canvas id", value: advancedFilterState.userId, placeholder: "e.g. 104985", onChange: (e) => {
|
|
3195
3633
|
const { value } = e.target;
|
|
3196
3634
|
// Only update if value contains only numbers
|
|
3197
3635
|
if (/^\d+$/.test(value)) {
|
|
@@ -3225,10 +3663,10 @@ const LogReviewer = (props) => {
|
|
|
3225
3663
|
advancedFilterState,
|
|
3226
3664
|
});
|
|
3227
3665
|
}, checked: advancedFilterState.includeAdmins, ariaLabel: "show logs from admins" }))),
|
|
3228
|
-
React.createElement(TabBox, { title: "Course
|
|
3666
|
+
React.createElement(TabBox, { title: "Course" },
|
|
3229
3667
|
React.createElement("div", { className: "input-group mb-2" },
|
|
3230
3668
|
React.createElement("span", { className: "input-group-text" }, "Course Name"),
|
|
3231
|
-
React.createElement("input", { type: "text", className: "form-control", "aria-label": "query for course name", value: advancedFilterState.courseName, onChange: (e) => {
|
|
3669
|
+
React.createElement("input", { type: "text", className: "form-control", "aria-label": "query for course name", value: advancedFilterState.courseName, placeholder: "e.g. GLC 200", onChange: (e) => {
|
|
3232
3670
|
advancedFilterState.courseName = e.target.value;
|
|
3233
3671
|
dispatch({
|
|
3234
3672
|
type: ActionType.UpdateAdvancedFilterState,
|
|
@@ -3237,7 +3675,7 @@ const LogReviewer = (props) => {
|
|
|
3237
3675
|
} })),
|
|
3238
3676
|
React.createElement("div", { className: "input-group mb-2" },
|
|
3239
3677
|
React.createElement("span", { className: "input-group-text" }, "Course Canvas Id"),
|
|
3240
|
-
React.createElement("input", { type: "text", className: "form-control", "aria-label": "query for course canvas id", value: advancedFilterState.courseId, onChange: (e) => {
|
|
3678
|
+
React.createElement("input", { type: "text", className: "form-control", "aria-label": "query for course canvas id", value: advancedFilterState.courseId, placeholder: "e.g. 15948", onChange: (e) => {
|
|
3241
3679
|
const { value } = e.target;
|
|
3242
3680
|
// Only update if value contains only numbers
|
|
3243
3681
|
if (/^\d+$/.test(value)) {
|
|
@@ -3249,7 +3687,7 @@ const LogReviewer = (props) => {
|
|
|
3249
3687
|
advancedFilterState,
|
|
3250
3688
|
});
|
|
3251
3689
|
} }))),
|
|
3252
|
-
React.createElement(TabBox, { title: "Device
|
|
3690
|
+
React.createElement(TabBox, { title: "Device" },
|
|
3253
3691
|
React.createElement(ButtonInputGroup, { label: "Device Type" },
|
|
3254
3692
|
React.createElement(RadioButton, { text: "All Devices", ariaLabel: "show logs from all devices", selected: advancedFilterState.isMobile === undefined, onSelected: () => {
|
|
3255
3693
|
advancedFilterState.isMobile = undefined;
|
|
@@ -3295,32 +3733,33 @@ const LogReviewer = (props) => {
|
|
|
3295
3733
|
advancedFilterState,
|
|
3296
3734
|
});
|
|
3297
3735
|
}, noMarginOnRight: true })),
|
|
3298
|
-
React.createElement("div", { className: "
|
|
3299
|
-
React.createElement("
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
.
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
React.createElement("
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
.
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3736
|
+
advancedFilterState.source !== LogSource$1.Client && (React.createElement("div", { className: "mt-2" },
|
|
3737
|
+
React.createElement("div", { className: "input-group mb-2" },
|
|
3738
|
+
React.createElement("span", { className: "input-group-text" }, "Server Route Path"),
|
|
3739
|
+
React.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) => {
|
|
3740
|
+
advancedFilterState.courseName = ((e.target.value)
|
|
3741
|
+
.trim());
|
|
3742
|
+
dispatch({
|
|
3743
|
+
type: ActionType.UpdateAdvancedFilterState,
|
|
3744
|
+
advancedFilterState,
|
|
3745
|
+
});
|
|
3746
|
+
} })),
|
|
3747
|
+
React.createElement("div", { className: "input-group mb-2" },
|
|
3748
|
+
React.createElement("span", { className: "input-group-text" }, "Server Route Template"),
|
|
3749
|
+
React.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) => {
|
|
3750
|
+
advancedFilterState.courseName = ((e.target.value)
|
|
3751
|
+
.trim());
|
|
3752
|
+
dispatch({
|
|
3753
|
+
type: ActionType.UpdateAdvancedFilterState,
|
|
3754
|
+
advancedFilterState,
|
|
3755
|
+
});
|
|
3756
|
+
} })))))));
|
|
3318
3757
|
}
|
|
3319
3758
|
}
|
|
3320
3759
|
// Filters UI
|
|
3321
3760
|
const filters = (React.createElement(React.Fragment, null,
|
|
3322
3761
|
filterToggles,
|
|
3323
|
-
filterDrawer && (React.createElement(Drawer,
|
|
3762
|
+
filterDrawer && (React.createElement(Drawer, { customBackgroundColor: "#eee" }, filterDrawer))));
|
|
3324
3763
|
// Actually filter the logs
|
|
3325
3764
|
// > Perform filters
|
|
3326
3765
|
const logs = [];
|
|
@@ -3361,16 +3800,22 @@ const LogReviewer = (props) => {
|
|
|
3361
3800
|
// Whole context is deselected
|
|
3362
3801
|
contextFilterState[log.context] === false
|
|
3363
3802
|
// None of the subcontexts are selected
|
|
3364
|
-
|| (
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3803
|
+
|| (
|
|
3804
|
+
// Has subcontexts
|
|
3805
|
+
typeof contextFilterState[log.context] !== 'boolean'
|
|
3806
|
+
// None of the subcontexts are selected
|
|
3807
|
+
&& Object.values((_a = contextFilterState[log.context]) !== null && _a !== void 0 ? _a : {})
|
|
3808
|
+
.every((isSelected) => {
|
|
3809
|
+
return !isSelected;
|
|
3810
|
+
}))) {
|
|
3368
3811
|
return;
|
|
3369
3812
|
}
|
|
3370
3813
|
// Subcontext doesn't match
|
|
3371
3814
|
if (
|
|
3372
|
-
// Log
|
|
3373
|
-
log.
|
|
3815
|
+
// Log context is not "uncategorized" (no point in further filters)
|
|
3816
|
+
log.context !== LogBuiltInMetadata.Context.Uncategorized
|
|
3817
|
+
// Log has a subcontext
|
|
3818
|
+
&& log.subcontext
|
|
3374
3819
|
// Context has subcontexts
|
|
3375
3820
|
&& (contextFilterState[log.context]
|
|
3376
3821
|
&& contextFilterState[log.context] !== false
|
|
@@ -3382,12 +3827,16 @@ const LogReviewer = (props) => {
|
|
|
3382
3827
|
/* -------------- Tags -------------- */
|
|
3383
3828
|
// No tags match
|
|
3384
3829
|
if (
|
|
3385
|
-
//
|
|
3386
|
-
|
|
3830
|
+
// At least one tag is required
|
|
3831
|
+
Object.values(tagFilterState)
|
|
3832
|
+
.filter((isSelected) => {
|
|
3833
|
+
return isSelected;
|
|
3834
|
+
})
|
|
3835
|
+
.length > 0
|
|
3387
3836
|
// No tags match
|
|
3388
|
-
&&
|
|
3837
|
+
&& log.tags.every((tag) => {
|
|
3389
3838
|
return !tagFilterState[tag];
|
|
3390
|
-
}))
|
|
3839
|
+
})) {
|
|
3391
3840
|
return;
|
|
3392
3841
|
}
|
|
3393
3842
|
/* ------- Actions and Errors ------- */
|
|
@@ -3521,7 +3970,7 @@ const LogReviewer = (props) => {
|
|
|
3521
3970
|
// Device info exists
|
|
3522
3971
|
&& log.device
|
|
3523
3972
|
// Mobile filter doesn't match
|
|
3524
|
-
&& (advancedFilterState.isMobile
|
|
3973
|
+
&& (advancedFilterState.isMobile !== log.device.isMobile)) {
|
|
3525
3974
|
return;
|
|
3526
3975
|
}
|
|
3527
3976
|
// Log source doesn't match
|
|
@@ -3563,203 +4012,20 @@ const LogReviewer = (props) => {
|
|
|
3563
4012
|
/*----------------------------------------*/
|
|
3564
4013
|
/* Data */
|
|
3565
4014
|
/*----------------------------------------*/
|
|
3566
|
-
//
|
|
3567
|
-
const
|
|
3568
|
-
{
|
|
3569
|
-
title: 'First Name',
|
|
3570
|
-
param: 'userFirstName',
|
|
3571
|
-
type: ParamType$1.String,
|
|
3572
|
-
},
|
|
3573
|
-
{
|
|
3574
|
-
title: 'Last Name',
|
|
3575
|
-
param: 'userLastName',
|
|
3576
|
-
type: ParamType$1.String,
|
|
3577
|
-
},
|
|
3578
|
-
{
|
|
3579
|
-
title: 'Email',
|
|
3580
|
-
param: 'userEmail',
|
|
3581
|
-
type: ParamType$1.String,
|
|
3582
|
-
},
|
|
3583
|
-
{
|
|
3584
|
-
title: 'Canvas Id',
|
|
3585
|
-
param: 'userId',
|
|
3586
|
-
type: ParamType$1.Int,
|
|
3587
|
-
},
|
|
3588
|
-
{
|
|
3589
|
-
title: 'Student',
|
|
3590
|
-
param: 'isLearner',
|
|
3591
|
-
type: ParamType$1.Boolean,
|
|
3592
|
-
},
|
|
3593
|
-
{
|
|
3594
|
-
title: 'Teaching Staff',
|
|
3595
|
-
param: 'isTTM',
|
|
3596
|
-
type: ParamType$1.Boolean,
|
|
3597
|
-
startsHidden: true,
|
|
3598
|
-
},
|
|
3599
|
-
{
|
|
3600
|
-
title: 'Admin',
|
|
3601
|
-
param: 'isAdmin',
|
|
3602
|
-
type: ParamType$1.Boolean,
|
|
3603
|
-
startsHidden: true,
|
|
3604
|
-
},
|
|
3605
|
-
{
|
|
3606
|
-
title: 'Course Canvas Id',
|
|
3607
|
-
param: 'courseId',
|
|
3608
|
-
type: ParamType$1.Int,
|
|
3609
|
-
startsHidden: true,
|
|
3610
|
-
},
|
|
3611
|
-
{
|
|
3612
|
-
title: 'Course Name',
|
|
3613
|
-
param: 'courseName',
|
|
3614
|
-
type: ParamType$1.String,
|
|
3615
|
-
},
|
|
3616
|
-
{
|
|
3617
|
-
title: 'Browser Name',
|
|
3618
|
-
param: 'browser.name',
|
|
3619
|
-
type: ParamType$1.String,
|
|
3620
|
-
startsHidden: true,
|
|
3621
|
-
},
|
|
3622
|
-
{
|
|
3623
|
-
title: 'Browser Version',
|
|
3624
|
-
param: 'browser.version',
|
|
3625
|
-
type: ParamType$1.String,
|
|
3626
|
-
startsHidden: true,
|
|
3627
|
-
},
|
|
3628
|
-
{
|
|
3629
|
-
title: 'OS',
|
|
3630
|
-
param: 'device.os',
|
|
3631
|
-
type: ParamType$1.String,
|
|
3632
|
-
startsHidden: true,
|
|
3633
|
-
},
|
|
3634
|
-
{
|
|
3635
|
-
title: 'Mobile',
|
|
3636
|
-
param: 'device.isMobile',
|
|
3637
|
-
type: ParamType$1.Boolean,
|
|
3638
|
-
startsHidden: true,
|
|
3639
|
-
},
|
|
3640
|
-
{
|
|
3641
|
-
title: 'Year',
|
|
3642
|
-
param: 'year',
|
|
3643
|
-
type: ParamType$1.Int,
|
|
3644
|
-
},
|
|
3645
|
-
{
|
|
3646
|
-
title: 'Month',
|
|
3647
|
-
param: 'month',
|
|
3648
|
-
type: ParamType$1.Int,
|
|
3649
|
-
},
|
|
3650
|
-
{
|
|
3651
|
-
title: 'Day',
|
|
3652
|
-
param: 'day',
|
|
3653
|
-
type: ParamType$1.Int,
|
|
3654
|
-
},
|
|
3655
|
-
{
|
|
3656
|
-
title: 'Hour',
|
|
3657
|
-
param: 'hour',
|
|
3658
|
-
type: ParamType$1.Int,
|
|
3659
|
-
},
|
|
3660
|
-
{
|
|
3661
|
-
title: 'Minute',
|
|
3662
|
-
param: 'minute',
|
|
3663
|
-
type: ParamType$1.Int,
|
|
3664
|
-
startsHidden: true,
|
|
3665
|
-
},
|
|
3666
|
-
{
|
|
3667
|
-
title: 'Timestamp',
|
|
3668
|
-
param: 'timestamp',
|
|
3669
|
-
type: ParamType$1.Int,
|
|
3670
|
-
startsHidden: true,
|
|
3671
|
-
},
|
|
3672
|
-
{
|
|
3673
|
-
title: 'Context',
|
|
3674
|
-
param: 'context',
|
|
3675
|
-
type: ParamType$1.String,
|
|
3676
|
-
},
|
|
3677
|
-
{
|
|
3678
|
-
title: 'Subcontext',
|
|
3679
|
-
param: 'subcontext',
|
|
3680
|
-
type: ParamType$1.String,
|
|
3681
|
-
},
|
|
3682
|
-
{
|
|
3683
|
-
title: 'Tags',
|
|
3684
|
-
param: 'tags',
|
|
3685
|
-
type: ParamType$1.JSON,
|
|
3686
|
-
startsHidden: true,
|
|
3687
|
-
},
|
|
3688
|
-
{
|
|
3689
|
-
title: 'Log Level',
|
|
3690
|
-
param: 'level',
|
|
3691
|
-
type: ParamType$1.String,
|
|
3692
|
-
startsHidden: true,
|
|
3693
|
-
},
|
|
3694
|
-
{
|
|
3695
|
-
title: 'Metadata',
|
|
3696
|
-
param: 'metadata',
|
|
3697
|
-
type: ParamType$1.JSON,
|
|
3698
|
-
startsHidden: true,
|
|
3699
|
-
},
|
|
3700
|
-
{
|
|
3701
|
-
title: 'Source',
|
|
3702
|
-
param: 'source',
|
|
3703
|
-
type: ParamType$1.String,
|
|
3704
|
-
},
|
|
3705
|
-
{
|
|
3706
|
-
title: 'Server Route Path',
|
|
3707
|
-
param: 'routePath',
|
|
3708
|
-
type: ParamType$1.String,
|
|
3709
|
-
startsHidden: true,
|
|
3710
|
-
},
|
|
3711
|
-
{
|
|
3712
|
-
title: 'Server Route Template',
|
|
3713
|
-
param: 'routeTemplate',
|
|
3714
|
-
type: ParamType$1.String,
|
|
3715
|
-
startsHidden: true,
|
|
3716
|
-
},
|
|
3717
|
-
{
|
|
3718
|
-
title: 'Type',
|
|
3719
|
-
param: 'type',
|
|
3720
|
-
type: ParamType$1.String,
|
|
3721
|
-
},
|
|
3722
|
-
{
|
|
3723
|
-
title: 'Error Message',
|
|
3724
|
-
param: 'errorMessage',
|
|
3725
|
-
type: ParamType$1.String,
|
|
3726
|
-
startsHidden: true,
|
|
3727
|
-
},
|
|
3728
|
-
{
|
|
3729
|
-
title: 'Error Code',
|
|
3730
|
-
param: 'errorCode',
|
|
3731
|
-
type: ParamType$1.String,
|
|
3732
|
-
startsHidden: true,
|
|
3733
|
-
},
|
|
3734
|
-
{
|
|
3735
|
-
title: 'Error Stack',
|
|
3736
|
-
param: 'errorStack',
|
|
3737
|
-
type: ParamType$1.String,
|
|
3738
|
-
startsHidden: true,
|
|
3739
|
-
},
|
|
3740
|
-
{
|
|
3741
|
-
title: 'Action Target',
|
|
3742
|
-
param: 'target',
|
|
3743
|
-
type: ParamType$1.String,
|
|
3744
|
-
startsHidden: true,
|
|
3745
|
-
},
|
|
3746
|
-
{
|
|
3747
|
-
title: 'Action Type',
|
|
3748
|
-
param: 'action',
|
|
3749
|
-
type: ParamType$1.String,
|
|
3750
|
-
startsHidden: true,
|
|
3751
|
-
},
|
|
3752
|
-
];
|
|
3753
|
-
// Create intelliTable
|
|
3754
|
-
const dataTable = (logs.length === 0
|
|
3755
|
-
? (React.createElement("div", { className: "alert alert-warning" },
|
|
4015
|
+
// Nothing to show notice
|
|
4016
|
+
const noLogsNotice = (logs.length === 0
|
|
4017
|
+
? (React.createElement("div", { className: "alert alert-warning text-center mt-2" },
|
|
3756
4018
|
React.createElement("h4", { className: "m-1" }, "No Logs to Show"),
|
|
3757
4019
|
React.createElement("div", null, "Either your filters are too strict or no matching logs have been created yet.")))
|
|
3758
|
-
:
|
|
4020
|
+
: undefined);
|
|
4021
|
+
// Create intelliTable
|
|
4022
|
+
const dataTable = (React.createElement(IntelliTable, { title: "Matching Logs:", csvName: `Logs from ${getHumanReadableDate()}`, id: "logs", data: logs, columns: columns }));
|
|
3759
4023
|
// Main body
|
|
3760
4024
|
body = (React.createElement(React.Fragment, null,
|
|
3761
4025
|
filters,
|
|
3762
|
-
|
|
4026
|
+
React.createElement("div", { className: "mt-2" },
|
|
4027
|
+
dataTable,
|
|
4028
|
+
noLogsNotice)));
|
|
3763
4029
|
}
|
|
3764
4030
|
/* ---------- Wrap in Modal --------- */
|
|
3765
4031
|
return (React.createElement("div", { className: "LogReviewer-outer-container" },
|
|
@@ -3767,9 +4033,10 @@ const LogReviewer = (props) => {
|
|
|
3767
4033
|
React.createElement("div", { className: "LogReviewer-inner-container" },
|
|
3768
4034
|
React.createElement("div", { className: "LogReviewer-header" },
|
|
3769
4035
|
React.createElement("div", { className: "LogReviewer-header-title" },
|
|
3770
|
-
React.createElement("h3", { className: "m-0" }, "Log Review Dashboard")),
|
|
3771
|
-
React.createElement("
|
|
3772
|
-
React.createElement(
|
|
4036
|
+
React.createElement("h3", { className: "text-center m-0" }, "Log Review Dashboard")),
|
|
4037
|
+
React.createElement("div", { style: { width: 0 } },
|
|
4038
|
+
React.createElement("button", { type: "button", className: "LogReviewer-header-close-button btn btn-dark btn-lg pe-0", "aria-label": "close log reviewer panel", onClick: onClose },
|
|
4039
|
+
React.createElement(FontAwesomeIcon, { icon: faTimes })))),
|
|
3773
4040
|
React.createElement("div", { className: "LogReviewer-contents" }, body))));
|
|
3774
4041
|
};
|
|
3775
4042
|
|
|
@@ -3791,6 +4058,50 @@ const HOUR_IN_MS = 3600000;
|
|
|
3791
4058
|
*/
|
|
3792
4059
|
const DAY_IN_MS = 86400000;
|
|
3793
4060
|
|
|
4061
|
+
/**
|
|
4062
|
+
* Check if the user is on mobile
|
|
4063
|
+
* from https://stackoverflow.com/questions/11381673/detecting-a-mobile-browser
|
|
4064
|
+
* @returns true if user is on a mobile device
|
|
4065
|
+
*/
|
|
4066
|
+
const checkIfMobileOrTablet = () => {
|
|
4067
|
+
var _a;
|
|
4068
|
+
try {
|
|
4069
|
+
let check = false;
|
|
4070
|
+
((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)))
|
|
4071
|
+
check = true; })((navigator !== null && navigator !== void 0 ? navigator : {}).userAgent || (navigator !== null && navigator !== void 0 ? navigator : {}).vendor || ((_a = window) !== null && _a !== void 0 ? _a : {}).opera);
|
|
4072
|
+
return check;
|
|
4073
|
+
}
|
|
4074
|
+
catch (err) {
|
|
4075
|
+
// Probably this code is being executed on a server
|
|
4076
|
+
return false;
|
|
4077
|
+
}
|
|
4078
|
+
};
|
|
4079
|
+
const isMobileOrTablet = checkIfMobileOrTablet();
|
|
4080
|
+
/**
|
|
4081
|
+
* Dynamic words determined by the user's platform
|
|
4082
|
+
* @author Gabe Abrams
|
|
4083
|
+
*/
|
|
4084
|
+
const DynamicWord = {
|
|
4085
|
+
Click: (isMobileOrTablet
|
|
4086
|
+
? 'tap'
|
|
4087
|
+
: 'click'),
|
|
4088
|
+
ClickCapitalized: (isMobileOrTablet
|
|
4089
|
+
? 'Tap'
|
|
4090
|
+
: 'Click'),
|
|
4091
|
+
App: (isMobileOrTablet
|
|
4092
|
+
? 'app'
|
|
4093
|
+
: 'application'),
|
|
4094
|
+
AppCapitalized: (isMobileOrTablet
|
|
4095
|
+
? 'App'
|
|
4096
|
+
: 'Application'),
|
|
4097
|
+
Device: (isMobileOrTablet
|
|
4098
|
+
? 'device'
|
|
4099
|
+
: 'computer'),
|
|
4100
|
+
DeviceCapitalized: (isMobileOrTablet
|
|
4101
|
+
? 'Device'
|
|
4102
|
+
: 'Computer'),
|
|
4103
|
+
};
|
|
4104
|
+
|
|
3794
4105
|
/**
|
|
3795
4106
|
* Shorten text so it fits into a certain number of chars
|
|
3796
4107
|
* @author Gabe Abrams
|
|
@@ -3865,6 +4176,18 @@ const floorToNumDecimals = (num, numDecimals) => {
|
|
|
3865
4176
|
return (Math.floor(num * rounder) / rounder);
|
|
3866
4177
|
};
|
|
3867
4178
|
|
|
4179
|
+
/**
|
|
4180
|
+
* Force a number to stay within specific bounds
|
|
4181
|
+
* @author Gabe Abrams
|
|
4182
|
+
* @param num the number to move into the bounds
|
|
4183
|
+
* @param min the minimum number in the bound
|
|
4184
|
+
* @param max the maximum number in the bound
|
|
4185
|
+
* @returns bounded number
|
|
4186
|
+
*/
|
|
4187
|
+
const forceNumIntoBounds = (num, min, max) => {
|
|
4188
|
+
return Math.max(min, Math.min(max, num));
|
|
4189
|
+
};
|
|
4190
|
+
|
|
3868
4191
|
/**
|
|
3869
4192
|
* Pad a number's decimal with zeros on the right
|
|
3870
4193
|
* (e.g. 5.2 becomes 5.20 with 2 digit padding)
|
|
@@ -4100,15 +4423,17 @@ const initServer = (opts) => {
|
|
|
4100
4423
|
},
|
|
4101
4424
|
handler: ({ params }) => __awaiter(void 0, void 0, void 0, function* () {
|
|
4102
4425
|
// Get user info
|
|
4103
|
-
const { userId, isAdmin } = params;
|
|
4426
|
+
const { year, month, userId, isAdmin, } = params;
|
|
4104
4427
|
// Validate user
|
|
4105
|
-
// isAdmin is already checked because path starts with '/admin'
|
|
4106
4428
|
const canReview = yield canReviewLogs(userId, isAdmin);
|
|
4107
4429
|
if (!canReview) {
|
|
4108
4430
|
throw new ErrorWithCode('You cannot access this resource because you do not have the appropriate permissions.', ReactKitErrorCode$1.NotAllowedToReviewLogs);
|
|
4109
4431
|
}
|
|
4110
4432
|
// Query for logs
|
|
4111
|
-
const logs = yield _logCollection.find({
|
|
4433
|
+
const logs = yield _logCollection.find({
|
|
4434
|
+
year,
|
|
4435
|
+
month,
|
|
4436
|
+
});
|
|
4112
4437
|
// Return logs
|
|
4113
4438
|
return logs;
|
|
4114
4439
|
}),
|
|
@@ -4757,7 +5082,7 @@ const genRouteHandler = (opts) => {
|
|
|
4757
5082
|
isAdmin: !!launchInfo.isAdmin,
|
|
4758
5083
|
isTTM: !!launchInfo.isTTM,
|
|
4759
5084
|
courseId: launchInfo.courseId,
|
|
4760
|
-
courseName: launchInfo.
|
|
5085
|
+
courseName: launchInfo.contextLabel,
|
|
4761
5086
|
browser,
|
|
4762
5087
|
device,
|
|
4763
5088
|
year,
|
|
@@ -4784,7 +5109,7 @@ const genRouteHandler = (opts) => {
|
|
|
4784
5109
|
}
|
|
4785
5110
|
: {
|
|
4786
5111
|
type: LogType$1.Action,
|
|
4787
|
-
target: ((_m = opts.target) !== null && _m !== void 0 ? _m : LogBuiltInMetadata.Target.
|
|
5112
|
+
target: ((_m = opts.target) !== null && _m !== void 0 ? _m : LogBuiltInMetadata.Target.NoTarget),
|
|
4788
5113
|
action: ((_o = opts.action) !== null && _o !== void 0 ? _o : LogAction$1.Unknown),
|
|
4789
5114
|
});
|
|
4790
5115
|
// Source-specific info
|
|
@@ -4997,29 +5322,6 @@ const startMinWait = (minWaitMs) => {
|
|
|
4997
5322
|
});
|
|
4998
5323
|
};
|
|
4999
5324
|
|
|
5000
|
-
// Import shared helpers
|
|
5001
|
-
/**
|
|
5002
|
-
* Get a human-readable description of a date (all in ET)
|
|
5003
|
-
* @author Gabe Abrams
|
|
5004
|
-
* @param [dateOrTimestamp=today] the date or timestamp for the date to describe
|
|
5005
|
-
* @returns human-readable description of the date
|
|
5006
|
-
*/
|
|
5007
|
-
const getHumanReadableDate = (dateOrTimestamp) => {
|
|
5008
|
-
// Get the date info
|
|
5009
|
-
const { month, day, year, } = getTimeInfoInET(dateOrTimestamp);
|
|
5010
|
-
const currYear = getTimeInfoInET().year;
|
|
5011
|
-
// Get the short month description
|
|
5012
|
-
const monthName = getMonthName(month).short;
|
|
5013
|
-
// Create start of description
|
|
5014
|
-
let description = `${monthName} ${day}${getOrdinal(day)}`;
|
|
5015
|
-
// Add on year if it's different
|
|
5016
|
-
if (year !== currYear) {
|
|
5017
|
-
description += ` ${year}`;
|
|
5018
|
-
}
|
|
5019
|
-
// Return description
|
|
5020
|
-
return description;
|
|
5021
|
-
};
|
|
5022
|
-
|
|
5023
5325
|
/**
|
|
5024
5326
|
* Get the current part of day (morning, evening, etc.)
|
|
5025
5327
|
* @author Gabe Abrams
|
|
@@ -5192,5 +5494,5 @@ var DayOfWeek;
|
|
|
5192
5494
|
})(DayOfWeek || (DayOfWeek = {}));
|
|
5193
5495
|
var DayOfWeek$1 = DayOfWeek;
|
|
5194
5496
|
|
|
5195
|
-
export { AppWrapper, ButtonInputGroup, CSVDownloadButton, CheckboxButton, CopiableBox, DAY_IN_MS, DayOfWeek$1 as DayOfWeek, Drawer, ErrorBox, ErrorWithCode, HOUR_IN_MS, IntelliTable, ItemPicker, LoadingSpinner, LogAction$1 as LogAction, LogBuiltInMetadata, LogReviewer, LogSource$1 as LogSource, LogType$1 as LogType, MINUTE_IN_MS, Modal, ModalButtonType$1 as ModalButtonType, ModalSize$1 as ModalSize, ModalType$1 as ModalType, ParamType$1 as ParamType, PopFailureMark, PopPendingMark, PopSuccessMark, RadioButton, ReactKitErrorCode$1 as ReactKitErrorCode, SimpleDateChooser, TabBox, Variant$1 as Variant, abbreviate, alert$1 as alert, avg, canReviewLogs, ceilToNumDecimals, confirm, floorToNumDecimals, forceNumIntoBounds, genCSV, genRouteHandler, getHumanReadableDate, getMonthName, getOrdinal, getPartOfDay, getTimeInfoInET, handleError, handleSuccess, initLogCollection, initServer, logClientEvent, onlyKeepLetters, padDecimalZeros, padZerosLeft, parallelLimit, roundToNumDecimals, showFatalError, startMinWait, stringsToHumanReadableList, stubServerEndpoint, sum, visitServerEndpoint, waitMs };
|
|
5497
|
+
export { AppWrapper, ButtonInputGroup, CSVDownloadButton, CheckboxButton, CopiableBox, DAY_IN_MS, DayOfWeek$1 as DayOfWeek, Drawer, DynamicWord, ErrorBox, ErrorWithCode, HOUR_IN_MS, IntelliTable, ItemPicker, LoadingSpinner, LogAction$1 as LogAction, LogBuiltInMetadata, LogReviewer, LogSource$1 as LogSource, LogType$1 as LogType, MINUTE_IN_MS, Modal, ModalButtonType$1 as ModalButtonType, ModalSize$1 as ModalSize, ModalType$1 as ModalType, ParamType$1 as ParamType, PopFailureMark, PopPendingMark, PopSuccessMark, RadioButton, ReactKitErrorCode$1 as ReactKitErrorCode, SimpleDateChooser, TabBox, Variant$1 as Variant, abbreviate, alert$1 as alert, avg, canReviewLogs, ceilToNumDecimals, confirm, floorToNumDecimals, forceNumIntoBounds, genCSV, genRouteHandler, getHumanReadableDate, getMonthName, getOrdinal, getPartOfDay, getTimeInfoInET, handleError, handleSuccess, initLogCollection, initServer, logClientEvent, onlyKeepLetters, padDecimalZeros, padZerosLeft, parallelLimit, roundToNumDecimals, showFatalError, startMinWait, stringsToHumanReadableList, stubServerEndpoint, sum, visitServerEndpoint, waitMs };
|
|
5196
5498
|
//# sourceMappingURL=index.js.map
|