@@ -3,13 +3,16 @@ import { DataTable } from "primereact/datatable";
33import { Column } from "primereact/column" ;
44import Head from "next/head" ;
55import { weatherTemplate , getWeatherIndex } from "../components/weatherTemplate" ;
6+ import { basePath } from "../next.config.js" ;
7+ import { SearchForm } from "../components/searchForm" ;
68
79
810export default function Home ( ) {
9- const [ loading , setLoading ] = useState ( true ) ;
10- const [ jobs , setJobs ] = useState ( [ ] ) ;
11- const [ rows , setRows ] = useState ( [ ] ) ;
12- const [ expandedRows , setExpandedRows ] = useState ( [ ] ) ;
11+ const [ loading , setLoading ] = useState ( true ) ;
12+ const [ jobs , setJobs ] = useState ( [ ] ) ;
13+ const [ rows , setRows ] = useState ( [ ] ) ;
14+ const [ expandedRows , setExpandedRows ] = useState ( [ ] ) ;
15+ const [ keepSearch , setKeepSearch ] = useState ( true ) ;
1316
1417 useEffect ( ( ) => {
1518 const fetchData = async ( ) => {
@@ -42,15 +45,62 @@ export default function Home() {
4245 fetchData ( ) ;
4346 } , [ ] ) ;
4447
48+ // Filters the jobs s.t. all values must be contained in the name.
49+ const matchAll = ( filteredJobs , values ) => {
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 , values ) => {
61+ return filteredJobs . filter ( ( job ) => {
62+ const jobName = job . name . toLowerCase ( ) ;
63+ return values . some ( ( val ) => {
64+ const decodedValue = decodeURIComponent ( val ) . toLowerCase ( ) ;
65+ return jobName . includes ( decodedValue ) ;
66+ } ) ;
67+ } ) ;
68+ } ;
69+
70+ //Filter based on the URL.
71+ const filterURL = ( filteredJobs ) => {
72+ const urlParams = new URLSearchParams ( window . location . search ) ;
73+ switch ( urlParams . get ( "matchMode" ) ) {
74+ case "and" :
75+ filteredJobs = matchAll ( filteredJobs , urlParams . getAll ( "value" ) ) ;
76+ break ;
77+ case "or" :
78+ filteredJobs = matchAny ( filteredJobs , urlParams . getAll ( "value" ) ) ;
79+ break ;
80+ default :
81+ break ;
82+ }
83+ return filteredJobs ;
84+ } ;
85+
4586 useEffect ( ( ) => {
4687 setLoading ( true ) ;
4788
48- // Create rows to set into table.
49- const rows = jobs . map ( ( job ) => ( {
50- ...job ,
51- weather : getWeatherIndex ( job ) ,
52- } ) ) ;
53- setRows ( rows ) ;
89+ //Filter based on the URL.
90+ let filteredJobs = filterURL ( jobs ) ;
91+
92+ //Set the rows for the table.
93+ setRows (
94+ filteredJobs . map ( ( job ) => ( {
95+ name : job . name ,
96+ runs : job . runs ,
97+ fails : job . fails ,
98+ skips : job . skips ,
99+ required : job . required ,
100+ weather : getWeatherIndex ( job ) ,
101+ } ) )
102+ ) ;
103+
54104 setLoading ( false ) ;
55105 } , [ jobs ] ) ;
56106
@@ -67,6 +117,11 @@ export default function Home() {
67117 setExpandedRows ( updatedExpandedRows ) ;
68118 } ;
69119
120+ const buttonClass = ( active ) => `tab md:px-4 px-2 py-2 border-2
121+ ${ active ? "border-blue-500 bg-blue-500 text-white"
122+ : "border-gray-300 bg-white hover:bg-gray-100" } `;
123+
124+
70125 // Template for rendering the Name column as a clickable item
71126 const nameTemplate = ( rowData ) => {
72127 return (
@@ -121,6 +176,39 @@ export default function Home() {
121176 ) ;
122177 } ;
123178
179+ // Apply search terms to the URL and reload the page.
180+ const handleSearch = ( e ) => {
181+ // Prevent the default behavior so that we can keep search terms.
182+ e . preventDefault ( ) ;
183+ const matchMode = e . target . matchMode . value ;
184+ const value = e . target . value . value . trimEnd ( ) ;
185+ if ( value ) {
186+ // Append the new matchMode regardless of if search terms were kept.
187+ const path = new URLSearchParams ( ) ;
188+ path . append ( "matchMode" , matchMode ) ;
189+ if ( keepSearch ) {
190+ // If keepSearch is true, add existing parameters in the URL.
191+ const urlParams = new URLSearchParams ( window . location . search ) ;
192+ urlParams . getAll ( "value" ) . forEach ( ( val ) => {
193+ path . append ( "value" , val ) ;
194+ } ) ;
195+ }
196+ //Add the search term from the form and redirect.
197+ path . append ( "value" , value ) ;
198+ window . location . assign ( `${ basePath } /?${ path . toString ( ) } ` ) ;
199+ }
200+ } ;
201+
202+ // Clear the search parameters, but only if they exist.
203+ const clearSearch = ( ) => {
204+ const urlParts = window . location . href . split ( "?" ) ;
205+ if ( urlParts [ 1 ] !== undefined ) {
206+ window . location . assign ( urlParts [ 0 ] ) ;
207+ }
208+ }
209+
210+
211+
124212 const renderTable = ( ) => (
125213 < DataTable
126214 value = { rows }
@@ -185,9 +273,27 @@ export default function Home() {
185273 "m-0 h-full p-4 overflow-x-hidden overflow-y-auto bg-surface-ground font-normal text-text-color antialiased select-text"
186274 }
187275 >
188- < div className = "mt-4 text-lg" > Total Rows: { rows . length } </ div >
276+ < div className = "space-x-2 mx-auto" >
277+ < button
278+ className = { buttonClass ( ) }
279+ onClick = { ( ) => clearSearch ( ) } >
280+ Clear Search
281+ </ button >
282+ < button
283+ className = { buttonClass ( keepSearch ) }
284+ onClick = { ( ) => setKeepSearch ( ! keepSearch ) } >
285+ Keep URL Search Terms
286+ </ button >
287+ </ div >
288+
289+ < SearchForm handleSearch = { handleSearch } />
290+
291+ < div className = "mt-1 text-center md:text-lg text-base" >
292+ Total Rows: { rows . length }
293+ </ div >
294+
189295 < div > { renderTable ( ) } </ div >
190296 </ main >
191297 </ div >
192298 ) ;
193- }
299+ }
0 commit comments