@@ -2,20 +2,23 @@ import { useEffect, useState } from "react";
22import { DataTable } from "primereact/datatable" ;
33import { Column } from "primereact/column" ;
44import { weatherTemplate , getWeatherIndex } from "../components/weatherTemplate" ;
5+ import { basePath } from "../next.config.js" ;
6+ import { SearchForm } from "../components/searchForm" ;
57
68
79export default function Home ( ) {
8- const [ loading , setLoading ] = useState ( true ) ;
9- const [ jobs , setJobs ] = useState ( [ ] ) ;
10- const [ rows , setRows ] = useState ( [ ] ) ;
11- const [ expandedRows , setExpandedRows ] = useState ( [ ] ) ;
10+ const [ loading , setLoading ] = useState ( true ) ;
11+ const [ jobs , setJobs ] = useState ( [ ] ) ;
12+ const [ rows , setRows ] = useState ( [ ] ) ;
13+ const [ expandedRows , setExpandedRows ] = useState ( [ ] ) ;
14+ const [ keepSearch , setKeepSearch ] = useState ( true ) ;
1215
1316 useEffect ( ( ) => {
1417 const fetchData = async ( ) => {
1518 let data = { } ;
1619
1720 if ( process . env . NODE_ENV === "development" ) {
18- data = ( await import ( "../job_stats.json" ) ) . default ;
21+ data = ( await import ( "../localData/ job_stats.json" ) ) . default ;
1922 } else {
2023 const response = await fetch (
2124 "https://raw.githubusercontent.com/kata-containers/kata-containers.github.io" +
@@ -41,15 +44,59 @@ export default function Home() {
4144 fetchData ( ) ;
4245 } , [ ] ) ;
4346
47+ // Filters the jobs s.t. all values must be contained in the name.
48+ const matchAll = ( filteredJobs , values ) => {
49+ return filteredJobs . filter ( ( job ) => {
50+ const jobName = job . name . toLowerCase ( ) ;
51+ return values . every ( ( val ) => {
52+ const decodedValue = decodeURIComponent ( val ) . toLowerCase ( ) ;
53+ return jobName . includes ( decodedValue ) ;
54+ } ) ;
55+ } ) ;
56+ } ;
57+
58+ // Filters the jobs s.t. at least one value must be contained in the name.
59+ const matchAny = ( filteredJobs , values ) => {
60+ return filteredJobs . filter ( ( job ) => {
61+ const jobName = job . name . toLowerCase ( ) ;
62+ return values . some ( ( val ) => {
63+ const decodedValue = decodeURIComponent ( val ) . toLowerCase ( ) ;
64+ return jobName . includes ( decodedValue ) ;
65+ } ) ;
66+ } ) ;
67+ } ;
68+
69+
4470 useEffect ( ( ) => {
4571 setLoading ( true ) ;
72+ let filteredJobs = jobs ;
73+
74+ //Filter based on the URL.
75+ const urlParams = new URLSearchParams ( window . location . search ) ;
76+ switch ( urlParams . get ( "matchMode" ) ) {
77+ case "and" :
78+ filteredJobs = matchAll ( filteredJobs , urlParams . getAll ( "value" ) ) ;
79+ break ;
80+ case "or" :
81+ filteredJobs = matchAny ( filteredJobs , urlParams . getAll ( "value" ) ) ;
82+ break ;
83+ default :
84+ break ;
85+ }
86+
87+
88+ //Set the rows for the table.
89+ setRows (
90+ filteredJobs . map ( ( job ) => ( {
91+ name : job . name ,
92+ runs : job . runs ,
93+ fails : job . fails ,
94+ skips : job . skips ,
95+ required : job . required ,
96+ weather : getWeatherIndex ( job ) ,
97+ } ) )
98+ ) ;
4699
47- // Create rows to set into table.
48- const rows = jobs . map ( ( job ) => ( {
49- ...job ,
50- weather : getWeatherIndex ( job ) ,
51- } ) ) ;
52- setRows ( rows ) ;
53100 setLoading ( false ) ;
54101 } , [ jobs ] ) ;
55102
@@ -66,6 +113,11 @@ export default function Home() {
66113 setExpandedRows ( updatedExpandedRows ) ;
67114 } ;
68115
116+ const buttonClass = ( active ) => `tab md:px-4 px-2 py-2 border-2
117+ ${ active ? "border-blue-500 bg-blue-500 text-white"
118+ : "border-gray-300 bg-white hover:bg-gray-100" } `;
119+
120+
69121 // Template for rendering the Name column as a clickable item
70122 const nameTemplate = ( rowData ) => {
71123 return (
@@ -120,6 +172,39 @@ export default function Home() {
120172 ) ;
121173 } ;
122174
175+ // Apply search terms to the URL and reload the page.
176+ const handleSearch = ( e ) => {
177+ // Prevent the default behavior so that we can keep search terms.
178+ e . preventDefault ( ) ;
179+ const matchMode = e . target . matchMode . value ;
180+ const value = e . target . value . value . trimEnd ( ) ;
181+ if ( value ) {
182+ // Append the new matchMode regardless of if search terms were kept.
183+ const path = new URLSearchParams ( ) ;
184+ path . append ( "matchMode" , matchMode ) ;
185+ if ( keepSearch ) {
186+ // If keepSearch is true, add existing parameters in the URL.
187+ const urlParams = new URLSearchParams ( window . location . search ) ;
188+ urlParams . getAll ( "value" ) . forEach ( ( val ) => {
189+ path . append ( "value" , val ) ;
190+ } ) ;
191+ }
192+ //Add the search term from the form and redirect.
193+ path . append ( "value" , value ) ;
194+ window . location . assign ( `${ basePath } /?${ path . toString ( ) } ` ) ;
195+ }
196+ } ;
197+
198+ // Clear the search parameters, but only if they exist.
199+ const clearSearch = ( ) => {
200+ const urlParts = window . location . href . split ( "?" ) ;
201+ if ( urlParts [ 1 ] !== undefined ) {
202+ window . location . assign ( urlParts [ 0 ] ) ;
203+ }
204+ }
205+
206+
207+
123208 const renderTable = ( ) => (
124209 < DataTable
125210 value = { rows }
@@ -178,9 +263,28 @@ export default function Home() {
178263 "m-0 h-full p-4 overflow-x-hidden overflow-y-auto bg-surface-ground font-normal text-text-color antialiased select-text"
179264 }
180265 >
266+ < div className = "space-x-2 mx-auto" >
267+ < button
268+ className = { buttonClass ( ) }
269+ onClick = { ( ) => clearSearch ( ) } >
270+ Clear Search
271+ </ button >
272+ < button
273+ className = { buttonClass ( keepSearch ) }
274+ onClick = { ( ) => setKeepSearch ( ! keepSearch ) } >
275+ Keep URL Search Terms
276+ </ button >
277+ </ div >
278+
279+ < SearchForm handleSearch = { handleSearch } />
280+
281+ < div className = "mt-1 text-center md:text-lg text-base" >
282+ Total Rows: { rows . length }
283+ </ div >
284+
181285 < div > { renderTable ( ) } </ div >
182286 < div className = "mt-4 text-lg" > Total Rows: { rows . length } </ div >
183287 </ main >
184288 </ div >
185289 ) ;
186- }
290+ }
0 commit comments