dce-reactkit 3.2.2-beta.2 → 3.2.2-beta.21

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';
@@ -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,34 @@ 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
+ LogMetadata.Context
528
+ // Context has children already
529
+ && typeof LogMetadata.Context[context] !== 'string'
530
+ ) {
531
+ (LogMetadata.Context as any)[context][LogBuiltInMetadata.Context.Uncategorized] = (
532
+ LogBuiltInMetadata.Context.Uncategorized
533
+ );
534
+ }
535
+ })
536
+ // > Add built-in contexts
537
+ LogMetadata.Context = (LogMetadata.Context ?? {});
538
+ Object.keys(LogBuiltInMetadata.Context).forEach((context) => {
539
+ if (LogMetadata.Context) {
540
+ LogMetadata.Context[context] = context;
541
+ }
542
+ });
543
+ // > Add built-in targets
544
+ LogMetadata.Target = (LogMetadata.Target ?? {});
545
+ Object.keys(LogBuiltInMetadata.Target).forEach((target) => {
546
+ if (LogMetadata.Target) {
547
+ LogMetadata.Target[target] = target;
548
+ }
549
+ });
550
+
480
551
  /* -------------- State ------------- */
481
552
 
482
553
  // Create initial date filter state
@@ -550,6 +621,10 @@ const LogReviewer: React.FC<Props> = (props) => {
550
621
  Object.values(LogAction).forEach((action) => {
551
622
  initActionErrorFilterState.action[action] = true;
552
623
  });
624
+ // Add built-in targets
625
+ Object.values(LogBuiltInMetadata.Target).forEach((target) => {
626
+ initActionErrorFilterState.target[target] = true;
627
+ });
553
628
 
554
629
  // Initial state
555
630
  const initialState: State = {
@@ -600,18 +675,23 @@ const LogReviewer: React.FC<Props> = (props) => {
600
675
  let month = newDateFilterState.startDate.month;
601
676
  while (
602
677
  // Earlier year
603
- (year <= newDateFilterState.endDate.year)
678
+ (year < newDateFilterState.endDate.year)
604
679
  // Current year but included month
605
680
  || (
606
681
  year === newDateFilterState.endDate.year
607
682
  && month <= newDateFilterState.endDate.month
608
683
  )
609
684
  ) {
610
- // Add to list
611
- toLoad.push({
612
- year,
613
- month,
614
- });
685
+ // Add to list if not already loaded
686
+ if (
687
+ !logMap[year]
688
+ || !logMap[year][month]
689
+ ) {
690
+ toLoad.push({
691
+ year,
692
+ month,
693
+ });
694
+ }
615
695
 
616
696
  // Increment
617
697
  month += 1;
@@ -641,6 +721,7 @@ const LogReviewer: React.FC<Props> = (props) => {
641
721
 
642
722
  // Check which year/month combos we need to load
643
723
  const toLoad = listMonthsToLoad(newDateFilterState);
724
+ console.log('Filter update:', newDateFilterState, toLoad, logMap);
644
725
 
645
726
  // If nothing to load, finished
646
727
  if (toLoad.length === 0) {
@@ -727,16 +808,16 @@ const LogReviewer: React.FC<Props> = (props) => {
727
808
 
728
809
  // Filter toggle
729
810
  const filterToggles = (
730
- <div className="LogReviewer-filter-toggles d-flex align-items-center justify-content-center">
811
+ <div className="LogReviewer-filter-toggles">
731
812
  <h3 className="m-0">
732
813
  Filters:
733
814
  </h3>
734
- <div className="LogReviewer-filter-toggle-buttons">
815
+ <div className="LogReviewer-filter-toggle-buttons alert alert-secondary p-2 m-0">
735
816
  {/* Date */}
736
817
  <button
737
818
  type="button"
738
819
  id="LogReviewer-toggle-date-filter-drawer"
739
- className={`btn btn-${FilterDrawer.Date === expandedFilterDrawer} me-2`}
820
+ className={`btn btn-${FilterDrawer.Date === expandedFilterDrawer ? 'warning' : 'light'} me-2`}
740
821
  aria-label="toggle date filter drawer"
741
822
  onClick={() => {
742
823
  dispatch({
@@ -755,7 +836,7 @@ const LogReviewer: React.FC<Props> = (props) => {
755
836
  <button
756
837
  type="button"
757
838
  id="LogReviewer-toggle-context-filter-drawer"
758
- className={`btn btn-${FilterDrawer.Context === expandedFilterDrawer} me-2`}
839
+ className={`btn btn-${FilterDrawer.Context === expandedFilterDrawer ? 'warning' : 'light'} me-2`}
759
840
  aria-label="toggle context filter drawer"
760
841
  onClick={() => {
761
842
  dispatch({
@@ -771,29 +852,32 @@ const LogReviewer: React.FC<Props> = (props) => {
771
852
  Context
772
853
  </button>
773
854
  {/* 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>
855
+ {/* Skip if no tags are used */}
856
+ {(LogMetadata.Tag && Object.keys(LogMetadata.Tag).length > 0) && (
857
+ <button
858
+ type="button"
859
+ id="LogReviewer-toggle-tag-filter-drawer"
860
+ className={`btn btn-${FilterDrawer.Tag === expandedFilterDrawer ? 'warning' : 'light'} me-2`}
861
+ aria-label="toggle tag filter drawer"
862
+ onClick={() => {
863
+ dispatch({
864
+ type: ActionType.ToggleFilterDrawer,
865
+ filterDrawer: FilterDrawer.Tag,
866
+ });
867
+ }}
868
+ >
869
+ <FontAwesomeIcon
870
+ icon={faTag}
871
+ className="me-2"
872
+ />
873
+ Tag
874
+ </button>
875
+ )}
792
876
  {/* Action */}
793
877
  <button
794
878
  type="button"
795
879
  id="LogReviewer-toggle-action-filter-drawer"
796
- className={`btn btn-${FilterDrawer.Action === expandedFilterDrawer} me-2`}
880
+ className={`btn btn-${FilterDrawer.Action === expandedFilterDrawer ? 'warning' : 'light'} me-2`}
797
881
  aria-label="toggle action and error filter drawer"
798
882
  onClick={() => {
799
883
  dispatch({
@@ -812,7 +896,7 @@ const LogReviewer: React.FC<Props> = (props) => {
812
896
  <button
813
897
  type="button"
814
898
  id="LogReviewer-toggle-advanced-filter-drawer"
815
- className={`btn btn-${FilterDrawer.Advanced === expandedFilterDrawer}`}
899
+ className={`btn btn-${FilterDrawer.Advanced === expandedFilterDrawer ? 'warning' : 'light'}`}
816
900
  aria-label="toggle advanced filter drawer"
817
901
  onClick={() => {
818
902
  dispatch({
@@ -843,14 +927,11 @@ const LogReviewer: React.FC<Props> = (props) => {
843
927
  year={dateFilterState.startDate.year}
844
928
  month={dateFilterState.startDate.month}
845
929
  day={dateFilterState.startDate.day}
930
+ chooseFromPast
931
+ numMonthsToShow={12}
846
932
  onChange={(month, day, year) => {
847
- dispatch({
848
- type: ActionType.UpdateDateFilterState,
849
- dateFilterState: {
850
- ...dateFilterState,
851
- startDate: { month, day, year },
852
- },
853
- });
933
+ dateFilterState.startDate = { month, day, year };
934
+ handleDateRangeUpdated(dateFilterState);
854
935
  }}
855
936
  />
856
937
  {' '}
@@ -862,14 +943,28 @@ const LogReviewer: React.FC<Props> = (props) => {
862
943
  year={dateFilterState.endDate.year}
863
944
  month={dateFilterState.endDate.month}
864
945
  day={dateFilterState.endDate.day}
946
+ chooseFromPast
947
+ numMonthsToShow={12}
865
948
  onChange={(month, day, year) => {
866
- dispatch({
867
- type: ActionType.UpdateDateFilterState,
868
- dateFilterState: {
869
- ...dateFilterState,
870
- endDate: { month, day, year },
871
- },
872
- });
949
+ if (
950
+ year < dateFilterState.startDate.year
951
+ || (
952
+ year === dateFilterState.startDate.year
953
+ && month < dateFilterState.startDate.month
954
+ )
955
+ || (
956
+ year === dateFilterState.startDate.year
957
+ && month === dateFilterState.startDate.month
958
+ && day < dateFilterState.startDate.day
959
+ )
960
+ ) {
961
+ return alert(
962
+ 'Invalid Start Date',
963
+ 'The start date cannot be before the end date.',
964
+ );
965
+ }
966
+ dateFilterState.endDate = { month, day, year };
967
+ handleDateRangeUpdated(dateFilterState);
873
968
  }}
874
969
  />
875
970
  </TabBox>
@@ -899,7 +994,7 @@ const LogReviewer: React.FC<Props> = (props) => {
899
994
  })
900
995
  .map((subcontext) => {
901
996
  return {
902
- id: `${context}-${subcontext}`,
997
+ id: subcontext,
903
998
  name: genHumanReadableName(subcontext),
904
999
  isGroup: false,
905
1000
  checked: !!value[subcontext],
@@ -908,7 +1003,7 @@ const LogReviewer: React.FC<Props> = (props) => {
908
1003
  );
909
1004
  const item: PickableItem = {
910
1005
  id: context,
911
- name: context,
1006
+ name: genHumanReadableName(context),
912
1007
  isGroup: true,
913
1008
  children,
914
1009
  };
@@ -922,6 +1017,8 @@ const LogReviewer: React.FC<Props> = (props) => {
922
1017
  title="Context"
923
1018
  items={pickableItems}
924
1019
  onChanged={(updatedItems) => {
1020
+ console.log('Items before:', pickableItems);
1021
+ console.log('Updated Items:', updatedItems);
925
1022
  // Update our state
926
1023
  updatedItems.forEach((pickableItem) => {
927
1024
  if (pickableItem.isGroup) {
@@ -1023,7 +1120,10 @@ const LogReviewer: React.FC<Props> = (props) => {
1023
1120
  ) && (
1024
1121
  <TabBox title="Action Log Details">
1025
1122
  {/* Action */}
1026
- <ButtonInputGroup label="Action">
1123
+ <ButtonInputGroup
1124
+ label="Action"
1125
+ className="mb-2"
1126
+ >
1027
1127
  {
1028
1128
  Object.keys(LogAction)
1029
1129
  .map((action, i) => {
@@ -1048,6 +1148,13 @@ const LogReviewer: React.FC<Props> = (props) => {
1048
1148
  </ButtonInputGroup>
1049
1149
  {/* Target */}
1050
1150
  <ButtonInputGroup label="Target">
1151
+ {/* Nothing here */}
1152
+ {(Object.keys(LogMetadata.Target ?? {}).length === 0) && (
1153
+ <div>
1154
+ This app does not have any targets yet.
1155
+ </div>
1156
+ )}
1157
+ {/* List of targets */}
1051
1158
  {
1052
1159
  Object.keys(LogMetadata.Target ?? {})
1053
1160
  .map((target, i) => {
@@ -1394,53 +1501,58 @@ const LogReviewer: React.FC<Props> = (props) => {
1394
1501
  />
1395
1502
  </ButtonInputGroup>
1396
1503
 
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>
1504
+ {/* Server filters */}
1505
+ {advancedFilterState.source !== LogSource.Client && (
1506
+ <div className="mt-2">
1507
+ {/* Route path */}
1508
+ <div className="input-group mb-2">
1509
+ <span className="input-group-text">
1510
+ Server Route Path
1511
+ </span>
1512
+ <input
1513
+ type="text"
1514
+ className="form-control"
1515
+ aria-label="query for server route path"
1516
+ placeholder="e.g. /api/ttm/courses/12345"
1517
+ value={advancedFilterState.routePath}
1518
+ onChange={(e) => {
1519
+ advancedFilterState.courseName = (
1520
+ (e.target.value)
1521
+ .trim()
1522
+ );
1523
+ dispatch({
1524
+ type: ActionType.UpdateAdvancedFilterState,
1525
+ advancedFilterState,
1526
+ });
1527
+ }}
1528
+ />
1529
+ </div>
1420
1530
 
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>
1531
+ {/* Route template */}
1532
+ <div className="input-group mb-2">
1533
+ <span className="input-group-text">
1534
+ Server Route Template
1535
+ </span>
1536
+ <input
1537
+ type="text"
1538
+ className="form-control"
1539
+ aria-label="query for server route template"
1540
+ value={advancedFilterState.routeTemplate}
1541
+ placeholder="e.g. /api/ttm/courses/:courseId"
1542
+ onChange={(e) => {
1543
+ advancedFilterState.courseName = (
1544
+ (e.target.value)
1545
+ .trim()
1546
+ );
1547
+ dispatch({
1548
+ type: ActionType.UpdateAdvancedFilterState,
1549
+ advancedFilterState,
1550
+ });
1551
+ }}
1552
+ />
1553
+ </div>
1554
+ </div>
1555
+ )}
1444
1556
  </TabBox>
1445
1557
  </>
1446
1558
  );
@@ -1463,9 +1575,10 @@ const LogReviewer: React.FC<Props> = (props) => {
1463
1575
  // > Perform filters
1464
1576
  const logs: Log[] = [];
1465
1577
  Object.keys(logMap).forEach((year) => {
1466
- Object.keys(logMap).forEach((month) => {
1578
+ Object.keys(logMap[year]).forEach((month) => {
1467
1579
  logMap[year][month].forEach((log) => {
1468
1580
  /* ----------- Date Filter ---------- */
1581
+ console.log('Log:', log);
1469
1582
 
1470
1583
  // Before start date
1471
1584
  if (
@@ -1483,6 +1596,7 @@ const LogReviewer: React.FC<Props> = (props) => {
1483
1596
  && (log.day < dateFilterState.startDate.day)
1484
1597
  )
1485
1598
  ) {
1599
+ console.log('RULED OUT: START');
1486
1600
  return;
1487
1601
  }
1488
1602
 
@@ -1502,6 +1616,7 @@ const LogReviewer: React.FC<Props> = (props) => {
1502
1616
  && (log.day > dateFilterState.endDate.day)
1503
1617
  )
1504
1618
  ) {
1619
+ console.log('RULED OUT: END');
1505
1620
  return;
1506
1621
  }
1507
1622
 
@@ -1519,13 +1634,16 @@ const LogReviewer: React.FC<Props> = (props) => {
1519
1634
  })
1520
1635
  )
1521
1636
  ) {
1637
+ console.log('RULED OUT: CONTEXT');
1522
1638
  return;
1523
1639
  }
1524
1640
 
1525
1641
  // Subcontext doesn't match
1526
1642
  if (
1643
+ // Log context is not "uncategorized" (no point in further filters)
1644
+ log.context !== LogBuiltInMetadata.Context.Uncategorized
1527
1645
  // Log has a subcontext
1528
- log.subcontext
1646
+ && log.subcontext
1529
1647
  // Context has subcontexts
1530
1648
  && (
1531
1649
  contextFilterState[log.context]
@@ -1535,6 +1653,7 @@ const LogReviewer: React.FC<Props> = (props) => {
1535
1653
  // Subcontext is not selected
1536
1654
  && !(contextFilterState as any)[log.context][log.subcontext]
1537
1655
  ) {
1656
+ console.log('RULED OUT: SUBCONTEXT');
1538
1657
  return;
1539
1658
  }
1540
1659
 
@@ -1542,15 +1661,11 @@ const LogReviewer: React.FC<Props> = (props) => {
1542
1661
 
1543
1662
  // No tags match
1544
1663
  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];
1551
- })
1552
- )
1664
+ log.tags.every((tag) => {
1665
+ return !tagFilterState[tag];
1666
+ })
1553
1667
  ) {
1668
+ console.log('RULED OUT: TAGS');
1554
1669
  return;
1555
1670
  }
1556
1671
 
@@ -1563,6 +1678,7 @@ const LogReviewer: React.FC<Props> = (props) => {
1563
1678
  // Log type doesn't match
1564
1679
  && actionErrorFilterState.type !== log.type
1565
1680
  ) {
1681
+ console.log('RULED OUT: TYPE');
1566
1682
  return;
1567
1683
  }
1568
1684
 
@@ -1579,6 +1695,7 @@ const LogReviewer: React.FC<Props> = (props) => {
1579
1695
  actionErrorFilterState.errorMessage.trim().toLowerCase(),
1580
1696
  )
1581
1697
  ) {
1698
+ console.log('RULED OUT: ERROR MESSAGE');
1582
1699
  return;
1583
1700
  }
1584
1701
 
@@ -1593,6 +1710,7 @@ const LogReviewer: React.FC<Props> = (props) => {
1593
1710
  actionErrorFilterState.errorCode.trim().toUpperCase(),
1594
1711
  )
1595
1712
  ) {
1713
+ console.log('RULED OUT: ERROR CODE');
1596
1714
  return;
1597
1715
  }
1598
1716
  }
@@ -1606,6 +1724,7 @@ const LogReviewer: React.FC<Props> = (props) => {
1606
1724
  // Target isn't selected
1607
1725
  && !actionErrorFilterState.target[log.target]
1608
1726
  ) {
1727
+ console.log('RULED OUT: TARGET');
1609
1728
  return;
1610
1729
  }
1611
1730
 
@@ -1616,6 +1735,7 @@ const LogReviewer: React.FC<Props> = (props) => {
1616
1735
  // Action isn't selected
1617
1736
  && !actionErrorFilterState.action[log.action]
1618
1737
  ) {
1738
+ console.log('RULED OUT: ACTION');
1619
1739
  return;
1620
1740
  }
1621
1741
  }
@@ -1631,6 +1751,7 @@ const LogReviewer: React.FC<Props> = (props) => {
1631
1751
  advancedFilterState.userFirstName.toLowerCase().trim(),
1632
1752
  )
1633
1753
  ) {
1754
+ console.log('RULED OUT: FIRST');
1634
1755
  return;
1635
1756
  }
1636
1757
 
@@ -1643,6 +1764,7 @@ const LogReviewer: React.FC<Props> = (props) => {
1643
1764
  advancedFilterState.userLastName.toLowerCase().trim(),
1644
1765
  )
1645
1766
  ) {
1767
+ console.log('RULED OUT: LAST');
1646
1768
  return;
1647
1769
  }
1648
1770
 
@@ -1655,6 +1777,7 @@ const LogReviewer: React.FC<Props> = (props) => {
1655
1777
  advancedFilterState.userEmail.toLowerCase().trim(),
1656
1778
  )
1657
1779
  ) {
1780
+ console.log('RULED OUT: EMAIL');
1658
1781
  return;
1659
1782
  }
1660
1783
 
@@ -1667,6 +1790,7 @@ const LogReviewer: React.FC<Props> = (props) => {
1667
1790
  advancedFilterState.userId.trim(),
1668
1791
  )
1669
1792
  ) {
1793
+ console.log('RULED OUT: USER ID');
1670
1794
  return;
1671
1795
  }
1672
1796
 
@@ -1677,6 +1801,7 @@ const LogReviewer: React.FC<Props> = (props) => {
1677
1801
  // Learners aren't included
1678
1802
  && !advancedFilterState.includeLearners
1679
1803
  ) {
1804
+ console.log('RULED OUT: LEARNER');
1680
1805
  return;
1681
1806
  }
1682
1807
 
@@ -1687,6 +1812,7 @@ const LogReviewer: React.FC<Props> = (props) => {
1687
1812
  // TTMs aren't included
1688
1813
  && !advancedFilterState.includeTTMs
1689
1814
  ) {
1815
+ console.log('RULED OUT: TTM');
1690
1816
  return;
1691
1817
  }
1692
1818
 
@@ -1697,6 +1823,7 @@ const LogReviewer: React.FC<Props> = (props) => {
1697
1823
  // Admins aren't included
1698
1824
  && !advancedFilterState.includeAdmins
1699
1825
  ) {
1826
+ console.log('RULED OUT: ADMIN');
1700
1827
  return;
1701
1828
  }
1702
1829
 
@@ -1709,6 +1836,7 @@ const LogReviewer: React.FC<Props> = (props) => {
1709
1836
  advancedFilterState.courseId.trim(),
1710
1837
  )
1711
1838
  ) {
1839
+ console.log('RULED OUT: COURSE ID');
1712
1840
  return;
1713
1841
  }
1714
1842
 
@@ -1721,6 +1849,7 @@ const LogReviewer: React.FC<Props> = (props) => {
1721
1849
  advancedFilterState.courseName.trim(),
1722
1850
  )
1723
1851
  ) {
1852
+ console.log('RULED OUT: COURSE NAME');
1724
1853
  return;
1725
1854
  }
1726
1855
 
@@ -1733,6 +1862,7 @@ const LogReviewer: React.FC<Props> = (props) => {
1733
1862
  // Mobile filter doesn't match
1734
1863
  && (advancedFilterState.isMobile === log.device.isMobile)
1735
1864
  ) {
1865
+ console.log('RULED OUT: MOBILE');
1736
1866
  return;
1737
1867
  }
1738
1868
 
@@ -1745,6 +1875,7 @@ const LogReviewer: React.FC<Props> = (props) => {
1745
1875
  // Source filter doesn't match
1746
1876
  && (advancedFilterState.source !== log.source)
1747
1877
  ) {
1878
+ console.log('RULED OUT: SOURCE');
1748
1879
  return;
1749
1880
  }
1750
1881
 
@@ -1757,6 +1888,7 @@ const LogReviewer: React.FC<Props> = (props) => {
1757
1888
  // Route path doesn't match
1758
1889
  && !(log.routePath.includes(advancedFilterState.routePath.trim()))
1759
1890
  ) {
1891
+ console.log('RULED OUT: PATH');
1760
1892
  return;
1761
1893
  }
1762
1894
 
@@ -1769,6 +1901,7 @@ const LogReviewer: React.FC<Props> = (props) => {
1769
1901
  // Route template doesn't match
1770
1902
  && !(log.routeTemplate.includes(advancedFilterState.routeTemplate.trim()))
1771
1903
  ) {
1904
+ console.log('RULED OUT: TEMPLATE');
1772
1905
  return;
1773
1906
  }
1774
1907
 
@@ -1974,19 +2107,40 @@ const LogReviewer: React.FC<Props> = (props) => {
1974
2107
 
1975
2108
  // Create intelliTable
1976
2109
  const dataTable = (
1977
- <IntelliTable
1978
- title="Matching Logs"
1979
- id="logs"
1980
- data={logs}
1981
- columns={columns}
1982
- />
2110
+ logs.length === 0
2111
+ ? (
2112
+ <>
2113
+ <h3 className="m-0">
2114
+ Matching Logs:
2115
+ </h3>
2116
+ <div className="alert alert-warning text-center">
2117
+ <h4 className="m-1">
2118
+ No Logs to Show
2119
+ </h4>
2120
+ <div>
2121
+ Either your filters are too strict or no matching logs have been
2122
+ created yet.
2123
+ </div>
2124
+ </div>
2125
+ </>
2126
+ )
2127
+ : (
2128
+ <IntelliTable
2129
+ title="Matching Logs:"
2130
+ id="logs"
2131
+ data={logs}
2132
+ columns={columns}
2133
+ />
2134
+ )
1983
2135
  );
1984
2136
 
1985
2137
  // Main body
1986
2138
  body = (
1987
2139
  <>
1988
2140
  {filters}
1989
- {dataTable}
2141
+ <div className="mt-2">
2142
+ {dataTable}
2143
+ </div>
1990
2144
  </>
1991
2145
  );
1992
2146
  }
@@ -2001,26 +2155,22 @@ const LogReviewer: React.FC<Props> = (props) => {
2001
2155
  <div className="LogReviewer-inner-container">
2002
2156
  <div className="LogReviewer-header">
2003
2157
  <div className="LogReviewer-header-title">
2004
- <h1 className="m-0">
2158
+ <h3 className="text-center m-0">
2005
2159
  Log Review Dashboard
2006
- </h1>
2160
+ </h3>
2161
+ </div>
2162
+ <div style={{ width: 0 }}>
2163
+ <button
2164
+ type="button"
2165
+ className="LogReviewer-header-close-button btn btn-dark btn-lg pe-0"
2166
+ aria-label="close log reviewer panel"
2167
+ onClick={onClose}
2168
+ >
2169
+ <FontAwesomeIcon
2170
+ icon={faTimes}
2171
+ />
2172
+ </button>
2007
2173
  </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
2174
  </div>
2025
2175
  <div className="LogReviewer-contents">
2026
2176
  {body}