Skip to content

Commit 0dc1ad2

Browse files
committed
implemented company filtering
1 parent 120c281 commit 0dc1ad2

File tree

6 files changed

+275
-23
lines changed

6 files changed

+275
-23
lines changed
Lines changed: 56 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,73 @@
11
"use client";
22

3-
import { useRouter, useSearchParams } from "next/navigation";
3+
import { IndustryType } from "@cooper/db/schema";
4+
import { useRouter } from "next/navigation";
45

56
import { CompanyCardPreview } from "~/app/_components/companies/company-card-preview";
67
import LoadingResults from "~/app/_components/loading-results";
78
import NoResults from "~/app/_components/no-results";
9+
import SearchFilter from "~/app/_components/search/search-filter";
810
import { api } from "~/trpc/react";
911

10-
export default function Companies() {
11-
const searchParams = useSearchParams();
12-
const searchValue = searchParams.get("search") ?? ""; // Get search query from URL
13-
12+
export default function Companies({
13+
searchParams,
14+
}: {
15+
searchParams?: {
16+
industry?: IndustryType;
17+
location?: string;
18+
search?: string;
19+
};
20+
}) {
1421
const companies = api.company.list.useQuery({
15-
search: searchValue,
22+
options: {
23+
industry: searchParams?.industry,
24+
location: searchParams?.location,
25+
},
26+
search: searchParams?.search,
1627
});
1728

29+
const locationQuery = api.location.getById.useQuery(
30+
{ id: searchParams?.location ?? "" },
31+
{ enabled: !!searchParams?.location },
32+
);
33+
1834
const router = useRouter();
1935

2036
return (
21-
<>
37+
<div className="w-[95%] justify-center">
38+
<SearchFilter
39+
searchType="COMPANIES"
40+
industry={searchParams?.industry}
41+
location={locationQuery.data}
42+
/>
43+
<hr className="my-4 border-t border-[#9A9A9A] w-full" />
44+
<div className="text-[26px]">
45+
{searchParams?.industry ? (
46+
<>
47+
<span className="font-bold">
48+
{searchParams.industry.charAt(0) +
49+
searchParams.industry.slice(1).toLowerCase()}
50+
</span>{" "}
51+
Companies
52+
</>
53+
) : (
54+
"Companies"
55+
)}
56+
{locationQuery.data && (
57+
<>
58+
{" in "}
59+
<span className="font-bold">
60+
{locationQuery.data.city}
61+
{locationQuery.data.state ? `, ${locationQuery.data.state}` : ""}
62+
</span>
63+
</>
64+
)}
65+
</div>
66+
<div className="text-cooper-gray-400">
67+
{companies.data?.length ?? 0} results
68+
</div>
2269
{companies.isSuccess && companies.data.length > 0 ? (
23-
<div className="mb-8 mt-6 grid h-[86dvh] w-3/4 grid-cols-1 gap-4 overflow-y-scroll md:grid-cols-2 xl:grid-cols-3">
70+
<div className="mb-8 mt-6 grid h-[86dvh] grid-cols-1 gap-4 overflow-y-auto md:grid-cols-2 xl:grid-cols-3 ">
2471
{companies.data.map((company) => (
2572
<div
2673
key={company.id}
@@ -36,6 +83,6 @@ export default function Companies() {
3683
) : companies.isPending ? (
3784
<LoadingResults className="h-full" />
3885
) : null}
39-
</>
86+
</div>
4087
);
4188
}

apps/web/src/app/_components/header-layout.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@ export default async function HeaderLayout({
2828
return (
2929
<div className="flex h-screen flex-col justify-between">
3030
<Header auth={button} />
31-
<article className="flex h-screen w-screen flex-col items-center justify-start">
31+
<article className="flex h-screen flex-col items-center justify-start">
3232
<div className="mx-0 mt-2 flex h-[6dvh] justify-center xl:mt-0 xl:hidden">
33-
<SearchFilter searchClassName="w-screen px-4 mb-2" />
33+
<SearchFilter searchClassName="px-4 mb-2" />
3434
</div>
3535
{children}
3636
</article>
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
import { useState } from "react";
2+
import { useFormContext } from "react-hook-form";
3+
4+
import { Button } from "@cooper/ui/button";
5+
import { FormControl, FormField, FormItem } from "@cooper/ui/form";
6+
import { useCallback } from "react";
7+
import {
8+
Select,
9+
SelectContent,
10+
SelectGroup,
11+
SelectItem,
12+
SelectSeparator,
13+
SelectTrigger,
14+
SelectValue,
15+
} from "@cooper/ui/select";
16+
import { Industry } from "@cooper/db/schema";
17+
import type { IndustryType, LocationType } from "@cooper/db/schema";
18+
import { api } from "~/trpc/react";
19+
import { usePathname, useRouter } from "next/navigation";
20+
import { z } from "zod";
21+
22+
interface SearchBarProps {
23+
industry?: IndustryType;
24+
location?: LocationType;
25+
}
26+
27+
const _formSchema = z.object({
28+
searchIndustry: z.string().optional(),
29+
searchLocation: z.string().optional(),
30+
});
31+
type FormSchema = z.infer<typeof _formSchema>;
32+
33+
export function CompanySearchBar({ industry, location }: SearchBarProps) {
34+
const form = useFormContext();
35+
const { handleSubmit, setValue } = form;
36+
const router = useRouter();
37+
const pathName = usePathname();
38+
39+
const [selectedIndustry, setSelectedIndustry] = useState<string | undefined>(
40+
industry,
41+
);
42+
const [selectedLocation, setSelectedLocation] = useState<string | undefined>(
43+
location?.city,
44+
);
45+
const { data: locations = [] } = api.location.list.useQuery();
46+
47+
const onSubmit = (data: FormSchema) => {
48+
router.push(pathName + "?" + createQueryString(data));
49+
};
50+
51+
const createQueryString = useCallback(
52+
({ searchIndustry, searchLocation }: FormSchema) => {
53+
const params = new URLSearchParams();
54+
if (searchIndustry) {
55+
params.set("industry", searchIndustry);
56+
}
57+
if (searchLocation) {
58+
params.set("location", searchLocation);
59+
}
60+
61+
return params.toString();
62+
},
63+
[],
64+
);
65+
66+
return (
67+
<div className="flex flex-row w-full justify-between items-center pt-4">
68+
<div className="text-[30px] justify-left">Browse Companies</div>
69+
<div className="flex flex-row gap-6 min-w-0">
70+
<FormField
71+
control={form.control}
72+
name="searchIndustry"
73+
render={({ field }) => (
74+
<FormItem className="col-span-5 lg:col-span-2">
75+
<FormControl>
76+
<Select
77+
onValueChange={(value) => {
78+
setSelectedIndustry(value);
79+
const finalValue = value === "INDUSTRY" ? undefined : value;
80+
setValue(field.name, finalValue);
81+
void handleSubmit(onSubmit)();
82+
}}
83+
value={selectedIndustry}
84+
>
85+
<SelectTrigger className="h-12 w-[340px] rounded-none border-2 border-l-0 border-t-0 border-[#9A9A9A] text-lg placeholder:opacity-50 focus:ring-0 active:ring-0 lg:rounded-md lg:border-2">
86+
<SelectValue placeholder="Industry" />
87+
</SelectTrigger>
88+
<SelectContent>
89+
<SelectGroup>
90+
<SelectItem className="font-bold" value="INDUSTRY">
91+
Industry
92+
</SelectItem>
93+
<SelectSeparator />
94+
{Object.entries(Industry).map(([key, value]) => (
95+
<SelectItem key={value} value={value}>
96+
{key.charAt(0) + key.slice(1).toLowerCase()}
97+
</SelectItem>
98+
))}
99+
</SelectGroup>
100+
</SelectContent>
101+
</Select>
102+
</FormControl>
103+
</FormItem>
104+
)}
105+
/>
106+
<FormField
107+
control={form.control}
108+
name="searchLocation"
109+
render={({ field }) => (
110+
<FormItem className="col-span-5 lg:col-span-2">
111+
<FormControl>
112+
<Select
113+
onValueChange={(value) => {
114+
setSelectedLocation(value);
115+
const finalValue = value === "LOCATION" ? undefined : value;
116+
setValue(field.name, finalValue);
117+
void handleSubmit(onSubmit)();
118+
}}
119+
value={selectedLocation}
120+
>
121+
<SelectTrigger className="h-12 w-[340px] rounded-none border-2 border-l-0 border-t-0 border-[#9A9A9A] text-lg placeholder:opacity-50 focus:ring-0 active:ring-0 lg:rounded-md lg:border-2">
122+
<SelectValue placeholder="Location" />
123+
</SelectTrigger>
124+
<SelectContent>
125+
<SelectGroup>
126+
<SelectItem className="font-bold" value="LOCATION">
127+
Location
128+
</SelectItem>
129+
<SelectSeparator />
130+
{locations.map((location: LocationType) => (
131+
<SelectItem key={location.id} value={location.id}>
132+
{location.city + ", " + location.state}
133+
</SelectItem>
134+
))}
135+
</SelectGroup>
136+
</SelectContent>
137+
</Select>
138+
</FormControl>
139+
</FormItem>
140+
)}
141+
/>
142+
<Button
143+
className="bg-white hover:bg-white hover:text-[#9A9A9A] border-white text-black"
144+
onClick={() => {
145+
setSelectedIndustry("INDUSTRY");
146+
setSelectedLocation("LOCATION");
147+
form.setValue("searchIndustry", undefined);
148+
form.setValue("searchLocation", undefined);
149+
}}
150+
>
151+
Clear
152+
</Button>
153+
</div>
154+
</div>
155+
);
156+
}

apps/web/src/app/_components/search/search-filter.tsx

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,18 @@ import { zodResolver } from "@hookform/resolvers/zod";
66
import { useForm } from "react-hook-form";
77
import { z } from "zod";
88

9-
import { WorkEnvironment, WorkTerm } from "@cooper/db/schema";
9+
import {
10+
IndustryType,
11+
LocationType,
12+
WorkEnvironment,
13+
WorkTerm,
14+
} from "@cooper/db/schema";
1015
import { cn } from "@cooper/ui";
1116
import { Form } from "@cooper/ui/form";
1217

1318
import { ReviewSearchBar } from "~/app/_components/search/review-search-bar";
1419
import { SimpleSearchBar } from "./simple-search-bar";
20+
import { CompanySearchBar } from "./company-search-bar";
1521

1622
const formSchema = z.object({
1723
searchText: z.string(),
@@ -25,6 +31,8 @@ const formSchema = z.object({
2531
message: "Invalid cycle type",
2632
})
2733
.optional(),
34+
searchIndustry: z.string().optional(),
35+
searchLocation: z.string().optional(),
2836
});
2937

3038
export type SearchFilterFormType = typeof formSchema;
@@ -35,7 +43,9 @@ interface SearchFilterProps {
3543
term?: "INPERSON" | "HYBRID" | "REMOTE";
3644
alternatePathname?: string;
3745
searchClassName?: string;
38-
searchType?: "REVIEWS" | "SIMPLE";
46+
searchType?: "REVIEWS" | "SIMPLE" | "COMPANIES";
47+
industry?: IndustryType;
48+
location?: LocationType;
3949
}
4050

4151
/**
@@ -51,6 +61,8 @@ export default function SearchFilter({
5161
alternatePathname,
5262
searchClassName,
5363
searchType = "SIMPLE",
64+
industry,
65+
location,
5466
}: SearchFilterProps) {
5567
const form = useForm<z.infer<typeof formSchema>>({
5668
resolver: zodResolver(formSchema),
@@ -65,9 +77,9 @@ export default function SearchFilter({
6577
const pathName = usePathname();
6678

6779
const createQueryString = useCallback(
68-
({ searchText, searchCycle, searchTerm }: z.infer<typeof formSchema>) => {
80+
({ searchCycle, searchTerm }: z.infer<typeof formSchema>) => {
6981
// Initialize URLSearchParams with the required searchText
70-
const params = new URLSearchParams({ search: searchText });
82+
const params = new URLSearchParams();
7183

7284
// Conditionally add searchCycle and searchTerm if they have values
7385
if (searchCycle) {
@@ -77,9 +89,9 @@ export default function SearchFilter({
7789
params.set("term", searchTerm);
7890
}
7991

80-
return params.toString(); // Returns a query string, e.g., "search=yo&cycle=SPRING"
92+
return params.toString();
8193
},
82-
[],
94+
[searchType],
8395
);
8496

8597
function onSubmit(values: z.infer<typeof formSchema>) {
@@ -92,15 +104,15 @@ export default function SearchFilter({
92104

93105
return (
94106
<Form {...form}>
95-
<form
96-
onSubmit={form.handleSubmit(onSubmit)}
97-
className={cn("w-[100vw]", searchClassName)}
98-
>
107+
<form onSubmit={form.handleSubmit(onSubmit)} className={searchClassName}>
99108
<div className={cn("flex justify-center")}>
100109
{searchType === "SIMPLE" && <SimpleSearchBar />}
101110
{searchType === "REVIEWS" && (
102111
<ReviewSearchBar cycle={cycle} term={term} />
103112
)}
113+
{searchType === "COMPANIES" && (
114+
<CompanySearchBar industry={industry} location={location} />
115+
)}
104116
</div>
105117
</form>
106118
</Form>

apps/web/src/app/_components/search/simple-search-bar.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export function SimpleSearchBar() {
1919
const newLocal =
2020
"h-12 border border-l-0 border-cooper-blue-800 text-lg text-cooper-blue-600 placeholder:text-cooper-blue-600 rounded-r-lg rounded-l-none";
2121
return (
22-
<div className="flex w-full rounded-lg">
22+
<div className="flex w-full rounded-lg w-[700px]">
2323
<Button
2424
className="h-12 rounded-l-lg rounded-r-none border border-l border-r-0 border-t border-cooper-blue-800 bg-white p-0 px-2 text-lg hover:bg-cooper-blue-200 focus:border-r-0 focus:ring-0 active:ring-0"
2525
type="submit"

0 commit comments

Comments
 (0)