1+ 
12import  {  useEffect ,  useState  }  from  "react" ; 
23import  {  DataTable  }  from  "primereact/datatable" ; 
34import  {  Column  }  from  "primereact/column" ; 
45import  {  weatherTemplate ,  getWeatherIndex  }  from  "../components/weatherTemplate" ; 
6+ import  {  basePath  }  from  "../next.config.js" ; 
57
68
79export  default  function  Home ( )  { 
810  const  [ loading ,  setLoading ]  =  useState ( true ) ; 
911  const  [ jobs ,  setJobs ]  =  useState ( [ ] ) ; 
1012  const  [ rows ,  setRows ]  =  useState ( [ ] ) ; 
1113  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 ( "../data/ 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,55 @@ 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 ,  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+ 
4472  useEffect ( ( )  =>  { 
4573    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+     ) ; 
4695
47-     // Create rows to set into table. 
48-     const  rows  =  jobs . map ( ( job )  =>  ( { 
49-       ...job , 
50-       weather : getWeatherIndex ( job ) , 
51-     } ) ) ; 
52-     setRows ( rows ) ; 
5396    setLoading ( false ) ; 
5497  } ,  [ jobs ] ) ; 
5598
@@ -66,6 +109,11 @@ export default function Home() {
66109    setExpandedRows ( updatedExpandedRows ) ; 
67110  } ; 
68111
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+ 
69117  // Template for rendering the Name column as a clickable item 
70118  const  nameTemplate  =  ( rowData )  =>  { 
71119    return  ( 
@@ -120,6 +168,39 @@ export default function Home() {
120168    ) ; 
121169  } ; 
122170
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+ 
123204  const  renderTable  =  ( )  =>  ( 
124205    < DataTable 
125206      value = { rows } 
@@ -178,9 +259,46 @@ export default function Home() {
178259          "m-0 h-full p-4 overflow-x-hidden overflow-y-auto bg-surface-ground font-normal text-text-color antialiased select-text" 
179260        } 
180261      > 
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+ 
181299        < div > { renderTable ( ) } </ div > 
182300        < div  className = "mt-4 text-lg" > Total Rows: { rows . length } </ div > 
183301      </ main > 
184302    </ div > 
185303  ) ; 
186- } 
304+ } 
0 commit comments