From 96da147c0550b09e5feed7ec79ee4e928715551b Mon Sep 17 00:00:00 2001 From: Samir Khairati Date: Tue, 30 Jul 2024 00:54:08 +0530 Subject: [PATCH 01/12] feat: add search component, badges, redirect to view timetable --- frontend/src/SearchResults.tsx | 47 +++++++++++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/frontend/src/SearchResults.tsx b/frontend/src/SearchResults.tsx index efd011d..a2ef14e 100644 --- a/frontend/src/SearchResults.tsx +++ b/frontend/src/SearchResults.tsx @@ -1,12 +1,23 @@ import { queryOptions, useQuery } from "@tanstack/react-query"; import { ErrorComponent, Route } from "@tanstack/react-router"; import axios, { AxiosError } from "axios"; -import { z } from "zod"; -import { timetableWithSectionsType } from "../../lib/src"; +import type { z } from "zod"; +import type { timetableWithSectionsType } from "../../lib/src"; import authenticatedRoute from "./AuthenticatedRoute"; import { ToastAction } from "./components/ui/toast"; import { useToast } from "./components/ui/use-toast"; import { router } from "./main"; +import { useEffect } from "react"; + +import { + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, +} from "@/components/ui/card" +import { Badge } from "@/components/ui/badge" const fetchSearchDetails = async ( query: string, @@ -30,7 +41,7 @@ const searchQueryOptions = (query: string) => const searchRoute = new Route({ getParentRoute: () => authenticatedRoute, - path: "search/$query", + path: "/search/$query", component: SearchResults, loader: ({ context: { queryClient }, params }) => queryClient @@ -117,11 +128,41 @@ function SearchResults() { const { query } = searchRoute.useParams(); // @ts-ignore Suppress unused variable warning, needs to be removed when the page is finished const searchQueryResult = useQuery(searchQueryOptions(query)); + + useEffect(() => { + console.log(searchQueryResult.data); + }, [searchQueryResult]); + return (

Search Results

+
+ { + searchQueryResult.data?.map((timetable) => { + return ( + router.navigate({ to: `/view/${timetable.id}` })}> + + {timetable.name} + By: {timetable.authorId} + + +
+ Year {timetable.year} + Sem {timetable.semester} + {timetable.acadYear} + {timetable.degrees} + {timetable.archived ? Archived : null} +
+
+
+ ); + }) + } +
+ +
); } From c76f80875341ca631ab32dbbf3542c01d4860e2a Mon Sep 17 00:00:00 2001 From: Samir Khairati Date: Tue, 30 Jul 2024 00:58:05 +0530 Subject: [PATCH 02/12] chore: remove logging of timetable --- .DS_Store | Bin 0 -> 6148 bytes backend/.DS_Store | Bin 0 -> 8196 bytes frontend/src/SearchResults.tsx | 56 ++++++++++++++++----------------- 3 files changed, 27 insertions(+), 29 deletions(-) create mode 100644 .DS_Store create mode 100644 backend/.DS_Store diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..7e5e0e632ab9023f820243a2ea4962e4db8b406e GIT binary patch literal 6148 zcmeHK&ubGw7=4p2G;O6Uo)mi6ikEab=+!^Mi~0}f`)0=G+iVhg5|Q~}=54+o^S!*?%wz$W#a?>>hym0&1fywA zLrmf_DpvEJIiis@x@!x|t9iCr$qbI5Kv3XcR6u^a5iVm5kFZ_({a$^(t7BQ-Y___2 zi@y57-D}xW}Pvee!j>b2X~_)h@=;FJix(Pv}#9N3ipHOUHE2xo%@Wc0EsOb=P5W-4{E@()SFN z&(Ws#_mSDF9+`9ocz~WUxtiDeOM9J$<27<^&GR(*CEoTN7wV@np4npc8HZsG3Iqj$ z0w)T{{*Z78rXCB2cI)6|Ujc{_-PU+*_iSP!4r1!Da7Yi$xKg4kHU1UD_$|!1a>i4i zUpRE-Ft#(-@tuu-Lov2H{fSM7NgakcC=e7VD=^_c#&Z7O9{v7bE()K50zrZQN&yo! zmz#_HO8#uEd^tI56OI)QG07_&+7eFwIJOsZ6rba;##ka3#MEQqkRDq2BVc6+Q&8Zq GD)1XKiS5e( literal 0 HcmV?d00001 diff --git a/backend/.DS_Store b/backend/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..f380e450c2d7166449cc50c836b07fe24c0995e9 GIT binary patch literal 8196 zcmeHM&ubGw6n>M25N)CL)_Pir7Y}t!6-yChTh^1JND*(^Y_c|?Np?xH^|~id1qF}d z!9T$N!IM`7&w}u#9QdFkX-YpkvT8m}-OuO=wr3b`_??5ZWE{j^|kE1mhdF81f(uwE~k?Be%4GyB`$D!9LR6x>RNqXEqC(mfhdNCQY0y+J+d;L{GZ zF_t5FMjhyUil|8m^da>zC+&TVchS4h#bb?2_c0fMU#)lH*G8lWei1!F#3uYB%!Ke+ zR`?jQgV_*K17)#;aUW;p7GocKx^4C}zFF|BAcD+WMjP1YiVOAOSG|3LmHES`hv(D@ zW@6Z9m1kq0ig_ZH6-V1N%;Zpc(m@VmoxT3I=lgrHzCo6La!-!ZXV$GeLui^)oVT$1 zhu}H_8woNAvw57VU3+<)t)17W7qf|bUh$F}+vccZ~1^NNb&woWe3ufTN7?{zK7iIl_Bme#XiLK9pnE__t z?=v9E{-(cyJVZ_|vetIcx6oyicBw|?0-?%rpeo0K;~xw$c3>)fJcFr5%%Jg$07U}} JX5f!9@B>fI0;T`} literal 0 HcmV?d00001 diff --git a/frontend/src/SearchResults.tsx b/frontend/src/SearchResults.tsx index a2ef14e..b1a0e9c 100644 --- a/frontend/src/SearchResults.tsx +++ b/frontend/src/SearchResults.tsx @@ -16,8 +16,8 @@ import { CardFooter, CardHeader, CardTitle, -} from "@/components/ui/card" -import { Badge } from "@/components/ui/badge" +} from "@/components/ui/card"; +import { Badge } from "@/components/ui/badge"; const fetchSearchDetails = async ( query: string, @@ -129,40 +129,38 @@ function SearchResults() { // @ts-ignore Suppress unused variable warning, needs to be removed when the page is finished const searchQueryResult = useQuery(searchQueryOptions(query)); - useEffect(() => { - console.log(searchQueryResult.data); - }, [searchQueryResult]); - return (

Search Results

- { - searchQueryResult.data?.map((timetable) => { - return ( - router.navigate({ to: `/view/${timetable.id}` })}> - - {timetable.name} - By: {timetable.authorId} - - -
- Year {timetable.year} - Sem {timetable.semester} - {timetable.acadYear} - {timetable.degrees} - {timetable.archived ? Archived : null} -
-
-
- ); - }) - } + {searchQueryResult.data?.map((timetable) => { + return ( + router.navigate({ to: `/view/${timetable.id}` })} + > + + {timetable.name} + By: {timetable.authorId} + + +
+ Year {timetable.year} + Sem {timetable.semester} + {timetable.acadYear} + {timetable.degrees} + {timetable.archived ? ( + Archived + ) : null} +
+
+
+ ); + })}
- -
); } From e316748bcc5eb1f2c39afebb6d5dd507369ad8e3 Mon Sep 17 00:00:00 2001 From: Samir Khairati Date: Tue, 30 Jul 2024 02:59:39 +0530 Subject: [PATCH 03/12] feat: search filters, loading and no results component --- frontend/package.json | 1 + frontend/pnpm-lock.yaml | 393 ++++++++++++++----------- frontend/src/SearchResults.tsx | 17 ++ frontend/src/components/SearchBar.tsx | 88 +++--- frontend/src/components/ui/command.tsx | 153 ++++++++++ 5 files changed, 445 insertions(+), 207 deletions(-) create mode 100644 frontend/src/components/ui/command.tsx diff --git a/frontend/package.json b/frontend/package.json index 80e3390..6ae0ab8 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -33,6 +33,7 @@ "axios": "^1.7.2", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", + "cmdk": "^1.0.0", "html-to-image": "^1.11.11", "lucide": "^0.302.0", "lucide-react": "^0.302.0", diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index 9c22f8e..eca77b1 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -10,49 +10,49 @@ importers: dependencies: '@radix-ui/react-alert-dialog': specifier: ^1.0.5 - version: 1.0.5(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) + version: 1.0.5(@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-dialog': specifier: ^1.0.5 - version: 1.0.5(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) + version: 1.0.5(@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-dropdown-menu': specifier: ^2.0.6 - version: 2.0.6(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) + version: 2.0.6(@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-label': specifier: ^2.0.2 - version: 2.0.2(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) + version: 2.0.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-popover': specifier: ^1.0.7 - version: 1.0.7(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) + version: 1.0.7(@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.0.0 - version: 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) + version: 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': specifier: ^1.0.2 version: 1.0.2(@types/react@18.3.3)(react@18.3.1) '@radix-ui/react-switch': specifier: ^1.0.3 - version: 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) + version: 1.0.3(@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-tabs': specifier: ^1.0.4 - version: 1.0.4(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) + version: 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) '@radix-ui/react-toast': specifier: ^1.1.5 - version: 1.1.5(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) + version: 1.1.5(@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-tooltip': specifier: ^1.0.7 - version: 1.0.7(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) + version: 1.0.7(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@tanstack/react-query': specifier: ^5.40.1 version: 5.40.1(react@18.3.1) '@tanstack/react-query-devtools': specifier: ^5.40.1 - version: 5.40.1(@tanstack/react-query@5.40.1)(react@18.3.1) + version: 5.40.1(@tanstack/react-query@5.40.1(react@18.3.1))(react@18.3.1) '@tanstack/react-router': specifier: ^1.34.9 - version: 1.34.9(react-dom@18.3.1)(react@18.3.1) + version: 1.34.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@tanstack/router-devtools': specifier: ^1.34.9 - version: 1.34.9(@tanstack/react-router@1.34.9)(csstype@3.1.3)(react-dom@18.3.1)(react@18.3.1) + version: 1.34.9(@tanstack/react-router@1.34.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(csstype@3.1.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@types/chrome': specifier: ^0.0.268 version: 0.0.268 @@ -67,7 +67,7 @@ importers: version: 18.3.0 '@vitejs/plugin-react-swc': specifier: ^3.7.0 - version: 3.7.0(vite@5.2.12) + version: 3.7.0(vite@5.2.12(@types/node@20.14.1)) autoprefixer: specifier: ^10.4.19 version: 10.4.19(postcss@8.4.38) @@ -80,6 +80,9 @@ importers: clsx: specifier: ^2.1.1 version: 2.1.1 + cmdk: + specifier: ^1.0.0 + version: 1.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) html-to-image: specifier: ^1.11.11 version: 1.11.11 @@ -94,7 +97,7 @@ importers: version: 8.4.38 postcss-load-config: specifier: ^5.1.0 - version: 5.1.0(postcss@8.4.38) + version: 5.1.0(jiti@1.21.0)(postcss@8.4.38) react: specifier: ^18.3.1 version: 18.3.1 @@ -1093,6 +1096,12 @@ packages: resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} engines: {node: '>=6'} + cmdk@1.0.0: + resolution: {integrity: sha512-gDzVf0a09TvoJ5jnuPvygTB77+XdOSwEmJ88L6XPFPlv7T3RxbP9jgenfylrAMD0+Le1aO0nVjQUzl2g+vjz5Q==} + peerDependencies: + react: ^18.0.0 + react-dom: ^18.0.0 + color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} @@ -1808,7 +1817,7 @@ snapshots: '@floating-ui/core': 1.6.2 '@floating-ui/utils': 0.2.2 - '@floating-ui/react-dom@2.1.0(react-dom@18.3.1)(react@18.3.1)': + '@floating-ui/react-dom@2.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@floating-ui/dom': 1.6.5 react: 18.3.1 @@ -1865,415 +1874,449 @@ snapshots: dependencies: '@babel/runtime': 7.24.6 - '@radix-ui/react-alert-dialog@1.0.5(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1)': + '@radix-ui/react-alert-dialog@1.0.5(@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.24.6 '@radix-ui/primitive': 1.0.1 '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.3)(react@18.3.1) '@radix-ui/react-context': 1.0.1(@types/react@18.3.3)(react@18.3.1) - '@radix-ui/react-dialog': 1.0.5(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) + '@radix-ui/react-dialog': 1.0.5(@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': 1.0.3(@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.0.2(@types/react@18.3.3)(react@18.3.1) - '@types/react': 18.3.3 - '@types/react-dom': 18.3.0 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.3 + '@types/react-dom': 18.3.0 - '@radix-ui/react-arrow@1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1)': + '@radix-ui/react-arrow@1.0.3(@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.24.6 - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) - '@types/react': 18.3.3 - '@types/react-dom': 18.3.0 + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.3 + '@types/react-dom': 18.3.0 - '@radix-ui/react-collection@1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1)': + '@radix-ui/react-collection@1.0.3(@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.24.6 '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.3)(react@18.3.1) '@radix-ui/react-context': 1.0.1(@types/react@18.3.3)(react@18.3.1) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) + '@radix-ui/react-primitive': 1.0.3(@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.0.2(@types/react@18.3.3)(react@18.3.1) - '@types/react': 18.3.3 - '@types/react-dom': 18.3.0 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.3 + '@types/react-dom': 18.3.0 '@radix-ui/react-compose-refs@1.0.1(@types/react@18.3.3)(react@18.3.1)': dependencies: '@babel/runtime': 7.24.6 - '@types/react': 18.3.3 react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.3 '@radix-ui/react-context@1.0.1(@types/react@18.3.3)(react@18.3.1)': dependencies: '@babel/runtime': 7.24.6 - '@types/react': 18.3.3 react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.3 - '@radix-ui/react-dialog@1.0.5(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1)': + '@radix-ui/react-dialog@1.0.5(@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.24.6 '@radix-ui/primitive': 1.0.1 '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.3)(react@18.3.1) '@radix-ui/react-context': 1.0.1(@types/react@18.3.3)(react@18.3.1) - '@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) + '@radix-ui/react-dismissable-layer': 1.0.5(@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.0.1(@types/react@18.3.3)(react@18.3.1) - '@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) + '@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) '@radix-ui/react-id': 1.0.1(@types/react@18.3.3)(react@18.3.1) - '@radix-ui/react-portal': 1.0.4(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) - '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) + '@radix-ui/react-portal': 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) + '@radix-ui/react-presence': 1.0.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-primitive': 1.0.3(@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.0.2(@types/react@18.3.3)(react@18.3.1) '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.3.3)(react@18.3.1) - '@types/react': 18.3.3 - '@types/react-dom': 18.3.0 aria-hidden: 1.2.4 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) react-remove-scroll: 2.5.5(@types/react@18.3.3)(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.3 + '@types/react-dom': 18.3.0 '@radix-ui/react-direction@1.0.1(@types/react@18.3.3)(react@18.3.1)': dependencies: '@babel/runtime': 7.24.6 - '@types/react': 18.3.3 react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.3 - '@radix-ui/react-dismissable-layer@1.0.5(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1)': + '@radix-ui/react-dismissable-layer@1.0.5(@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.24.6 '@radix-ui/primitive': 1.0.1 '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.3)(react@18.3.1) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) + '@radix-ui/react-primitive': 1.0.3(@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-use-callback-ref': 1.0.1(@types/react@18.3.3)(react@18.3.1) '@radix-ui/react-use-escape-keydown': 1.0.3(@types/react@18.3.3)(react@18.3.1) - '@types/react': 18.3.3 - '@types/react-dom': 18.3.0 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.3 + '@types/react-dom': 18.3.0 - '@radix-ui/react-dropdown-menu@2.0.6(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1)': + '@radix-ui/react-dropdown-menu@2.0.6(@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.24.6 '@radix-ui/primitive': 1.0.1 '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.3)(react@18.3.1) '@radix-ui/react-context': 1.0.1(@types/react@18.3.3)(react@18.3.1) '@radix-ui/react-id': 1.0.1(@types/react@18.3.3)(react@18.3.1) - '@radix-ui/react-menu': 2.0.6(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) + '@radix-ui/react-menu': 2.0.6(@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': 1.0.3(@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-use-controllable-state': 1.0.1(@types/react@18.3.3)(react@18.3.1) - '@types/react': 18.3.3 - '@types/react-dom': 18.3.0 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.3 + '@types/react-dom': 18.3.0 '@radix-ui/react-focus-guards@1.0.1(@types/react@18.3.3)(react@18.3.1)': dependencies: '@babel/runtime': 7.24.6 - '@types/react': 18.3.3 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)': + '@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.24.6 '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.3)(react@18.3.1) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) + '@radix-ui/react-primitive': 1.0.3(@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-use-callback-ref': 1.0.1(@types/react@18.3.3)(react@18.3.1) - '@types/react': 18.3.3 - '@types/react-dom': 18.3.0 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.3 + '@types/react-dom': 18.3.0 '@radix-ui/react-id@1.0.1(@types/react@18.3.3)(react@18.3.1)': dependencies: '@babel/runtime': 7.24.6 '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.3.3)(react@18.3.1) - '@types/react': 18.3.3 react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.3 - '@radix-ui/react-label@2.0.2(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1)': + '@radix-ui/react-label@2.0.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: '@babel/runtime': 7.24.6 - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) - '@types/react': 18.3.3 - '@types/react-dom': 18.3.0 + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.3 + '@types/react-dom': 18.3.0 - '@radix-ui/react-menu@2.0.6(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1)': + '@radix-ui/react-menu@2.0.6(@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.24.6 '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) + '@radix-ui/react-collection': 1.0.3(@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.0.1(@types/react@18.3.3)(react@18.3.1) '@radix-ui/react-context': 1.0.1(@types/react@18.3.3)(react@18.3.1) '@radix-ui/react-direction': 1.0.1(@types/react@18.3.3)(react@18.3.1) - '@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) + '@radix-ui/react-dismissable-layer': 1.0.5(@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.0.1(@types/react@18.3.3)(react@18.3.1) - '@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) + '@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) '@radix-ui/react-id': 1.0.1(@types/react@18.3.3)(react@18.3.1) - '@radix-ui/react-popper': 1.1.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) - '@radix-ui/react-portal': 1.0.4(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) - '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) - '@radix-ui/react-roving-focus': 1.0.4(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) + '@radix-ui/react-popper': 1.1.3(@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.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) + '@radix-ui/react-presence': 1.0.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-primitive': 1.0.3(@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-roving-focus': 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) '@radix-ui/react-slot': 1.0.2(@types/react@18.3.3)(react@18.3.1) '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.3.3)(react@18.3.1) - '@types/react': 18.3.3 - '@types/react-dom': 18.3.0 aria-hidden: 1.2.4 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) react-remove-scroll: 2.5.5(@types/react@18.3.3)(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.3 + '@types/react-dom': 18.3.0 - '@radix-ui/react-popover@1.0.7(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1)': + '@radix-ui/react-popover@1.0.7(@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.24.6 '@radix-ui/primitive': 1.0.1 '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.3)(react@18.3.1) '@radix-ui/react-context': 1.0.1(@types/react@18.3.3)(react@18.3.1) - '@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) + '@radix-ui/react-dismissable-layer': 1.0.5(@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.0.1(@types/react@18.3.3)(react@18.3.1) - '@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) + '@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) '@radix-ui/react-id': 1.0.1(@types/react@18.3.3)(react@18.3.1) - '@radix-ui/react-popper': 1.1.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) - '@radix-ui/react-portal': 1.0.4(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) - '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) + '@radix-ui/react-popper': 1.1.3(@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.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) + '@radix-ui/react-presence': 1.0.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-primitive': 1.0.3(@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.0.2(@types/react@18.3.3)(react@18.3.1) '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.3.3)(react@18.3.1) - '@types/react': 18.3.3 - '@types/react-dom': 18.3.0 aria-hidden: 1.2.4 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) react-remove-scroll: 2.5.5(@types/react@18.3.3)(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.3 + '@types/react-dom': 18.3.0 - '@radix-ui/react-popper@1.1.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1)': + '@radix-ui/react-popper@1.1.3(@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.24.6 - '@floating-ui/react-dom': 2.1.0(react-dom@18.3.1)(react@18.3.1) - '@radix-ui/react-arrow': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) + '@floating-ui/react-dom': 2.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-arrow': 1.0.3(@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.0.1(@types/react@18.3.3)(react@18.3.1) '@radix-ui/react-context': 1.0.1(@types/react@18.3.3)(react@18.3.1) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) + '@radix-ui/react-primitive': 1.0.3(@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-use-callback-ref': 1.0.1(@types/react@18.3.3)(react@18.3.1) '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.3.3)(react@18.3.1) '@radix-ui/react-use-rect': 1.0.1(@types/react@18.3.3)(react@18.3.1) '@radix-ui/react-use-size': 1.0.1(@types/react@18.3.3)(react@18.3.1) '@radix-ui/rect': 1.0.1 - '@types/react': 18.3.3 - '@types/react-dom': 18.3.0 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.3 + '@types/react-dom': 18.3.0 - '@radix-ui/react-portal@1.0.4(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1)': + '@radix-ui/react-portal@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.24.6 - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) - '@types/react': 18.3.3 - '@types/react-dom': 18.3.0 + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.3 + '@types/react-dom': 18.3.0 - '@radix-ui/react-presence@1.0.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1)': + '@radix-ui/react-presence@1.0.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)': dependencies: '@babel/runtime': 7.24.6 '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.3)(react@18.3.1) '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.3.3)(react@18.3.1) - '@types/react': 18.3.3 - '@types/react-dom': 18.3.0 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.3 + '@types/react-dom': 18.3.0 - '@radix-ui/react-primitive@1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1)': + '@radix-ui/react-primitive@1.0.3(@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.24.6 '@radix-ui/react-slot': 1.0.2(@types/react@18.3.3)(react@18.3.1) - '@types/react': 18.3.3 - '@types/react-dom': 18.3.0 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.3 + '@types/react-dom': 18.3.0 - '@radix-ui/react-roving-focus@1.0.4(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1)': + '@radix-ui/react-roving-focus@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.24.6 '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) + '@radix-ui/react-collection': 1.0.3(@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.0.1(@types/react@18.3.3)(react@18.3.1) '@radix-ui/react-context': 1.0.1(@types/react@18.3.3)(react@18.3.1) '@radix-ui/react-direction': 1.0.1(@types/react@18.3.3)(react@18.3.1) '@radix-ui/react-id': 1.0.1(@types/react@18.3.3)(react@18.3.1) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) + '@radix-ui/react-primitive': 1.0.3(@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-use-callback-ref': 1.0.1(@types/react@18.3.3)(react@18.3.1) '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.3.3)(react@18.3.1) - '@types/react': 18.3.3 - '@types/react-dom': 18.3.0 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.3 + '@types/react-dom': 18.3.0 - '@radix-ui/react-select@2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1)': + '@radix-ui/react-select@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)': dependencies: '@babel/runtime': 7.24.6 '@radix-ui/number': 1.0.1 '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) + '@radix-ui/react-collection': 1.0.3(@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.0.1(@types/react@18.3.3)(react@18.3.1) '@radix-ui/react-context': 1.0.1(@types/react@18.3.3)(react@18.3.1) '@radix-ui/react-direction': 1.0.1(@types/react@18.3.3)(react@18.3.1) - '@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) + '@radix-ui/react-dismissable-layer': 1.0.5(@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.0.1(@types/react@18.3.3)(react@18.3.1) - '@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) + '@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) '@radix-ui/react-id': 1.0.1(@types/react@18.3.3)(react@18.3.1) - '@radix-ui/react-popper': 1.1.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) - '@radix-ui/react-portal': 1.0.4(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) + '@radix-ui/react-popper': 1.1.3(@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.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) + '@radix-ui/react-primitive': 1.0.3(@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.0.2(@types/react@18.3.3)(react@18.3.1) '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.3.3)(react@18.3.1) '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.3.3)(react@18.3.1) '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.3.3)(react@18.3.1) '@radix-ui/react-use-previous': 1.0.1(@types/react@18.3.3)(react@18.3.1) - '@radix-ui/react-visually-hidden': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) - '@types/react': 18.3.3 - '@types/react-dom': 18.3.0 + '@radix-ui/react-visually-hidden': 1.0.3(@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.5.5(@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.24.6 '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.3)(react@18.3.1) - '@types/react': 18.3.3 react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.3 - '@radix-ui/react-switch@1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1)': + '@radix-ui/react-switch@1.0.3(@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.24.6 '@radix-ui/primitive': 1.0.1 '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.3)(react@18.3.1) '@radix-ui/react-context': 1.0.1(@types/react@18.3.3)(react@18.3.1) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) + '@radix-ui/react-primitive': 1.0.3(@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-use-controllable-state': 1.0.1(@types/react@18.3.3)(react@18.3.1) '@radix-ui/react-use-previous': 1.0.1(@types/react@18.3.3)(react@18.3.1) '@radix-ui/react-use-size': 1.0.1(@types/react@18.3.3)(react@18.3.1) - '@types/react': 18.3.3 - '@types/react-dom': 18.3.0 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.3 + '@types/react-dom': 18.3.0 - '@radix-ui/react-tabs@1.0.4(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1)': + '@radix-ui/react-tabs@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.24.6 '@radix-ui/primitive': 1.0.1 '@radix-ui/react-context': 1.0.1(@types/react@18.3.3)(react@18.3.1) '@radix-ui/react-direction': 1.0.1(@types/react@18.3.3)(react@18.3.1) '@radix-ui/react-id': 1.0.1(@types/react@18.3.3)(react@18.3.1) - '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) - '@radix-ui/react-roving-focus': 1.0.4(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) + '@radix-ui/react-presence': 1.0.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-primitive': 1.0.3(@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-roving-focus': 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) '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.3.3)(react@18.3.1) - '@types/react': 18.3.3 - '@types/react-dom': 18.3.0 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.3 + '@types/react-dom': 18.3.0 - '@radix-ui/react-toast@1.1.5(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1)': + '@radix-ui/react-toast@1.1.5(@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.24.6 '@radix-ui/primitive': 1.0.1 - '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) + '@radix-ui/react-collection': 1.0.3(@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.0.1(@types/react@18.3.3)(react@18.3.1) '@radix-ui/react-context': 1.0.1(@types/react@18.3.3)(react@18.3.1) - '@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) - '@radix-ui/react-portal': 1.0.4(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) - '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) + '@radix-ui/react-dismissable-layer': 1.0.5(@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.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) + '@radix-ui/react-presence': 1.0.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-primitive': 1.0.3(@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-use-callback-ref': 1.0.1(@types/react@18.3.3)(react@18.3.1) '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.3.3)(react@18.3.1) '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.3.3)(react@18.3.1) - '@radix-ui/react-visually-hidden': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) - '@types/react': 18.3.3 - '@types/react-dom': 18.3.0 + '@radix-ui/react-visually-hidden': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.3 + '@types/react-dom': 18.3.0 - '@radix-ui/react-tooltip@1.0.7(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1)': + '@radix-ui/react-tooltip@1.0.7(@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.24.6 '@radix-ui/primitive': 1.0.1 '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.3)(react@18.3.1) '@radix-ui/react-context': 1.0.1(@types/react@18.3.3)(react@18.3.1) - '@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) + '@radix-ui/react-dismissable-layer': 1.0.5(@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.0.1(@types/react@18.3.3)(react@18.3.1) - '@radix-ui/react-popper': 1.1.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) - '@radix-ui/react-portal': 1.0.4(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) - '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) + '@radix-ui/react-popper': 1.1.3(@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.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) + '@radix-ui/react-presence': 1.0.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-primitive': 1.0.3(@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.0.2(@types/react@18.3.3)(react@18.3.1) '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.3.3)(react@18.3.1) - '@radix-ui/react-visually-hidden': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) - '@types/react': 18.3.3 - '@types/react-dom': 18.3.0 + '@radix-ui/react-visually-hidden': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.3 + '@types/react-dom': 18.3.0 '@radix-ui/react-use-callback-ref@1.0.1(@types/react@18.3.3)(react@18.3.1)': dependencies: '@babel/runtime': 7.24.6 - '@types/react': 18.3.3 react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.3 '@radix-ui/react-use-controllable-state@1.0.1(@types/react@18.3.3)(react@18.3.1)': dependencies: '@babel/runtime': 7.24.6 '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.3.3)(react@18.3.1) - '@types/react': 18.3.3 react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.3 '@radix-ui/react-use-escape-keydown@1.0.3(@types/react@18.3.3)(react@18.3.1)': dependencies: '@babel/runtime': 7.24.6 '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.3.3)(react@18.3.1) - '@types/react': 18.3.3 react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.3 '@radix-ui/react-use-layout-effect@1.0.1(@types/react@18.3.3)(react@18.3.1)': dependencies: '@babel/runtime': 7.24.6 - '@types/react': 18.3.3 react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.3 '@radix-ui/react-use-previous@1.0.1(@types/react@18.3.3)(react@18.3.1)': dependencies: '@babel/runtime': 7.24.6 - '@types/react': 18.3.3 react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.3 '@radix-ui/react-use-rect@1.0.1(@types/react@18.3.3)(react@18.3.1)': dependencies: '@babel/runtime': 7.24.6 '@radix-ui/rect': 1.0.1 - '@types/react': 18.3.3 react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.3 '@radix-ui/react-use-size@1.0.1(@types/react@18.3.3)(react@18.3.1)': dependencies: '@babel/runtime': 7.24.6 '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.3.3)(react@18.3.1) - '@types/react': 18.3.3 react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.3 - '@radix-ui/react-visually-hidden@1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1)': + '@radix-ui/react-visually-hidden@1.0.3(@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.24.6 - '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1)(react@18.3.1) - '@types/react': 18.3.3 - '@types/react-dom': 18.3.0 + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.3 + '@types/react-dom': 18.3.0 '@radix-ui/rect@1.0.1': dependencies: @@ -2385,7 +2428,7 @@ snapshots: '@tanstack/query-devtools@5.37.1': {} - '@tanstack/react-query-devtools@5.40.1(@tanstack/react-query@5.40.1)(react@18.3.1)': + '@tanstack/react-query-devtools@5.40.1(@tanstack/react-query@5.40.1(react@18.3.1))(react@18.3.1)': dependencies: '@tanstack/query-devtools': 5.37.1 '@tanstack/react-query': 5.40.1(react@18.3.1) @@ -2396,25 +2439,25 @@ snapshots: '@tanstack/query-core': 5.40.0 react: 18.3.1 - '@tanstack/react-router@1.34.9(react-dom@18.3.1)(react@18.3.1)': + '@tanstack/react-router@1.34.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@tanstack/history': 1.31.16 - '@tanstack/react-store': 0.2.1(react-dom@18.3.1)(react@18.3.1) + '@tanstack/react-store': 0.2.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) tiny-invariant: 1.3.3 tiny-warning: 1.0.3 - '@tanstack/react-store@0.2.1(react-dom@18.3.1)(react@18.3.1)': + '@tanstack/react-store@0.2.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@tanstack/store': 0.1.3 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) use-sync-external-store: 1.2.2(react@18.3.1) - '@tanstack/router-devtools@1.34.9(@tanstack/react-router@1.34.9)(csstype@3.1.3)(react-dom@18.3.1)(react@18.3.1)': + '@tanstack/router-devtools@1.34.9(@tanstack/react-router@1.34.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(csstype@3.1.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@tanstack/react-router': 1.34.9(react-dom@18.3.1)(react@18.3.1) + '@tanstack/react-router': 1.34.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1) clsx: 2.1.1 date-fns: 2.30.0 goober: 2.1.14(csstype@3.1.3) @@ -2462,7 +2505,7 @@ snapshots: '@types/prop-types': 15.7.12 csstype: 3.1.3 - '@vitejs/plugin-react-swc@3.7.0(vite@5.2.12)': + '@vitejs/plugin-react-swc@3.7.0(vite@5.2.12(@types/node@20.14.1))': dependencies: '@swc/core': 1.5.24 vite: 5.2.12(@types/node@20.14.1) @@ -2555,6 +2598,16 @@ snapshots: clsx@2.1.1: {} + cmdk@1.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): + dependencies: + '@radix-ui/react-dialog': 1.0.5(@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': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + transitivePeerDependencies: + - '@types/react' + - '@types/react-dom' + color-convert@2.0.1: dependencies: color-name: 1.1.4 @@ -2818,14 +2871,17 @@ snapshots: postcss-load-config@4.0.2(postcss@8.4.38): dependencies: lilconfig: 3.1.1 - postcss: 8.4.38 yaml: 2.4.3 + optionalDependencies: + postcss: 8.4.38 - postcss-load-config@5.1.0(postcss@8.4.38): + postcss-load-config@5.1.0(jiti@1.21.0)(postcss@8.4.38): dependencies: lilconfig: 3.1.1 - postcss: 8.4.38 yaml: 2.4.3 + optionalDependencies: + jiti: 1.21.0 + postcss: 8.4.38 postcss-nested@6.0.1(postcss@8.4.38): dependencies: @@ -2866,28 +2922,31 @@ snapshots: react-remove-scroll-bar@2.3.6(@types/react@18.3.3)(react@18.3.1): dependencies: - '@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.2 + optionalDependencies: + '@types/react': 18.3.3 react-remove-scroll@2.5.5(@types/react@18.3.3)(react@18.3.1): dependencies: - '@types/react': 18.3.3 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.2 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-style-singleton@2.2.1(@types/react@18.3.3)(react@18.3.1): dependencies: - '@types/react': 18.3.3 get-nonce: 1.0.1 invariant: 2.2.4 react: 18.3.1 tslib: 2.6.2 + optionalDependencies: + '@types/react': 18.3.3 react@18.3.1: dependencies: @@ -3055,16 +3114,18 @@ snapshots: use-callback-ref@1.3.2(@types/react@18.3.3)(react@18.3.1): dependencies: - '@types/react': 18.3.3 react: 18.3.1 tslib: 2.6.2 + optionalDependencies: + '@types/react': 18.3.3 use-sidecar@1.1.2(@types/react@18.3.3)(react@18.3.1): dependencies: - '@types/react': 18.3.3 detect-node-es: 1.1.0 react: 18.3.1 tslib: 2.6.2 + optionalDependencies: + '@types/react': 18.3.3 use-sync-external-store@1.2.2(react@18.3.1): dependencies: @@ -3079,11 +3140,11 @@ snapshots: vite@5.2.12(@types/node@20.14.1): dependencies: - '@types/node': 20.14.1 esbuild: 0.20.2 postcss: 8.4.38 rollup: 4.18.0 optionalDependencies: + '@types/node': 20.14.1 fsevents: 2.3.3 which@2.0.2: diff --git a/frontend/src/SearchResults.tsx b/frontend/src/SearchResults.tsx index b1a0e9c..f67476b 100644 --- a/frontend/src/SearchResults.tsx +++ b/frontend/src/SearchResults.tsx @@ -18,6 +18,7 @@ import { CardTitle, } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; +import { Loader2 } from "lucide-react"; const fetchSearchDetails = async ( query: string, @@ -160,6 +161,22 @@ function SearchResults() { ); })} + {searchQueryResult.isFetching ? ( +
+

+ +

+
+ ) : null + } + {searchQueryResult.data?.length === 0 ? ( +
+

+ No results found +

+
+ ) : null + } ); diff --git a/frontend/src/components/SearchBar.tsx b/frontend/src/components/SearchBar.tsx index 5030923..2b97b04 100644 --- a/frontend/src/components/SearchBar.tsx +++ b/frontend/src/components/SearchBar.tsx @@ -1,22 +1,22 @@ import { router } from "@/main"; -import { ListFilter, Search } from "lucide-react"; -import { useRef } from "react"; -import { Button } from "./ui/button"; -import { - DropdownMenu, - DropdownMenuCheckboxItem, - DropdownMenuContent, - DropdownMenuLabel, - DropdownMenuSeparator, - DropdownMenuTrigger, -} from "./ui/dropdown-menu"; -import { Input } from "./ui/input"; +import { useEffect, useRef, useState } from "react"; import { useToast } from "./ui/use-toast"; +import { Input } from "@/components/ui/input" +import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuLabel, DropdownMenuSeparator } from "@/components/ui/dropdown-menu" +import { Button } from "@/components/ui/button" +import { ChevronDown, Search } from "lucide-react"; +import { yearType } from '../../../lib/src/zodFieldTypes'; + const SearchBar = () => { + + const [open, setOpen] = useState(false) + const [query, setQuery] = useState() + const [year, setYear] = useState() + const [semester, setSemester] = useState() + const { toast } = useToast(); - const searchRef = useRef(null); - const handleSearch = async (query: string | undefined) => { + const handleSearch = async (query: string | undefined, semester: string | undefined, year: string | undefined) => { if (query === undefined || query.length < 2) { toast({ title: "Error", @@ -25,47 +25,53 @@ const SearchBar = () => { }); return; } + const searchString = query + (semester ? `&semester=${semester}` : "") + (year ? `&year=${year}` : ""); router.navigate({ - to: "/search/$query", + to: `/search/${searchString}`, params: { query }, }); }; return ( -
-
- - handleSearch(searchRef.current?.value)} - /> -
+
+ setQuery(e.target.value)} + type="search" + placeholder="Search..." + className="flex-1 rounded-md border border-input bg-transparent px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50" + /> - - - Filter by + + Year - Course - Name - Archived + setYear(value)}> + Year 1 + Year 2 + Year 3 + Year 4 + Year 4 + + + Semester + + setSemester(value)}> + Sem 1 + Sem 2 + +
); }; export default SearchBar; + diff --git a/frontend/src/components/ui/command.tsx b/frontend/src/components/ui/command.tsx new file mode 100644 index 0000000..d623ee0 --- /dev/null +++ b/frontend/src/components/ui/command.tsx @@ -0,0 +1,153 @@ +import * as React from "react" +import { type DialogProps } from "@radix-ui/react-dialog" +import { Command as CommandPrimitive } from "cmdk" +import { Search } from "lucide-react" + +import { cn } from "@/lib/utils" +import { Dialog, DialogContent } from "@/components/ui/dialog" + +const Command = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +Command.displayName = CommandPrimitive.displayName + +interface CommandDialogProps extends DialogProps {} + +const CommandDialog = ({ children, ...props }: CommandDialogProps) => { + return ( + + + + {children} + + + + ) +} + +const CommandInput = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( +
+ + +
+)) + +CommandInput.displayName = CommandPrimitive.Input.displayName + +const CommandList = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) + +CommandList.displayName = CommandPrimitive.List.displayName + +const CommandEmpty = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>((props, ref) => ( + +)) + +CommandEmpty.displayName = CommandPrimitive.Empty.displayName + +const CommandGroup = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) + +CommandGroup.displayName = CommandPrimitive.Group.displayName + +const CommandSeparator = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +CommandSeparator.displayName = CommandPrimitive.Separator.displayName + +const CommandItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) + +CommandItem.displayName = CommandPrimitive.Item.displayName + +const CommandShortcut = ({ + className, + ...props +}: React.HTMLAttributes) => { + return ( + + ) +} +CommandShortcut.displayName = "CommandShortcut" + +export { + Command, + CommandDialog, + CommandInput, + CommandList, + CommandEmpty, + CommandGroup, + CommandItem, + CommandShortcut, + CommandSeparator, +} From 6ff7473d082be8ff6afc1fe85c91ee529685849b Mon Sep 17 00:00:00 2001 From: Samir Khairati Date: Tue, 30 Jul 2024 03:07:25 +0530 Subject: [PATCH 04/12] style: biome --- frontend/src/SearchResults.tsx | 10 ++--- frontend/src/components/SearchBar.tsx | 57 ++++++++++++++++++--------- 2 files changed, 42 insertions(+), 25 deletions(-) diff --git a/frontend/src/SearchResults.tsx b/frontend/src/SearchResults.tsx index f67476b..2bc9672 100644 --- a/frontend/src/SearchResults.tsx +++ b/frontend/src/SearchResults.tsx @@ -7,17 +7,15 @@ import authenticatedRoute from "./AuthenticatedRoute"; import { ToastAction } from "./components/ui/toast"; import { useToast } from "./components/ui/use-toast"; import { router } from "./main"; -import { useEffect } from "react"; +import { Badge } from "@/components/ui/badge"; import { Card, CardContent, CardDescription, - CardFooter, CardHeader, CardTitle, } from "@/components/ui/card"; -import { Badge } from "@/components/ui/badge"; import { Loader2 } from "lucide-react"; const fetchSearchDetails = async ( @@ -167,16 +165,14 @@ function SearchResults() {

- ) : null - } + ) : null} {searchQueryResult.data?.length === 0 ? (

No results found

- ) : null - } + ) : null} ); diff --git a/frontend/src/components/SearchBar.tsx b/frontend/src/components/SearchBar.tsx index 2b97b04..d70b7b7 100644 --- a/frontend/src/components/SearchBar.tsx +++ b/frontend/src/components/SearchBar.tsx @@ -1,22 +1,32 @@ import { router } from "@/main"; -import { useEffect, useRef, useState } from "react"; +import { useState } from "react"; import { useToast } from "./ui/use-toast"; -import { Input } from "@/components/ui/input" -import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuLabel, DropdownMenuSeparator } from "@/components/ui/dropdown-menu" -import { Button } from "@/components/ui/button" +import { Button } from "@/components/ui/button"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuLabel, + DropdownMenuRadioGroup, + DropdownMenuRadioItem, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; +import { Input } from "@/components/ui/input"; import { ChevronDown, Search } from "lucide-react"; -import { yearType } from '../../../lib/src/zodFieldTypes'; const SearchBar = () => { - - const [open, setOpen] = useState(false) - const [query, setQuery] = useState() - const [year, setYear] = useState() - const [semester, setSemester] = useState() + const [open, setOpen] = useState(false); + const [query, setQuery] = useState(); + const [year, setYear] = useState(); + const [semester, setSemester] = useState(); const { toast } = useToast(); - const handleSearch = async (query: string | undefined, semester: string | undefined, year: string | undefined) => { + const handleSearch = async ( + query: string | undefined, + semester: string | undefined, + year: string | undefined, + ) => { if (query === undefined || query.length < 2) { toast({ title: "Error", @@ -25,7 +35,10 @@ const SearchBar = () => { }); return; } - const searchString = query + (semester ? `&semester=${semester}` : "") + (year ? `&year=${year}` : ""); + const searchString = + query + + (semester ? `&semester=${semester}` : "") + + (year ? `&year=${year}` : ""); router.navigate({ to: `/search/${searchString}`, params: { query }, @@ -37,20 +50,26 @@ const SearchBar = () => { value={query} onChange={(e) => setQuery(e.target.value)} type="search" - placeholder="Search..." + placeholder="Search (optional)..." className="flex-1 rounded-md border border-input bg-transparent px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50" /> - Year - setYear(value)}> + setYear(value)} + > Year 1 Year 2 Year 3 @@ -60,7 +79,10 @@ const SearchBar = () => { Semester - setSemester(value)}> + setSemester(value)} + > Sem 1 Sem 2 @@ -74,4 +96,3 @@ const SearchBar = () => { }; export default SearchBar; - From 9a477377d76ece62550ce9e0cf79bd9fbaa0bb78 Mon Sep 17 00:00:00 2001 From: skoriop Date: Tue, 30 Jul 2024 09:03:03 +0530 Subject: [PATCH 05/12] fix: make grid responsive --- frontend/src/SearchResults.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frontend/src/SearchResults.tsx b/frontend/src/SearchResults.tsx index 2bc9672..4c3e18f 100644 --- a/frontend/src/SearchResults.tsx +++ b/frontend/src/SearchResults.tsx @@ -125,7 +125,6 @@ const searchRoute = new Route({ function SearchResults() { const { query } = searchRoute.useParams(); - // @ts-ignore Suppress unused variable warning, needs to be removed when the page is finished const searchQueryResult = useQuery(searchQueryOptions(query)); return ( @@ -133,7 +132,7 @@ function SearchResults() {

Search Results

-
+
{searchQueryResult.data?.map((timetable) => { return ( Date: Tue, 30 Jul 2024 09:06:22 +0530 Subject: [PATCH 06/12] feat: change badges --- frontend/src/SearchResults.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/frontend/src/SearchResults.tsx b/frontend/src/SearchResults.tsx index 4c3e18f..d118456 100644 --- a/frontend/src/SearchResults.tsx +++ b/frontend/src/SearchResults.tsx @@ -146,9 +146,8 @@ function SearchResults() {
- Year {timetable.year} - Sem {timetable.semester} - {timetable.acadYear} + {timetable.year}-{timetable.semester} + {timetable.acadYear}-{(timetable.acadYear + 1).toString().substring(2)} {timetable.degrees} {timetable.archived ? ( Archived From ad463dedeaf7de9466a13c17b279e5cd92de4269 Mon Sep 17 00:00:00 2001 From: skoriop Date: Tue, 30 Jul 2024 09:25:23 +0530 Subject: [PATCH 07/12] feat: switch to search params --- frontend/src/SearchResults.tsx | 33 +++++++++++++++++++-------- frontend/src/components/SearchBar.tsx | 14 +++++------- 2 files changed, 29 insertions(+), 18 deletions(-) diff --git a/frontend/src/SearchResults.tsx b/frontend/src/SearchResults.tsx index d118456..cb0e33e 100644 --- a/frontend/src/SearchResults.tsx +++ b/frontend/src/SearchResults.tsx @@ -22,7 +22,7 @@ const fetchSearchDetails = async ( query: string, ): Promise[]> => { const response = await axios.get[]>( - `/api/timetable/search?query=${query}`, + `/api/timetable/search?${query}`, { headers: { "Content-Type": "application/json", @@ -32,19 +32,27 @@ const fetchSearchDetails = async ( return response.data; }; -const searchQueryOptions = (query: string) => - queryOptions({ +const searchQueryOptions = (deps: Record) => { + console.log(deps); + for (const key of Object.keys(deps)) { + if (deps[key] === undefined) delete deps[key]; + } + const query = new URLSearchParams(deps).toString(); + return queryOptions({ queryKey: ["search_timetables", query], queryFn: () => fetchSearchDetails(query), }); +}; const searchRoute = new Route({ getParentRoute: () => authenticatedRoute, - path: "/search/$query", + path: "/search", component: SearchResults, - loader: ({ context: { queryClient }, params }) => + validateSearch: (search) => search, + loaderDeps: ({ search }) => search, + loader: ({ context: { queryClient }, deps }) => queryClient - .ensureQueryData(searchQueryOptions(params.query)) + .ensureQueryData(searchQueryOptions(deps)) .catch((error: Error) => { if ( error instanceof AxiosError && @@ -124,8 +132,8 @@ const searchRoute = new Route({ }); function SearchResults() { - const { query } = searchRoute.useParams(); - const searchQueryResult = useQuery(searchQueryOptions(query)); + const deps = searchRoute.useLoaderDeps(); + const searchQueryResult = useQuery(searchQueryOptions(deps)); return (
@@ -146,8 +154,13 @@ function SearchResults() {
- {timetable.year}-{timetable.semester} - {timetable.acadYear}-{(timetable.acadYear + 1).toString().substring(2)} + + {timetable.year}-{timetable.semester} + + + {timetable.acadYear}- + {(timetable.acadYear + 1).toString().substring(2)} + {timetable.degrees} {timetable.archived ? ( Archived diff --git a/frontend/src/components/SearchBar.tsx b/frontend/src/components/SearchBar.tsx index d70b7b7..c9e85bb 100644 --- a/frontend/src/components/SearchBar.tsx +++ b/frontend/src/components/SearchBar.tsx @@ -16,7 +16,6 @@ import { Input } from "@/components/ui/input"; import { ChevronDown, Search } from "lucide-react"; const SearchBar = () => { - const [open, setOpen] = useState(false); const [query, setQuery] = useState(); const [year, setYear] = useState(); const [semester, setSemester] = useState(); @@ -27,20 +26,19 @@ const SearchBar = () => { semester: string | undefined, year: string | undefined, ) => { - if (query === undefined || query.length < 2) { + if (query === undefined || !query.length) { toast({ title: "Error", variant: "destructive", - description: "Search query has to be atleast 2 characters long", + description: "Search query cannot be empty", }); return; } - const searchString = - query + - (semester ? `&semester=${semester}` : "") + - (year ? `&year=${year}` : ""); + const searchString = `query=${query ?? ""}${ + semester ?? `&semester=${semester}` + }${year ?? `&year=${year}`}`; router.navigate({ - to: `/search/${searchString}`, + to: `/search?${searchString}`, params: { query }, }); }; From fbead78a01631493ed80be9e2943b89b49746aa0 Mon Sep 17 00:00:00 2001 From: skoriop Date: Tue, 30 Jul 2024 09:53:10 +0530 Subject: [PATCH 08/12] feat: make search query optional --- .../controllers/timetable/searchTimetable.ts | 16 +++++++++---- frontend/src/components/SearchBar.tsx | 23 +++++++------------ 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/backend/src/controllers/timetable/searchTimetable.ts b/backend/src/controllers/timetable/searchTimetable.ts index 6795270..6ce403c 100644 --- a/backend/src/controllers/timetable/searchTimetable.ts +++ b/backend/src/controllers/timetable/searchTimetable.ts @@ -15,7 +15,7 @@ import { validate } from "../../middleware/zodValidateRequest.js"; const searchTimetableSchema = z.object({ query: z.object({ - query: namedNonEmptyStringType("search query"), + query: namedNonEmptyStringType("search query").optional(), // note that this implies a limit of 500 on the number of search results from: namedIntegerType("search results start index") .gte(0, { @@ -66,6 +66,7 @@ export const searchTimetable = async (req: Request, res: Response) => { } = req.query; const usefulQueryParams = { + query, from, year, name, @@ -77,19 +78,26 @@ export const searchTimetable = async (req: Request, res: Response) => { instructor: instructorQuery, }; - let searchServiceURL = `${env.SEARCH_SERVICE_URL}/timetable/search?query=${query}`; + let searchServiceURL = `${env.SEARCH_SERVICE_URL}/timetable/search?`; for (const [key, value] of Object.entries(usefulQueryParams)) { if (value === undefined) continue; if (Array.isArray(value)) { for (const v of value) { - searchServiceURL += `&${key}=${v}`; + searchServiceURL += `${key}=${v}&`; } } else { - searchServiceURL += `&${key}=${value}`; + searchServiceURL += `${key}=${value}&`; } } + if (searchServiceURL.endsWith("&")) { + searchServiceURL = searchServiceURL.substring( + 0, + searchServiceURL.length - 1, + ); + } + const response = await fetch(searchServiceURL, { method: "GET", headers: { "Content-Type": "application/json" }, diff --git a/frontend/src/components/SearchBar.tsx b/frontend/src/components/SearchBar.tsx index c9e85bb..8fd3788 100644 --- a/frontend/src/components/SearchBar.tsx +++ b/frontend/src/components/SearchBar.tsx @@ -1,6 +1,5 @@ import { router } from "@/main"; import { useState } from "react"; -import { useToast } from "./ui/use-toast"; import { Button } from "@/components/ui/button"; import { @@ -20,25 +19,19 @@ const SearchBar = () => { const [year, setYear] = useState(); const [semester, setSemester] = useState(); - const { toast } = useToast(); const handleSearch = async ( query: string | undefined, semester: string | undefined, year: string | undefined, ) => { - if (query === undefined || !query.length) { - toast({ - title: "Error", - variant: "destructive", - description: "Search query cannot be empty", - }); - return; - } - const searchString = `query=${query ?? ""}${ - semester ?? `&semester=${semester}` - }${year ?? `&year=${year}`}`; + let searchString = `?${query ? `query=${query}&` : ""}${ + semester ? `semester=${semester}&` : "" + }${year ? `year=${year}&` : ""}`; + if (searchString.endsWith("&") || searchString.endsWith("?")) + searchString = searchString.substring(0, searchString.length - 1); + router.navigate({ - to: `/search?${searchString}`, + to: `/search${searchString}`, params: { query }, }); }; @@ -72,7 +65,7 @@ const SearchBar = () => { Year 2 Year 3 Year 4 - Year 4 + Year 5 Semester From 54449b576457be038c201097a069acdfdb58dbe2 Mon Sep 17 00:00:00 2001 From: skoriop Date: Tue, 30 Jul 2024 10:41:12 +0530 Subject: [PATCH 09/12] feat: add pagination --- .../controllers/timetable/searchTimetable.ts | 12 +- frontend/src/SearchResults.tsx | 49 +++++++- frontend/src/components/ui/pagination.tsx | 117 ++++++++++++++++++ 3 files changed, 168 insertions(+), 10 deletions(-) create mode 100644 frontend/src/components/ui/pagination.tsx diff --git a/backend/src/controllers/timetable/searchTimetable.ts b/backend/src/controllers/timetable/searchTimetable.ts index 6ce403c..0ef6fd7 100644 --- a/backend/src/controllers/timetable/searchTimetable.ts +++ b/backend/src/controllers/timetable/searchTimetable.ts @@ -17,12 +17,12 @@ const searchTimetableSchema = z.object({ query: z.object({ query: namedNonEmptyStringType("search query").optional(), // note that this implies a limit of 500 on the number of search results - from: namedIntegerType("search results start index") + page: namedIntegerType("search results page") .gte(0, { - message: "invalid search results start index", + message: "invalid search results page", }) - .lte(500, { - message: "invalid search results start index", + .lte(50, { + message: "invalid search results page", }) .optional(), year: namedCollegeYearType("search filter").optional(), @@ -54,7 +54,7 @@ export const searchTimetable = async (req: Request, res: Response) => { try { const { query, - from, + page, year, name, authorId, @@ -67,7 +67,7 @@ export const searchTimetable = async (req: Request, res: Response) => { const usefulQueryParams = { query, - from, + from: parseInt((page as string | undefined) ?? "0") * 12, year, name, authorId, diff --git a/frontend/src/SearchResults.tsx b/frontend/src/SearchResults.tsx index cb0e33e..f9f8c9d 100644 --- a/frontend/src/SearchResults.tsx +++ b/frontend/src/SearchResults.tsx @@ -17,6 +17,14 @@ import { CardTitle, } from "@/components/ui/card"; import { Loader2 } from "lucide-react"; +import { useState } from "react"; +import { + Pagination, + PaginationContent, + PaginationItem, + PaginationNext, + PaginationPrevious, +} from "./components/ui/pagination"; const fetchSearchDetails = async ( query: string, @@ -132,14 +140,47 @@ const searchRoute = new Route({ }); function SearchResults() { - const deps = searchRoute.useLoaderDeps(); + const initDeps = searchRoute.useLoaderDeps(); + const [deps, setDeps] = useState(initDeps); const searchQueryResult = useQuery(searchQueryOptions(deps)); return (
-

- Search Results -

+
+

+ Search Results +

+ + + + + setDeps((deps) => ({ + ...deps, + page: Math.max( + 0, + ((deps.page as number | undefined) ?? 0) - 1, + ), + })) + } + /> + + + + setDeps((deps) => ({ + ...deps, + page: Math.min( + 50, + ((deps.page as number | undefined) ?? 0) + 1, + ), + })) + } + /> + + + +
{searchQueryResult.data?.map((timetable) => { return ( diff --git a/frontend/src/components/ui/pagination.tsx b/frontend/src/components/ui/pagination.tsx new file mode 100644 index 0000000..ea40d19 --- /dev/null +++ b/frontend/src/components/ui/pagination.tsx @@ -0,0 +1,117 @@ +import * as React from "react" +import { ChevronLeft, ChevronRight, MoreHorizontal } from "lucide-react" + +import { cn } from "@/lib/utils" +import { ButtonProps, buttonVariants } from "@/components/ui/button" + +const Pagination = ({ className, ...props }: React.ComponentProps<"nav">) => ( +