Skip to content

Commit

Permalink
dashboard: Add search box and link to URL
Browse files Browse the repository at this point in the history
Adds an input form for searching job names. Searches are appended to the URL.

Fixes #4

Signed-off-by: Anna Finn <[email protected]>
  • Loading branch information
afinn12 committed Nov 7, 2024
1 parent 9b993f0 commit 8bea19a
Show file tree
Hide file tree
Showing 2 changed files with 138 additions and 12 deletions.
22 changes: 22 additions & 0 deletions components/searchForm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
export const SearchForm = ({ handleSearch }) => {
return (
<div className="flex flex-col items-center md:text-base text-xs">
<div className="flex min-[1126px]:justify-end justify-center w-full">
<form className="p-2 bg-gray-700 rounded-md flex flex-row" onSubmit={(e) => handleSearch(e)}>
<div>
<label className="block text-white">Match Mode:</label>
<select name="matchMode" className="px-1 h-fit rounded-lg">
<option value="or">Match Any</option>
<option value="and">Match All</option>
</select>
</div>
<div className="mx-2">
<label className="block text-white">Search Text:</label>
<input type="text" name="value" required></input>
</div>
<button type="submit" className="bg-blue-500 text-white px-4 rounded-3xl">Submit</button>
</form>
</div>
</div>
);
};
128 changes: 116 additions & 12 deletions pages/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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" +
Expand All @@ -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]);

Expand All @@ -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 (
Expand Down Expand Up @@ -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 = () => (
<DataTable
value={rows}
Expand Down Expand Up @@ -178,9 +263,28 @@ export default function Home() {
"m-0 h-full p-4 overflow-x-hidden overflow-y-auto bg-surface-ground font-normal text-text-color antialiased select-text"
}
>
<div className="space-x-2 mx-auto">
<button
className={buttonClass()}
onClick={() => clearSearch()}>
Clear Search
</button>
<button
className={buttonClass(keepSearch)}
onClick={() => setKeepSearch(!keepSearch)}>
Keep URL Search Terms
</button>
</div>

<SearchForm handleSearch={handleSearch} />

<div className="mt-1 text-center md:text-lg text-base">
Total Rows: {rows.length}
</div>

<div>{renderTable()}</div>
<div className="mt-4 text-lg">Total Rows: {rows.length}</div>
</main>
</div>
);
}
}

0 comments on commit 8bea19a

Please sign in to comment.