diff --git a/components/searchForm.js b/components/searchForm.js new file mode 100644 index 0000000..63f5551 --- /dev/null +++ b/components/searchForm.js @@ -0,0 +1,22 @@ +export const SearchForm = ({ handleSearch }) => { + return ( +
+
+
handleSearch(e)}> +
+ + +
+
+ + +
+ +
+
+
+ ); +}; \ No newline at end of file diff --git a/pages/index.js b/pages/index.js index b12d28a..d7a210d 100644 --- a/pages/index.js +++ b/pages/index.js @@ -2,20 +2,23 @@ import { useEffect, useState } from "react"; import { DataTable } from "primereact/datatable"; import { Column } from "primereact/column"; import { weatherTemplate, getWeatherIndex } from "../components/weatherTemplate"; +import { basePath } from "../next.config.js"; +import { SearchForm } from "../components/searchForm"; export default function Home() { - const [loading, setLoading] = useState(true); - const [jobs, setJobs] = useState([]); - const [rows, setRows] = useState([]); - const [expandedRows, setExpandedRows] = useState([]); + const [loading, setLoading] = useState(true); + const [jobs, setJobs] = useState([]); + const [rows, setRows] = useState([]); + const [expandedRows, setExpandedRows] = useState([]); + const [keepSearch, setKeepSearch] = useState(true); useEffect(() => { const fetchData = async () => { let data = {}; if (process.env.NODE_ENV === "development") { - data = (await import("../job_stats.json")).default; + data = (await import("../localData/job_stats.json")).default; } else { const response = await fetch( "https://raw.githubusercontent.com/kata-containers/kata-containers.github.io" + @@ -41,15 +44,59 @@ export default function Home() { fetchData(); }, []); + // Filters the jobs s.t. all values must be contained in the name. + const matchAll = (filteredJobs, values) => { + return filteredJobs.filter((job) => { + const jobName = job.name.toLowerCase(); + return values.every((val) => { + const decodedValue = decodeURIComponent(val).toLowerCase(); + return jobName.includes(decodedValue); + }); + }); + }; + + // Filters the jobs s.t. at least one value must be contained in the name. + const matchAny = (filteredJobs, values) => { + return filteredJobs.filter((job) => { + const jobName = job.name.toLowerCase(); + return values.some((val) => { + const decodedValue = decodeURIComponent(val).toLowerCase(); + return jobName.includes(decodedValue); + }); + }); + }; + + useEffect(() => { setLoading(true); + let filteredJobs = jobs; + + //Filter based on the URL. + const urlParams = new URLSearchParams(window.location.search); + switch(urlParams.get("matchMode")) { + case "and": + filteredJobs = matchAll(filteredJobs, urlParams.getAll("value")); + break; + case "or": + filteredJobs = matchAny(filteredJobs, urlParams.getAll("value")); + break; + default: + break; + } + + + //Set the rows for the table. + setRows( + filteredJobs.map((job) => ({ + name : job.name, + runs : job.runs, + fails : job.fails, + skips : job.skips, + required : job.required, + weather : getWeatherIndex(job), + })) + ); - // Create rows to set into table. - const rows = jobs.map((job) => ({ - ...job, - weather: getWeatherIndex(job), - })); - setRows(rows); setLoading(false); }, [jobs]); @@ -66,6 +113,11 @@ export default function Home() { setExpandedRows(updatedExpandedRows); }; + const buttonClass = (active) => `tab md:px-4 px-2 py-2 border-2 + ${active ? "border-blue-500 bg-blue-500 text-white" + : "border-gray-300 bg-white hover:bg-gray-100"}`; + + // Template for rendering the Name column as a clickable item const nameTemplate = (rowData) => { return ( @@ -120,6 +172,39 @@ export default function Home() { ); }; + // Apply search terms to the URL and reload the page. + const handleSearch= (e) => { + // Prevent the default behavior so that we can keep search terms. + e.preventDefault(); + const matchMode = e.target.matchMode.value; + const value = e.target.value.value.trimEnd(); + if (value) { + // Append the new matchMode regardless of if search terms were kept. + const path = new URLSearchParams(); + path.append("matchMode", matchMode); + if (keepSearch) { + // If keepSearch is true, add existing parameters in the URL. + const urlParams = new URLSearchParams(window.location.search); + urlParams.getAll("value").forEach((val) => { + path.append("value", val); + }); + } + //Add the search term from the form and redirect. + path.append("value", value); + window.location.assign(`${basePath}/?${path.toString()}`); + } + }; + + // Clear the search parameters, but only if they exist. + const clearSearch = () => { + const urlParts = window.location.href.split("?"); + if(urlParts[1] !== undefined){ + window.location.assign(urlParts[0]); + } + } + + + const renderTable = () => ( +
+ + +
+ + + +
+ Total Rows: {rows.length} +
+
{renderTable()}
Total Rows: {rows.length}
); -} +} \ No newline at end of file