+
);
}
diff --git a/apps/web/src/app/_components/search/search-filter.tsx b/apps/web/src/app/_components/search/search-filter.tsx
index e0376fd..0d2224d 100644
--- a/apps/web/src/app/_components/search/search-filter.tsx
+++ b/apps/web/src/app/_components/search/search-filter.tsx
@@ -1,58 +1,86 @@
"use client";
import { useCallback } from "react";
-import { usePathname, useRouter, useSearchParams } from "next/navigation";
+import { usePathname, useRouter } from "next/navigation";
import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form";
import { z } from "zod";
+import { WorkEnvironment, WorkTerm } from "@cooper/db/schema";
import { Form } from "@cooper/ui/form";
import { SearchBar } from "~/app/_components/search/search-bar";
const formSchema = z.object({
searchText: z.string(),
+ searchCycle: z
+ .nativeEnum(WorkTerm, {
+ message: "Invalid cycle type",
+ })
+ .optional(),
+ searchTerm: z
+ .nativeEnum(WorkEnvironment, {
+ message: "Invalid cycle type",
+ })
+ .optional(),
});
export type SearchFilterFormType = typeof formSchema;
interface SearchFilterProps {
search?: string;
+ cycle?: "FALL" | "SPRING" | "SUMMER";
+ term?: "INPERSON" | "HYBRID" | "REMOTE";
+ alternatePathname?: string;
}
/**
* Handles searching logic, updates the search param base on user search and passes the text to backend with fuzzy searching.
- * @param param0 user input text that's passed to the fuzzy search
+ * @param param0 user input text that's passed to the fuzzy search, cycle filter, and term filter.
+ * - alternatePathname example: "/roles" or "/companies"
* @returns the search bar with the user inputted text
*/
-export default function SearchFilter({ search }: SearchFilterProps) {
+export default function SearchFilter({
+ search,
+ cycle,
+ term,
+ alternatePathname,
+}: SearchFilterProps) {
const form = useForm
>({
resolver: zodResolver(formSchema),
defaultValues: {
searchText: search ?? "",
+ searchCycle: cycle,
+ searchTerm: term,
},
});
- const searchParams = useSearchParams();
const router = useRouter();
const pathName = usePathname();
const createQueryString = useCallback(
- (name: string, value: string) => {
- const params = new URLSearchParams(searchParams);
- params.set(name, value);
- return params.toString();
+ ({ searchText, searchCycle, searchTerm }: z.infer) => {
+ // Initialize URLSearchParams with the required searchText
+ const params = new URLSearchParams({ search: searchText });
+
+ // Conditionally add searchCycle and searchTerm if they have values
+ if (searchCycle) {
+ params.set("cycle", searchCycle);
+ }
+ if (searchTerm) {
+ params.set("term", searchTerm);
+ }
+
+ return params.toString(); // Returns a query string, e.g., "search=yo&cycle=SPRING"
},
- [searchParams],
+ [],
);
function onSubmit(values: z.infer) {
- if (values.searchText != "") {
- router.push(
- pathName + `/?${createQueryString("search", values.searchText)}`,
- );
+ if (alternatePathname) {
+ router.push(alternatePathname + "?" + createQueryString(values));
} else {
- router.push(pathName);
+ router.push(pathName + "?" + createQueryString(values));
}
}
@@ -60,7 +88,7 @@ export default function SearchFilter({ search }: SearchFilterProps) {
diff --git a/packages/ui/package.json b/packages/ui/package.json
index cdcde64..f1c076b 100644
--- a/packages/ui/package.json
+++ b/packages/ui/package.json
@@ -27,6 +27,7 @@
"@radix-ui/react-label": "^2.1.0",
"@radix-ui/react-popover": "^1.1.1",
"@radix-ui/react-radio-group": "^1.2.0",
+ "@radix-ui/react-select": "^2.1.2",
"@radix-ui/react-slot": "^1.1.0",
"@radix-ui/react-toast": "^1.2.2",
"class-variance-authority": "^0.7.0",
diff --git a/packages/ui/src/select.tsx b/packages/ui/src/select.tsx
new file mode 100644
index 0000000..d45f1a8
--- /dev/null
+++ b/packages/ui/src/select.tsx
@@ -0,0 +1,160 @@
+"use client";
+
+import * as React from "react";
+import * as SelectPrimitive from "@radix-ui/react-select";
+import { Check, ChevronDown, ChevronUp } from "lucide-react";
+
+import { cn } from "@cooper/ui";
+
+const Select = SelectPrimitive.Root;
+
+const SelectGroup = SelectPrimitive.Group;
+
+const SelectValue = SelectPrimitive.Value;
+
+const SelectTrigger = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+ span]:line-clamp-1",
+ className,
+ )}
+ {...props}
+ >
+ {children}
+
+
+
+
+));
+SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
+
+const SelectScrollUpButton = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+
+
+));
+SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
+
+const SelectScrollDownButton = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+
+
+));
+SelectScrollDownButton.displayName =
+ SelectPrimitive.ScrollDownButton.displayName;
+
+const SelectContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, position = "popper", ...props }, ref) => (
+
+
+
+
+ {children}
+
+
+
+
+));
+SelectContent.displayName = SelectPrimitive.Content.displayName;
+
+const SelectLabel = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+SelectLabel.displayName = SelectPrimitive.Label.displayName;
+
+const SelectItem = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+
+
+
+
+
+
+
+ {children}
+
+));
+SelectItem.displayName = SelectPrimitive.Item.displayName;
+
+const SelectSeparator = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
+
+export {
+ Select,
+ SelectGroup,
+ SelectValue,
+ SelectTrigger,
+ SelectContent,
+ SelectLabel,
+ SelectItem,
+ SelectSeparator,
+ SelectScrollUpButton,
+ SelectScrollDownButton,
+};
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index cfa1601..36531fd 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -411,6 +411,9 @@ importers:
'@radix-ui/react-radio-group':
specifier: ^1.2.0
version: 1.2.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-select':
+ specifier: ^2.1.2
+ version: 2.1.2(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@radix-ui/react-slot':
specifier: ^1.1.0
version: 1.1.0(@types/react@18.3.3)(react@18.3.1)
@@ -2457,6 +2460,9 @@ packages:
'@polka/url@1.0.0-next.25':
resolution: {integrity: sha512-j7P6Rgr3mmtdkeDGTe0E/aYyWEWVtc5yFXtHCRHs28/jptDEWfaVOc5T7cblqy1XKPPfCxJc/8DwQ5YgLOZOVQ==}
+ '@radix-ui/number@1.1.0':
+ resolution: {integrity: sha512-V3gRzhVNU1ldS5XhAPTom1fOIo4ccrjjJgmE+LI2h/WaFpHmx0MQApT+KZHnx8abG6Avtfcz4WoEciMnpFT3HQ==}
+
'@radix-ui/primitive@1.0.1':
resolution: {integrity: sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==}
@@ -2652,6 +2658,15 @@ packages:
'@types/react':
optional: true
+ '@radix-ui/react-focus-guards@1.1.1':
+ resolution: {integrity: sha512-pSIwfrT1a6sIoDASCSpFwOasEwKTZWDw/iBdtnqKO7v6FeOzYJ7U53cPzYFVR3geGGXgVHaH+CdngrrAzqUGxg==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
'@radix-ui/react-focus-scope@1.0.4':
resolution: {integrity: sha512-sL04Mgvf+FmyvZeYfNu1EPAaaxD+aw7cYeIB9L9Fvq8+urhltTRaEo5ysKOpHuKPclsZcSUMKlN05x4u+CINpA==}
peerDependencies:
@@ -2883,6 +2898,19 @@ packages:
'@types/react-dom':
optional: true
+ '@radix-ui/react-select@2.1.2':
+ resolution: {integrity: sha512-rZJtWmorC7dFRi0owDmoijm6nSJH1tVw64QGiNIZ9PNLyBDtG+iAq+XGsya052At4BfarzY/Dhv9wrrUr6IMZA==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
'@radix-ui/react-slot@1.0.2':
resolution: {integrity: sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==}
peerDependencies:
@@ -5108,7 +5136,6 @@ packages:
engines: {node: '>=4.0'}
peerDependencies:
eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8
-
eslint-plugin-react-hooks@5.1.0-rc-7c8e5e7a-20241101:
resolution: {integrity: sha512-90qwhATd1dlXHJ7uNmAIelLkfG3XnxR4vdbgG9WUbd81DT0Na+mXg9VVDYG+3/OQSdkAQlac24/hVQevRHMOpw==}
engines: {node: '>=10'}
@@ -7786,6 +7813,16 @@ packages:
'@types/react':
optional: true
+ react-remove-scroll@2.6.0:
+ resolution: {integrity: sha512-I2U4JVEsQenxDAKaVa3VZ/JeJZe0/2DxPWL8Tj8yLKctQJQiZM52pn/GWFpSp8dftjM3pSAHVJZscAnC/y+ySQ==}
+ engines: {node: '>=10'}
+ peerDependencies:
+ '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
react-router-config@5.1.1:
resolution: {integrity: sha512-DuanZjaD8mQp1ppHjgnnUnyOlqYXZVjnov/JzFhjLEwd3Z4dYjMSnqrEzzGThH47vpCOqPPwJM2FtthLeJ8Pbg==}
peerDependencies:
@@ -11614,6 +11651,8 @@ snapshots:
'@polka/url@1.0.0-next.25': {}
+ '@radix-ui/number@1.1.0': {}
+
'@radix-ui/primitive@1.0.1':
dependencies:
'@babel/runtime': 7.25.4
@@ -11808,6 +11847,12 @@ snapshots:
optionalDependencies:
'@types/react': 18.3.3
+ '@radix-ui/react-focus-guards@1.1.1(@types/react@18.3.3)(react@18.3.1)':
+ dependencies:
+ react: 18.3.1
+ optionalDependencies:
+ '@types/react': 18.3.3
+
'@radix-ui/react-focus-scope@1.0.4(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@babel/runtime': 7.25.4
@@ -12041,6 +12086,35 @@ snapshots:
'@types/react': 18.3.3
'@types/react-dom': 18.3.0
+ '@radix-ui/react-select@2.1.2(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+ dependencies:
+ '@radix-ui/number': 1.1.0
+ '@radix-ui/primitive': 1.1.0
+ '@radix-ui/react-collection': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.3)(react@18.3.1)
+ '@radix-ui/react-context': 1.1.1(@types/react@18.3.3)(react@18.3.1)
+ '@radix-ui/react-direction': 1.1.0(@types/react@18.3.3)(react@18.3.1)
+ '@radix-ui/react-dismissable-layer': 1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-focus-guards': 1.1.1(@types/react@18.3.3)(react@18.3.1)
+ '@radix-ui/react-focus-scope': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-id': 1.1.0(@types/react@18.3.3)(react@18.3.1)
+ '@radix-ui/react-popper': 1.2.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-portal': 1.1.2(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-slot': 1.1.0(@types/react@18.3.3)(react@18.3.1)
+ '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.3)(react@18.3.1)
+ '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.3)(react@18.3.1)
+ '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.3)(react@18.3.1)
+ '@radix-ui/react-use-previous': 1.1.0(@types/react@18.3.3)(react@18.3.1)
+ '@radix-ui/react-visually-hidden': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ aria-hidden: 1.2.4
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ react-remove-scroll: 2.6.0(@types/react@18.3.3)(react@18.3.1)
+ optionalDependencies:
+ '@types/react': 18.3.3
+ '@types/react-dom': 18.3.0
+
'@radix-ui/react-slot@1.0.2(@types/react@18.3.3)(react@18.3.1)':
dependencies:
'@babel/runtime': 7.25.4
@@ -14519,7 +14593,7 @@ snapshots:
object.fromentries: 2.0.8
safe-regex-test: 1.0.3
string.prototype.includes: 2.0.0
-
+
eslint-plugin-react-hooks@5.1.0-rc-7c8e5e7a-20241101(eslint@9.7.0):
dependencies:
eslint: 9.7.0
@@ -17729,6 +17803,17 @@ snapshots:
optionalDependencies:
'@types/react': 18.3.3
+ react-remove-scroll@2.6.0(@types/react@18.3.3)(react@18.3.1):
+ dependencies:
+ react: 18.3.1
+ react-remove-scroll-bar: 2.3.6(@types/react@18.3.3)(react@18.3.1)
+ react-style-singleton: 2.2.1(@types/react@18.3.3)(react@18.3.1)
+ tslib: 2.6.3
+ use-callback-ref: 1.3.2(@types/react@18.3.3)(react@18.3.1)
+ use-sidecar: 1.1.2(@types/react@18.3.3)(react@18.3.1)
+ optionalDependencies:
+ '@types/react': 18.3.3
+
react-router-config@5.1.1(react-router@5.3.4(react@18.3.1))(react@18.3.1):
dependencies:
'@babel/runtime': 7.25.4