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.
@@ -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 += chars;
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: action.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[contextValue._] = {};
580
+ initContextFilterState[context] = {};
509
581
  Object.values((LogMetadata.Context ?? {})[context]).forEach((subcontext) => {
510
- const subcontextValue = contextValue[subcontext];
511
- (initContextFilterState[contextValue._] as { [k: string]: boolean })[subcontextValue] = true;
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] = true;
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 <= newDateFilterState.endDate.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
- toLoad.push({
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 d-flex align-items-center justify-content-center">
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
- <button
775
- type="button"
776
- id="LogReviewer-toggle-tag-filter-drawer"
777
- className={`btn btn-${FilterDrawer.Tag === expandedFilterDrawer} me-2`}
778
- aria-label="toggle tag filter drawer"
779
- onClick={() => {
780
- dispatch({
781
- type: ActionType.ToggleFilterDrawer,
782
- filterDrawer: FilterDrawer.Tag,
783
- });
784
- }}
785
- >
786
- <FontAwesomeIcon
787
- icon={faTag}
788
- className="me-2"
789
- />
790
- Tag
791
- </button>
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
- dispatch({
848
- type: ActionType.UpdateDateFilterState,
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
- dispatch({
867
- type: ActionType.UpdateDateFilterState,
868
- dateFilterState: {
869
- ...dateFilterState,
870
- endDate: { month, day, year },
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: `${context}-${subcontext}`,
1003
+ id: subcontext,
903
1004
  name: genHumanReadableName(subcontext),
904
1005
  isGroup: false,
905
- checked: !!value[subcontext],
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
- (contextFilterState as any)[pickableItem.id][subcontextItem.id] = (
931
- (subcontextItem as any).checked
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={`include logs tagged with "${description}" in results`}
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 label="Action">
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
- {/* Route path */}
1398
- <div className="input-group mb-2">
1399
- <span className="input-group-text">
1400
- Server Route Path
1401
- </span>
1402
- <input
1403
- type="text"
1404
- className="form-control"
1405
- aria-label="query for server route path"
1406
- placeholder="e.g. /api/ttm/courses/12345"
1407
- value={advancedFilterState.routePath}
1408
- onChange={(e) => {
1409
- advancedFilterState.courseName = (
1410
- (e.target.value)
1411
- .trim()
1412
- );
1413
- dispatch({
1414
- type: ActionType.UpdateAdvancedFilterState,
1415
- advancedFilterState,
1416
- });
1417
- }}
1418
- />
1419
- </div>
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
- {/* Route template */}
1422
- <div className="input-group mb-2">
1423
- <span className="input-group-text">
1424
- Server Route Template
1425
- </span>
1426
- <input
1427
- type="text"
1428
- className="form-control"
1429
- aria-label="query for server route template"
1430
- value={advancedFilterState.routeTemplate}
1431
- placeholder="e.g. /api/ttm/courses/:courseId"
1432
- onChange={(e) => {
1433
- advancedFilterState.courseName = (
1434
- (e.target.value)
1435
- .trim()
1436
- );
1437
- dispatch({
1438
- type: ActionType.UpdateAdvancedFilterState,
1439
- advancedFilterState,
1440
- });
1441
- }}
1442
- />
1443
- </div>
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
- Object.values(contextFilterState[log.context] ?? {})
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
- // Log has at least one tag
1546
- log.tags.length > 0
1547
- // No tags match
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
- <IntelliTable
1978
- title="Matching Logs"
1979
- id="logs"
1980
- data={logs}
1981
- columns={columns}
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
- {dataTable}
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
- <h1 className="m-0">
2179
+ <h3 className="text-center m-0">
2005
2180
  Log Review Dashboard
2006
- </h1>
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}