dce-reactkit 3.2.2-beta.3 → 3.2.2-beta.30
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 +360 -207
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/types/components/ButtonInputGroup.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/server/initServer.d.ts +1 -1
- package/dist/cjs/types/types/LogBuiltInMetadata.d.ts +1 -1
- package/dist/esm/index.js +360 -207
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/types/components/ButtonInputGroup.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/server/initServer.d.ts +1 -1
- package/dist/esm/types/types/LogBuiltInMetadata.d.ts +1 -1
- package/dist/index.d.ts +4 -2
- package/package.json +1 -1
- package/src/components/ButtonInputGroup.tsx +4 -1
- package/src/components/IntelliTable.tsx +4 -4
- package/src/components/ItemPicker/NestableItemList.tsx +32 -14
- package/src/components/ItemPicker/index.tsx +4 -0
- package/src/components/LogReviewer.tsx +314 -143
- package/src/components/SimpleDateChooser.tsx +20 -7
- package/src/constants/LOG_REVIEW_STATUS_ROUTE.ts +1 -1
- package/src/helpers/genRouteHandler.ts +1 -1
- package/src/helpers/logClientEvent.tsx +6 -1
- package/src/server/initServer.ts +93 -73
- package/src/types/LogBuiltInMetadata.ts +5 -5
package/dist/esm/index.js
CHANGED
|
@@ -511,17 +511,29 @@ 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
|
|
|
525
|
+
/**
|
|
526
|
+
* Allowed log levels
|
|
527
|
+
* @author Gabe Abrams
|
|
528
|
+
*/
|
|
529
|
+
var LogLevel;
|
|
530
|
+
(function (LogLevel) {
|
|
531
|
+
LogLevel["Warn"] = "Warn";
|
|
532
|
+
LogLevel["Info"] = "Info";
|
|
533
|
+
LogLevel["Debug"] = "Debug";
|
|
534
|
+
})(LogLevel || (LogLevel = {}));
|
|
535
|
+
var LogLevel$1 = LogLevel;
|
|
536
|
+
|
|
525
537
|
// Keep track of whether or not session expiry has already been handled
|
|
526
538
|
let sessionAlreadyExpired = false;
|
|
527
539
|
/*------------------------------------------------------------------------*/
|
|
@@ -640,7 +652,7 @@ const visitServerEndpoint = (opts) => __awaiter(void 0, void 0, void 0, function
|
|
|
640
652
|
* @author Gabe Abrams
|
|
641
653
|
*/
|
|
642
654
|
const logClientEvent = (opts) => __awaiter(void 0, void 0, void 0, function* () {
|
|
643
|
-
var _a, _b, _c, _d, _e, _f;
|
|
655
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
644
656
|
return visitServerEndpoint({
|
|
645
657
|
path: LOG_ROUTE_PATH,
|
|
646
658
|
method: 'POST',
|
|
@@ -649,8 +661,9 @@ const logClientEvent = (opts) => __awaiter(void 0, void 0, void 0, function* ()
|
|
|
649
661
|
? opts.context
|
|
650
662
|
: ((_b = ((_a = opts.context) !== null && _a !== void 0 ? _a : {})._) !== null && _b !== void 0 ? _b : LogBuiltInMetadata.Context.Uncategorized)),
|
|
651
663
|
subcontext: ((_c = opts.subcontext) !== null && _c !== void 0 ? _c : LogBuiltInMetadata.Context.Uncategorized),
|
|
652
|
-
|
|
653
|
-
|
|
664
|
+
level: ((_d = opts.level) !== null && _d !== void 0 ? _d : LogLevel$1.Info),
|
|
665
|
+
tags: JSON.stringify((_e = opts.tags) !== null && _e !== void 0 ? _e : []),
|
|
666
|
+
metadata: JSON.stringify((_f = opts.metadata) !== null && _f !== void 0 ? _f : {}),
|
|
654
667
|
errorMessage: (opts.error
|
|
655
668
|
? opts.error.message
|
|
656
669
|
: undefined),
|
|
@@ -661,7 +674,7 @@ const logClientEvent = (opts) => __awaiter(void 0, void 0, void 0, function* ()
|
|
|
661
674
|
? opts.error.stack
|
|
662
675
|
: undefined),
|
|
663
676
|
target: (opts.action
|
|
664
|
-
? ((
|
|
677
|
+
? ((_g = opts.target) !== null && _g !== void 0 ? _g : LogBuiltInMetadata.Target.NoTarget)
|
|
665
678
|
: undefined),
|
|
666
679
|
action: (opts.action
|
|
667
680
|
? opts.action
|
|
@@ -1167,14 +1180,14 @@ const ButtonInputGroup = (props) => {
|
|
|
1167
1180
|
/*------------------------------------------------------------------------*/
|
|
1168
1181
|
/* -------------- Props ------------- */
|
|
1169
1182
|
// Destructure all props
|
|
1170
|
-
const { label, minLabelWidth, children, } = props;
|
|
1183
|
+
const { label, minLabelWidth, children, className, } = props;
|
|
1171
1184
|
/*------------------------------------------------------------------------*/
|
|
1172
1185
|
/* Render */
|
|
1173
1186
|
/*------------------------------------------------------------------------*/
|
|
1174
1187
|
/*----------------------------------------*/
|
|
1175
1188
|
/* Main UI */
|
|
1176
1189
|
/*----------------------------------------*/
|
|
1177
|
-
return (React.createElement("div", { className:
|
|
1190
|
+
return (React.createElement("div", { className: `input-group ${className !== null && className !== void 0 ? className : ''}` },
|
|
1178
1191
|
React.createElement("div", { className: "input-group-prepend d-flex w-100" },
|
|
1179
1192
|
React.createElement("span", { className: "input-group-text", style: {
|
|
1180
1193
|
minWidth: (minLabelWidth !== null && minLabelWidth !== void 0 ? minLabelWidth : undefined),
|
|
@@ -1344,12 +1357,25 @@ const SimpleDateChooser = (props) => {
|
|
|
1344
1357
|
// Figure out which days are allowed
|
|
1345
1358
|
const days = [];
|
|
1346
1359
|
const numDaysInMonth = (new Date(year, month, 0)).getDate();
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1360
|
+
if (chooseFromPast) {
|
|
1361
|
+
// Past selection
|
|
1362
|
+
const numDaysToAdd = ((month === today.month)
|
|
1363
|
+
? today.day // Current month, only add up to today
|
|
1364
|
+
: numDaysInMonth // Past month, add all days
|
|
1365
|
+
);
|
|
1366
|
+
for (let day = 1; day <= numDaysToAdd; day++) {
|
|
1367
|
+
days.push(day);
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
else {
|
|
1371
|
+
// Future selection: add all remaining days of the month
|
|
1372
|
+
const firstDay = (month === today.month
|
|
1373
|
+
? today.day // Current month: start at current date
|
|
1374
|
+
: 1 // Future month: start at beginning of month
|
|
1375
|
+
);
|
|
1376
|
+
for (let day = firstDay; day <= numDaysInMonth; day++) {
|
|
1377
|
+
days.push(day);
|
|
1378
|
+
}
|
|
1353
1379
|
}
|
|
1354
1380
|
choices.push({
|
|
1355
1381
|
choiceName: `${monthName} ${year}`,
|
|
@@ -1890,24 +1916,25 @@ const CopiableBox = (props) => {
|
|
|
1890
1916
|
/**
|
|
1891
1917
|
* Reusable nested item picker
|
|
1892
1918
|
* @author Yuen Ler Chow
|
|
1919
|
+
* @author Gabe Abrams
|
|
1893
1920
|
*/
|
|
1894
1921
|
/* ------------- Actions ------------ */
|
|
1895
1922
|
// Types of actions
|
|
1896
1923
|
var ActionType$2;
|
|
1897
1924
|
(function (ActionType) {
|
|
1898
|
-
// Toggle whether
|
|
1899
|
-
ActionType["
|
|
1925
|
+
// Toggle whether a child are being shown
|
|
1926
|
+
ActionType["ToggleChild"] = "toggle-child";
|
|
1900
1927
|
})(ActionType$2 || (ActionType$2 = {}));
|
|
1901
1928
|
/**
|
|
1902
1929
|
* Reducer that executes actions
|
|
1903
|
-
* @author
|
|
1930
|
+
* @author Gabe Abrams
|
|
1904
1931
|
* @param state current state
|
|
1905
1932
|
* @param action action to execute
|
|
1906
1933
|
*/
|
|
1907
1934
|
const reducer$2 = (state, action) => {
|
|
1908
1935
|
switch (action.type) {
|
|
1909
|
-
case ActionType$2.
|
|
1910
|
-
return {
|
|
1936
|
+
case ActionType$2.ToggleChild: {
|
|
1937
|
+
return Object.assign(Object.assign({}, state), { childExpanded: Object.assign(Object.assign({}, state.childExpanded), { [String(action.id)]: !state.childExpanded[String(action.id)] }) });
|
|
1911
1938
|
}
|
|
1912
1939
|
default: {
|
|
1913
1940
|
return state;
|
|
@@ -1925,14 +1952,19 @@ const NestableItemList = (props) => {
|
|
|
1925
1952
|
// Destructure all props
|
|
1926
1953
|
const { items, onChanged, } = props;
|
|
1927
1954
|
/* -------------- State ------------- */
|
|
1955
|
+
// Create initial map of child expanded booleans
|
|
1956
|
+
const initChildExpanded = {};
|
|
1957
|
+
items.forEach((item) => {
|
|
1958
|
+
initChildExpanded[String(item.id)] = false;
|
|
1959
|
+
});
|
|
1928
1960
|
// Initial state
|
|
1929
1961
|
const initialState = {
|
|
1930
|
-
|
|
1962
|
+
childExpanded: initChildExpanded,
|
|
1931
1963
|
};
|
|
1932
1964
|
// Initialize state
|
|
1933
1965
|
const [state, dispatch] = useReducer(reducer$2, initialState);
|
|
1934
1966
|
// Destructure common state
|
|
1935
|
-
const {
|
|
1967
|
+
const { childExpanded, } = state;
|
|
1936
1968
|
/*------------------------------------------------------------------------*/
|
|
1937
1969
|
/* Component Functions */
|
|
1938
1970
|
/*------------------------------------------------------------------------*/
|
|
@@ -2020,14 +2052,15 @@ const NestableItemList = (props) => {
|
|
|
2020
2052
|
backgroundColor: 'transparent',
|
|
2021
2053
|
}, type: "button", onClick: () => {
|
|
2022
2054
|
dispatch({
|
|
2023
|
-
type: ActionType$2.
|
|
2055
|
+
type: ActionType$2.ToggleChild,
|
|
2056
|
+
id: item.id,
|
|
2024
2057
|
});
|
|
2025
|
-
}, "aria-label": `${
|
|
2026
|
-
React.createElement(FontAwesomeIcon, { icon:
|
|
2058
|
+
}, "aria-label": `${childExpanded[item.id] ? 'Hide' : 'Show'} items in ${item.name}` },
|
|
2059
|
+
React.createElement(FontAwesomeIcon, { icon: childExpanded[item.id] ? faChevronDown : faChevronRight })))),
|
|
2027
2060
|
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) => {
|
|
2028
2061
|
onChanged(changeChecked(item.id, checked, items));
|
|
2029
2062
|
}, ariaLabel: `Select ${item.name}`, checkedVariant: Variant$1.Light }),
|
|
2030
|
-
item.isGroup &&
|
|
2063
|
+
(item.isGroup && childExpanded[item.id]) && (React.createElement("div", { className: "NestableItemList-children-container", style: {
|
|
2031
2064
|
paddingLeft: '2.2rem',
|
|
2032
2065
|
} },
|
|
2033
2066
|
React.createElement(NestableItemList, { items: item.children, onChanged: (updatedItems) => {
|
|
@@ -2049,7 +2082,7 @@ const ItemPicker = (props) => {
|
|
|
2049
2082
|
/*------------------------------------------------------------------------*/
|
|
2050
2083
|
/* -------------- Props ------------- */
|
|
2051
2084
|
// Destructure all props
|
|
2052
|
-
const { title, items, onChanged, } = props;
|
|
2085
|
+
const { title, items, onChanged, noBottomMargin, } = props;
|
|
2053
2086
|
/*------------------------------------------------------------------------*/
|
|
2054
2087
|
/* Component Functions */
|
|
2055
2088
|
/*------------------------------------------------------------------------*/
|
|
@@ -2059,7 +2092,7 @@ const ItemPicker = (props) => {
|
|
|
2059
2092
|
/*----------------------------------------*/
|
|
2060
2093
|
/* Main UI */
|
|
2061
2094
|
/*----------------------------------------*/
|
|
2062
|
-
return (React.createElement(TabBox, { title: title },
|
|
2095
|
+
return (React.createElement(TabBox, { title: title, noBottomMargin: noBottomMargin },
|
|
2063
2096
|
React.createElement("div", { style: { overflowX: 'auto' } },
|
|
2064
2097
|
React.createElement(NestableItemList, { items: items, onChanged: onChanged }))));
|
|
2065
2098
|
};
|
|
@@ -2376,9 +2409,9 @@ const IntelliTable = (props) => {
|
|
|
2376
2409
|
// Create the cell UI
|
|
2377
2410
|
return (React.createElement("th", { key: column.param, scope: "col", id: `IntelliTable-${id}-header-${column.param}` },
|
|
2378
2411
|
React.createElement("div", { className: "d-flex align-items-center justify-content-center flex-row h-100" },
|
|
2379
|
-
React.createElement("
|
|
2412
|
+
React.createElement("span", { className: "text-nowrap" }, column.title),
|
|
2380
2413
|
React.createElement("div", null,
|
|
2381
|
-
React.createElement("button", { type: "button", className: "btn btn-light", "aria-label": sortButtonAriaLabel, onClick: () => {
|
|
2414
|
+
React.createElement("button", { type: "button", className: "btn btn-light btn-sm ms-1", "aria-label": sortButtonAriaLabel, onClick: () => {
|
|
2382
2415
|
dispatch({
|
|
2383
2416
|
type: ActionType$1.ToggleSortColumn,
|
|
2384
2417
|
param: column.param,
|
|
@@ -2526,7 +2559,7 @@ const IntelliTable = (props) => {
|
|
|
2526
2559
|
React.createElement("h3", { className: "m-0" }, title),
|
|
2527
2560
|
React.createElement("div", { className: "flex-grow-1 text-end" },
|
|
2528
2561
|
React.createElement(CSVDownloadButton, { "aria-label": `download data as csv for ${title}`, id: `IntelliTable-${id}-download-as-csv`, filename: `${title}.csv`, csv: csv }),
|
|
2529
|
-
React.createElement("button", { type: "button", className: "btn btn-secondary", "aria-label": `show panel for customizing which columns show in table ${title}`, id: `IntelliTable-${id}-show-column-customization-modal`, onClick: () => {
|
|
2562
|
+
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: () => {
|
|
2530
2563
|
dispatch({
|
|
2531
2564
|
type: ActionType$1.ToggleColVisCusModalVisibility,
|
|
2532
2565
|
});
|
|
@@ -2590,6 +2623,11 @@ const style = `
|
|
|
2590
2623
|
border: 0.05rem solid black;
|
|
2591
2624
|
border-radius: 0.5rem;
|
|
2592
2625
|
overflow: hidden;
|
|
2626
|
+
padding: 0.7rem;
|
|
2627
|
+
|
|
2628
|
+
/* Solid background */
|
|
2629
|
+
background-color: white;
|
|
2630
|
+
color: black;
|
|
2593
2631
|
|
|
2594
2632
|
/* Place contents in flex column */
|
|
2595
2633
|
flex-direction: column;
|
|
@@ -2616,6 +2654,27 @@ const style = `
|
|
|
2616
2654
|
/* Vertical scroll */
|
|
2617
2655
|
overflow-y: auto;
|
|
2618
2656
|
}
|
|
2657
|
+
|
|
2658
|
+
.LogReviewer-header-close-button {
|
|
2659
|
+
border: 0 !important;
|
|
2660
|
+
background-color: transparent !important;
|
|
2661
|
+
padding-top: 0 !important;
|
|
2662
|
+
padding-bottom: 0 !important;
|
|
2663
|
+
padding-right: 1em !important;
|
|
2664
|
+
margin: 0 !important;
|
|
2665
|
+
color: #444 !important;
|
|
2666
|
+
|
|
2667
|
+
right: 0 !important;
|
|
2668
|
+
position: absolute !important;
|
|
2669
|
+
}
|
|
2670
|
+
.LogReviewer-header-close-button:hover {
|
|
2671
|
+
border: 0 !important;
|
|
2672
|
+
background-color: transparent !important;
|
|
2673
|
+
padding-top: 0 !important;
|
|
2674
|
+
padding-bottom: 0 !important;
|
|
2675
|
+
margin: 0 !important;
|
|
2676
|
+
color: #000 !important;
|
|
2677
|
+
}
|
|
2619
2678
|
`;
|
|
2620
2679
|
/*------------------------------------------------------------------------*/
|
|
2621
2680
|
/* Static Functions */
|
|
@@ -2635,7 +2694,7 @@ const genHumanReadableName = (machineReadableName) => {
|
|
|
2635
2694
|
// Uppercase! Add a space before
|
|
2636
2695
|
humanReadableName += ' ';
|
|
2637
2696
|
}
|
|
2638
|
-
humanReadableName +=
|
|
2697
|
+
humanReadableName += char;
|
|
2639
2698
|
});
|
|
2640
2699
|
// Trim and return
|
|
2641
2700
|
return humanReadableName.trim();
|
|
@@ -2697,7 +2756,19 @@ const reducer = (state, action) => {
|
|
|
2697
2756
|
return Object.assign(Object.assign({}, state), { contextFilterState: action.contextFilterState });
|
|
2698
2757
|
}
|
|
2699
2758
|
case ActionType.UpdateTagFilterState: {
|
|
2700
|
-
|
|
2759
|
+
const { tagFilterState } = action;
|
|
2760
|
+
// Select all if every tag is deselected
|
|
2761
|
+
const numTagsSelected = (Object.values(tagFilterState)
|
|
2762
|
+
.filter((isSelected) => {
|
|
2763
|
+
return isSelected;
|
|
2764
|
+
})
|
|
2765
|
+
.length);
|
|
2766
|
+
if (numTagsSelected === 0) {
|
|
2767
|
+
Object.keys(tagFilterState).forEach((tag) => {
|
|
2768
|
+
tagFilterState[tag] = true;
|
|
2769
|
+
});
|
|
2770
|
+
}
|
|
2771
|
+
return Object.assign(Object.assign({}, state), { tagFilterState });
|
|
2701
2772
|
}
|
|
2702
2773
|
case ActionType.UpdateActionErrorFilterState: {
|
|
2703
2774
|
return Object.assign(Object.assign({}, state), { actionErrorFilterState: action.actionErrorFilterState });
|
|
@@ -2717,10 +2788,35 @@ const LogReviewer = (props) => {
|
|
|
2717
2788
|
/*------------------------------------------------------------------------*/
|
|
2718
2789
|
/* Setup */
|
|
2719
2790
|
/*------------------------------------------------------------------------*/
|
|
2720
|
-
var _a, _b, _c, _d, _e;
|
|
2791
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
2721
2792
|
/* -------------- Props ------------- */
|
|
2722
2793
|
// Destructure props
|
|
2723
2794
|
const { LogMetadata, onClose, } = props;
|
|
2795
|
+
// Add built-in LogMetadata
|
|
2796
|
+
// > Add "uncategorized" subcontext to each context
|
|
2797
|
+
Object.keys((_a = LogMetadata.Context) !== null && _a !== void 0 ? _a : {}).forEach((context) => {
|
|
2798
|
+
if (
|
|
2799
|
+
// Context exists
|
|
2800
|
+
LogMetadata.Context
|
|
2801
|
+
// Context has children already
|
|
2802
|
+
&& typeof LogMetadata.Context[context] !== 'string') {
|
|
2803
|
+
LogMetadata.Context[context][LogBuiltInMetadata.Context.Uncategorized] = (LogBuiltInMetadata.Context.Uncategorized);
|
|
2804
|
+
}
|
|
2805
|
+
});
|
|
2806
|
+
// > Add built-in contexts
|
|
2807
|
+
LogMetadata.Context = ((_b = LogMetadata.Context) !== null && _b !== void 0 ? _b : {});
|
|
2808
|
+
Object.keys(LogBuiltInMetadata.Context).forEach((context) => {
|
|
2809
|
+
if (LogMetadata.Context) {
|
|
2810
|
+
LogMetadata.Context[context] = context;
|
|
2811
|
+
}
|
|
2812
|
+
});
|
|
2813
|
+
// > Add built-in targets
|
|
2814
|
+
LogMetadata.Target = ((_c = LogMetadata.Target) !== null && _c !== void 0 ? _c : {});
|
|
2815
|
+
Object.keys(LogBuiltInMetadata.Target).forEach((target) => {
|
|
2816
|
+
if (LogMetadata.Target) {
|
|
2817
|
+
LogMetadata.Target[target] = target;
|
|
2818
|
+
}
|
|
2819
|
+
});
|
|
2724
2820
|
/* -------------- State ------------- */
|
|
2725
2821
|
// Create initial date filter state
|
|
2726
2822
|
const today = getTimeInfoInET();
|
|
@@ -2740,26 +2836,30 @@ const LogReviewer = (props) => {
|
|
|
2740
2836
|
};
|
|
2741
2837
|
// Create initial context filter state
|
|
2742
2838
|
const initContextFilterState = {};
|
|
2743
|
-
Object.keys((
|
|
2839
|
+
Object.keys((_d = LogMetadata.Context) !== null && _d !== void 0 ? _d : {}).forEach((context) => {
|
|
2744
2840
|
var _a, _b;
|
|
2745
2841
|
const contextValue = ((_a = LogMetadata.Context) !== null && _a !== void 0 ? _a : {})[context];
|
|
2746
2842
|
if (typeof contextValue === 'string') {
|
|
2747
|
-
// Case: no subcontexts
|
|
2843
|
+
// Case: no subcontexts, init as checked
|
|
2748
2844
|
initContextFilterState[contextValue] = true;
|
|
2749
2845
|
}
|
|
2750
2846
|
else {
|
|
2751
2847
|
// Case: subcontexts exist
|
|
2752
|
-
initContextFilterState[
|
|
2848
|
+
initContextFilterState[context] = {};
|
|
2753
2849
|
Object.values(((_b = LogMetadata.Context) !== null && _b !== void 0 ? _b : {})[context]).forEach((subcontext) => {
|
|
2754
|
-
|
|
2755
|
-
|
|
2850
|
+
// Skip self ("_")
|
|
2851
|
+
if (subcontext === '_') {
|
|
2852
|
+
return;
|
|
2853
|
+
}
|
|
2854
|
+
// Initialize as checked
|
|
2855
|
+
initContextFilterState[context][subcontext] = true;
|
|
2756
2856
|
});
|
|
2757
2857
|
}
|
|
2758
2858
|
});
|
|
2759
2859
|
// Create initial tag filter state
|
|
2760
2860
|
const initTagFilterState = {};
|
|
2761
|
-
Object.values((
|
|
2762
|
-
initTagFilterState[tagValue] =
|
|
2861
|
+
Object.values((_e = LogMetadata.Tag) !== null && _e !== void 0 ? _e : {}).forEach((tagValue) => {
|
|
2862
|
+
initTagFilterState[tagValue] = false;
|
|
2763
2863
|
});
|
|
2764
2864
|
// Create advanced filter state
|
|
2765
2865
|
const initAdvancedFilterState = {
|
|
@@ -2785,12 +2885,16 @@ const LogReviewer = (props) => {
|
|
|
2785
2885
|
target: {},
|
|
2786
2886
|
action: {},
|
|
2787
2887
|
};
|
|
2788
|
-
Object.values((
|
|
2888
|
+
Object.values((_f = LogMetadata.Target) !== null && _f !== void 0 ? _f : {}).forEach((target) => {
|
|
2789
2889
|
initActionErrorFilterState.target[target] = true;
|
|
2790
2890
|
});
|
|
2791
2891
|
Object.values(LogAction$1).forEach((action) => {
|
|
2792
2892
|
initActionErrorFilterState.action[action] = true;
|
|
2793
2893
|
});
|
|
2894
|
+
// Add built-in targets
|
|
2895
|
+
Object.values(LogBuiltInMetadata.Target).forEach((target) => {
|
|
2896
|
+
initActionErrorFilterState.target[target] = true;
|
|
2897
|
+
});
|
|
2794
2898
|
// Initial state
|
|
2795
2899
|
const initialState = {
|
|
2796
2900
|
loading: true,
|
|
@@ -2824,15 +2928,18 @@ const LogReviewer = (props) => {
|
|
|
2824
2928
|
let month = newDateFilterState.startDate.month;
|
|
2825
2929
|
while (
|
|
2826
2930
|
// Earlier year
|
|
2827
|
-
(year
|
|
2931
|
+
(year < newDateFilterState.endDate.year)
|
|
2828
2932
|
// Current year but included month
|
|
2829
2933
|
|| (year === newDateFilterState.endDate.year
|
|
2830
2934
|
&& month <= newDateFilterState.endDate.month)) {
|
|
2831
|
-
// Add to list
|
|
2832
|
-
|
|
2833
|
-
year
|
|
2834
|
-
|
|
2835
|
-
|
|
2935
|
+
// Add to list if not already loaded
|
|
2936
|
+
if (!logMap[year]
|
|
2937
|
+
|| !logMap[year][month]) {
|
|
2938
|
+
toLoad.push({
|
|
2939
|
+
year,
|
|
2940
|
+
month,
|
|
2941
|
+
});
|
|
2942
|
+
}
|
|
2836
2943
|
// Increment
|
|
2837
2944
|
month += 1;
|
|
2838
2945
|
if (month > 12) {
|
|
@@ -2856,6 +2963,7 @@ const LogReviewer = (props) => {
|
|
|
2856
2963
|
});
|
|
2857
2964
|
// Check which year/month combos we need to load
|
|
2858
2965
|
const toLoad = listMonthsToLoad(newDateFilterState);
|
|
2966
|
+
console.log('Filter update:', newDateFilterState, toLoad, logMap);
|
|
2859
2967
|
// If nothing to load, finished
|
|
2860
2968
|
if (toLoad.length === 0) {
|
|
2861
2969
|
return;
|
|
@@ -2920,10 +3028,10 @@ const LogReviewer = (props) => {
|
|
|
2920
3028
|
/* Filters */
|
|
2921
3029
|
/*----------------------------------------*/
|
|
2922
3030
|
// Filter toggle
|
|
2923
|
-
const filterToggles = (React.createElement("div", { className: "LogReviewer-filter-toggles
|
|
3031
|
+
const filterToggles = (React.createElement("div", { className: "LogReviewer-filter-toggles" },
|
|
2924
3032
|
React.createElement("h3", { className: "m-0" }, "Filters:"),
|
|
2925
|
-
React.createElement("div", { className: "LogReviewer-filter-toggle-buttons" },
|
|
2926
|
-
React.createElement("button", { type: "button", id: "LogReviewer-toggle-date-filter-drawer", className: `btn btn-${FilterDrawer.Date === expandedFilterDrawer} me-2`, "aria-label": "toggle date filter drawer", onClick: () => {
|
|
3033
|
+
React.createElement("div", { className: "LogReviewer-filter-toggle-buttons alert alert-secondary p-2 m-0" },
|
|
3034
|
+
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: () => {
|
|
2927
3035
|
dispatch({
|
|
2928
3036
|
type: ActionType.ToggleFilterDrawer,
|
|
2929
3037
|
filterDrawer: FilterDrawer.Date,
|
|
@@ -2931,7 +3039,7 @@ const LogReviewer = (props) => {
|
|
|
2931
3039
|
} },
|
|
2932
3040
|
React.createElement(FontAwesomeIcon, { icon: faCalendar, className: "me-2" }),
|
|
2933
3041
|
"Date"),
|
|
2934
|
-
React.createElement("button", { type: "button", id: "LogReviewer-toggle-context-filter-drawer", className: `btn btn-${FilterDrawer.Context === expandedFilterDrawer} me-2`, "aria-label": "toggle context filter drawer", onClick: () => {
|
|
3042
|
+
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: () => {
|
|
2935
3043
|
dispatch({
|
|
2936
3044
|
type: ActionType.ToggleFilterDrawer,
|
|
2937
3045
|
filterDrawer: FilterDrawer.Context,
|
|
@@ -2939,15 +3047,15 @@ const LogReviewer = (props) => {
|
|
|
2939
3047
|
} },
|
|
2940
3048
|
React.createElement(FontAwesomeIcon, { icon: faCircle, className: "me-2" }),
|
|
2941
3049
|
"Context"),
|
|
2942
|
-
React.createElement("button", { type: "button", id: "LogReviewer-toggle-tag-filter-drawer", className: `btn btn-${FilterDrawer.Tag === expandedFilterDrawer} me-2`, "aria-label": "toggle tag filter drawer", onClick: () => {
|
|
3050
|
+
(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: () => {
|
|
2943
3051
|
dispatch({
|
|
2944
3052
|
type: ActionType.ToggleFilterDrawer,
|
|
2945
3053
|
filterDrawer: FilterDrawer.Tag,
|
|
2946
3054
|
});
|
|
2947
3055
|
} },
|
|
2948
3056
|
React.createElement(FontAwesomeIcon, { icon: faTag, className: "me-2" }),
|
|
2949
|
-
"Tag"),
|
|
2950
|
-
React.createElement("button", { type: "button", id: "LogReviewer-toggle-action-filter-drawer", className: `btn btn-${FilterDrawer.Action === expandedFilterDrawer} me-2`, "aria-label": "toggle action and error filter drawer", onClick: () => {
|
|
3057
|
+
"Tag")),
|
|
3058
|
+
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: () => {
|
|
2951
3059
|
dispatch({
|
|
2952
3060
|
type: ActionType.ToggleFilterDrawer,
|
|
2953
3061
|
filterDrawer: FilterDrawer.Action,
|
|
@@ -2955,7 +3063,7 @@ const LogReviewer = (props) => {
|
|
|
2955
3063
|
} },
|
|
2956
3064
|
React.createElement(FontAwesomeIcon, { icon: faHammer, className: "me-2" }),
|
|
2957
3065
|
"Action"),
|
|
2958
|
-
React.createElement("button", { type: "button", id: "LogReviewer-toggle-advanced-filter-drawer", className: `btn btn-${FilterDrawer.Advanced === expandedFilterDrawer}`, "aria-label": "toggle advanced filter drawer", onClick: () => {
|
|
3066
|
+
React.createElement("button", { type: "button", id: "LogReviewer-toggle-advanced-filter-drawer", className: `btn btn-${FilterDrawer.Advanced === expandedFilterDrawer ? 'warning' : 'light'}`, "aria-label": "toggle advanced filter drawer", onClick: () => {
|
|
2959
3067
|
dispatch({
|
|
2960
3068
|
type: ActionType.ToggleFilterDrawer,
|
|
2961
3069
|
filterDrawer: FilterDrawer.Advanced,
|
|
@@ -2968,25 +3076,29 @@ const LogReviewer = (props) => {
|
|
|
2968
3076
|
if (expandedFilterDrawer) {
|
|
2969
3077
|
if (expandedFilterDrawer === FilterDrawer.Date) {
|
|
2970
3078
|
filterDrawer = (React.createElement(TabBox, { title: "Date" },
|
|
2971
|
-
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) => {
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
dateFilterState: Object.assign(Object.assign({}, dateFilterState), { startDate: { month, day, year } }),
|
|
2975
|
-
});
|
|
3079
|
+
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: 12, onChange: (month, day, year) => {
|
|
3080
|
+
dateFilterState.startDate = { month, day, year };
|
|
3081
|
+
handleDateRangeUpdated(dateFilterState);
|
|
2976
3082
|
} }),
|
|
2977
3083
|
' ',
|
|
2978
3084
|
"to",
|
|
2979
3085
|
' ',
|
|
2980
|
-
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) => {
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
3086
|
+
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) => {
|
|
3087
|
+
if (year < dateFilterState.startDate.year
|
|
3088
|
+
|| (year === dateFilterState.startDate.year
|
|
3089
|
+
&& month < dateFilterState.startDate.month)
|
|
3090
|
+
|| (year === dateFilterState.startDate.year
|
|
3091
|
+
&& month === dateFilterState.startDate.month
|
|
3092
|
+
&& day < dateFilterState.startDate.day)) {
|
|
3093
|
+
return alert$1('Invalid Start Date', 'The start date cannot be before the end date.');
|
|
3094
|
+
}
|
|
3095
|
+
dateFilterState.endDate = { month, day, year };
|
|
3096
|
+
handleDateRangeUpdated(dateFilterState);
|
|
2985
3097
|
} })));
|
|
2986
3098
|
}
|
|
2987
3099
|
else if (expandedFilterDrawer === FilterDrawer.Context) {
|
|
2988
3100
|
// Create item picker items
|
|
2989
|
-
const pickableItems = (Object.keys((
|
|
3101
|
+
const pickableItems = (Object.keys((_g = LogMetadata.Context) !== null && _g !== void 0 ? _g : {})
|
|
2990
3102
|
.map((context) => {
|
|
2991
3103
|
var _a;
|
|
2992
3104
|
const value = ((_a = LogMetadata.Context) !== null && _a !== void 0 ? _a : {})[context];
|
|
@@ -3007,15 +3119,15 @@ const LogReviewer = (props) => {
|
|
|
3007
3119
|
})
|
|
3008
3120
|
.map((subcontext) => {
|
|
3009
3121
|
return {
|
|
3010
|
-
id:
|
|
3122
|
+
id: subcontext,
|
|
3011
3123
|
name: genHumanReadableName(subcontext),
|
|
3012
3124
|
isGroup: false,
|
|
3013
|
-
checked:
|
|
3125
|
+
checked: contextFilterState[context][subcontext],
|
|
3014
3126
|
};
|
|
3015
3127
|
}));
|
|
3016
3128
|
const item = {
|
|
3017
3129
|
id: context,
|
|
3018
|
-
name: context,
|
|
3130
|
+
name: genHumanReadableName(context),
|
|
3019
3131
|
isGroup: true,
|
|
3020
3132
|
children,
|
|
3021
3133
|
};
|
|
@@ -3028,7 +3140,9 @@ const LogReviewer = (props) => {
|
|
|
3028
3140
|
if (pickableItem.isGroup) {
|
|
3029
3141
|
// Has subcontexts
|
|
3030
3142
|
pickableItem.children.forEach((subcontextItem) => {
|
|
3031
|
-
|
|
3143
|
+
if (!subcontextItem.isGroup) {
|
|
3144
|
+
contextFilterState[pickableItem.id][subcontextItem.id] = (subcontextItem.checked);
|
|
3145
|
+
}
|
|
3032
3146
|
});
|
|
3033
3147
|
}
|
|
3034
3148
|
else {
|
|
@@ -3047,7 +3161,7 @@ const LogReviewer = (props) => {
|
|
|
3047
3161
|
filterDrawer = (React.createElement(TabBox, { title: "Tags" }, Object.keys(tagFilterState)
|
|
3048
3162
|
.map((tag, i) => {
|
|
3049
3163
|
const description = genHumanReadableName(tag);
|
|
3050
|
-
return (React.createElement(CheckboxButton, { id: `LogReviewer-tag-${tag}-checkbox`, text: description, ariaLabel: `
|
|
3164
|
+
return (React.createElement(CheckboxButton, { id: `LogReviewer-tag-${tag}-checkbox`, text: description, ariaLabel: `require that logs be tagged with "${description}" or any other selected tag`, noMarginOnRight: i === Object.keys(tagFilterState).length - 1, checked: tagFilterState[tag], onChanged: (checked) => {
|
|
3051
3165
|
tagFilterState[tag] = checked;
|
|
3052
3166
|
} }));
|
|
3053
3167
|
})));
|
|
@@ -3079,7 +3193,7 @@ const LogReviewer = (props) => {
|
|
|
3079
3193
|
}, ariaLabel: "only show error logs", selected: actionErrorFilterState.type === LogType$1.Error, noMarginOnRight: true })),
|
|
3080
3194
|
(actionErrorFilterState.type === undefined
|
|
3081
3195
|
|| actionErrorFilterState.type === LogType$1.Action) && (React.createElement(TabBox, { title: "Action Log Details" },
|
|
3082
|
-
React.createElement(ButtonInputGroup, { label: "Action" }, Object.keys(LogAction$1)
|
|
3196
|
+
React.createElement(ButtonInputGroup, { label: "Action", className: "mb-2" }, Object.keys(LogAction$1)
|
|
3083
3197
|
.map((action, i) => {
|
|
3084
3198
|
const description = genHumanReadableName(action);
|
|
3085
3199
|
return (React.createElement(CheckboxButton, { id: `LogReviewer-action-${action}-checkbox`, text: description, ariaLabel: `include logs with action type "${description}" in results`, noMarginOnRight: i === Object.keys(LogAction$1).length - 1, onChanged: (checked) => {
|
|
@@ -3090,18 +3204,20 @@ const LogReviewer = (props) => {
|
|
|
3090
3204
|
});
|
|
3091
3205
|
} }));
|
|
3092
3206
|
})),
|
|
3093
|
-
React.createElement(ButtonInputGroup, { label: "Target" },
|
|
3094
|
-
.
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3207
|
+
React.createElement(ButtonInputGroup, { label: "Target" },
|
|
3208
|
+
(Object.keys((_h = LogMetadata.Target) !== null && _h !== void 0 ? _h : {}).length === 0) && (React.createElement("div", null, "This app does not have any targets yet.")),
|
|
3209
|
+
Object.keys((_j = LogMetadata.Target) !== null && _j !== void 0 ? _j : {})
|
|
3210
|
+
.map((target, i) => {
|
|
3211
|
+
var _a;
|
|
3212
|
+
const description = genHumanReadableName(target);
|
|
3213
|
+
return (React.createElement(CheckboxButton, { id: `LogReviewer-target-${target}-checkbox`, text: description, ariaLabel: `include logs with target "${description}" in results`, onChanged: (checked) => {
|
|
3214
|
+
actionErrorFilterState.target[target] = checked;
|
|
3215
|
+
dispatch({
|
|
3216
|
+
type: ActionType.UpdateActionErrorFilterState,
|
|
3217
|
+
actionErrorFilterState,
|
|
3218
|
+
});
|
|
3219
|
+
}, noMarginOnRight: i === Object.keys((_a = LogMetadata.Target) !== null && _a !== void 0 ? _a : {}).length - 1 }));
|
|
3220
|
+
})))),
|
|
3105
3221
|
(actionErrorFilterState.type === undefined
|
|
3106
3222
|
|| actionErrorFilterState.type === LogType$1.Error) && (React.createElement(TabBox, { title: "Error Log Details" },
|
|
3107
3223
|
React.createElement("div", { className: "input-group mb-2" },
|
|
@@ -3263,26 +3379,27 @@ const LogReviewer = (props) => {
|
|
|
3263
3379
|
advancedFilterState,
|
|
3264
3380
|
});
|
|
3265
3381
|
}, noMarginOnRight: true })),
|
|
3266
|
-
React.createElement("div", { className: "
|
|
3267
|
-
React.createElement("
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
.
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
React.createElement("
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
.
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
|
|
3285
|
-
|
|
3382
|
+
advancedFilterState.source !== LogSource$1.Client && (React.createElement("div", { className: "mt-2" },
|
|
3383
|
+
React.createElement("div", { className: "input-group mb-2" },
|
|
3384
|
+
React.createElement("span", { className: "input-group-text" }, "Server Route Path"),
|
|
3385
|
+
React.createElement("input", { type: "text", className: "form-control", "aria-label": "query for server route path", placeholder: "e.g. /api/ttm/courses/12345", value: advancedFilterState.routePath, onChange: (e) => {
|
|
3386
|
+
advancedFilterState.courseName = ((e.target.value)
|
|
3387
|
+
.trim());
|
|
3388
|
+
dispatch({
|
|
3389
|
+
type: ActionType.UpdateAdvancedFilterState,
|
|
3390
|
+
advancedFilterState,
|
|
3391
|
+
});
|
|
3392
|
+
} })),
|
|
3393
|
+
React.createElement("div", { className: "input-group mb-2" },
|
|
3394
|
+
React.createElement("span", { className: "input-group-text" }, "Server Route Template"),
|
|
3395
|
+
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) => {
|
|
3396
|
+
advancedFilterState.courseName = ((e.target.value)
|
|
3397
|
+
.trim());
|
|
3398
|
+
dispatch({
|
|
3399
|
+
type: ActionType.UpdateAdvancedFilterState,
|
|
3400
|
+
advancedFilterState,
|
|
3401
|
+
});
|
|
3402
|
+
} })))))));
|
|
3286
3403
|
}
|
|
3287
3404
|
}
|
|
3288
3405
|
// Filters UI
|
|
@@ -3293,10 +3410,11 @@ const LogReviewer = (props) => {
|
|
|
3293
3410
|
// > Perform filters
|
|
3294
3411
|
const logs = [];
|
|
3295
3412
|
Object.keys(logMap).forEach((year) => {
|
|
3296
|
-
Object.keys(logMap).forEach((month) => {
|
|
3413
|
+
Object.keys(logMap[year]).forEach((month) => {
|
|
3297
3414
|
logMap[year][month].forEach((log) => {
|
|
3298
|
-
/* ----------- Date Filter ---------- */
|
|
3299
3415
|
var _a;
|
|
3416
|
+
/* ----------- Date Filter ---------- */
|
|
3417
|
+
console.log('Log:', log);
|
|
3300
3418
|
// Before start date
|
|
3301
3419
|
if (
|
|
3302
3420
|
// Previous year
|
|
@@ -3308,6 +3426,7 @@ const LogReviewer = (props) => {
|
|
|
3308
3426
|
|| ((log.year === dateFilterState.startDate.year)
|
|
3309
3427
|
&& (log.month === dateFilterState.startDate.month)
|
|
3310
3428
|
&& (log.day < dateFilterState.startDate.day))) {
|
|
3429
|
+
console.log('RULED OUT: START');
|
|
3311
3430
|
return;
|
|
3312
3431
|
}
|
|
3313
3432
|
// After end date
|
|
@@ -3321,6 +3440,7 @@ const LogReviewer = (props) => {
|
|
|
3321
3440
|
|| ((log.year === dateFilterState.endDate.year)
|
|
3322
3441
|
&& (log.month === dateFilterState.endDate.month)
|
|
3323
3442
|
&& (log.day > dateFilterState.endDate.day))) {
|
|
3443
|
+
console.log('RULED OUT: END');
|
|
3324
3444
|
return;
|
|
3325
3445
|
}
|
|
3326
3446
|
/* --------- Context Filter --------- */
|
|
@@ -3329,33 +3449,48 @@ const LogReviewer = (props) => {
|
|
|
3329
3449
|
// Whole context is deselected
|
|
3330
3450
|
contextFilterState[log.context] === false
|
|
3331
3451
|
// None of the subcontexts are selected
|
|
3332
|
-
|| (
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3452
|
+
|| (
|
|
3453
|
+
// Has subcontexts
|
|
3454
|
+
typeof contextFilterState[log.context] !== 'boolean'
|
|
3455
|
+
// None of the subcontexts are selected
|
|
3456
|
+
&& Object.values((_a = contextFilterState[log.context]) !== null && _a !== void 0 ? _a : {})
|
|
3457
|
+
.every((isSelected) => {
|
|
3458
|
+
return !isSelected;
|
|
3459
|
+
}))) {
|
|
3460
|
+
// TODO: figure out why context filter is still not working, why actions and targets filter UI sucks and doesn't wrap and doesn't select
|
|
3461
|
+
console.log('RULED OUT: CONTEXT');
|
|
3462
|
+
console.log(log.context, contextFilterState);
|
|
3336
3463
|
return;
|
|
3337
3464
|
}
|
|
3338
3465
|
// Subcontext doesn't match
|
|
3339
3466
|
if (
|
|
3340
|
-
// Log
|
|
3341
|
-
log.
|
|
3467
|
+
// Log context is not "uncategorized" (no point in further filters)
|
|
3468
|
+
log.context !== LogBuiltInMetadata.Context.Uncategorized
|
|
3469
|
+
// Log has a subcontext
|
|
3470
|
+
&& log.subcontext
|
|
3342
3471
|
// Context has subcontexts
|
|
3343
3472
|
&& (contextFilterState[log.context]
|
|
3344
3473
|
&& contextFilterState[log.context] !== false
|
|
3345
3474
|
&& contextFilterState[log.context] !== true)
|
|
3346
3475
|
// Subcontext is not selected
|
|
3347
3476
|
&& !contextFilterState[log.context][log.subcontext]) {
|
|
3477
|
+
console.log('RULED OUT: SUBCONTEXT');
|
|
3348
3478
|
return;
|
|
3349
3479
|
}
|
|
3350
3480
|
/* -------------- Tags -------------- */
|
|
3351
3481
|
// No tags match
|
|
3352
3482
|
if (
|
|
3353
|
-
//
|
|
3354
|
-
|
|
3483
|
+
// At least one tag is required
|
|
3484
|
+
Object.values(tagFilterState)
|
|
3485
|
+
.filter((isSelected) => {
|
|
3486
|
+
return isSelected;
|
|
3487
|
+
})
|
|
3488
|
+
.length > 0
|
|
3355
3489
|
// No tags match
|
|
3356
|
-
&&
|
|
3490
|
+
&& log.tags.every((tag) => {
|
|
3357
3491
|
return !tagFilterState[tag];
|
|
3358
|
-
}))
|
|
3492
|
+
})) {
|
|
3493
|
+
console.log('RULED OUT: TAGS');
|
|
3359
3494
|
return;
|
|
3360
3495
|
}
|
|
3361
3496
|
/* ------- Actions and Errors ------- */
|
|
@@ -3365,6 +3500,7 @@ const LogReviewer = (props) => {
|
|
|
3365
3500
|
actionErrorFilterState.type !== undefined
|
|
3366
3501
|
// Log type doesn't match
|
|
3367
3502
|
&& actionErrorFilterState.type !== log.type) {
|
|
3503
|
+
console.log('RULED OUT: TYPE');
|
|
3368
3504
|
return;
|
|
3369
3505
|
}
|
|
3370
3506
|
// Filter errors
|
|
@@ -3377,6 +3513,7 @@ const LogReviewer = (props) => {
|
|
|
3377
3513
|
&& actionErrorFilterState.errorMessage.trim().length > 0
|
|
3378
3514
|
// Message doesn't match
|
|
3379
3515
|
&& log.errorMessage.toLowerCase().includes(actionErrorFilterState.errorMessage.trim().toLowerCase())) {
|
|
3516
|
+
console.log('RULED OUT: ERROR MESSAGE');
|
|
3380
3517
|
return;
|
|
3381
3518
|
}
|
|
3382
3519
|
// Code doesn't match
|
|
@@ -3387,6 +3524,7 @@ const LogReviewer = (props) => {
|
|
|
3387
3524
|
&& actionErrorFilterState.errorCode.trim().length > 0
|
|
3388
3525
|
// Code doesn't match
|
|
3389
3526
|
&& log.errorCode.toUpperCase().includes(actionErrorFilterState.errorCode.trim().toUpperCase())) {
|
|
3527
|
+
console.log('RULED OUT: ERROR CODE');
|
|
3390
3528
|
return;
|
|
3391
3529
|
}
|
|
3392
3530
|
}
|
|
@@ -3398,6 +3536,7 @@ const LogReviewer = (props) => {
|
|
|
3398
3536
|
log.target
|
|
3399
3537
|
// Target isn't selected
|
|
3400
3538
|
&& !actionErrorFilterState.target[log.target]) {
|
|
3539
|
+
console.log('RULED OUT: TARGET');
|
|
3401
3540
|
return;
|
|
3402
3541
|
}
|
|
3403
3542
|
// Action
|
|
@@ -3406,6 +3545,7 @@ const LogReviewer = (props) => {
|
|
|
3406
3545
|
log.action
|
|
3407
3546
|
// Action isn't selected
|
|
3408
3547
|
&& !actionErrorFilterState.action[log.action]) {
|
|
3548
|
+
console.log('RULED OUT: ACTION');
|
|
3409
3549
|
return;
|
|
3410
3550
|
}
|
|
3411
3551
|
}
|
|
@@ -3416,6 +3556,7 @@ const LogReviewer = (props) => {
|
|
|
3416
3556
|
log.userFirstName
|
|
3417
3557
|
// First name query doesn't match
|
|
3418
3558
|
&& !log.userFirstName.toLowerCase().includes(advancedFilterState.userFirstName.toLowerCase().trim())) {
|
|
3559
|
+
console.log('RULED OUT: FIRST');
|
|
3419
3560
|
return;
|
|
3420
3561
|
}
|
|
3421
3562
|
// Last name doesn't match
|
|
@@ -3424,6 +3565,7 @@ const LogReviewer = (props) => {
|
|
|
3424
3565
|
log.userLastName
|
|
3425
3566
|
// Last name query doesn't match
|
|
3426
3567
|
&& !log.userLastName.toLowerCase().includes(advancedFilterState.userLastName.toLowerCase().trim())) {
|
|
3568
|
+
console.log('RULED OUT: LAST');
|
|
3427
3569
|
return;
|
|
3428
3570
|
}
|
|
3429
3571
|
// Email doesn't match
|
|
@@ -3432,6 +3574,7 @@ const LogReviewer = (props) => {
|
|
|
3432
3574
|
log.userEmail
|
|
3433
3575
|
// Email query doesn't match
|
|
3434
3576
|
&& !log.userEmail.toLowerCase().includes(advancedFilterState.userEmail.toLowerCase().trim())) {
|
|
3577
|
+
console.log('RULED OUT: EMAIL');
|
|
3435
3578
|
return;
|
|
3436
3579
|
}
|
|
3437
3580
|
// User id doesn't match
|
|
@@ -3440,6 +3583,7 @@ const LogReviewer = (props) => {
|
|
|
3440
3583
|
log.userId
|
|
3441
3584
|
// User id doesn't match
|
|
3442
3585
|
&& !String(log.userId).includes(advancedFilterState.userId.trim())) {
|
|
3586
|
+
console.log('RULED OUT: USER ID');
|
|
3443
3587
|
return;
|
|
3444
3588
|
}
|
|
3445
3589
|
// Learner not allowed
|
|
@@ -3448,6 +3592,7 @@ const LogReviewer = (props) => {
|
|
|
3448
3592
|
log.isLearner
|
|
3449
3593
|
// Learners aren't included
|
|
3450
3594
|
&& !advancedFilterState.includeLearners) {
|
|
3595
|
+
console.log('RULED OUT: LEARNER');
|
|
3451
3596
|
return;
|
|
3452
3597
|
}
|
|
3453
3598
|
// TTM not allowed
|
|
@@ -3456,6 +3601,7 @@ const LogReviewer = (props) => {
|
|
|
3456
3601
|
log.isTTM
|
|
3457
3602
|
// TTMs aren't included
|
|
3458
3603
|
&& !advancedFilterState.includeTTMs) {
|
|
3604
|
+
console.log('RULED OUT: TTM');
|
|
3459
3605
|
return;
|
|
3460
3606
|
}
|
|
3461
3607
|
// Admin not allowed
|
|
@@ -3464,6 +3610,7 @@ const LogReviewer = (props) => {
|
|
|
3464
3610
|
log.isAdmin
|
|
3465
3611
|
// Admins aren't included
|
|
3466
3612
|
&& !advancedFilterState.includeAdmins) {
|
|
3613
|
+
console.log('RULED OUT: ADMIN');
|
|
3467
3614
|
return;
|
|
3468
3615
|
}
|
|
3469
3616
|
// Course Id doesn't match
|
|
@@ -3472,6 +3619,7 @@ const LogReviewer = (props) => {
|
|
|
3472
3619
|
log.courseId
|
|
3473
3620
|
// Course Id doesn't match
|
|
3474
3621
|
&& !String(log.courseId).includes(advancedFilterState.courseId.trim())) {
|
|
3622
|
+
console.log('RULED OUT: COURSE ID');
|
|
3475
3623
|
return;
|
|
3476
3624
|
}
|
|
3477
3625
|
// Course name doesn't match
|
|
@@ -3480,6 +3628,7 @@ const LogReviewer = (props) => {
|
|
|
3480
3628
|
log.courseName
|
|
3481
3629
|
// Course name doesn't match
|
|
3482
3630
|
&& !String(log.courseName).includes(advancedFilterState.courseName.trim())) {
|
|
3631
|
+
console.log('RULED OUT: COURSE NAME');
|
|
3483
3632
|
return;
|
|
3484
3633
|
}
|
|
3485
3634
|
// Mobile filter doesn't match
|
|
@@ -3490,6 +3639,7 @@ const LogReviewer = (props) => {
|
|
|
3490
3639
|
&& log.device
|
|
3491
3640
|
// Mobile filter doesn't match
|
|
3492
3641
|
&& (advancedFilterState.isMobile === log.device.isMobile)) {
|
|
3642
|
+
console.log('RULED OUT: MOBILE');
|
|
3493
3643
|
return;
|
|
3494
3644
|
}
|
|
3495
3645
|
// Log source doesn't match
|
|
@@ -3500,6 +3650,7 @@ const LogReviewer = (props) => {
|
|
|
3500
3650
|
&& log.source
|
|
3501
3651
|
// Source filter doesn't match
|
|
3502
3652
|
&& (advancedFilterState.source !== log.source)) {
|
|
3653
|
+
console.log('RULED OUT: SOURCE');
|
|
3503
3654
|
return;
|
|
3504
3655
|
}
|
|
3505
3656
|
// Route path doesn't match (Only for server source)
|
|
@@ -3510,6 +3661,7 @@ const LogReviewer = (props) => {
|
|
|
3510
3661
|
&& (advancedFilterState.routePath.trim().length)
|
|
3511
3662
|
// Route path doesn't match
|
|
3512
3663
|
&& !(log.routePath.includes(advancedFilterState.routePath.trim()))) {
|
|
3664
|
+
console.log('RULED OUT: PATH');
|
|
3513
3665
|
return;
|
|
3514
3666
|
}
|
|
3515
3667
|
// Route template doesn't match (Only for server source)
|
|
@@ -3520,6 +3672,7 @@ const LogReviewer = (props) => {
|
|
|
3520
3672
|
&& (advancedFilterState.routeTemplate.trim().length)
|
|
3521
3673
|
// Route template doesn't match
|
|
3522
3674
|
&& !(log.routeTemplate.includes(advancedFilterState.routeTemplate.trim()))) {
|
|
3675
|
+
console.log('RULED OUT: TEMPLATE');
|
|
3523
3676
|
return;
|
|
3524
3677
|
}
|
|
3525
3678
|
/* -------------- Done -------------- */
|
|
@@ -3719,11 +3872,17 @@ const LogReviewer = (props) => {
|
|
|
3719
3872
|
},
|
|
3720
3873
|
];
|
|
3721
3874
|
// Create intelliTable
|
|
3722
|
-
const dataTable = (
|
|
3875
|
+
const dataTable = (logs.length === 0
|
|
3876
|
+
? (React.createElement(React.Fragment, null,
|
|
3877
|
+
React.createElement("h3", { className: "m-0" }, "Matching Logs:"),
|
|
3878
|
+
React.createElement("div", { className: "alert alert-warning text-center" },
|
|
3879
|
+
React.createElement("h4", { className: "m-1" }, "No Logs to Show"),
|
|
3880
|
+
React.createElement("div", null, "Either your filters are too strict or no matching logs have been created yet."))))
|
|
3881
|
+
: (React.createElement(IntelliTable, { title: "Matching Logs:", id: "logs", data: logs, columns: columns })));
|
|
3723
3882
|
// Main body
|
|
3724
3883
|
body = (React.createElement(React.Fragment, null,
|
|
3725
3884
|
filters,
|
|
3726
|
-
dataTable));
|
|
3885
|
+
React.createElement("div", { className: "mt-2" }, dataTable)));
|
|
3727
3886
|
}
|
|
3728
3887
|
/* ---------- Wrap in Modal --------- */
|
|
3729
3888
|
return (React.createElement("div", { className: "LogReviewer-outer-container" },
|
|
@@ -3731,14 +3890,10 @@ const LogReviewer = (props) => {
|
|
|
3731
3890
|
React.createElement("div", { className: "LogReviewer-inner-container" },
|
|
3732
3891
|
React.createElement("div", { className: "LogReviewer-header" },
|
|
3733
3892
|
React.createElement("div", { className: "LogReviewer-header-title" },
|
|
3734
|
-
React.createElement("
|
|
3735
|
-
React.createElement("
|
|
3736
|
-
|
|
3737
|
-
|
|
3738
|
-
padding: 0,
|
|
3739
|
-
margin: 0,
|
|
3740
|
-
} },
|
|
3741
|
-
React.createElement(FontAwesomeIcon, { icon: faTimes }))),
|
|
3893
|
+
React.createElement("h3", { className: "text-center m-0" }, "Log Review Dashboard")),
|
|
3894
|
+
React.createElement("div", { style: { width: 0 } },
|
|
3895
|
+
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 },
|
|
3896
|
+
React.createElement(FontAwesomeIcon, { icon: faTimes })))),
|
|
3742
3897
|
React.createElement("div", { className: "LogReviewer-contents" }, body))));
|
|
3743
3898
|
};
|
|
3744
3899
|
|
|
@@ -3884,7 +4039,7 @@ const padZerosLeft = (num, numDigits) => {
|
|
|
3884
4039
|
* access to log review
|
|
3885
4040
|
* @author Gabe Abrams
|
|
3886
4041
|
*/
|
|
3887
|
-
const LOG_REVIEW_STATUS_ROUTE =
|
|
4042
|
+
const LOG_REVIEW_STATUS_ROUTE = `${ROUTE_PATH_PREFIX}/logs/access_allowed`;
|
|
3888
4043
|
|
|
3889
4044
|
// Stored copy of caccl functions
|
|
3890
4045
|
let _cacclGetLaunchInfo;
|
|
@@ -3926,7 +4081,7 @@ const internalGetLogCollection = () => {
|
|
|
3926
4081
|
* @param opts.getLaunchInfo CACCL LTI's get launch info function
|
|
3927
4082
|
* @param [opts.logCollection] mongo collection from dce-mango to use for
|
|
3928
4083
|
* storing logs. If none is included, logs are written to the console
|
|
3929
|
-
* @param [opts.logReviewAdmins=all
|
|
4084
|
+
* @param [opts.logReviewAdmins=all] info on which admins can review
|
|
3930
4085
|
* logs from the client. If not included, all Canvas admins are allowed to
|
|
3931
4086
|
* review logs. If null, no Canvas admins are allowed to review logs.
|
|
3932
4087
|
* If an array of Canvas userIds (numbers), only Canvas admins with those
|
|
@@ -4009,71 +4164,81 @@ const initServer = (opts) => {
|
|
|
4009
4164
|
/*----------------------------------------*/
|
|
4010
4165
|
/* Log Reviewer */
|
|
4011
4166
|
/*----------------------------------------*/
|
|
4012
|
-
|
|
4013
|
-
|
|
4014
|
-
|
|
4015
|
-
|
|
4016
|
-
|
|
4017
|
-
|
|
4018
|
-
|
|
4019
|
-
|
|
4020
|
-
|
|
4021
|
-
|
|
4022
|
-
|
|
4023
|
-
|
|
4024
|
-
|
|
4025
|
-
|
|
4026
|
-
|
|
4027
|
-
|
|
4028
|
-
|
|
4029
|
-
|
|
4030
|
-
|
|
4167
|
+
/**
|
|
4168
|
+
* Check if a given user is allowed to review logs
|
|
4169
|
+
* @author Gabe Abrams
|
|
4170
|
+
* @param userId the id of the user
|
|
4171
|
+
* @param isAdmin if true, the user is an admin
|
|
4172
|
+
* @returns true if the user can review logs
|
|
4173
|
+
*/
|
|
4174
|
+
const canReviewLogs = (userId, isAdmin) => __awaiter(void 0, void 0, void 0, function* () {
|
|
4175
|
+
// Immediately deny access if user is not an admin
|
|
4176
|
+
if (!isAdmin) {
|
|
4177
|
+
return false;
|
|
4178
|
+
}
|
|
4179
|
+
// If all admins are allowed, we're done
|
|
4180
|
+
if (!opts.logReviewAdmins) {
|
|
4181
|
+
return true;
|
|
4182
|
+
}
|
|
4183
|
+
// Do a dynamic check
|
|
4184
|
+
try {
|
|
4185
|
+
// Array of userIds
|
|
4186
|
+
if (Array.isArray(opts.logReviewAdmins)) {
|
|
4187
|
+
return opts.logReviewAdmins.some((allowedId) => {
|
|
4188
|
+
return (userId === allowedId);
|
|
4189
|
+
});
|
|
4031
4190
|
}
|
|
4032
|
-
|
|
4033
|
-
|
|
4034
|
-
|
|
4191
|
+
// Must be a collection
|
|
4192
|
+
const matches = yield opts.logReviewAdmins.find({ userId });
|
|
4193
|
+
// Make sure at least one entry matches
|
|
4194
|
+
return matches.length > 0;
|
|
4195
|
+
}
|
|
4196
|
+
catch (err) {
|
|
4197
|
+
// If an error occurred, simply return false
|
|
4198
|
+
return false;
|
|
4199
|
+
}
|
|
4200
|
+
});
|
|
4201
|
+
/**
|
|
4202
|
+
* Check if the current user has access to logs
|
|
4203
|
+
* @author Gabe Abrams
|
|
4204
|
+
* @returns {boolean} true if user has access
|
|
4205
|
+
*/
|
|
4206
|
+
opts.app.get(LOG_REVIEW_STATUS_ROUTE, genRouteHandler({
|
|
4207
|
+
handler: ({ params }) => __awaiter(void 0, void 0, void 0, function* () {
|
|
4208
|
+
const { userId, isAdmin } = params;
|
|
4209
|
+
const canReview = yield canReviewLogs(userId, isAdmin);
|
|
4210
|
+
return canReview;
|
|
4211
|
+
}),
|
|
4212
|
+
}));
|
|
4213
|
+
/**
|
|
4214
|
+
* Get all logs for a certain month
|
|
4215
|
+
* @author Gabe Abrams
|
|
4216
|
+
* @param {number} year the year to query (e.g. 2022)
|
|
4217
|
+
* @param {number} month the month to query (e.g. 1 = January)
|
|
4218
|
+
* @returns {Log[]} list of logs from the given month
|
|
4219
|
+
*/
|
|
4220
|
+
opts.app.get(`${LOG_REVIEW_ROUTE_PATH_PREFIX}/years/:year/months/:month`, genRouteHandler({
|
|
4221
|
+
paramTypes: {
|
|
4222
|
+
year: ParamType$1.Int,
|
|
4223
|
+
month: ParamType$1.Int,
|
|
4224
|
+
},
|
|
4225
|
+
handler: ({ params }) => __awaiter(void 0, void 0, void 0, function* () {
|
|
4226
|
+
// Get user info
|
|
4227
|
+
const { year, month, userId, isAdmin, } = params;
|
|
4228
|
+
// Validate user
|
|
4229
|
+
const canReview = yield canReviewLogs(userId, isAdmin);
|
|
4230
|
+
if (!canReview) {
|
|
4231
|
+
throw new ErrorWithCode('You cannot access this resource because you do not have the appropriate permissions.', ReactKitErrorCode$1.NotAllowedToReviewLogs);
|
|
4035
4232
|
}
|
|
4036
|
-
|
|
4037
|
-
|
|
4038
|
-
|
|
4039
|
-
|
|
4040
|
-
|
|
4041
|
-
|
|
4042
|
-
|
|
4043
|
-
|
|
4044
|
-
|
|
4045
|
-
const canReview = yield canReviewLogs(userId);
|
|
4046
|
-
return canReview;
|
|
4047
|
-
}),
|
|
4048
|
-
}));
|
|
4049
|
-
/**
|
|
4050
|
-
* Get all logs for a certain month
|
|
4051
|
-
* @author Gabe Abrams
|
|
4052
|
-
* @param {number} year the year to query (e.g. 2022)
|
|
4053
|
-
* @param {number} month the month to query (e.g. 1 = January)
|
|
4054
|
-
* @returns {Log[]} list of logs from the given month
|
|
4055
|
-
*/
|
|
4056
|
-
opts.app.post(`${LOG_REVIEW_ROUTE_PATH_PREFIX}/years/:year/months/:month`, genRouteHandler({
|
|
4057
|
-
paramTypes: {
|
|
4058
|
-
year: ParamType$1.Int,
|
|
4059
|
-
month: ParamType$1.Int,
|
|
4060
|
-
},
|
|
4061
|
-
handler: ({ params }) => __awaiter(void 0, void 0, void 0, function* () {
|
|
4062
|
-
// Get user info
|
|
4063
|
-
const { userId } = params;
|
|
4064
|
-
// Validate user
|
|
4065
|
-
// isAdmin is already checked because path starts with '/admin'
|
|
4066
|
-
const canReview = yield canReviewLogs(userId);
|
|
4067
|
-
if (!canReview) {
|
|
4068
|
-
throw new ErrorWithCode('You cannot access this resource because you do not have the appropriate permissions.', ReactKitErrorCode$1.NotAllowedToReviewLogs);
|
|
4069
|
-
}
|
|
4070
|
-
// Query for logs
|
|
4071
|
-
const logs = yield _logCollection.find({ userId });
|
|
4072
|
-
// Return logs
|
|
4073
|
-
return logs;
|
|
4074
|
-
}),
|
|
4075
|
-
}));
|
|
4076
|
-
}
|
|
4233
|
+
// Query for logs
|
|
4234
|
+
const logs = yield _logCollection.find({
|
|
4235
|
+
year,
|
|
4236
|
+
month,
|
|
4237
|
+
});
|
|
4238
|
+
// Return logs
|
|
4239
|
+
return logs;
|
|
4240
|
+
}),
|
|
4241
|
+
}));
|
|
4077
4242
|
};
|
|
4078
4243
|
|
|
4079
4244
|
// Import shared types
|
|
@@ -4373,18 +4538,6 @@ const parseUserAgent = (userAgent) => {
|
|
|
4373
4538
|
};
|
|
4374
4539
|
};
|
|
4375
4540
|
|
|
4376
|
-
/**
|
|
4377
|
-
* Allowed log levels
|
|
4378
|
-
* @author Gabe Abrams
|
|
4379
|
-
*/
|
|
4380
|
-
var LogLevel;
|
|
4381
|
-
(function (LogLevel) {
|
|
4382
|
-
LogLevel["Warn"] = "Warn";
|
|
4383
|
-
LogLevel["Info"] = "Info";
|
|
4384
|
-
LogLevel["Debug"] = "Debug";
|
|
4385
|
-
})(LogLevel || (LogLevel = {}));
|
|
4386
|
-
var LogLevel$1 = LogLevel;
|
|
4387
|
-
|
|
4388
4541
|
/**
|
|
4389
4542
|
* Generate an express API route handler
|
|
4390
4543
|
* @author Gabe Abrams
|
|
@@ -4757,7 +4910,7 @@ const genRouteHandler = (opts) => {
|
|
|
4757
4910
|
}
|
|
4758
4911
|
: {
|
|
4759
4912
|
type: LogType$1.Action,
|
|
4760
|
-
target: ((_m = opts.target) !== null && _m !== void 0 ? _m : LogBuiltInMetadata.Target.
|
|
4913
|
+
target: ((_m = opts.target) !== null && _m !== void 0 ? _m : LogBuiltInMetadata.Target.NoTarget),
|
|
4761
4914
|
action: ((_o = opts.action) !== null && _o !== void 0 ? _o : LogAction$1.Unknown),
|
|
4762
4915
|
});
|
|
4763
4916
|
// Source-specific info
|