Skip to content

implemented company filtering #195

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Apr 10, 2025
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 56 additions & 9 deletions apps/web/src/app/(pages)/(dashboard)/companies/page.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,73 @@
"use client";

import { useRouter, useSearchParams } from "next/navigation";
import { IndustryType } from "@cooper/db/schema";

Check warning on line 3 in apps/web/src/app/(pages)/(dashboard)/companies/page.tsx

View workflow job for this annotation

GitHub Actions / lint

All imports in the declaration are only used as types. Use `import type`
import { useRouter } from "next/navigation";

import { CompanyCardPreview } from "~/app/_components/companies/company-card-preview";
import LoadingResults from "~/app/_components/loading-results";
import NoResults from "~/app/_components/no-results";
import SearchFilter from "~/app/_components/search/search-filter";
import { api } from "~/trpc/react";

export default function Companies() {
const searchParams = useSearchParams();
const searchValue = searchParams.get("search") ?? ""; // Get search query from URL

export default function Companies({
searchParams,
}: {
searchParams?: {
industry?: IndustryType;
location?: string;
search?: string;
};
}) {
const companies = api.company.list.useQuery({
search: searchValue,
options: {
industry: searchParams?.industry,
location: searchParams?.location,
},
search: searchParams?.search,
});

const locationQuery = api.location.getById.useQuery(
{ id: searchParams?.location ?? "" },
{ enabled: !!searchParams?.location },
);

const router = useRouter();

return (
<>
<div className="w-[95%] justify-center">
<SearchFilter
searchType="COMPANIES"
industry={searchParams?.industry}
location={locationQuery.data}
/>
<hr className="my-4 border-t border-[#9A9A9A] w-full" />
<div className="text-[26px]">
{searchParams?.industry ? (
<>
<span className="font-bold">
{searchParams.industry.charAt(0) +
searchParams.industry.slice(1).toLowerCase()}
</span>{" "}
Companies
</>
) : (
"Companies"
)}
{locationQuery.data && (
<>
{" in "}
<span className="font-bold">
{locationQuery.data.city}
{locationQuery.data.state ? `, ${locationQuery.data.state}` : ""}
</span>
</>
)}
</div>
<div className="text-cooper-gray-400">
{companies.data?.length ?? 0} results
</div>
{companies.isSuccess && companies.data.length > 0 ? (
<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">
<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 ">
{companies.data.map((company) => (
<div
key={company.id}
Expand All @@ -36,6 +83,6 @@
) : companies.isPending ? (
<LoadingResults className="h-full" />
) : null}
</>
</div>
);
}
4 changes: 2 additions & 2 deletions apps/web/src/app/_components/header-layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ export default async function HeaderLayout({
return (
<div className="flex h-screen flex-col justify-between">
<Header auth={button} />
<article className="flex h-screen w-screen flex-col items-center justify-start">
<article className="flex h-screen flex-col items-center justify-start">
<div className="mx-0 mt-2 flex h-[6dvh] justify-center xl:mt-0 xl:hidden">
<SearchFilter searchClassName="w-screen px-4 mb-2" />
<SearchFilter searchClassName="px-4 mb-2" />
</div>
{children}
</article>
Expand Down
156 changes: 156 additions & 0 deletions apps/web/src/app/_components/search/company-search-bar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
import { useState } from "react";
import { useFormContext } from "react-hook-form";

import { Button } from "@cooper/ui/button";
import { FormControl, FormField, FormItem } from "@cooper/ui/form";
import { useCallback } from "react";
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectSeparator,
SelectTrigger,
SelectValue,
} from "@cooper/ui/select";
import { Industry } from "@cooper/db/schema";
import type { IndustryType, LocationType } from "@cooper/db/schema";
import { api } from "~/trpc/react";
import { usePathname, useRouter } from "next/navigation";
import { z } from "zod";

interface SearchBarProps {
industry?: IndustryType;
location?: LocationType;
}

const _formSchema = z.object({
searchIndustry: z.string().optional(),
searchLocation: z.string().optional(),
});
type FormSchema = z.infer<typeof _formSchema>;

export function CompanySearchBar({ industry, location }: SearchBarProps) {
const form = useFormContext();
const { handleSubmit, setValue } = form;
const router = useRouter();
const pathName = usePathname();

const [selectedIndustry, setSelectedIndustry] = useState<string | undefined>(
industry,
);
const [selectedLocation, setSelectedLocation] = useState<string | undefined>(
location?.city,
);
const { data: locations = [] } = api.location.list.useQuery();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is causing the extreme latency which I'm seeing on the preview deployment. I think Tracy has solved this problem though. Spend some time looking through review-section.tsx in the form to see how Tracy implemented a Locations combobox.
This should help with the problems we are facing right now. Once we get that figured out I can review (it is too slow to test right now)


const onSubmit = (data: FormSchema) => {
router.push(pathName + "?" + createQueryString(data));
};

const createQueryString = useCallback(
({ searchIndustry, searchLocation }: FormSchema) => {
const params = new URLSearchParams();
if (searchIndustry) {
params.set("industry", searchIndustry);
}
if (searchLocation) {
params.set("location", searchLocation);
}

return params.toString();
},
[],
);

return (
<div className="flex flex-row w-full justify-between items-center pt-4">
<div className="text-[30px] justify-left">Browse Companies</div>
<div className="flex flex-row gap-6 min-w-0">
<FormField
control={form.control}
name="searchIndustry"
render={({ field }) => (
<FormItem className="col-span-5 lg:col-span-2">
<FormControl>
<Select
onValueChange={(value) => {
setSelectedIndustry(value);
const finalValue = value === "INDUSTRY" ? undefined : value;
setValue(field.name, finalValue);
void handleSubmit(onSubmit)();
}}
value={selectedIndustry}
>
<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">
<SelectValue placeholder="Industry" />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectItem className="font-bold" value="INDUSTRY">
Industry
</SelectItem>
<SelectSeparator />
{Object.entries(Industry).map(([key, value]) => (
<SelectItem key={value} value={value}>
{key.charAt(0) + key.slice(1).toLowerCase()}
</SelectItem>
))}
</SelectGroup>
</SelectContent>
</Select>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="searchLocation"
render={({ field }) => (
<FormItem className="col-span-5 lg:col-span-2">
<FormControl>
<Select
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We will probably need to replace this select with the Combobox mentioned above.

onValueChange={(value) => {
setSelectedLocation(value);
const finalValue = value === "LOCATION" ? undefined : value;
setValue(field.name, finalValue);
void handleSubmit(onSubmit)();
}}
value={selectedLocation}
>
<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">
<SelectValue placeholder="Location" />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectItem className="font-bold" value="LOCATION">
Location
</SelectItem>
<SelectSeparator />
{locations.map((location: LocationType) => (
<SelectItem key={location.id} value={location.id}>
{location.city + ", " + location.state}
</SelectItem>
))}
</SelectGroup>
</SelectContent>
</Select>
</FormControl>
</FormItem>
)}
/>
<Button
className="bg-white hover:bg-white hover:text-[#9A9A9A] border-white text-black"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here we can make the text color here the same as the border color you used above to match the figma

onClick={() => {
setSelectedIndustry("INDUSTRY");
setSelectedLocation("LOCATION");
form.setValue("searchIndustry", undefined);
form.setValue("searchLocation", undefined);
}}
>
Clear
</Button>
</div>
</div>
);
}
29 changes: 22 additions & 7 deletions apps/web/src/app/_components/search/search-filter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,18 @@
import { useForm } from "react-hook-form";
import { z } from "zod";

import { WorkEnvironment, WorkTerm } from "@cooper/db/schema";
import {

Check warning on line 9 in apps/web/src/app/_components/search/search-filter.tsx

View workflow job for this annotation

GitHub Actions / lint

Imports "IndustryType" and "LocationType" are only used as type
IndustryType,
LocationType,
WorkEnvironment,
WorkTerm,
} from "@cooper/db/schema";
import { cn } from "@cooper/ui";
import { Form } from "@cooper/ui/form";

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

const formSchema = z.object({
searchText: z.string(),
Expand All @@ -25,6 +31,8 @@
message: "Invalid cycle type",
})
.optional(),
searchIndustry: z.string().optional(),
searchLocation: z.string().optional(),
});

export type SearchFilterFormType = typeof formSchema;
Expand All @@ -35,7 +43,9 @@
term?: "INPERSON" | "HYBRID" | "REMOTE";
alternatePathname?: string;
searchClassName?: string;
searchType?: "REVIEWS" | "SIMPLE";
searchType?: "REVIEWS" | "SIMPLE" | "COMPANIES";
industry?: IndustryType;
location?: LocationType;
}

/**
Expand All @@ -51,6 +61,8 @@
alternatePathname,
searchClassName,
searchType = "SIMPLE",
industry,
location,
}: SearchFilterProps) {
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
Expand All @@ -65,9 +77,9 @@
const pathName = usePathname();

const createQueryString = useCallback(
({ searchText, searchCycle, searchTerm }: z.infer<typeof formSchema>) => {
({ searchCycle, searchTerm }: z.infer<typeof formSchema>) => {
// Initialize URLSearchParams with the required searchText
const params = new URLSearchParams({ search: searchText });
const params = new URLSearchParams();

// Conditionally add searchCycle and searchTerm if they have values
if (searchCycle) {
Expand All @@ -77,9 +89,9 @@
params.set("term", searchTerm);
}

return params.toString(); // Returns a query string, e.g., "search=yo&cycle=SPRING"
return params.toString();
},
[],
[searchType],

Check warning on line 94 in apps/web/src/app/_components/search/search-filter.tsx

View workflow job for this annotation

GitHub Actions / lint

React Hook useCallback has an unnecessary dependency: 'searchType'. Either exclude it or remove the dependency array
);

function onSubmit(values: z.infer<typeof formSchema>) {
Expand All @@ -94,13 +106,16 @@
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className={cn("w-[100vw]", searchClassName)}
className={cn("w-[90vw]", searchClassName)}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right now the Clear button doesn't align with the right hand side of the screen like it does with the figma

Screenshot 2025-04-08 at 10 15 23 AM

One solution I found (which also preserves the old styles) is

      <form
        onSubmit={form.handleSubmit(onSubmit)}
        className={cn(
          "w-[98vw] border border-blue-700",
          searchType === "COMPANIES" && "w-full",
          searchClassName,
        )}
      >

Let me know if the w-[98vw] still gives you that weird issue we encountered where the width of the companies page is too much

>
<div className={cn("flex justify-center")}>
{searchType === "SIMPLE" && <SimpleSearchBar />}
{searchType === "REVIEWS" && (
<ReviewSearchBar cycle={cycle} term={term} />
)}
{searchType === "COMPANIES" && (
<CompanySearchBar industry={industry} location={location} />
)}
</div>
</form>
</Form>
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/app/_components/search/simple-search-bar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export function SimpleSearchBar() {
const newLocal =
"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";
return (
<div className="flex w-full rounded-lg">
<div className="flex w-full rounded-lg w-full">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like there are two w-full here so we can remove the second

<Button
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"
type="submit"
Expand Down
Loading