Skip to content

Commit

Permalink
Adds field searching to search box
Browse files Browse the repository at this point in the history
  • Loading branch information
CarterRoll committed Oct 8, 2024
1 parent f6f93c5 commit 9abbcad
Show file tree
Hide file tree
Showing 9 changed files with 2,270 additions and 1,433 deletions.
3 changes: 2 additions & 1 deletion frontend/app/(dashboard)/data/page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import ProjectCatalogue from "@/components/ProjectCatalogue";
import Search from "@/components/Search";
import { getRemoteUrl } from "@/helpers/utils";
import dynamic from "next/dynamic";
const Map = dynamic(() => import("@/components/Map"), {
Expand All @@ -17,7 +18,7 @@ export default async function Dashboard() {

return (
<main className="flex flex-col z-40 m-5 gap-5">
<input type="text" placeholder="Search" className="input input-bordered w-24 md:w-auto bg-neutral-content" />
<Search placeholder="Search"></Search>
<div className="flex-grow">
<Map></Map>
</div>
Expand Down
23 changes: 21 additions & 2 deletions frontend/app/(dashboard)/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import Image from "next/image";
export default async function Dashboard() {
return (
<main className="flex flex-col gap-5 grow">
<Image src="/images/beautiful-photo-sea-waves_58702-11300.avif" alt="" className="fixed z-0 object-cover min-h-full w-full" width={1920} height={500}/>
<Image priority={true} src="/images/beautiful-photo-sea-waves_58702-11300.avif" alt="" className="fixed z-0 object-cover min-h-full w-full" width={1920} height={500}/>
<div className="z-30 mx-20 my-10">
<div className="carousel w-full">
<div id="slide1" className="carousel-item relative w-full">
Expand Down Expand Up @@ -53,7 +53,26 @@ export default async function Dashboard() {
</div>
</div>
<div className="z-30 bg-base-100 px-20 grow">
test
<div>test</div>
<div>test</div>
<div>test</div>
<div>test</div>
<div>test</div>
<div>test</div>
<div>test</div>
<div>test</div>
<div>test</div>
<div>test</div>
<div>test</div>
<div>test</div>
<div>test</div>
<div>test</div>
<div>test</div>
<div>test</div>
<div>test</div>
<div>test</div>
<div>test</div>
<div>test</div>
</div>
</main>
);
Expand Down
4 changes: 2 additions & 2 deletions frontend/components/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export default function Header() {
{/* <SmallNav/> */}
<Link className="normal-case text-xl h-20 w-64 bg-neutral rounded-3xl lg:rounded-l-none pr-4" href={`${getBaseUrl()}`}>
<div className="avatar w-full h-full">
<Image src="/images/ngiHeaderLogo2022.png" alt="" fill={true} style={{objectFit: "contain"}}/>
<Image src="/images/ngiHeaderLogo2022.png" alt="" fill={true} style={{objectFit: "contain"}} sizes="(max-width: 768px) 100vw, 33vw"/>
</div>
</Link>
<div className="hidden lg:block">
Expand All @@ -32,7 +32,7 @@ export default function Header() {
</div>
<div className="navbar-end mr-5">
<SignedIn>
<UserButton afterSignOutUrl="/" />
<UserButton />
</SignedIn>
<SignedOut>
<SignInButton>
Expand Down
4 changes: 2 additions & 2 deletions frontend/components/ProjectCatalogue.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ export default async function ProjectCatalogue() {
<div className="p-5 bg-primary rounded-xl">
<h1 className="text-3xl font-bold border-b-2 border-black mb-5">Projects:</h1>
<div className="flex flex-col gap-3">
{projects.map((proj) => (
<Link key={proj.id} href={`${getBaseUrl()}/data/projects/${proj.project_id}`}>
{projects.map((proj, i) => (
<Link key={i} href={`${getBaseUrl()}/data/projects/${proj.project_id}`}>
<div className="card bg-neutral-content">
<div className="card-body">
<h2 className="card-title">{proj.project_name}</h2>
Expand Down
100 changes: 100 additions & 0 deletions frontend/components/Search.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
"use client";

import { useSearchParams, usePathname, useRouter } from "next/navigation";
import { useState } from "react";
import { useDebouncedCallback } from "use-debounce";

export default function Search({ placeholder }: { placeholder: string }) {
const searchParams = useSearchParams();
const pathname = usePathname();
const { replace } = useRouter();
//const [searchSuggs, setSearchSuggs] = useState([] as Array<{}>);
const [searchFieldSuggs, setSearchFieldSuggs] = useState([] as Array<string>);

const testArr = [
{ title: "1", description: "The number 1" },
{ title: "2", description: "The number 2" },
{ title: "3", description: "The number 3" },
{ title: "4", description: "The number 4" },
{ title: "5", description: "The number 5" }
];

const handleSearch = useDebouncedCallback((search) => {
const params = new URLSearchParams(searchParams);
if (search) {
params.set("q", search);

const splitSearch = search.match(/\w+/g); //match on any word (\w+) with an optional trailing colon (:?)
//set to array of keys that match entire search
setSearchFieldSuggs(Object.keys(testArr[0]).filter((e) => e.toLowerCase().includes(splitSearch[splitSearch.length-1].toLowerCase())));
//set to array of objects where any value in the object matches the entire search
//setSearchSuggs(testArr.filter((e) => {
// return Object.values(e).filter((e2) => e2.toLowerCase().includes(search.toLowerCase())).length;
//}));

} else {
params.delete("q");

setSearchFieldSuggs([]);
//setSearchSuggs([]);
}
replace(`${pathname}?${params.toString()}`);
}, 300);

function suggClick(e: React.MouseEvent) {
const params = new URLSearchParams(searchParams);

//replace the last word with the clicked on suggestion
let splitSearch = params.get("q")!.match(/\w+:?/g)!; //match on any word (\w+) with an optional trailing colon (:?). ! to assert existence since we know they both exist, otherwise you couldn't click on the div
const lastLen = splitSearch[splitSearch.length-1].length;
const newVal = params.get("q")!.trim().slice(0, -lastLen) + (e.target as HTMLElement).innerText + " ";
params.set("q", newVal); //TS can't detect the type of e.target properly, so we assert it inline
replace(`${pathname}?${params.toString()}`);

const inp = document.getElementById("searchInput")! as HTMLInputElement;
inp.focus();
inp.value = newVal;

setSearchFieldSuggs([]);
}

return (
<div className="relative flex flex-1 flex-shrink-0 relative inline-block">
<label className="input input-bordered flex items-center gap-2 bg-neutral-content w-full">
<input
id="searchInput"
type="text"
className="grow"
placeholder={placeholder}
onChange={(e) => {
handleSearch(e.target.value);
}}
defaultValue={searchParams.get("q")?.toString()}
/>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
className="h-4 w-4 opacity-70"
>
<path
fillRule="evenodd"
d="M9.965 11.026a5 5 0 1 1 1.06-1.06l2.755 2.754a.75.75 0 1 1-1.06 1.06l-2.755-2.754ZM10.5 7a3.5 3.5 0 1 1-7 0 3.5 3.5 0 0 1 7 0Z"
clipRule="evenodd"
/>
</svg>
</label>
{/*{(searchFieldSuggs.length > 0 || searchSuggs.length > 0) &&*/}
{searchFieldSuggs.length > 0 &&
<div className="absolute top-full inset-x-0 z-[10000] bg-neutral-content px-3 py-3 rounded-lg mt-1 flex flex-col">
{searchFieldSuggs.map((s, i) => (
<div key={i} onClick={suggClick} className="cursor-pointer hover:bg-neutral active:bg-neutral px-3 py-1 rounded-lg">{s}:</div>
))}
{/*{searchSuggs.map((s, i) => (
<div key={i} className="cursor-pointer hover:bg-neutral active:bg-neutral px-3 py-1 rounded-lg">{s.title}</div>
))}*/}
</div>
}
</div>
);
}
55 changes: 16 additions & 39 deletions frontend/middleware.ts
Original file line number Diff line number Diff line change
@@ -1,42 +1,19 @@
import { authMiddleware } from "@clerk/nextjs";
import { clerkMiddleware, createRouteMatcher } from "@clerk/nextjs/server";

export default authMiddleware({
publicRoutes: (request) => {
return !(request.nextUrl.pathname.startsWith("/admin") || request.nextUrl.pathname.startsWith("/api/admin")
|| request.nextUrl.pathname.startsWith("/tourmaline") || request.nextUrl.pathname.startsWith("/api/tourmaline"))
},
});
const isProtectedRoute = createRouteMatcher(["/admin(.*)", "/api/admin(.*)", "/tourmaline(.*)", "/api/tourmaline(.*)"]);

export const config = {
matcher: ["/((?!.+\\.[\\w]+$|_next).*)", "/", "/(api|trpc)(.*)"],
};
export default clerkMiddleware(
(auth, req) => {
if (isProtectedRoute(req)) auth().protect()
},
//{ debug: true }
);

// import { authMiddleware, redirectToSignIn } from "@clerk/nextjs";
// import { NextResponse } from "next/server";

// export default authMiddleware({
// publicRoutes: (request) => {
// return !(request.nextUrl.pathname.startsWith("/admin") || request.nextUrl.pathname.startsWith("/api/admin"));
// },
// afterAuth(auth, req, evt) {
// // Handle users who aren't authenticated
// if (!auth.userId && !auth.isPublicRoute) {
// return redirectToSignIn({ returnBackUrl: req.url });
// }
// // Redirect logged in users to organization selection page if they are not active in an organization
// if (
// auth.userId &&
// !auth.orgId &&
// req.nextUrl.pathname !== "/org-selection"
// ) {
// const orgSelection = new URL("/org-selection", req.url);
// return NextResponse.redirect(orgSelection);
// }
// // If the user is logged in and trying to access a protected route, allow them to access route
// if (auth.userId && !auth.isPublicRoute) {
// return NextResponse.next();
// }
// // Allow users visiting public routes to access them
// return NextResponse.next();
// },
// });
export const config = {
matcher: [
// Skip Next.js internals and all static files, unless found in search params
"/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)",
// Always run for API routes
"/(api|trpc)(.*)",
],
}
Loading

0 comments on commit 9abbcad

Please sign in to comment.