@@ -5,6 +5,8 @@ import Head from "next/head";
55import { weatherTemplate , getWeatherIndex } from "../components/weatherTemplate" ;
66import { OverlayPanel } from 'primereact/overlaypanel' ;
77import MaintainerMapping from "../maintainers.yml" ;
8+ import { basePath } from "../next.config.js" ;
9+ import { SearchForm } from "../components/searchForm" ;
810
911
1012export default function Home ( ) {
@@ -13,6 +15,7 @@ export default function Home() {
1315 const [ rows , setRows ] = useState ( [ ] ) ;
1416 const [ expandedRows , setExpandedRows ] = useState ( [ ] ) ;
1517 const [ requiredFilter , setRequiredFilter ] = useState ( false ) ;
18+ const [ keepSearch , setKeepSearch ] = useState ( true ) ;
1619
1720 useEffect ( ( ) => {
1821 const fetchData = async ( ) => {
@@ -52,13 +55,51 @@ export default function Home() {
5255 filteredJobs = filteredJobs . filter ( ( job ) => job . required ) ;
5356 }
5457 return filteredJobs ;
58+ }
59+
60+ // Filters the jobs s.t. all values must be contained in the name.
61+ const matchAll = ( filteredJobs , values ) => {
62+ return filteredJobs . filter ( ( job ) => {
63+ const jobName = job . name . toLowerCase ( ) ;
64+ return values . every ( ( val ) => {
65+ const decodedValue = decodeURIComponent ( val ) . toLowerCase ( ) ;
66+ return jobName . includes ( decodedValue ) ;
67+ } ) ;
68+ } ) ;
69+ } ;
70+
71+ // Filters the jobs s.t. at least one value must be contained in the name.
72+ const matchAny = ( filteredJobs , values ) => {
73+ return filteredJobs . filter ( ( job ) => {
74+ const jobName = job . name . toLowerCase ( ) ;
75+ return values . some ( ( val ) => {
76+ const decodedValue = decodeURIComponent ( val ) . toLowerCase ( ) ;
77+ return jobName . includes ( decodedValue ) ;
78+ } ) ;
79+ } ) ;
80+ } ;
81+
82+ //Filter based on the URL.
83+ const filterURL = ( filteredJobs ) => {
84+ const urlParams = new URLSearchParams ( window . location . search ) ;
85+ switch ( urlParams . get ( "matchMode" ) ) {
86+ case "and" :
87+ filteredJobs = matchAll ( filteredJobs , urlParams . getAll ( "value" ) ) ;
88+ break ;
89+ case "or" :
90+ filteredJobs = matchAny ( filteredJobs , urlParams . getAll ( "value" ) ) ;
91+ break ;
92+ default :
93+ break ;
94+ }
95+ return filteredJobs ;
5596 } ;
5697
5798 useEffect ( ( ) => {
5899 setLoading ( true ) ;
59100
60- // Filter based on required tag.
61101 let filteredJobs = filterRequired ( jobs ) ;
102+ filteredJobs = filterURL ( filteredJobs ) ;
62103
63104 //Set the rows for the table.
64105 setRows (
@@ -87,8 +128,8 @@ export default function Home() {
87128 setExpandedRows ( updatedExpandedRows ) ;
88129 } ;
89130
90- const buttonClass = ( active ) => `tab md:px-4 px-2 py-2 border-2
91- ${ active ? "border-blue-500 bg-blue-500 text-white"
131+ const buttonClass = ( active ) => `tab md:px-4 px-2 py-2 border-2
132+ ${ active ? "border-blue-500 bg-blue-500 text-white"
92133 : "border-gray-300 bg-white hover:bg-gray-100" } `;
93134
94135
@@ -251,6 +292,39 @@ export default function Home() {
251292 ) ;
252293 } ;
253294
295+ // Apply search terms to the URL and reload the page.
296+ const handleSearch = ( e ) => {
297+ // Prevent the default behavior so that we can keep search terms.
298+ e . preventDefault ( ) ;
299+ const matchMode = e . target . matchMode . value ;
300+ const value = e . target . value . value . trimEnd ( ) ;
301+ if ( value ) {
302+ // Append the new matchMode regardless of if search terms were kept.
303+ const path = new URLSearchParams ( ) ;
304+ path . append ( "matchMode" , matchMode ) ;
305+ if ( keepSearch ) {
306+ // If keepSearch is true, add existing parameters in the URL.
307+ const urlParams = new URLSearchParams ( window . location . search ) ;
308+ urlParams . getAll ( "value" ) . forEach ( ( val ) => {
309+ path . append ( "value" , val ) ;
310+ } ) ;
311+ }
312+ //Add the search term from the form and redirect.
313+ path . append ( "value" , value ) ;
314+ window . location . assign ( `${ basePath } /?${ path . toString ( ) } ` ) ;
315+ }
316+ } ;
317+
318+ // Clear the search parameters, but only if they exist.
319+ const clearSearch = ( ) => {
320+ const urlParts = window . location . href . split ( "?" ) ;
321+ if ( urlParts [ 1 ] !== undefined ) {
322+ window . location . assign ( urlParts [ 0 ] ) ;
323+ }
324+ }
325+
326+
327+
254328 const renderTable = ( ) => (
255329 < DataTable
256330 value = { rows }
@@ -315,14 +389,33 @@ export default function Home() {
315389 "m-0 h-full p-4 overflow-x-hidden overflow-y-auto bg-surface-ground font-normal text-text-color antialiased select-text"
316390 }
317391 >
318- < button
319- className = { buttonClass ( requiredFilter ) }
320- onClick = { ( ) => setRequiredFilter ( ! requiredFilter ) } >
321- Required Jobs Only
322- </ button >
323- < div className = "mt-4 text-lg" > Total Rows: { rows . length } </ div >
392+
393+ < div className = "space-x-2 mx-auto" >
394+ < button
395+ className = { buttonClass ( requiredFilter ) }
396+ onClick = { ( ) => setRequiredFilter ( ! requiredFilter ) } >
397+ Required Jobs Only
398+ </ button >
399+ < button
400+ className = { buttonClass ( ) }
401+ onClick = { ( ) => clearSearch ( ) } >
402+ Clear Search
403+ </ button >
404+ < button
405+ className = { buttonClass ( keepSearch ) }
406+ onClick = { ( ) => setKeepSearch ( ! keepSearch ) } >
407+ Keep URL Search Terms
408+ </ button >
409+ </ div >
410+
411+ < SearchForm handleSearch = { handleSearch } />
412+
413+ < div className = "mt-1 text-center md:text-lg text-base" >
414+ Total Rows: { rows . length }
415+ </ div >
416+
324417 < div > { renderTable ( ) } </ div >
325418 </ main >
326419 </ div >
327420 ) ;
328- }
421+ }
0 commit comments