dce-reactkit 3.2.2-beta.3 → 3.2.2-beta.31
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 +361 -208
- 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 +361 -208
- 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 +5 -5
- 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
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
// Import shared helpers
|
|
22
22
|
import visitServerEndpoint from '../helpers/visitServerEndpoint';
|
|
23
23
|
import getTimeInfoInET from '../helpers/getTimeInfoInET';
|
|
24
|
-
import { showFatalError } from './AppWrapper';
|
|
24
|
+
import { alert, showFatalError } from './AppWrapper';
|
|
25
25
|
|
|
26
26
|
// Import shared constants
|
|
27
27
|
import LOG_REVIEW_ROUTE_PATH_PREFIX from '../constants/LOG_REVIEW_ROUTE_PATH_PREFIX';
|
|
@@ -34,6 +34,7 @@ import LogType from '../types/LogType';
|
|
|
34
34
|
import LogAction from '../types/LogAction';
|
|
35
35
|
import ParamType from '../types/ParamType';
|
|
36
36
|
import IntelliTableColumn from '../types/IntelliTableColumn';
|
|
37
|
+
import LogBuiltInMetadata from '../types/LogBuiltInMetadata';
|
|
37
38
|
|
|
38
39
|
// Import shared components
|
|
39
40
|
import SimpleDateChooser from './SimpleDateChooser';
|
|
@@ -121,7 +122,7 @@ type ContextFilterState = {
|
|
|
121
122
|
|
|
122
123
|
// Tag filter state
|
|
123
124
|
type TagFilterState = {
|
|
124
|
-
[k: string]: boolean
|
|
125
|
+
[k: string]: boolean // tag => true if in the list of tags to show
|
|
125
126
|
};
|
|
126
127
|
|
|
127
128
|
// Action filter state (only relevant for action logs)
|
|
@@ -206,6 +207,11 @@ const style = `
|
|
|
206
207
|
border: 0.05rem solid black;
|
|
207
208
|
border-radius: 0.5rem;
|
|
208
209
|
overflow: hidden;
|
|
210
|
+
padding: 0.7rem;
|
|
211
|
+
|
|
212
|
+
/* Solid background */
|
|
213
|
+
background-color: white;
|
|
214
|
+
color: black;
|
|
209
215
|
|
|
210
216
|
/* Place contents in flex column */
|
|
211
217
|
flex-direction: column;
|
|
@@ -232,6 +238,27 @@ const style = `
|
|
|
232
238
|
/* Vertical scroll */
|
|
233
239
|
overflow-y: auto;
|
|
234
240
|
}
|
|
241
|
+
|
|
242
|
+
.LogReviewer-header-close-button {
|
|
243
|
+
border: 0 !important;
|
|
244
|
+
background-color: transparent !important;
|
|
245
|
+
padding-top: 0 !important;
|
|
246
|
+
padding-bottom: 0 !important;
|
|
247
|
+
padding-right: 1em !important;
|
|
248
|
+
margin: 0 !important;
|
|
249
|
+
color: #444 !important;
|
|
250
|
+
|
|
251
|
+
right: 0 !important;
|
|
252
|
+
position: absolute !important;
|
|
253
|
+
}
|
|
254
|
+
.LogReviewer-header-close-button:hover {
|
|
255
|
+
border: 0 !important;
|
|
256
|
+
background-color: transparent !important;
|
|
257
|
+
padding-top: 0 !important;
|
|
258
|
+
padding-bottom: 0 !important;
|
|
259
|
+
margin: 0 !important;
|
|
260
|
+
color: #000 !important;
|
|
261
|
+
}
|
|
235
262
|
`;
|
|
236
263
|
|
|
237
264
|
/*------------------------------------------------------------------------*/
|
|
@@ -254,7 +281,7 @@ const genHumanReadableName = (machineReadableName: string) => {
|
|
|
254
281
|
// Uppercase! Add a space before
|
|
255
282
|
humanReadableName += ' ';
|
|
256
283
|
}
|
|
257
|
-
humanReadableName +=
|
|
284
|
+
humanReadableName += char;
|
|
258
285
|
});
|
|
259
286
|
|
|
260
287
|
// Trim and return
|
|
@@ -437,9 +464,25 @@ const reducer = (state: State, action: Action): State => {
|
|
|
437
464
|
};
|
|
438
465
|
}
|
|
439
466
|
case ActionType.UpdateTagFilterState: {
|
|
467
|
+
const { tagFilterState } = action;
|
|
468
|
+
|
|
469
|
+
// Select all if every tag is deselected
|
|
470
|
+
const numTagsSelected = (
|
|
471
|
+
Object.values(tagFilterState)
|
|
472
|
+
.filter((isSelected) => {
|
|
473
|
+
return isSelected;
|
|
474
|
+
})
|
|
475
|
+
.length
|
|
476
|
+
);
|
|
477
|
+
if (numTagsSelected === 0) {
|
|
478
|
+
Object.keys(tagFilterState).forEach((tag) => {
|
|
479
|
+
tagFilterState[tag] = true;
|
|
480
|
+
});
|
|
481
|
+
}
|
|
482
|
+
|
|
440
483
|
return {
|
|
441
484
|
...state,
|
|
442
|
-
tagFilterState
|
|
485
|
+
tagFilterState,
|
|
443
486
|
};
|
|
444
487
|
}
|
|
445
488
|
case ActionType.UpdateActionErrorFilterState: {
|
|
@@ -477,6 +520,35 @@ const LogReviewer: React.FC<Props> = (props) => {
|
|
|
477
520
|
onClose,
|
|
478
521
|
} = props;
|
|
479
522
|
|
|
523
|
+
// Add built-in LogMetadata
|
|
524
|
+
// > Add "uncategorized" subcontext to each context
|
|
525
|
+
Object.keys(LogMetadata.Context ?? {}).forEach((context) => {
|
|
526
|
+
if (
|
|
527
|
+
// Context exists
|
|
528
|
+
LogMetadata.Context
|
|
529
|
+
// Context has children already
|
|
530
|
+
&& typeof LogMetadata.Context[context] !== 'string'
|
|
531
|
+
) {
|
|
532
|
+
(LogMetadata.Context as any)[context][LogBuiltInMetadata.Context.Uncategorized] = (
|
|
533
|
+
LogBuiltInMetadata.Context.Uncategorized
|
|
534
|
+
);
|
|
535
|
+
}
|
|
536
|
+
})
|
|
537
|
+
// > Add built-in contexts
|
|
538
|
+
LogMetadata.Context = (LogMetadata.Context ?? {});
|
|
539
|
+
Object.keys(LogBuiltInMetadata.Context).forEach((context) => {
|
|
540
|
+
if (LogMetadata.Context) {
|
|
541
|
+
LogMetadata.Context[context] = context;
|
|
542
|
+
}
|
|
543
|
+
});
|
|
544
|
+
// > Add built-in targets
|
|
545
|
+
LogMetadata.Target = (LogMetadata.Target ?? {});
|
|
546
|
+
Object.keys(LogBuiltInMetadata.Target).forEach((target) => {
|
|
547
|
+
if (LogMetadata.Target) {
|
|
548
|
+
LogMetadata.Target[target] = target;
|
|
549
|
+
}
|
|
550
|
+
});
|
|
551
|
+
|
|
480
552
|
/* -------------- State ------------- */
|
|
481
553
|
|
|
482
554
|
// Create initial date filter state
|
|
@@ -501,14 +573,19 @@ const LogReviewer: React.FC<Props> = (props) => {
|
|
|
501
573
|
Object.keys(LogMetadata.Context ?? {}).forEach((context) => {
|
|
502
574
|
const contextValue = (LogMetadata.Context ?? {})[context];
|
|
503
575
|
if (typeof contextValue === 'string') {
|
|
504
|
-
// Case: no subcontexts
|
|
576
|
+
// Case: no subcontexts, init as checked
|
|
505
577
|
initContextFilterState[contextValue] = true;
|
|
506
578
|
} else {
|
|
507
579
|
// Case: subcontexts exist
|
|
508
|
-
initContextFilterState[
|
|
580
|
+
initContextFilterState[context] = {};
|
|
509
581
|
Object.values((LogMetadata.Context ?? {})[context]).forEach((subcontext) => {
|
|
510
|
-
|
|
511
|
-
(
|
|
582
|
+
// Skip self ("_")
|
|
583
|
+
if (subcontext === '_') {
|
|
584
|
+
return;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
// Initialize as checked
|
|
588
|
+
(initContextFilterState[context] as any)[subcontext] = true;
|
|
512
589
|
});
|
|
513
590
|
}
|
|
514
591
|
});
|
|
@@ -516,7 +593,7 @@ const LogReviewer: React.FC<Props> = (props) => {
|
|
|
516
593
|
// Create initial tag filter state
|
|
517
594
|
const initTagFilterState: TagFilterState = {};
|
|
518
595
|
Object.values(LogMetadata.Tag ?? {}).forEach((tagValue) => {
|
|
519
|
-
initTagFilterState[tagValue] =
|
|
596
|
+
initTagFilterState[tagValue] = false;
|
|
520
597
|
});
|
|
521
598
|
|
|
522
599
|
// Create advanced filter state
|
|
@@ -550,6 +627,10 @@ const LogReviewer: React.FC<Props> = (props) => {
|
|
|
550
627
|
Object.values(LogAction).forEach((action) => {
|
|
551
628
|
initActionErrorFilterState.action[action] = true;
|
|
552
629
|
});
|
|
630
|
+
// Add built-in targets
|
|
631
|
+
Object.values(LogBuiltInMetadata.Target).forEach((target) => {
|
|
632
|
+
initActionErrorFilterState.target[target] = true;
|
|
633
|
+
});
|
|
553
634
|
|
|
554
635
|
// Initial state
|
|
555
636
|
const initialState: State = {
|
|
@@ -600,18 +681,23 @@ const LogReviewer: React.FC<Props> = (props) => {
|
|
|
600
681
|
let month = newDateFilterState.startDate.month;
|
|
601
682
|
while (
|
|
602
683
|
// Earlier year
|
|
603
|
-
(year
|
|
684
|
+
(year < newDateFilterState.endDate.year)
|
|
604
685
|
// Current year but included month
|
|
605
686
|
|| (
|
|
606
687
|
year === newDateFilterState.endDate.year
|
|
607
688
|
&& month <= newDateFilterState.endDate.month
|
|
608
689
|
)
|
|
609
690
|
) {
|
|
610
|
-
// Add to list
|
|
611
|
-
|
|
612
|
-
year
|
|
613
|
-
month
|
|
614
|
-
|
|
691
|
+
// Add to list if not already loaded
|
|
692
|
+
if (
|
|
693
|
+
!logMap[year]
|
|
694
|
+
|| !logMap[year][month]
|
|
695
|
+
) {
|
|
696
|
+
toLoad.push({
|
|
697
|
+
year,
|
|
698
|
+
month,
|
|
699
|
+
});
|
|
700
|
+
}
|
|
615
701
|
|
|
616
702
|
// Increment
|
|
617
703
|
month += 1;
|
|
@@ -641,6 +727,7 @@ const LogReviewer: React.FC<Props> = (props) => {
|
|
|
641
727
|
|
|
642
728
|
// Check which year/month combos we need to load
|
|
643
729
|
const toLoad = listMonthsToLoad(newDateFilterState);
|
|
730
|
+
console.log('Filter update:', newDateFilterState, toLoad, logMap);
|
|
644
731
|
|
|
645
732
|
// If nothing to load, finished
|
|
646
733
|
if (toLoad.length === 0) {
|
|
@@ -727,16 +814,16 @@ const LogReviewer: React.FC<Props> = (props) => {
|
|
|
727
814
|
|
|
728
815
|
// Filter toggle
|
|
729
816
|
const filterToggles = (
|
|
730
|
-
<div className="LogReviewer-filter-toggles
|
|
817
|
+
<div className="LogReviewer-filter-toggles">
|
|
731
818
|
<h3 className="m-0">
|
|
732
819
|
Filters:
|
|
733
820
|
</h3>
|
|
734
|
-
<div className="LogReviewer-filter-toggle-buttons">
|
|
821
|
+
<div className="LogReviewer-filter-toggle-buttons alert alert-secondary p-2 m-0">
|
|
735
822
|
{/* Date */}
|
|
736
823
|
<button
|
|
737
824
|
type="button"
|
|
738
825
|
id="LogReviewer-toggle-date-filter-drawer"
|
|
739
|
-
className={`btn btn-${FilterDrawer.Date === expandedFilterDrawer} me-2`}
|
|
826
|
+
className={`btn btn-${FilterDrawer.Date === expandedFilterDrawer ? 'warning' : 'light'} me-2`}
|
|
740
827
|
aria-label="toggle date filter drawer"
|
|
741
828
|
onClick={() => {
|
|
742
829
|
dispatch({
|
|
@@ -755,7 +842,7 @@ const LogReviewer: React.FC<Props> = (props) => {
|
|
|
755
842
|
<button
|
|
756
843
|
type="button"
|
|
757
844
|
id="LogReviewer-toggle-context-filter-drawer"
|
|
758
|
-
className={`btn btn-${FilterDrawer.Context === expandedFilterDrawer} me-2`}
|
|
845
|
+
className={`btn btn-${FilterDrawer.Context === expandedFilterDrawer ? 'warning' : 'light'} me-2`}
|
|
759
846
|
aria-label="toggle context filter drawer"
|
|
760
847
|
onClick={() => {
|
|
761
848
|
dispatch({
|
|
@@ -771,29 +858,32 @@ const LogReviewer: React.FC<Props> = (props) => {
|
|
|
771
858
|
Context
|
|
772
859
|
</button>
|
|
773
860
|
{/* Tag */}
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
861
|
+
{/* Skip if no tags are used */}
|
|
862
|
+
{(LogMetadata.Tag && Object.keys(LogMetadata.Tag).length > 0) && (
|
|
863
|
+
<button
|
|
864
|
+
type="button"
|
|
865
|
+
id="LogReviewer-toggle-tag-filter-drawer"
|
|
866
|
+
className={`btn btn-${FilterDrawer.Tag === expandedFilterDrawer ? 'warning' : 'light'} me-2`}
|
|
867
|
+
aria-label="toggle tag filter drawer"
|
|
868
|
+
onClick={() => {
|
|
869
|
+
dispatch({
|
|
870
|
+
type: ActionType.ToggleFilterDrawer,
|
|
871
|
+
filterDrawer: FilterDrawer.Tag,
|
|
872
|
+
});
|
|
873
|
+
}}
|
|
874
|
+
>
|
|
875
|
+
<FontAwesomeIcon
|
|
876
|
+
icon={faTag}
|
|
877
|
+
className="me-2"
|
|
878
|
+
/>
|
|
879
|
+
Tag
|
|
880
|
+
</button>
|
|
881
|
+
)}
|
|
792
882
|
{/* Action */}
|
|
793
883
|
<button
|
|
794
884
|
type="button"
|
|
795
885
|
id="LogReviewer-toggle-action-filter-drawer"
|
|
796
|
-
className={`btn btn-${FilterDrawer.Action === expandedFilterDrawer} me-2`}
|
|
886
|
+
className={`btn btn-${FilterDrawer.Action === expandedFilterDrawer ? 'warning' : 'light'} me-2`}
|
|
797
887
|
aria-label="toggle action and error filter drawer"
|
|
798
888
|
onClick={() => {
|
|
799
889
|
dispatch({
|
|
@@ -812,7 +902,7 @@ const LogReviewer: React.FC<Props> = (props) => {
|
|
|
812
902
|
<button
|
|
813
903
|
type="button"
|
|
814
904
|
id="LogReviewer-toggle-advanced-filter-drawer"
|
|
815
|
-
className={`btn btn-${FilterDrawer.Advanced === expandedFilterDrawer}`}
|
|
905
|
+
className={`btn btn-${FilterDrawer.Advanced === expandedFilterDrawer ? 'warning' : 'light'}`}
|
|
816
906
|
aria-label="toggle advanced filter drawer"
|
|
817
907
|
onClick={() => {
|
|
818
908
|
dispatch({
|
|
@@ -843,14 +933,11 @@ const LogReviewer: React.FC<Props> = (props) => {
|
|
|
843
933
|
year={dateFilterState.startDate.year}
|
|
844
934
|
month={dateFilterState.startDate.month}
|
|
845
935
|
day={dateFilterState.startDate.day}
|
|
936
|
+
chooseFromPast
|
|
937
|
+
numMonthsToShow={12}
|
|
846
938
|
onChange={(month, day, year) => {
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
dateFilterState: {
|
|
850
|
-
...dateFilterState,
|
|
851
|
-
startDate: { month, day, year },
|
|
852
|
-
},
|
|
853
|
-
});
|
|
939
|
+
dateFilterState.startDate = { month, day, year };
|
|
940
|
+
handleDateRangeUpdated(dateFilterState);
|
|
854
941
|
}}
|
|
855
942
|
/>
|
|
856
943
|
{' '}
|
|
@@ -862,14 +949,28 @@ const LogReviewer: React.FC<Props> = (props) => {
|
|
|
862
949
|
year={dateFilterState.endDate.year}
|
|
863
950
|
month={dateFilterState.endDate.month}
|
|
864
951
|
day={dateFilterState.endDate.day}
|
|
952
|
+
chooseFromPast
|
|
953
|
+
numMonthsToShow={12}
|
|
865
954
|
onChange={(month, day, year) => {
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
955
|
+
if (
|
|
956
|
+
year < dateFilterState.startDate.year
|
|
957
|
+
|| (
|
|
958
|
+
year === dateFilterState.startDate.year
|
|
959
|
+
&& month < dateFilterState.startDate.month
|
|
960
|
+
)
|
|
961
|
+
|| (
|
|
962
|
+
year === dateFilterState.startDate.year
|
|
963
|
+
&& month === dateFilterState.startDate.month
|
|
964
|
+
&& day < dateFilterState.startDate.day
|
|
965
|
+
)
|
|
966
|
+
) {
|
|
967
|
+
return alert(
|
|
968
|
+
'Invalid Start Date',
|
|
969
|
+
'The start date cannot be before the end date.',
|
|
970
|
+
);
|
|
971
|
+
}
|
|
972
|
+
dateFilterState.endDate = { month, day, year };
|
|
973
|
+
handleDateRangeUpdated(dateFilterState);
|
|
873
974
|
}}
|
|
874
975
|
/>
|
|
875
976
|
</TabBox>
|
|
@@ -895,20 +996,20 @@ const LogReviewer: React.FC<Props> = (props) => {
|
|
|
895
996
|
const children: PickableItem[] = (
|
|
896
997
|
Object.keys(value)
|
|
897
998
|
.filter((subcontext) => {
|
|
898
|
-
return subcontext !== '_'
|
|
999
|
+
return subcontext !== '_';
|
|
899
1000
|
})
|
|
900
1001
|
.map((subcontext) => {
|
|
901
1002
|
return {
|
|
902
|
-
id:
|
|
1003
|
+
id: subcontext,
|
|
903
1004
|
name: genHumanReadableName(subcontext),
|
|
904
1005
|
isGroup: false,
|
|
905
|
-
checked:
|
|
1006
|
+
checked: (contextFilterState[context] as any)[subcontext],
|
|
906
1007
|
};
|
|
907
1008
|
})
|
|
908
1009
|
);
|
|
909
1010
|
const item: PickableItem = {
|
|
910
1011
|
id: context,
|
|
911
|
-
name: context,
|
|
1012
|
+
name: genHumanReadableName(context),
|
|
912
1013
|
isGroup: true,
|
|
913
1014
|
children,
|
|
914
1015
|
};
|
|
@@ -927,9 +1028,13 @@ const LogReviewer: React.FC<Props> = (props) => {
|
|
|
927
1028
|
if (pickableItem.isGroup) {
|
|
928
1029
|
// Has subcontexts
|
|
929
1030
|
pickableItem.children.forEach((subcontextItem) => {
|
|
930
|
-
(
|
|
931
|
-
(
|
|
932
|
-
|
|
1031
|
+
if (!subcontextItem.isGroup) {
|
|
1032
|
+
(
|
|
1033
|
+
contextFilterState[pickableItem.id] as { [k: string]: boolean }
|
|
1034
|
+
)[subcontextItem.id] = (
|
|
1035
|
+
subcontextItem.checked
|
|
1036
|
+
);
|
|
1037
|
+
}
|
|
933
1038
|
});
|
|
934
1039
|
} else {
|
|
935
1040
|
// No subcontexts
|
|
@@ -957,8 +1062,9 @@ const LogReviewer: React.FC<Props> = (props) => {
|
|
|
957
1062
|
<CheckboxButton
|
|
958
1063
|
id={`LogReviewer-tag-${tag}-checkbox`}
|
|
959
1064
|
text={description}
|
|
960
|
-
ariaLabel={`
|
|
1065
|
+
ariaLabel={`require that logs be tagged with "${description}" or any other selected tag`}
|
|
961
1066
|
noMarginOnRight={i === Object.keys(tagFilterState).length - 1}
|
|
1067
|
+
checked={tagFilterState[tag]}
|
|
962
1068
|
onChanged={(checked) => {
|
|
963
1069
|
tagFilterState[tag] = checked;
|
|
964
1070
|
}}
|
|
@@ -1023,7 +1129,10 @@ const LogReviewer: React.FC<Props> = (props) => {
|
|
|
1023
1129
|
) && (
|
|
1024
1130
|
<TabBox title="Action Log Details">
|
|
1025
1131
|
{/* Action */}
|
|
1026
|
-
<ButtonInputGroup
|
|
1132
|
+
<ButtonInputGroup
|
|
1133
|
+
label="Action"
|
|
1134
|
+
className="mb-2"
|
|
1135
|
+
>
|
|
1027
1136
|
{
|
|
1028
1137
|
Object.keys(LogAction)
|
|
1029
1138
|
.map((action, i) => {
|
|
@@ -1048,6 +1157,13 @@ const LogReviewer: React.FC<Props> = (props) => {
|
|
|
1048
1157
|
</ButtonInputGroup>
|
|
1049
1158
|
{/* Target */}
|
|
1050
1159
|
<ButtonInputGroup label="Target">
|
|
1160
|
+
{/* Nothing here */}
|
|
1161
|
+
{(Object.keys(LogMetadata.Target ?? {}).length === 0) && (
|
|
1162
|
+
<div>
|
|
1163
|
+
This app does not have any targets yet.
|
|
1164
|
+
</div>
|
|
1165
|
+
)}
|
|
1166
|
+
{/* List of targets */}
|
|
1051
1167
|
{
|
|
1052
1168
|
Object.keys(LogMetadata.Target ?? {})
|
|
1053
1169
|
.map((target, i) => {
|
|
@@ -1394,53 +1510,58 @@ const LogReviewer: React.FC<Props> = (props) => {
|
|
|
1394
1510
|
/>
|
|
1395
1511
|
</ButtonInputGroup>
|
|
1396
1512
|
|
|
1397
|
-
{/*
|
|
1398
|
-
|
|
1399
|
-
<
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1513
|
+
{/* Server filters */}
|
|
1514
|
+
{advancedFilterState.source !== LogSource.Client && (
|
|
1515
|
+
<div className="mt-2">
|
|
1516
|
+
{/* Route path */}
|
|
1517
|
+
<div className="input-group mb-2">
|
|
1518
|
+
<span className="input-group-text">
|
|
1519
|
+
Server Route Path
|
|
1520
|
+
</span>
|
|
1521
|
+
<input
|
|
1522
|
+
type="text"
|
|
1523
|
+
className="form-control"
|
|
1524
|
+
aria-label="query for server route path"
|
|
1525
|
+
placeholder="e.g. /api/ttm/courses/12345"
|
|
1526
|
+
value={advancedFilterState.routePath}
|
|
1527
|
+
onChange={(e) => {
|
|
1528
|
+
advancedFilterState.courseName = (
|
|
1529
|
+
(e.target.value)
|
|
1530
|
+
.trim()
|
|
1531
|
+
);
|
|
1532
|
+
dispatch({
|
|
1533
|
+
type: ActionType.UpdateAdvancedFilterState,
|
|
1534
|
+
advancedFilterState,
|
|
1535
|
+
});
|
|
1536
|
+
}}
|
|
1537
|
+
/>
|
|
1538
|
+
</div>
|
|
1420
1539
|
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1540
|
+
{/* Route template */}
|
|
1541
|
+
<div className="input-group mb-2">
|
|
1542
|
+
<span className="input-group-text">
|
|
1543
|
+
Server Route Template
|
|
1544
|
+
</span>
|
|
1545
|
+
<input
|
|
1546
|
+
type="text"
|
|
1547
|
+
className="form-control"
|
|
1548
|
+
aria-label="query for server route template"
|
|
1549
|
+
value={advancedFilterState.routeTemplate}
|
|
1550
|
+
placeholder="e.g. /api/ttm/courses/:courseId"
|
|
1551
|
+
onChange={(e) => {
|
|
1552
|
+
advancedFilterState.courseName = (
|
|
1553
|
+
(e.target.value)
|
|
1554
|
+
.trim()
|
|
1555
|
+
);
|
|
1556
|
+
dispatch({
|
|
1557
|
+
type: ActionType.UpdateAdvancedFilterState,
|
|
1558
|
+
advancedFilterState,
|
|
1559
|
+
});
|
|
1560
|
+
}}
|
|
1561
|
+
/>
|
|
1562
|
+
</div>
|
|
1563
|
+
</div>
|
|
1564
|
+
)}
|
|
1444
1565
|
</TabBox>
|
|
1445
1566
|
</>
|
|
1446
1567
|
);
|
|
@@ -1463,9 +1584,10 @@ const LogReviewer: React.FC<Props> = (props) => {
|
|
|
1463
1584
|
// > Perform filters
|
|
1464
1585
|
const logs: Log[] = [];
|
|
1465
1586
|
Object.keys(logMap).forEach((year) => {
|
|
1466
|
-
Object.keys(logMap).forEach((month) => {
|
|
1587
|
+
Object.keys(logMap[year]).forEach((month) => {
|
|
1467
1588
|
logMap[year][month].forEach((log) => {
|
|
1468
1589
|
/* ----------- Date Filter ---------- */
|
|
1590
|
+
console.log('Log:', log);
|
|
1469
1591
|
|
|
1470
1592
|
// Before start date
|
|
1471
1593
|
if (
|
|
@@ -1483,6 +1605,7 @@ const LogReviewer: React.FC<Props> = (props) => {
|
|
|
1483
1605
|
&& (log.day < dateFilterState.startDate.day)
|
|
1484
1606
|
)
|
|
1485
1607
|
) {
|
|
1608
|
+
console.log('RULED OUT: START');
|
|
1486
1609
|
return;
|
|
1487
1610
|
}
|
|
1488
1611
|
|
|
@@ -1502,6 +1625,7 @@ const LogReviewer: React.FC<Props> = (props) => {
|
|
|
1502
1625
|
&& (log.day > dateFilterState.endDate.day)
|
|
1503
1626
|
)
|
|
1504
1627
|
) {
|
|
1628
|
+
console.log('RULED OUT: END');
|
|
1505
1629
|
return;
|
|
1506
1630
|
}
|
|
1507
1631
|
|
|
@@ -1513,19 +1637,27 @@ const LogReviewer: React.FC<Props> = (props) => {
|
|
|
1513
1637
|
contextFilterState[log.context] === false
|
|
1514
1638
|
// None of the subcontexts are selected
|
|
1515
1639
|
|| (
|
|
1516
|
-
|
|
1640
|
+
// Has subcontexts
|
|
1641
|
+
typeof contextFilterState[log.context] !== 'boolean'
|
|
1642
|
+
// None of the subcontexts are selected
|
|
1643
|
+
&& Object.values(contextFilterState[log.context] ?? {})
|
|
1517
1644
|
.every((isSelected) => {
|
|
1518
1645
|
return !isSelected;
|
|
1519
1646
|
})
|
|
1520
1647
|
)
|
|
1521
1648
|
) {
|
|
1649
|
+
// 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
|
|
1650
|
+
console.log('RULED OUT: CONTEXT');
|
|
1651
|
+
console.log(log.context, contextFilterState);
|
|
1522
1652
|
return;
|
|
1523
1653
|
}
|
|
1524
1654
|
|
|
1525
1655
|
// Subcontext doesn't match
|
|
1526
1656
|
if (
|
|
1657
|
+
// Log context is not "uncategorized" (no point in further filters)
|
|
1658
|
+
log.context !== LogBuiltInMetadata.Context.Uncategorized
|
|
1527
1659
|
// Log has a subcontext
|
|
1528
|
-
log.subcontext
|
|
1660
|
+
&& log.subcontext
|
|
1529
1661
|
// Context has subcontexts
|
|
1530
1662
|
&& (
|
|
1531
1663
|
contextFilterState[log.context]
|
|
@@ -1535,6 +1667,7 @@ const LogReviewer: React.FC<Props> = (props) => {
|
|
|
1535
1667
|
// Subcontext is not selected
|
|
1536
1668
|
&& !(contextFilterState as any)[log.context][log.subcontext]
|
|
1537
1669
|
) {
|
|
1670
|
+
console.log('RULED OUT: SUBCONTEXT');
|
|
1538
1671
|
return;
|
|
1539
1672
|
}
|
|
1540
1673
|
|
|
@@ -1542,15 +1675,18 @@ const LogReviewer: React.FC<Props> = (props) => {
|
|
|
1542
1675
|
|
|
1543
1676
|
// No tags match
|
|
1544
1677
|
if (
|
|
1545
|
-
//
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
log.tags.every((tag) => {
|
|
1550
|
-
return !tagFilterState[tag];
|
|
1678
|
+
// At least one tag is required
|
|
1679
|
+
Object.values(tagFilterState)
|
|
1680
|
+
.filter((isSelected) => {
|
|
1681
|
+
return isSelected;
|
|
1551
1682
|
})
|
|
1552
|
-
|
|
1683
|
+
.length > 0
|
|
1684
|
+
// No tags match
|
|
1685
|
+
&& log.tags.every((tag) => {
|
|
1686
|
+
return !tagFilterState[tag];
|
|
1687
|
+
})
|
|
1553
1688
|
) {
|
|
1689
|
+
console.log('RULED OUT: TAGS');
|
|
1554
1690
|
return;
|
|
1555
1691
|
}
|
|
1556
1692
|
|
|
@@ -1563,6 +1699,7 @@ const LogReviewer: React.FC<Props> = (props) => {
|
|
|
1563
1699
|
// Log type doesn't match
|
|
1564
1700
|
&& actionErrorFilterState.type !== log.type
|
|
1565
1701
|
) {
|
|
1702
|
+
console.log('RULED OUT: TYPE');
|
|
1566
1703
|
return;
|
|
1567
1704
|
}
|
|
1568
1705
|
|
|
@@ -1579,6 +1716,7 @@ const LogReviewer: React.FC<Props> = (props) => {
|
|
|
1579
1716
|
actionErrorFilterState.errorMessage.trim().toLowerCase(),
|
|
1580
1717
|
)
|
|
1581
1718
|
) {
|
|
1719
|
+
console.log('RULED OUT: ERROR MESSAGE');
|
|
1582
1720
|
return;
|
|
1583
1721
|
}
|
|
1584
1722
|
|
|
@@ -1593,6 +1731,7 @@ const LogReviewer: React.FC<Props> = (props) => {
|
|
|
1593
1731
|
actionErrorFilterState.errorCode.trim().toUpperCase(),
|
|
1594
1732
|
)
|
|
1595
1733
|
) {
|
|
1734
|
+
console.log('RULED OUT: ERROR CODE');
|
|
1596
1735
|
return;
|
|
1597
1736
|
}
|
|
1598
1737
|
}
|
|
@@ -1606,6 +1745,7 @@ const LogReviewer: React.FC<Props> = (props) => {
|
|
|
1606
1745
|
// Target isn't selected
|
|
1607
1746
|
&& !actionErrorFilterState.target[log.target]
|
|
1608
1747
|
) {
|
|
1748
|
+
console.log('RULED OUT: TARGET');
|
|
1609
1749
|
return;
|
|
1610
1750
|
}
|
|
1611
1751
|
|
|
@@ -1616,6 +1756,7 @@ const LogReviewer: React.FC<Props> = (props) => {
|
|
|
1616
1756
|
// Action isn't selected
|
|
1617
1757
|
&& !actionErrorFilterState.action[log.action]
|
|
1618
1758
|
) {
|
|
1759
|
+
console.log('RULED OUT: ACTION');
|
|
1619
1760
|
return;
|
|
1620
1761
|
}
|
|
1621
1762
|
}
|
|
@@ -1631,6 +1772,7 @@ const LogReviewer: React.FC<Props> = (props) => {
|
|
|
1631
1772
|
advancedFilterState.userFirstName.toLowerCase().trim(),
|
|
1632
1773
|
)
|
|
1633
1774
|
) {
|
|
1775
|
+
console.log('RULED OUT: FIRST');
|
|
1634
1776
|
return;
|
|
1635
1777
|
}
|
|
1636
1778
|
|
|
@@ -1643,6 +1785,7 @@ const LogReviewer: React.FC<Props> = (props) => {
|
|
|
1643
1785
|
advancedFilterState.userLastName.toLowerCase().trim(),
|
|
1644
1786
|
)
|
|
1645
1787
|
) {
|
|
1788
|
+
console.log('RULED OUT: LAST');
|
|
1646
1789
|
return;
|
|
1647
1790
|
}
|
|
1648
1791
|
|
|
@@ -1655,6 +1798,7 @@ const LogReviewer: React.FC<Props> = (props) => {
|
|
|
1655
1798
|
advancedFilterState.userEmail.toLowerCase().trim(),
|
|
1656
1799
|
)
|
|
1657
1800
|
) {
|
|
1801
|
+
console.log('RULED OUT: EMAIL');
|
|
1658
1802
|
return;
|
|
1659
1803
|
}
|
|
1660
1804
|
|
|
@@ -1667,6 +1811,7 @@ const LogReviewer: React.FC<Props> = (props) => {
|
|
|
1667
1811
|
advancedFilterState.userId.trim(),
|
|
1668
1812
|
)
|
|
1669
1813
|
) {
|
|
1814
|
+
console.log('RULED OUT: USER ID');
|
|
1670
1815
|
return;
|
|
1671
1816
|
}
|
|
1672
1817
|
|
|
@@ -1677,6 +1822,7 @@ const LogReviewer: React.FC<Props> = (props) => {
|
|
|
1677
1822
|
// Learners aren't included
|
|
1678
1823
|
&& !advancedFilterState.includeLearners
|
|
1679
1824
|
) {
|
|
1825
|
+
console.log('RULED OUT: LEARNER');
|
|
1680
1826
|
return;
|
|
1681
1827
|
}
|
|
1682
1828
|
|
|
@@ -1687,6 +1833,7 @@ const LogReviewer: React.FC<Props> = (props) => {
|
|
|
1687
1833
|
// TTMs aren't included
|
|
1688
1834
|
&& !advancedFilterState.includeTTMs
|
|
1689
1835
|
) {
|
|
1836
|
+
console.log('RULED OUT: TTM');
|
|
1690
1837
|
return;
|
|
1691
1838
|
}
|
|
1692
1839
|
|
|
@@ -1697,6 +1844,7 @@ const LogReviewer: React.FC<Props> = (props) => {
|
|
|
1697
1844
|
// Admins aren't included
|
|
1698
1845
|
&& !advancedFilterState.includeAdmins
|
|
1699
1846
|
) {
|
|
1847
|
+
console.log('RULED OUT: ADMIN');
|
|
1700
1848
|
return;
|
|
1701
1849
|
}
|
|
1702
1850
|
|
|
@@ -1709,6 +1857,7 @@ const LogReviewer: React.FC<Props> = (props) => {
|
|
|
1709
1857
|
advancedFilterState.courseId.trim(),
|
|
1710
1858
|
)
|
|
1711
1859
|
) {
|
|
1860
|
+
console.log('RULED OUT: COURSE ID');
|
|
1712
1861
|
return;
|
|
1713
1862
|
}
|
|
1714
1863
|
|
|
@@ -1721,6 +1870,7 @@ const LogReviewer: React.FC<Props> = (props) => {
|
|
|
1721
1870
|
advancedFilterState.courseName.trim(),
|
|
1722
1871
|
)
|
|
1723
1872
|
) {
|
|
1873
|
+
console.log('RULED OUT: COURSE NAME');
|
|
1724
1874
|
return;
|
|
1725
1875
|
}
|
|
1726
1876
|
|
|
@@ -1733,6 +1883,7 @@ const LogReviewer: React.FC<Props> = (props) => {
|
|
|
1733
1883
|
// Mobile filter doesn't match
|
|
1734
1884
|
&& (advancedFilterState.isMobile === log.device.isMobile)
|
|
1735
1885
|
) {
|
|
1886
|
+
console.log('RULED OUT: MOBILE');
|
|
1736
1887
|
return;
|
|
1737
1888
|
}
|
|
1738
1889
|
|
|
@@ -1745,6 +1896,7 @@ const LogReviewer: React.FC<Props> = (props) => {
|
|
|
1745
1896
|
// Source filter doesn't match
|
|
1746
1897
|
&& (advancedFilterState.source !== log.source)
|
|
1747
1898
|
) {
|
|
1899
|
+
console.log('RULED OUT: SOURCE');
|
|
1748
1900
|
return;
|
|
1749
1901
|
}
|
|
1750
1902
|
|
|
@@ -1757,6 +1909,7 @@ const LogReviewer: React.FC<Props> = (props) => {
|
|
|
1757
1909
|
// Route path doesn't match
|
|
1758
1910
|
&& !(log.routePath.includes(advancedFilterState.routePath.trim()))
|
|
1759
1911
|
) {
|
|
1912
|
+
console.log('RULED OUT: PATH');
|
|
1760
1913
|
return;
|
|
1761
1914
|
}
|
|
1762
1915
|
|
|
@@ -1769,6 +1922,7 @@ const LogReviewer: React.FC<Props> = (props) => {
|
|
|
1769
1922
|
// Route template doesn't match
|
|
1770
1923
|
&& !(log.routeTemplate.includes(advancedFilterState.routeTemplate.trim()))
|
|
1771
1924
|
) {
|
|
1925
|
+
console.log('RULED OUT: TEMPLATE');
|
|
1772
1926
|
return;
|
|
1773
1927
|
}
|
|
1774
1928
|
|
|
@@ -1974,19 +2128,40 @@ const LogReviewer: React.FC<Props> = (props) => {
|
|
|
1974
2128
|
|
|
1975
2129
|
// Create intelliTable
|
|
1976
2130
|
const dataTable = (
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
2131
|
+
logs.length === 0
|
|
2132
|
+
? (
|
|
2133
|
+
<>
|
|
2134
|
+
<h3 className="m-0">
|
|
2135
|
+
Matching Logs:
|
|
2136
|
+
</h3>
|
|
2137
|
+
<div className="alert alert-warning text-center">
|
|
2138
|
+
<h4 className="m-1">
|
|
2139
|
+
No Logs to Show
|
|
2140
|
+
</h4>
|
|
2141
|
+
<div>
|
|
2142
|
+
Either your filters are too strict or no matching logs have been
|
|
2143
|
+
created yet.
|
|
2144
|
+
</div>
|
|
2145
|
+
</div>
|
|
2146
|
+
</>
|
|
2147
|
+
)
|
|
2148
|
+
: (
|
|
2149
|
+
<IntelliTable
|
|
2150
|
+
title="Matching Logs:"
|
|
2151
|
+
id="logs"
|
|
2152
|
+
data={logs}
|
|
2153
|
+
columns={columns}
|
|
2154
|
+
/>
|
|
2155
|
+
)
|
|
1983
2156
|
);
|
|
1984
2157
|
|
|
1985
2158
|
// Main body
|
|
1986
2159
|
body = (
|
|
1987
2160
|
<>
|
|
1988
2161
|
{filters}
|
|
1989
|
-
|
|
2162
|
+
<div className="mt-2">
|
|
2163
|
+
{dataTable}
|
|
2164
|
+
</div>
|
|
1990
2165
|
</>
|
|
1991
2166
|
);
|
|
1992
2167
|
}
|
|
@@ -2001,26 +2176,22 @@ const LogReviewer: React.FC<Props> = (props) => {
|
|
|
2001
2176
|
<div className="LogReviewer-inner-container">
|
|
2002
2177
|
<div className="LogReviewer-header">
|
|
2003
2178
|
<div className="LogReviewer-header-title">
|
|
2004
|
-
<
|
|
2179
|
+
<h3 className="text-center m-0">
|
|
2005
2180
|
Log Review Dashboard
|
|
2006
|
-
</
|
|
2181
|
+
</h3>
|
|
2182
|
+
</div>
|
|
2183
|
+
<div style={{ width: 0 }}>
|
|
2184
|
+
<button
|
|
2185
|
+
type="button"
|
|
2186
|
+
className="LogReviewer-header-close-button btn btn-dark btn-lg pe-0"
|
|
2187
|
+
aria-label="close log reviewer panel"
|
|
2188
|
+
onClick={onClose}
|
|
2189
|
+
>
|
|
2190
|
+
<FontAwesomeIcon
|
|
2191
|
+
icon={faTimes}
|
|
2192
|
+
/>
|
|
2193
|
+
</button>
|
|
2007
2194
|
</div>
|
|
2008
|
-
<button
|
|
2009
|
-
type="button"
|
|
2010
|
-
className="LogReviewer-header-close-button btn btn-lg"
|
|
2011
|
-
aria-label="close log reviewer panel"
|
|
2012
|
-
onClick={onClose}
|
|
2013
|
-
style={{
|
|
2014
|
-
border: 0,
|
|
2015
|
-
backgroundColor: 'transparent',
|
|
2016
|
-
padding: 0,
|
|
2017
|
-
margin: 0,
|
|
2018
|
-
}}
|
|
2019
|
-
>
|
|
2020
|
-
<FontAwesomeIcon
|
|
2021
|
-
icon={faTimes}
|
|
2022
|
-
/>
|
|
2023
|
-
</button>
|
|
2024
2195
|
</div>
|
|
2025
2196
|
<div className="LogReviewer-contents">
|
|
2026
2197
|
{body}
|