dce-reactkit 4.0.1 → 4.1.0

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.
Files changed (37) hide show
  1. package/dist/cjs/index.js +662 -771
  2. package/dist/cjs/index.js.map +1 -1
  3. package/dist/cjs/types/components/Pagination.d.ts +13 -0
  4. package/dist/cjs/types/constants/LOG_REVIEW_GET_LOGS_ROUTE.d.ts +6 -0
  5. package/dist/cjs/types/helpers/cloneDeep.d.ts +8 -0
  6. package/dist/cjs/types/index.d.ts +4 -1
  7. package/dist/cjs/types/types/LogReviewerFilterState/ActionErrorFilterState.d.ts +17 -0
  8. package/dist/cjs/types/types/LogReviewerFilterState/AdvancedFilterState.d.ts +21 -0
  9. package/dist/cjs/types/types/LogReviewerFilterState/ContextFilterState.d.ts +10 -0
  10. package/dist/cjs/types/types/LogReviewerFilterState/DateFilterState.d.ts +17 -0
  11. package/dist/cjs/types/types/LogReviewerFilterState/TagFilterState.d.ts +8 -0
  12. package/dist/cjs/types/types/LogReviewerFilterState/index.d.ts +17 -0
  13. package/dist/esm/index.js +661 -773
  14. package/dist/esm/index.js.map +1 -1
  15. package/dist/esm/types/components/Pagination.d.ts +13 -0
  16. package/dist/esm/types/constants/LOG_REVIEW_GET_LOGS_ROUTE.d.ts +6 -0
  17. package/dist/esm/types/helpers/cloneDeep.d.ts +8 -0
  18. package/dist/esm/types/index.d.ts +4 -1
  19. package/dist/esm/types/types/LogReviewerFilterState/ActionErrorFilterState.d.ts +17 -0
  20. package/dist/esm/types/types/LogReviewerFilterState/AdvancedFilterState.d.ts +21 -0
  21. package/dist/esm/types/types/LogReviewerFilterState/ContextFilterState.d.ts +10 -0
  22. package/dist/esm/types/types/LogReviewerFilterState/DateFilterState.d.ts +17 -0
  23. package/dist/esm/types/types/LogReviewerFilterState/TagFilterState.d.ts +8 -0
  24. package/dist/esm/types/types/LogReviewerFilterState/index.d.ts +17 -0
  25. package/dist/index.d.ts +98 -1
  26. package/package.json +2 -1
  27. package/src/components/LogReviewer.tsx +1133 -1307
  28. package/src/components/Pagination.tsx +172 -0
  29. package/src/constants/LOG_REVIEW_GET_LOGS_ROUTE.ts +9 -0
  30. package/src/helpers/cloneDeep.ts +11 -0
  31. package/src/index.ts +6 -0
  32. package/src/types/LogReviewerFilterState/ActionErrorFilterState.ts +24 -0
  33. package/src/types/LogReviewerFilterState/AdvancedFilterState.ts +36 -0
  34. package/src/types/LogReviewerFilterState/ContextFilterState.ts +16 -0
  35. package/src/types/LogReviewerFilterState/DateFilterState.ts +26 -0
  36. package/src/types/LogReviewerFilterState/TagFilterState.ts +9 -0
  37. package/src/types/LogReviewerFilterState/index.ts +24 -0
@@ -0,0 +1,172 @@
1
+ // Import React
2
+ import React from 'react';
3
+
4
+ // Import FontAwesome
5
+ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
6
+ import { faArrowLeft, faArrowRight } from '@fortawesome/free-solid-svg-icons';
7
+
8
+ /*------------------------------------------------------------------------*/
9
+ /* -------------------------------- Types ------------------------------- */
10
+ /*------------------------------------------------------------------------*/
11
+
12
+ // Props
13
+ type Props = {
14
+ // Current page number (1-indexed)
15
+ currentPage: number,
16
+ // Total number of pages
17
+ numPages: number,
18
+ // True if a page change is in progress (to disable buttons)
19
+ loading?: boolean,
20
+ /**
21
+ * Handler for when page is changed
22
+ * @param page - the new page number
23
+ */
24
+ onPageChanged: (page: number) => void,
25
+ };
26
+
27
+ /*------------------------------------------------------------------------*/
28
+ /* ------------------------------ Component ----------------------------- */
29
+ /*------------------------------------------------------------------------*/
30
+
31
+ const Pagination: React.FC<Props> = (props: Props) => {
32
+ /*------------------------------------------------------------------------*/
33
+ /* -------------------------------- Setup ------------------------------- */
34
+ /*------------------------------------------------------------------------*/
35
+
36
+ /* -------------- Props ------------- */
37
+
38
+ // Destructure props
39
+ const {
40
+ currentPage,
41
+ numPages,
42
+ loading = false,
43
+ onPageChanged,
44
+ } = props;
45
+
46
+ /*------------------------------------------------------------------------*/
47
+ /* ------------------------------- Render ------------------------------- */
48
+ /*------------------------------------------------------------------------*/
49
+
50
+ // Compute pages to display
51
+ const pages: number[] = [];
52
+ const delta = 2; // how many pages to show on either side of current
53
+ let start = Math.max(1, currentPage - delta);
54
+ let end = Math.min(numPages, currentPage + delta);
55
+
56
+ // If we are too close to the beginning or end, shift the window.
57
+ if (currentPage - delta < 1) {
58
+ end = Math.min(numPages, end + (1 - (currentPage - delta)));
59
+ }
60
+ if (currentPage + delta > numPages) {
61
+ start = Math.max(1, start - ((currentPage + delta) - numPages));
62
+ }
63
+ for (let i = start; i <= end; i++) {
64
+ pages.push(i);
65
+ }
66
+
67
+ // Render
68
+ return (
69
+ <nav
70
+ aria-label="Page navigation for logs"
71
+ className="mt-3"
72
+ >
73
+ <ul className="pagination justify-content-center">
74
+ {/* Previous Button */}
75
+ <li className={`page-item ${(currentPage <= 1 || loading) ? 'disabled' : ''}`}>
76
+ <button
77
+ type="button"
78
+ className="page-link"
79
+ onClick={() => { return onPageChanged(currentPage - 1); }}
80
+ disabled={currentPage <= 1 || loading}
81
+ aria-label="Go to previous page"
82
+ >
83
+ <FontAwesomeIcon icon={faArrowLeft} />
84
+ {' '}
85
+ Prev
86
+ </button>
87
+ </li>
88
+
89
+ {/* First page (if needed) */}
90
+ {(currentPage > 3 && pages[0] !== 1) && (
91
+ <li className="page-item">
92
+ <button
93
+ type="button"
94
+ className="page-link"
95
+ onClick={() => { return onPageChanged(1); }}
96
+ disabled={loading}
97
+ aria-label="Go to page 1"
98
+ >
99
+ 1
100
+ </button>
101
+ </li>
102
+ )}
103
+
104
+ {/* Ellipsis if gap between first page and start of window */}
105
+ {currentPage > 4 && (
106
+ <li className="page-item disabled">
107
+ <span className="page-link">...</span>
108
+ </li>
109
+ )}
110
+
111
+ {/* Page numbers */}
112
+ {pages.map((pageNum) => {
113
+ return (
114
+ <li key={pageNum} className={`page-item ${pageNum === currentPage ? 'active' : ''}`}>
115
+ <button
116
+ type="button"
117
+ className="page-link"
118
+ onClick={() => { return onPageChanged(pageNum); }}
119
+ disabled={loading}
120
+ aria-label={`Go to page ${pageNum}`}
121
+ >
122
+ {pageNum}
123
+ </button>
124
+ </li>
125
+ );
126
+ })}
127
+
128
+ {/* Ellipsis if gap between end of window and last page */}
129
+ {currentPage < numPages - 3 && (
130
+ <li className="page-item disabled">
131
+ <span className="page-link">...</span>
132
+ </li>
133
+ )}
134
+
135
+ {/* Last page (if needed) */}
136
+ {(
137
+ currentPage < numPages - 2
138
+ && pages[pages.length - 1] !== numPages
139
+ ) && (
140
+ <li className="page-item">
141
+ <button
142
+ type="button"
143
+ className="page-link"
144
+ onClick={() => { return onPageChanged(numPages); }}
145
+ disabled={loading}
146
+ aria-label="Go to last page"
147
+ >
148
+ {numPages}
149
+ </button>
150
+ </li>
151
+ )}
152
+
153
+ {/* Next Button */}
154
+ <li className={`page-item ${(currentPage >= numPages || loading) ? 'disabled' : ''}`}>
155
+ <button
156
+ type="button"
157
+ className="page-link"
158
+ onClick={() => { return onPageChanged(currentPage + 1); }}
159
+ disabled={currentPage >= numPages || loading}
160
+ aria-label="Go to next page"
161
+ >
162
+ Next
163
+ {' '}
164
+ <FontAwesomeIcon icon={faArrowRight} />
165
+ </button>
166
+ </li>
167
+ </ul>
168
+ </nav>
169
+ );
170
+ };
171
+
172
+ export default Pagination;
@@ -0,0 +1,9 @@
1
+ import ROUTE_PATH_PREFIX from './ROUTE_PATH_PREFIX';
2
+
3
+ /**
4
+ * Path of the route for getting logs for log review
5
+ * @author Gabe Abrams
6
+ */
7
+ const LOG_REVIEW_GET_LOGS_ROUTE = `/admin${ROUTE_PATH_PREFIX}/logs`;
8
+
9
+ export default LOG_REVIEW_GET_LOGS_ROUTE;
@@ -0,0 +1,11 @@
1
+ import clone from 'nanoclone';
2
+
3
+ /**
4
+ * Deeply clones an object
5
+ * @author Yuen Ler Chow
6
+ * @param obj the object to clone
7
+ * @returns a deep clone of the object
8
+ */
9
+ const cloneDeep: <T>(obj: T) => T = clone;
10
+
11
+ export default cloneDeep;
package/src/index.ts CHANGED
@@ -42,6 +42,7 @@ import DAY_IN_MS from './constants/DAY_IN_MS';
42
42
  import LOG_REVIEW_ROUTE_PATH_PREFIX from './constants/LOG_REVIEW_ROUTE_PATH_PREFIX';
43
43
  import LOG_ROUTE_PATH from './constants/LOG_ROUTE_PATH';
44
44
  import LOG_REVIEW_STATUS_ROUTE from './constants/LOG_REVIEW_STATUS_ROUTE';
45
+ import LOG_REVIEW_GET_LOGS_ROUTE from './constants/LOG_REVIEW_GET_LOGS_ROUTE';
45
46
 
46
47
  // Import dynamic constants
47
48
  import DynamicWord from './dynamicConstants/DynamicWord';
@@ -93,6 +94,7 @@ import someAsync from './helpers/asyncArrayFunctions/someAsync';
93
94
  import capitalize from './helpers/capitalize';
94
95
  import shuffleArray from './helpers/shuffleArray';
95
96
  import getWordCount from './helpers/getWordCount';
97
+ import cloneDeep from './helpers/cloneDeep';
96
98
 
97
99
  // Import types
98
100
  import ParamType from './types/ParamType';
@@ -115,6 +117,7 @@ import LogLevel from './types/LogLevel';
115
117
  import IntelliTableColumn from './types/IntelliTableColumn';
116
118
  import DropdownItemType from './types/DropdownItemType';
117
119
  import ReactKitErrorCode from './types/ReactKitErrorCode';
120
+ import LogReviewerFilterState from './types/LogReviewerFilterState';
118
121
 
119
122
  // Component-specific-types
120
123
  import PickableItem from './components/ItemPicker/types/PickableItem';
@@ -164,6 +167,7 @@ export {
164
167
  LOG_REVIEW_ROUTE_PATH_PREFIX,
165
168
  LOG_ROUTE_PATH,
166
169
  LOG_REVIEW_STATUS_ROUTE,
170
+ LOG_REVIEW_GET_LOGS_ROUTE,
167
171
  // Dynamic Constants
168
172
  DynamicWord,
169
173
  // Helpers
@@ -208,6 +212,7 @@ export {
208
212
  capitalize,
209
213
  shuffleArray,
210
214
  getWordCount,
215
+ cloneDeep,
211
216
  // Client helpers
212
217
  initClient,
213
218
  visitServerEndpoint,
@@ -238,6 +243,7 @@ export {
238
243
  LogLevel,
239
244
  IntelliTableColumn,
240
245
  DropdownItemType,
246
+ LogReviewerFilterState,
241
247
  // Component-specific-types
242
248
  PickableItem,
243
249
  DBEntry,
@@ -0,0 +1,24 @@
1
+ import LogType from '../LogType';
2
+
3
+ /**
4
+ * Action filter state (only relevant for action logs)
5
+ * @author Yuen Ler Chow
6
+ */
7
+ type ActionErrorFilterState = {
8
+ // Required type of log
9
+ type: LogType | undefined, // If undefined, no filter applied
10
+ // Query for error message (only relevant if type is error)
11
+ errorMessage: string, // If empty, no filter applied
12
+ // Query for error code (only relevant if type is error)
13
+ errorCode: string, // If empty, no filter applied
14
+ // Action targets to include (only relevant if type is action)
15
+ target: {
16
+ [k: string]: boolean
17
+ },
18
+ // Action types to include (only relevant if type is action)
19
+ action: {
20
+ [k: string]: boolean
21
+ },
22
+ };
23
+
24
+ export default ActionErrorFilterState;
@@ -0,0 +1,36 @@
1
+ import LogSource from '../LogSource';
2
+
3
+ /**
4
+ * Advanced filter state
5
+ * @author Yuen Ler Chow
6
+ */
7
+ type AdvancedFilterState = {
8
+ // Query for user first name (case insensitive)
9
+ userFirstName: string, // If empty, no filter applied
10
+ // Query for user last name (case insensitive)
11
+ userLastName: string, // If empty, no filter applied
12
+ // Query for user email (case insensitive)
13
+ userEmail: string, // If empty, no filter applied
14
+ // Match for userId (numerical)
15
+ userId: string, // If empty, no filter applied
16
+ // If true, include students
17
+ includeLearners: boolean,
18
+ // If true, include ttms
19
+ includeTTMs: boolean,
20
+ // If true, include admins
21
+ includeAdmins: boolean,
22
+ // Match for courseId (numerical)
23
+ courseId: string, // If empty, no filter applied
24
+ // Query for course name (case insensitive)
25
+ courseName: string, // If empty, no filter applied
26
+ // Required isMobile value
27
+ isMobile: (true | false | undefined), // If undefined, no filter applied
28
+ // Required log source value
29
+ source: LogSource | undefined, // If undefined, no filter applied
30
+ // Query for route path (only relevant if source is server)
31
+ routePath: string, // If empty, no filter applied
32
+ // Query for route template (only relevant if source is server)
33
+ routeTemplate: string, // If empty, no filter applied
34
+ };
35
+
36
+ export default AdvancedFilterState;
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Context filter state
3
+ * @author Yuen Ler Chow
4
+ */
5
+ type ContextFilterState = {
6
+ [k: string]: (
7
+ // No subcontexts
8
+ | boolean // True if selected
9
+ // Includes subcontexts
10
+ | {
11
+ [k: string]: boolean // True if selected
12
+ }
13
+ )
14
+ };
15
+
16
+ export default ContextFilterState;
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Date filter state
3
+ * @author Yuen Ler Chow
4
+ */
5
+ type DateFilterState = {
6
+ // Current start date
7
+ startDate: {
8
+ // Full year
9
+ year: number,
10
+ // 1-indexed month
11
+ month: number,
12
+ // 1-indexed day
13
+ day: number,
14
+ },
15
+ // Current end date
16
+ endDate: {
17
+ // Full year
18
+ year: number,
19
+ // 1-indexed month
20
+ month: number,
21
+ // 1-indexed day
22
+ day: number,
23
+ },
24
+ };
25
+
26
+ export default DateFilterState;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Tag filter state
3
+ * @author Yuen Ler Chow
4
+ */
5
+ type TagFilterState = {
6
+ [k: string]: boolean // tag => true if in the list of tags to show
7
+ };
8
+
9
+ export default TagFilterState;
@@ -0,0 +1,24 @@
1
+ import DateFilterState from './DateFilterState';
2
+ import ContextFilterState from './ContextFilterState';
3
+ import TagFilterState from './TagFilterState';
4
+ import ActionErrorFilterState from './ActionErrorFilterState';
5
+ import AdvancedFilterState from './AdvancedFilterState';
6
+
7
+ /**
8
+ * A bundle of filter state objects for each type of filter
9
+ * @author Gabe Abrams
10
+ */
11
+ type LogReviewerFilterState = {
12
+ // Date filter state
13
+ dateFilterState: DateFilterState,
14
+ // Context filter state
15
+ contextFilterState: ContextFilterState,
16
+ // Tag filter state
17
+ tagFilterState: TagFilterState,
18
+ // Action error filter state
19
+ actionErrorFilterState: ActionErrorFilterState,
20
+ // Advanced filter state
21
+ advancedFilterState: AdvancedFilterState,
22
+ };
23
+
24
+ export default LogReviewerFilterState;