From 56d20071817d2c4c8945fc695a7050503c2486df Mon Sep 17 00:00:00 2001 From: swve Date: Sun, 20 Oct 2024 23:12:33 +0200 Subject: [PATCH] feat: make external pages responsive --- .../[orgslug]/(withmenu)/collections/page.tsx | 141 ++++++------ .../activity/[activityid]/activity.tsx | 6 +- .../(withmenu)/course/[courseuuid]/course.tsx | 32 +-- .../[orgslug]/(withmenu)/courses/courses.tsx | 188 ++++++++-------- .../app/orgs/[orgslug]/(withmenu)/page.tsx | 148 ++++++------- .../orgs/[orgslug]/dash/courses/client.tsx | 171 +++++++-------- .../Thumbnails/CollectionThumbnail.tsx | 83 ++++--- .../Objects/Thumbnails/CourseThumbnail.tsx | 195 ++++++++--------- .../Wrappers/GeneralWrapper.tsx | 4 +- apps/web/components/ui/dropdown-menu.tsx | 205 ++++++++++++++++++ apps/web/package.json | 4 +- apps/web/pnpm-lock.yaml | 89 ++++++++ 12 files changed, 758 insertions(+), 508 deletions(-) create mode 100644 apps/web/components/ui/dropdown-menu.tsx diff --git a/apps/web/app/orgs/[orgslug]/(withmenu)/collections/page.tsx b/apps/web/app/orgs/[orgslug]/(withmenu)/collections/page.tsx index c09d9dce..343bfacf 100644 --- a/apps/web/app/orgs/[orgslug]/(withmenu)/collections/page.tsx +++ b/apps/web/app/orgs/[orgslug]/(withmenu)/collections/page.tsx @@ -74,87 +74,84 @@ const CollectionsPage = async (params: any) => { return ( -
- - - +
+ + - - - -
-
- {collections.map((collection: any) => ( -
- -
- ))} - {collections.length == 0 && ( -
-
-
- - - - -
-
-

+ + + + +

+
+ {collections.map((collection: any) => ( +
+ +
+ ))} + {collections.length === 0 && ( +
+
+
+ + + + +
+

No collections yet

-

+

+
+ + + + + +
- - - - -
-
- )} + )} +
) diff --git a/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/activity/[activityid]/activity.tsx b/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/activity/[activityid]/activity.tsx index 91e0aac7..3dc1853b 100644 --- a/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/activity/[activityid]/activity.tsx +++ b/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/activity/[activityid]/activity.tsx @@ -25,6 +25,7 @@ import AssignmentSubmissionProvider, { useAssignmentSubmission } from '@compone import toast from 'react-hot-toast' import { mutate } from 'swr' import ConfirmationModal from '@components/StyledElements/ConfirmationModal/ConfirmationModal' +import { useMediaQuery } from 'usehooks-ts' interface ActivityClientProps { activityid: string @@ -47,6 +48,7 @@ function ActivityClient(props: ActivityClientProps) { const [bgColor, setBgColor] = React.useState('bg-white') const [assignment, setAssignment] = React.useState(null) as any; const [markStatusButtonActive, setMarkStatusButtonActive] = React.useState(false); + function getChapterNameByActivityId(course: any, activity_id: any) { for (let i = 0; i < course.chapters.length; i++) { @@ -223,7 +225,7 @@ export function MarkStatus(props: { }) { const router = useRouter() const session = useLHSession() as any; - + const isMobile = useMediaQuery('(max-width: 768px)') async function markActivityAsCompleteFront() { const trail = await markActivityAsComplete( props.orgslug, @@ -263,7 +265,7 @@ export function MarkStatus(props: { {' '} - Mark as complete + {!isMobile && Mark as complete}
)} diff --git a/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/course.tsx b/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/course.tsx index fbf095de..b3311d8c 100644 --- a/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/course.tsx +++ b/apps/web/app/orgs/[orgslug]/(withmenu)/course/[courseuuid]/course.tsx @@ -18,6 +18,7 @@ import UserAvatar from '@components/Objects/UserAvatar' import CourseUpdates from '@components/Objects/CourseUpdates/CourseUpdates' import { CourseProvider } from '@components/Contexts/CourseContext' import { useLHSession } from '@components/Contexts/LHSessionContext' +import { useMediaQuery } from 'usehooks-ts' const CourseClient = (props: any) => { const [user, setUser] = useState({}) @@ -28,6 +29,7 @@ const CourseClient = (props: any) => { const course = props.course const org = useOrg() as any const router = useRouter() + const isMobile = useMediaQuery('(max-width: 768px)') function getLearningTags() { // create array of learnings from a string object (comma separated) @@ -72,21 +74,21 @@ const CourseClient = (props: any) => { ) : ( -
+

Course

-

{course.name}

+

{course.name}

-
- +
+ {!isMobile && - + }
{props.course?.thumbnail_image && org ? (
{ course={course} /> -
+

Description

@@ -141,7 +143,7 @@ const CourseClient = (props: any) => {
)} -

Course Lessons

+

Course Lessons

{course.chapters.map((chapter: any) => { return ( @@ -303,20 +305,20 @@ const CourseClient = (props: any) => { })}
-
+
{user && ( -
+
-
+
Author
-
+
{course.authors[0].first_name && course.authors[0].last_name && (
@@ -344,14 +346,14 @@ const CourseClient = (props: any) => { {isCourseStarted() ? ( ) : ( - } - /> - -
+
+
+ + + + } + dialogTitle="Create Course" + dialogDescription="Create a new course" + dialogTrigger={ + + } + /> + +
-
- {courses.map((course: any) => ( -
- -
- ))} - {courses.length == 0 && ( -
-
-
- - - - -
-
-

+
+ {courses.map((course: any) => ( +
+ +
+ ))} + {courses.length === 0 && ( +
+
+
+ + {/* ... SVG content ... */} + +
+

No courses yet

- {isUserAdmin ? (

- Create a course to add content -

) : (

- No courses available yet -

)} +

+ {isUserAdmin ? ( + "Create a course to add content" + ) : ( + "No courses available yet" + )} +

+ {isUserAdmin && ( +
+ + + } + dialogTitle="Create Course" + dialogDescription="Create a new course" + dialogTrigger={ + + } + /> + +
+ )}
- - - } - dialogTitle="Create Course" - dialogDescription="Create a new course" - dialogTrigger={ - - } - /> -
-
- )} + )} +

diff --git a/apps/web/app/orgs/[orgslug]/(withmenu)/page.tsx b/apps/web/app/orgs/[orgslug]/(withmenu)/page.tsx index 32c5df3b..004ce80b 100644 --- a/apps/web/app/orgs/[orgslug]/(withmenu)/page.tsx +++ b/apps/web/app/orgs/[orgslug]/(withmenu)/page.tsx @@ -82,48 +82,44 @@ const OrgHomePage = async (params: any) => { ) return ( -
+
{/* Collections */} -
-
+
+
-
- - - - - -
-
- {collections.map((collection: any) => ( -
- -
- ))} - {collections.length == 0 && ( -
-
-
-
+ + + + +
+
+ {collections.map((collection: any) => ( +
+ +
+ ))} + {collections.length === 0 && ( +
+
+
{ />
-
-

- No collections yet -

-

- -

-
+

+ No collections yet +

+

+ +

-
- )} + )} +
{/* Courses */} -
-
-
+
+
+ + + + +
- - - - - -
-
- {courses.map((course: any) => ( -
- -
- ))} - {courses.length == 0 && ( -
-
-
-
+
+ {courses.map((course: any) => ( +
+ +
+ ))} + {courses.length === 0 && ( +
+
+
{ />
-
-

- No courses yet -

-

- -

-
+

+ No courses yet +

+

+ +

-
- )} + )} +
diff --git a/apps/web/app/orgs/[orgslug]/dash/courses/client.tsx b/apps/web/app/orgs/[orgslug]/dash/courses/client.tsx index 31ae5ce3..57f1ba2a 100644 --- a/apps/web/app/orgs/[orgslug]/dash/courses/client.tsx +++ b/apps/web/app/orgs/[orgslug]/dash/courses/client.tsx @@ -1,12 +1,13 @@ 'use client' import BreadCrumbs from '@components/Dashboard/UI/BreadCrumbs' import CreateCourseModal from '@components/Objects/Modals/Course/Create/CreateCourse' -import CourseThumbnail from '@components/Objects/Thumbnails/CourseThumbnail' +import CourseThumbnail, { removeCoursePrefix } from '@components/Objects/Thumbnails/CourseThumbnail' import AuthenticatedClientElement from '@components/Security/AuthenticatedClientElement' import NewCourseButton from '@components/StyledElements/Buttons/NewCourseButton' import Modal from '@components/StyledElements/Modal/Modal' import { useSearchParams } from 'next/navigation' import React from 'react' +import useAdminStatus from '@components/Hooks/useAdminStatus' type CourseProps = { orgslug: string @@ -20,114 +21,106 @@ function CoursesHome(params: CourseProps) { const [newCourseModal, setNewCourseModal] = React.useState(isCreatingCourse) const orgslug = params.orgslug const courses = params.courses + const isUserAdmin = useAdminStatus() as any async function closeNewCourseModal() { setNewCourseModal(false) } return ( -
-
-
- -
-
Courses
- - - } - dialogTitle="Create Course" - dialogDescription="Create a new course" - dialogTrigger={ - - } - /> - -
+
+
+ +
+

Courses

+ + + } + dialogTitle="Create Course" + dialogDescription="Create a new course" + dialogTrigger={ + + } + /> +
-
+ +
{courses.map((course: any) => ( -
- +
+
))} - {courses.length == 0 && ( -
-
-
+ {courses.length === 0 && ( +
+
+
- - + {/* ... SVG content ... */}
-
-

- No courses yet -

-

- Create a course to add content -

-
- - - } - dialogTitle="Create Course" - dialogDescription="Create a new course" - dialogTrigger={ - - } - /> - +

+ No courses yet +

+

+ {isUserAdmin ? ( + "Create a course to add content" + ) : ( + "No courses available yet" + )} +

+ {isUserAdmin && ( +
+ + + } + dialogTitle="Create Course" + dialogDescription="Create a new course" + dialogTrigger={ + + } + /> + +
+ )}
)} diff --git a/apps/web/components/Objects/Thumbnails/CollectionThumbnail.tsx b/apps/web/components/Objects/Thumbnails/CollectionThumbnail.tsx index df27f05a..d1f92fe4 100644 --- a/apps/web/components/Objects/Thumbnails/CollectionThumbnail.tsx +++ b/apps/web/components/Objects/Thumbnails/CollectionThumbnail.tsx @@ -25,43 +25,42 @@ const removeCollectionPrefix = (collectionid: string) => { function CollectionThumbnail(props: PropsType) { const org = useOrg() as any return ( -
-
-
- {props.collection.courses.slice(0, 2).map((course: any) => ( - <> - -
- - - ))} +
+
+
+
+ {props.collection.courses.slice(0, 3).map((course: any, index: number) => ( +
+ ))} +
+
+ + {props.collection.name} + + + {props.collection.courses.length} course{props.collection.courses.length !== 1 ? 's' : ''} + +
- -

- {props.collection.name} -

- { orgId={props.org_id} checkMethod="roles" > -
+
- -
+ + } functionToExecute={() => deleteCollectionUI(props.collection_uuid)} status="warning" diff --git a/apps/web/components/Objects/Thumbnails/CourseThumbnail.tsx b/apps/web/components/Objects/Thumbnails/CourseThumbnail.tsx index 5a4ada50..dbb2a85d 100644 --- a/apps/web/components/Objects/Thumbnails/CourseThumbnail.tsx +++ b/apps/web/components/Objects/Thumbnails/CourseThumbnail.tsx @@ -6,146 +6,125 @@ import { getUriWithOrg } from '@services/config/config' import { deleteCourseFromBackend } from '@services/courses/courses' import { getCourseThumbnailMediaDirectory } from '@services/media/media' import { revalidateTags } from '@services/utils/ts/requests' -import { BookMinus, FilePenLine, Settings2, EllipsisVertical } from 'lucide-react' +import { BookMinus, FilePenLine, Settings2, MoreVertical } from 'lucide-react' import { useLHSession } from '@components/Contexts/LHSessionContext' import Link from 'next/link' import { useRouter } from 'next/navigation' -import React, { useEffect } from 'react' +import React from 'react' import toast from 'react-hot-toast' +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu" + +type Course = { + course_uuid: string + name: string + description: string + thumbnail_image: string + org_id: string +} type PropsType = { - course: any + course: Course orgslug: string + customLink?: string } -// function to remove "course_" from the course_uuid -function removeCoursePrefix(course_uuid: string) { - return course_uuid.replace('course_', '') -} +export const removeCoursePrefix = (course_uuid: string) => course_uuid.replace('course_', '') -function CourseThumbnail(props: PropsType) { - const router = useRouter() +function CourseThumbnail({ course, orgslug, customLink }: PropsType) { + const router = useRouter() const org = useOrg() as any - const session = useLHSession() as any; + const session = useLHSession() as any - async function deleteCourses(course_uuid: any) { - const toast_loading = toast.loading('Deleting course...') - await deleteCourseFromBackend(course_uuid, session.data?.tokens?.access_token) - toast.dismiss(toast_loading) - toast.success('Course deleted successfully') - await revalidateTags(['courses'], props.orgslug) - - router.refresh() + const deleteCourse = async () => { + const toastId = toast.loading('Deleting course...') + try { + await deleteCourseFromBackend(course.course_uuid, session.data?.tokens?.access_token) + await revalidateTags(['courses'], orgslug) + toast.success('Course deleted successfully') + router.refresh() + } catch (error) { + toast.error('Failed to delete course') + } finally { + toast.dismiss(toastId) + } } - useEffect(() => { }, [org]) + const thumbnailImage = course.thumbnail_image + ? getCourseThumbnailMediaDirectory(org?.org_uuid, course.course_uuid, course.thumbnail_image) + : '../empty_thumbnail.png' return (
- - - {props.course.thumbnail_image ? ( -
- ) : ( -
- )} + +
-
-

{props.course.name}

-

{props.course.description}

+
+

{course.name}

+

{course.description}

) } -const AdminEditsArea = (props: { +const AdminEditOptions = ({ course, orgSlug, deleteCourse }: { + course: Course orgSlug: string - courseId: string - course: any - deleteCourses: any + deleteCourse: () => Promise }) => { return ( -
- -
- -
- - -
- -
- - - - -
- } - functionToExecute={() => props.deleteCourses(props.courseId)} - status="warning" - > +
+ + + + + + + + Edit Content + + + + + Settings + + + + + Delete Course + + } + functionToExecute={deleteCourse} + status="warning" + /> + + +
) diff --git a/apps/web/components/StyledElements/Wrappers/GeneralWrapper.tsx b/apps/web/components/StyledElements/Wrappers/GeneralWrapper.tsx index 00dc7326..6e078b9b 100644 --- a/apps/web/components/StyledElements/Wrappers/GeneralWrapper.tsx +++ b/apps/web/components/StyledElements/Wrappers/GeneralWrapper.tsx @@ -1,9 +1,9 @@ function GeneralWrapperStyled({ children }: { children: React.ReactNode }) { return ( -
+
{children}
) } -export default GeneralWrapperStyled +export default GeneralWrapperStyled \ No newline at end of file diff --git a/apps/web/components/ui/dropdown-menu.tsx b/apps/web/components/ui/dropdown-menu.tsx new file mode 100644 index 00000000..76ad9152 --- /dev/null +++ b/apps/web/components/ui/dropdown-menu.tsx @@ -0,0 +1,205 @@ +"use client" + +import * as React from "react" +import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu" +import { + CheckIcon, + ChevronRightIcon, + DotFilledIcon, +} from "@radix-ui/react-icons" + +import { cn } from "@/lib/utils" + +const DropdownMenu = DropdownMenuPrimitive.Root + +const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger + +const DropdownMenuGroup = DropdownMenuPrimitive.Group + +const DropdownMenuPortal = DropdownMenuPrimitive.Portal + +const DropdownMenuSub = DropdownMenuPrimitive.Sub + +const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup + +const DropdownMenuSubTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean + } +>(({ className, inset, children, ...props }, ref) => ( + + {children} + + +)) +DropdownMenuSubTrigger.displayName = + DropdownMenuPrimitive.SubTrigger.displayName + +const DropdownMenuSubContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +DropdownMenuSubContent.displayName = + DropdownMenuPrimitive.SubContent.displayName + +const DropdownMenuContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, sideOffset = 4, ...props }, ref) => ( + + + +)) +DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName + +const DropdownMenuItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean + } +>(({ className, inset, ...props }, ref) => ( + svg]:size-4 [&>svg]:shrink-0", + inset && "pl-8", + className + )} + {...props} + /> +)) +DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName + +const DropdownMenuCheckboxItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, checked, ...props }, ref) => ( + + + + + + + {children} + +)) +DropdownMenuCheckboxItem.displayName = + DropdownMenuPrimitive.CheckboxItem.displayName + +const DropdownMenuRadioItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + + + + + {children} + +)) +DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName + +const DropdownMenuLabel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean + } +>(({ className, inset, ...props }, ref) => ( + +)) +DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName + +const DropdownMenuSeparator = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName + +const DropdownMenuShortcut = ({ + className, + ...props +}: React.HTMLAttributes) => { + return ( + + ) +} +DropdownMenuShortcut.displayName = "DropdownMenuShortcut" + +export { + DropdownMenu, + DropdownMenuTrigger, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuCheckboxItem, + DropdownMenuRadioItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuShortcut, + DropdownMenuGroup, + DropdownMenuPortal, + DropdownMenuSub, + DropdownMenuSubContent, + DropdownMenuSubTrigger, + DropdownMenuRadioGroup, +} diff --git a/apps/web/package.json b/apps/web/package.json index 10c0d6ee..bf2141ae 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -3,7 +3,7 @@ "version": "0.1.0", "private": true, "scripts": { - "dev": "next dev", + "dev": "next dev --turbo", "dev-https": "next dev --experimental-https -p 443", "build": "next build", "start": "next start", @@ -17,6 +17,7 @@ "@radix-ui/colors": "^0.1.9", "@radix-ui/react-aspect-ratio": "^1.1.0", "@radix-ui/react-dialog": "^1.1.2", + "@radix-ui/react-dropdown-menu": "^2.1.2", "@radix-ui/react-form": "^0.0.3", "@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-switch": "^1.1.1", @@ -69,6 +70,7 @@ "tailwind-scrollbar": "^3.1.0", "tailwindcss-animate": "^1.0.7", "unsplash-js": "^7.0.19", + "usehooks-ts": "^3.1.0", "uuid": "^9.0.1", "y-indexeddb": "^9.0.12", "y-prosemirror": "^1.2.12", diff --git a/apps/web/pnpm-lock.yaml b/apps/web/pnpm-lock.yaml index 9bd75d33..77e39efa 100644 --- a/apps/web/pnpm-lock.yaml +++ b/apps/web/pnpm-lock.yaml @@ -26,6 +26,9 @@ importers: '@radix-ui/react-dialog': specifier: ^1.1.2 version: 1.1.2(@types/react-dom@18.2.23)(@types/react@18.2.74)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-dropdown-menu': + specifier: ^2.1.2 + version: 2.1.2(@types/react-dom@18.2.23)(@types/react@18.2.74)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-form': specifier: ^0.0.3 version: 0.0.3(@types/react-dom@18.2.23)(@types/react@18.2.74)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -182,6 +185,9 @@ importers: unsplash-js: specifier: ^7.0.19 version: 7.0.19 + usehooks-ts: + specifier: ^3.1.0 + version: 3.1.0(react@18.3.1) uuid: specifier: ^9.0.1 version: 9.0.1 @@ -963,6 +969,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-dropdown-menu@2.1.2': + resolution: {integrity: sha512-GVZMR+eqK8/Kes0a36Qrv+i20bAPXSn8rCBTHx30w+3ECnR5o3xixAlqcVaYvLeyKUsm0aqyhWfmUcqufM8nYA==} + 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-focus-guards@1.1.1': resolution: {integrity: sha512-pSIwfrT1a6sIoDASCSpFwOasEwKTZWDw/iBdtnqKO7v6FeOzYJ7U53cPzYFVR3geGGXgVHaH+CdngrrAzqUGxg==} peerDependencies: @@ -1034,6 +1053,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-menu@2.1.2': + resolution: {integrity: sha512-lZ0R4qR2Al6fZ4yCCZzu/ReTFrylHFxIqy7OezIpWF4bL0o9biKo0pFIvkaew3TyZ9Fy5gYVrR5zCGZBVbO1zg==} + 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-popper@1.2.0': resolution: {integrity: sha512-ZnRMshKF43aBxVWPWvbj21+7TQCvhuULWJ4gNIKYpRlQt5xGRhLx66tMp8pya2UkGHTSlhpXwmjqltDYHhw7Vg==} peerDependencies: @@ -2866,6 +2898,9 @@ packages: lodash-es@4.17.21: resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==} + lodash.debounce@4.0.8: + resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} + lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} @@ -3876,6 +3911,12 @@ packages: peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 + usehooks-ts@3.1.0: + resolution: {integrity: sha512-bBIa7yUyPhE1BCc0GmR96VU/15l/9gP1Ch5mYdLcFBaFGQsdmXkvjV0TtOqW1yUd6VjIwDunm+flSciCQXujiw==} + engines: {node: '>=16.15.0'} + peerDependencies: + react: ^16.8.0 || ^17 || ^18 + util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} @@ -4788,6 +4829,21 @@ snapshots: '@types/react': 18.2.74 '@types/react-dom': 18.2.23 + '@radix-ui/react-dropdown-menu@2.1.2(@types/react-dom@18.2.23)(@types/react@18.2.74)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.0 + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.2.74)(react@18.3.1) + '@radix-ui/react-context': 1.1.1(@types/react@18.2.74)(react@18.3.1) + '@radix-ui/react-id': 1.1.0(@types/react@18.2.74)(react@18.3.1) + '@radix-ui/react-menu': 2.1.2(@types/react-dom@18.2.23)(@types/react@18.2.74)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.2.23)(@types/react@18.2.74)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.2.74)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.2.74 + '@types/react-dom': 18.2.23 + '@radix-ui/react-focus-guards@1.1.1(@types/react@18.2.74)(react@18.3.1)': dependencies: react: 18.3.1 @@ -4849,6 +4905,32 @@ snapshots: '@types/react': 18.2.74 '@types/react-dom': 18.2.23 + '@radix-ui/react-menu@2.1.2(@types/react-dom@18.2.23)(@types/react@18.2.74)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.0 + '@radix-ui/react-collection': 1.1.0(@types/react-dom@18.2.23)(@types/react@18.2.74)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.2.74)(react@18.3.1) + '@radix-ui/react-context': 1.1.1(@types/react@18.2.74)(react@18.3.1) + '@radix-ui/react-direction': 1.1.0(@types/react@18.2.74)(react@18.3.1) + '@radix-ui/react-dismissable-layer': 1.1.1(@types/react-dom@18.2.23)(@types/react@18.2.74)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-focus-guards': 1.1.1(@types/react@18.2.74)(react@18.3.1) + '@radix-ui/react-focus-scope': 1.1.0(@types/react-dom@18.2.23)(@types/react@18.2.74)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-id': 1.1.0(@types/react@18.2.74)(react@18.3.1) + '@radix-ui/react-popper': 1.2.0(@types/react-dom@18.2.23)(@types/react@18.2.74)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-portal': 1.1.2(@types/react-dom@18.2.23)(@types/react@18.2.74)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-presence': 1.1.1(@types/react-dom@18.2.23)(@types/react@18.2.74)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.2.23)(@types/react@18.2.74)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-roving-focus': 1.1.0(@types/react-dom@18.2.23)(@types/react@18.2.74)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.1.0(@types/react@18.2.74)(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.2.74)(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.2.74)(react@18.3.1) + optionalDependencies: + '@types/react': 18.2.74 + '@types/react-dom': 18.2.23 + '@radix-ui/react-popper@1.2.0(@types/react-dom@18.2.23)(@types/react@18.2.74)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@floating-ui/react-dom': 2.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -6979,6 +7061,8 @@ snapshots: lodash-es@4.17.21: {} + lodash.debounce@4.0.8: {} + lodash.merge@4.6.2: {} lodash@4.17.21: {} @@ -8095,6 +8179,11 @@ snapshots: dependencies: react: 18.3.1 + usehooks-ts@3.1.0(react@18.3.1): + dependencies: + lodash.debounce: 4.0.8 + react: 18.3.1 + util-deprecate@1.0.2: {} uuid@8.3.2: {}