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 kata-containers#4

Signed-off-by: Anna Finn <[email protected]>
  • Loading branch information
afinn12 committed Nov 22, 2024
1 parent 03c63b8 commit bcb9abc
Show file tree
Hide file tree
Showing 2 changed files with 125 additions and 10 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>
);
};
113 changes: 103 additions & 10 deletions pages/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import Head from "next/head";
import { weatherTemplate, getWeatherIndex } from "../components/weatherTemplate";
import { OverlayPanel } from 'primereact/overlaypanel';
import MaintainerMapping from "../maintainers.yml";
import { basePath } from "../next.config.js";
import { SearchForm } from "../components/searchForm";


export default function Home() {
Expand All @@ -13,6 +15,7 @@ export default function Home() {
const [rows, setRows] = useState([]);
const [expandedRows, setExpandedRows] = useState([]);
const [requiredFilter, setRequiredFilter] = useState(false);
const [keepSearch, setKeepSearch] = useState(true);

useEffect(() => {
const fetchData = async () => {
Expand Down Expand Up @@ -52,13 +55,51 @@ export default function Home() {
filteredJobs = filteredJobs.filter((job) => job.required);
}
return filteredJobs;
}

// 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);
});
});
};

//Filter based on the URL.
const filterURL = (filteredJobs) => {
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;
}
return filteredJobs;
};

useEffect(() => {
setLoading(true);

// Filter based on required tag.
let filteredJobs = filterRequired(jobs);
filteredJobs = filterURL(filteredJobs);

//Set the rows for the table.
setRows(
Expand Down Expand Up @@ -87,8 +128,8 @@ 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"
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"}`;


Expand Down Expand Up @@ -251,6 +292,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 @@ -315,14 +389,33 @@ 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"
}
>
<button
className={buttonClass(requiredFilter)}
onClick={() => setRequiredFilter(!requiredFilter)}>
Required Jobs Only
</button>
<div className="mt-4 text-lg">Total Rows: {rows.length}</div>

<div className="space-x-2 mx-auto">
<button
className={buttonClass(requiredFilter)}
onClick={() => setRequiredFilter(!requiredFilter)}>
Required Jobs Only
</button>
<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>
</main>
</div>
);
}
}

0 comments on commit bcb9abc

Please sign in to comment.