1
+
1
2
import { useEffect , useState } from "react" ;
2
3
import { DataTable } from "primereact/datatable" ;
3
4
import { Column } from "primereact/column" ;
4
5
import { weatherTemplate , getWeatherIndex } from "../components/weatherTemplate" ;
6
+ import { basePath } from "../next.config.js" ;
5
7
6
8
7
9
export default function Home ( ) {
8
10
const [ loading , setLoading ] = useState ( true ) ;
9
11
const [ jobs , setJobs ] = useState ( [ ] ) ;
10
12
const [ rows , setRows ] = useState ( [ ] ) ;
11
13
const [ expandedRows , setExpandedRows ] = useState ( [ ] ) ;
14
+ const [ keepSearch , setKeepSearch ] = useState ( true ) ;
12
15
13
16
useEffect ( ( ) => {
14
17
const fetchData = async ( ) => {
15
18
let data = { } ;
16
19
17
20
if ( process . env . NODE_ENV === "development" ) {
18
- data = ( await import ( "../job_stats.json" ) ) . default ;
21
+ data = ( await import ( "../data/ job_stats.json" ) ) . default ;
19
22
} else {
20
23
const response = await fetch (
21
24
"https://raw.githubusercontent.com/kata-containers/kata-containers.github.io" +
@@ -41,15 +44,55 @@ export default function Home() {
41
44
fetchData ( ) ;
42
45
} , [ ] ) ;
43
46
47
+ // Filters the jobs s.t. all values must be contained in the name.
48
+ const matchAll = ( filteredJobs , urlParams ) => {
49
+ const values = urlParams . getAll ( "value" ) ;
50
+ return filteredJobs . filter ( ( job ) => {
51
+ const jobName = job . name . toLowerCase ( ) ;
52
+ return values . every ( ( val ) => {
53
+ const decodedValue = decodeURIComponent ( val ) . toLowerCase ( ) ;
54
+ return jobName . includes ( decodedValue ) ;
55
+ } ) ;
56
+ } ) ;
57
+ } ;
58
+
59
+ // Filters the jobs s.t. at least one value must be contained in the name.
60
+ const matchAny = ( filteredJobs , urlParams ) => {
61
+ const values = urlParams . getAll ( "value" ) ;
62
+ return filteredJobs . filter ( ( job ) => {
63
+ const jobName = job . name . toLowerCase ( ) ;
64
+ return values . some ( ( val ) => {
65
+ const decodedValue = decodeURIComponent ( val ) . toLowerCase ( ) ;
66
+ return jobName . includes ( decodedValue ) ;
67
+ } ) ;
68
+ } ) ;
69
+ } ;
70
+
71
+
44
72
useEffect ( ( ) => {
45
73
setLoading ( true ) ;
74
+ let filteredJobs = jobs ;
75
+
76
+ //Filter based on the URL.
77
+ const urlParams = new URLSearchParams ( window . location . search ) ;
78
+ if ( urlParams . get ( "matchMode" ) === "and" ) {
79
+ filteredJobs = matchAll ( filteredJobs , urlParams ) ;
80
+ } else if ( urlParams . get ( "matchMode" ) === "or" ) {
81
+ filteredJobs = matchAny ( filteredJobs , urlParams ) ;
82
+ }
83
+
84
+ //Set the rows for the table.
85
+ setRows (
86
+ filteredJobs . map ( ( job ) => ( {
87
+ name : job . name ,
88
+ runs : job . runs ,
89
+ fails : job . fails ,
90
+ skips : job . skips ,
91
+ required : job . required ,
92
+ weather : getWeatherIndex ( job ) ,
93
+ } ) )
94
+ ) ;
46
95
47
- // Create rows to set into table.
48
- const rows = jobs . map ( ( job ) => ( {
49
- ...job ,
50
- weather : getWeatherIndex ( job ) ,
51
- } ) ) ;
52
- setRows ( rows ) ;
53
96
setLoading ( false ) ;
54
97
} , [ jobs ] ) ;
55
98
@@ -66,6 +109,11 @@ export default function Home() {
66
109
setExpandedRows ( updatedExpandedRows ) ;
67
110
} ;
68
111
112
+ const buttonClass = ( active ) => `tab md:px-4 px-2 py-2 border-2
113
+ ${ active ? "border-blue-500 bg-blue-500 text-white"
114
+ : "border-gray-300 bg-white hover:bg-gray-100" } `;
115
+
116
+
69
117
// Template for rendering the Name column as a clickable item
70
118
const nameTemplate = ( rowData ) => {
71
119
return (
@@ -120,6 +168,39 @@ export default function Home() {
120
168
) ;
121
169
} ;
122
170
171
+ // Apply search terms to the URL and reload the page.
172
+ const handleSearch = ( e ) => {
173
+ // Prevent the default behavior so that we can keep search terms.
174
+ e . preventDefault ( ) ;
175
+ const matchMode = e . target . matchMode . value ;
176
+ const value = e . target . value . value . trimEnd ( ) ;
177
+ if ( value ) {
178
+ // Append the new matchMode regardless of if search terms were kept.
179
+ const path = new URLSearchParams ( ) ;
180
+ path . append ( "matchMode" , matchMode ) ;
181
+ if ( keepSearch ) {
182
+ // If keepSearch is true, add existing parameters in the URL.
183
+ const urlParams = new URLSearchParams ( window . location . search ) ;
184
+ urlParams . getAll ( "value" ) . forEach ( ( val ) => {
185
+ path . append ( "value" , val ) ;
186
+ } ) ;
187
+ }
188
+ //Add the search term from the form and redirect.
189
+ path . append ( "value" , encodeURIComponent ( value ) ) ;
190
+ window . location . assign ( `${ basePath } /?${ path . toString ( ) } ` ) ;
191
+ }
192
+ } ;
193
+
194
+ // Clear the search parameters, but only if they exist.
195
+ const clearSearch = ( ) => {
196
+ const urlParts = window . location . href . split ( "?" ) ;
197
+ if ( urlParts [ 1 ] !== undefined ) {
198
+ window . location . assign ( urlParts [ 0 ] ) ;
199
+ }
200
+ }
201
+
202
+
203
+
123
204
const renderTable = ( ) => (
124
205
< DataTable
125
206
value = { rows }
@@ -178,9 +259,46 @@ export default function Home() {
178
259
"m-0 h-full p-4 overflow-x-hidden overflow-y-auto bg-surface-ground font-normal text-text-color antialiased select-text"
179
260
}
180
261
>
262
+ < div className = "space-x-2 mx-auto" >
263
+ < button
264
+ className = { buttonClass ( ) }
265
+ onClick = { ( ) => clearSearch ( ) } >
266
+ Clear Search
267
+ </ button >
268
+ < button
269
+ className = { buttonClass ( keepSearch ) }
270
+ onClick = { ( ) => setKeepSearch ( ! keepSearch ) } >
271
+ Keep URL Search Terms
272
+ </ button >
273
+ </ div >
274
+
275
+ < div className = "flex flex-col items-center md:text-base text-xs" >
276
+ < div className = "flex min-[1126px]:justify-end justify-center w-full" >
277
+ < form className = "p-2 bg-gray-700 rounded-md flex flex-row" onSubmit = { ( e ) => handleSearch ( e ) } >
278
+ < div >
279
+ < label className = "block text-white" > Match Mode:</ label >
280
+ < select name = "matchMode" className = "px-1 h-fit rounded-lg" >
281
+ < option value = "or" > Match Any</ option >
282
+ < option value = "and" > Match All</ option >
283
+ </ select >
284
+ </ div >
285
+ < div className = "mx-2" >
286
+ < label className = "block text-white" > Search Text:</ label >
287
+ < input type = "text" name = "value" required > </ input >
288
+ </ div >
289
+ < button type = "submit" className = "bg-blue-500 text-white px-4 rounded-3xl" > Submit</ button >
290
+ </ form >
291
+ </ div >
292
+ </ div >
293
+
294
+ < div className = "mt-1 text-center md:text-lg text-base" >
295
+ Total Rows: { rows . length }
296
+ </ div >
297
+
298
+
181
299
< div > { renderTable ( ) } </ div >
182
300
< div className = "mt-4 text-lg" > Total Rows: { rows . length } </ div >
183
301
</ main >
184
302
</ div >
185
303
) ;
186
- }
304
+ }
0 commit comments