feat: complete profile page
This commit is contained in:
parent
00e51205cf
commit
105eab9bad
59
frontend/app/[username]/page.tsx
Normal file
59
frontend/app/[username]/page.tsx
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import ProfilePage from "@/components/profile"
|
||||||
|
import ProfileNavbar from "@/components/profile/navbar"
|
||||||
|
import { Sandbox, User } from "@/lib/types"
|
||||||
|
import { currentUser } from "@clerk/nextjs"
|
||||||
|
|
||||||
|
export default async function Page({
|
||||||
|
params: { username: rawUsername },
|
||||||
|
}: {
|
||||||
|
params: { username: string }
|
||||||
|
}) {
|
||||||
|
const username = decodeURIComponent(rawUsername).replace("@", "")
|
||||||
|
const currentLoggedInUser = await currentUser()
|
||||||
|
console.log(username)
|
||||||
|
const [profileRespnse, dbUserResponse] = await Promise.all([
|
||||||
|
fetch(
|
||||||
|
`${process.env.NEXT_PUBLIC_DATABASE_WORKER_URL}/api/user?username=${username}`,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
Authorization: `${process.env.NEXT_PUBLIC_WORKERS_KEY}`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
),
|
||||||
|
fetch(
|
||||||
|
`${process.env.NEXT_PUBLIC_DATABASE_WORKER_URL}/api/user?id=${currentLoggedInUser?.id}`,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
Authorization: `${process.env.NEXT_PUBLIC_WORKERS_KEY}`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
),
|
||||||
|
])
|
||||||
|
|
||||||
|
const userProfile = (await profileRespnse.json()) as User
|
||||||
|
const dbUserData = (await dbUserResponse.json()) as User
|
||||||
|
const publicSandboxes: Sandbox[] = []
|
||||||
|
const privateSandboxes: Sandbox[] = []
|
||||||
|
|
||||||
|
userProfile?.sandbox?.forEach((sandbox) => {
|
||||||
|
if (sandbox.visibility === "public") {
|
||||||
|
publicSandboxes.push(sandbox)
|
||||||
|
} else if (sandbox.visibility === "private") {
|
||||||
|
privateSandboxes.push(sandbox)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const hasCurrentUser = Boolean(dbUserData?.id)
|
||||||
|
return (
|
||||||
|
<div className="">
|
||||||
|
<ProfileNavbar userData={dbUserData} />
|
||||||
|
<ProfilePage
|
||||||
|
publicSandboxes={publicSandboxes}
|
||||||
|
privateSandboxes={
|
||||||
|
userProfile?.id === dbUserData.id ? privateSandboxes : []
|
||||||
|
}
|
||||||
|
user={userProfile}
|
||||||
|
currentUser={hasCurrentUser ? dbUserData : null}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
@ -8,7 +8,7 @@ import DashboardNavbarSearch from "./search"
|
|||||||
|
|
||||||
export default function DashboardNavbar({ userData }: { userData: User }) {
|
export default function DashboardNavbar({ userData }: { userData: User }) {
|
||||||
return (
|
return (
|
||||||
<div className="h-16 px-4 w-full flex items-center justify-between border-b border-border">
|
<div className=" py-2 px-4 w-full flex items-center justify-between border-b border-border">
|
||||||
<div className="flex items-center space-x-4">
|
<div className="flex items-center space-x-4">
|
||||||
<Link
|
<Link
|
||||||
href="/"
|
href="/"
|
||||||
|
@ -11,13 +11,13 @@ import {
|
|||||||
} from "@/components/ui/dropdown-menu"
|
} from "@/components/ui/dropdown-menu"
|
||||||
|
|
||||||
export default function ProjectCardDropdown({
|
export default function ProjectCardDropdown({
|
||||||
sandbox,
|
visibility,
|
||||||
onVisibilityChange,
|
onVisibilityChange,
|
||||||
onDelete,
|
onDelete,
|
||||||
}: {
|
}: {
|
||||||
sandbox: Sandbox
|
visibility: Sandbox["visibility"]
|
||||||
onVisibilityChange: (sandbox: Sandbox) => void
|
onVisibilityChange: () => void
|
||||||
onDelete: (sandbox: Sandbox) => void
|
onDelete: () => void
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<DropdownMenu modal={false}>
|
<DropdownMenu modal={false}>
|
||||||
@ -34,11 +34,11 @@ export default function ProjectCardDropdown({
|
|||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
onVisibilityChange(sandbox)
|
onVisibilityChange()
|
||||||
}}
|
}}
|
||||||
className="cursor-pointer"
|
className="cursor-pointer"
|
||||||
>
|
>
|
||||||
{sandbox.visibility === "public" ? (
|
{visibility === "public" ? (
|
||||||
<>
|
<>
|
||||||
<Lock className="mr-2 h-4 w-4" />
|
<Lock className="mr-2 h-4 w-4" />
|
||||||
<span>Make Private</span>
|
<span>Make Private</span>
|
||||||
@ -53,7 +53,7 @@ export default function ProjectCardDropdown({
|
|||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
onDelete(sandbox)
|
onDelete()
|
||||||
}}
|
}}
|
||||||
className="!text-destructive cursor-pointer"
|
className="!text-destructive cursor-pointer"
|
||||||
>
|
>
|
||||||
|
@ -4,56 +4,154 @@ import { Card } from "@/components/ui/card"
|
|||||||
import { projectTemplates } from "@/lib/data"
|
import { projectTemplates } from "@/lib/data"
|
||||||
import { Sandbox } from "@/lib/types"
|
import { Sandbox } from "@/lib/types"
|
||||||
import { AnimatePresence, motion } from "framer-motion"
|
import { AnimatePresence, motion } from "framer-motion"
|
||||||
import { Clock, Globe, Lock } from "lucide-react"
|
import { Clock, Eye, Globe, Heart, Lock } from "lucide-react"
|
||||||
import Image from "next/image"
|
import Image from "next/image"
|
||||||
import { useRouter } from "next/navigation"
|
import { useRouter } from "next/navigation"
|
||||||
import { useEffect, useState } from "react"
|
import { memo, useEffect, useMemo, useState } from "react"
|
||||||
import ProjectCardDropdown from "./dropdown"
|
import ProjectCardDropdown from "./dropdown"
|
||||||
|
import { CanvasRevealEffect } from "./revealEffect"
|
||||||
|
|
||||||
export default function ProjectCard({
|
type BaseProjectCardProps = {
|
||||||
children,
|
id: string
|
||||||
sandbox,
|
name: string
|
||||||
onVisibilityChange,
|
type: string
|
||||||
onDelete,
|
visibility: "public" | "private"
|
||||||
deletingId,
|
createdAt: Date
|
||||||
}: {
|
likeCount: number
|
||||||
children?: React.ReactNode
|
viewCount: number
|
||||||
sandbox: Sandbox
|
}
|
||||||
onVisibilityChange: (sandbox: Sandbox) => void
|
|
||||||
onDelete: (sandbox: Sandbox) => void
|
type AuthenticatedProjectCardProps = BaseProjectCardProps & {
|
||||||
|
isAuthenticated: true
|
||||||
|
onVisibilityChange: (
|
||||||
|
sandbox: Pick<Sandbox, "id" | "name" | "visibility">
|
||||||
|
) => void
|
||||||
|
onDelete: (sandbox: Pick<Sandbox, "id" | "name">) => void
|
||||||
deletingId: string
|
deletingId: string
|
||||||
}) {
|
}
|
||||||
|
|
||||||
|
type UnauthenticatedProjectCardProps = BaseProjectCardProps & {
|
||||||
|
isAuthenticated: false
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProjectCardProps =
|
||||||
|
| AuthenticatedProjectCardProps
|
||||||
|
| UnauthenticatedProjectCardProps
|
||||||
|
|
||||||
|
const StatItem = memo(({ icon: Icon, value }: { icon: any; value: number }) => (
|
||||||
|
<div className="flex items-center space-x-1">
|
||||||
|
<Icon className="size-4" />
|
||||||
|
<span className="text-xs">{value}</span>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
|
||||||
|
StatItem.displayName = "StatItem"
|
||||||
|
|
||||||
|
const formatDate = (date: Date): string => {
|
||||||
|
const now = new Date()
|
||||||
|
const diffInMinutes = Math.floor((now.getTime() - date.getTime()) / 60000)
|
||||||
|
|
||||||
|
if (diffInMinutes < 1) return "Now"
|
||||||
|
if (diffInMinutes < 60) return `${diffInMinutes}m ago`
|
||||||
|
if (diffInMinutes < 1440) return `${Math.floor(diffInMinutes / 60)}h ago`
|
||||||
|
return `${Math.floor(diffInMinutes / 1440)}d ago`
|
||||||
|
}
|
||||||
|
|
||||||
|
const ProjectMetadata = memo(
|
||||||
|
({
|
||||||
|
visibility,
|
||||||
|
createdAt,
|
||||||
|
likeCount,
|
||||||
|
viewCount,
|
||||||
|
}: Pick<
|
||||||
|
BaseProjectCardProps,
|
||||||
|
"visibility" | "createdAt" | "likeCount" | "viewCount"
|
||||||
|
>) => {
|
||||||
|
const [date, setDate] = useState<string>()
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setDate(formatDate(new Date(createdAt)))
|
||||||
|
}, [createdAt])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col text-muted-foreground space-y-2 text-sm z-10">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div className="flex items-center">
|
||||||
|
{visibility === "private" ? (
|
||||||
|
<>
|
||||||
|
<Lock className="size-4 mr-2" /> Private
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Globe className="size-4 mr-2" /> Public
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex gap-4">
|
||||||
|
<div className="flex items-center">
|
||||||
|
<Clock className="size-4 mr-2" /> {date}
|
||||||
|
</div>
|
||||||
|
<StatItem icon={Heart} value={likeCount} />
|
||||||
|
<StatItem icon={Eye} value={viewCount} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
ProjectMetadata.displayName = "ProjectMetadata"
|
||||||
|
|
||||||
|
function ProjectCardComponent({
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
type,
|
||||||
|
visibility,
|
||||||
|
createdAt,
|
||||||
|
likeCount,
|
||||||
|
viewCount,
|
||||||
|
...props
|
||||||
|
}: ProjectCardProps) {
|
||||||
const [hovered, setHovered] = useState(false)
|
const [hovered, setHovered] = useState(false)
|
||||||
const [date, setDate] = useState<string>()
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
useEffect(() => {
|
const projectIcon = useMemo(
|
||||||
const createdAt = new Date(sandbox.createdAt)
|
() =>
|
||||||
const now = new Date()
|
projectTemplates.find((p) => p.id === type)?.icon ??
|
||||||
const diffInMinutes = Math.floor(
|
"/project-icons/node.svg",
|
||||||
(now.getTime() - createdAt.getTime()) / 60000
|
[type]
|
||||||
)
|
)
|
||||||
|
|
||||||
if (diffInMinutes < 1) {
|
const handleVisibilityChange = () => {
|
||||||
setDate("Now")
|
if (props.isAuthenticated) {
|
||||||
} else if (diffInMinutes < 60) {
|
props.onVisibilityChange({
|
||||||
setDate(`${diffInMinutes}m ago`)
|
id,
|
||||||
} else if (diffInMinutes < 1440) {
|
name,
|
||||||
setDate(`${Math.floor(diffInMinutes / 60)}h ago`)
|
visibility,
|
||||||
} else {
|
})
|
||||||
setDate(`${Math.floor(diffInMinutes / 1440)}d ago`)
|
|
||||||
}
|
}
|
||||||
}, [sandbox])
|
}
|
||||||
const projectIcon =
|
|
||||||
projectTemplates.find((p) => p.id === sandbox.type)?.icon ??
|
const handleDelete = () => {
|
||||||
"/project-icons/node.svg"
|
if (props.isAuthenticated) {
|
||||||
|
props.onDelete({
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
onClick={() => router.push(`/code/${sandbox.id}`)}
|
onClick={() => router.push(`/code/${id}`)}
|
||||||
onMouseEnter={() => setHovered(true)}
|
onMouseEnter={() => setHovered(true)}
|
||||||
onMouseLeave={() => setHovered(false)}
|
onMouseLeave={() => setHovered(false)}
|
||||||
className={`group/canvas-card p-4 h-48 flex flex-col justify-between items-start hover:border-muted-foreground/50 relative overflow-hidden transition-all`}
|
className={`
|
||||||
|
group/canvas-card p-4 h-48 flex flex-col justify-between items-start
|
||||||
|
hover:border-muted-foreground/50 relative overflow-hidden transition-all
|
||||||
|
${props.isAuthenticated && props.deletingId === id ? "opacity-50" : ""}
|
||||||
|
`}
|
||||||
>
|
>
|
||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
{hovered && (
|
{hovered && (
|
||||||
@ -62,38 +160,59 @@ export default function ProjectCard({
|
|||||||
animate={{ opacity: 1 }}
|
animate={{ opacity: 1 }}
|
||||||
className="h-full w-full absolute inset-0"
|
className="h-full w-full absolute inset-0"
|
||||||
>
|
>
|
||||||
{children}
|
<CanvasRevealEffect
|
||||||
|
animationSpeed={3}
|
||||||
|
containerClassName="bg-black"
|
||||||
|
colors={colors[type]}
|
||||||
|
dotSize={2}
|
||||||
|
/>
|
||||||
|
<div className="absolute inset-0 [mask-image:radial-gradient(400px_at_center,white,transparent)] bg-background/75" />
|
||||||
</motion.div>
|
</motion.div>
|
||||||
)}
|
)}
|
||||||
</AnimatePresence>
|
</AnimatePresence>
|
||||||
|
|
||||||
<div className="space-x-2 flex items-center justify-start w-full z-10">
|
<div className="space-x-2 flex items-center justify-start w-full z-10">
|
||||||
<Image alt="" src={projectIcon} width={20} height={20} />
|
<Image
|
||||||
<div className="font-medium static whitespace-nowrap w-full text-ellipsis overflow-hidden">
|
alt={`${type} project icon`}
|
||||||
{sandbox.name}
|
src={projectIcon}
|
||||||
</div>
|
width={20}
|
||||||
<ProjectCardDropdown
|
height={20}
|
||||||
sandbox={sandbox}
|
|
||||||
onVisibilityChange={onVisibilityChange}
|
|
||||||
onDelete={onDelete}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
<div className="font-medium static whitespace-nowrap w-full text-ellipsis overflow-hidden">
|
||||||
<div className="flex flex-col text-muted-foreground space-y-0.5 text-sm z-10">
|
{name}
|
||||||
<div className="flex items-center">
|
|
||||||
{sandbox.visibility === "private" ? (
|
|
||||||
<>
|
|
||||||
<Lock className="w-3 h-3 mr-2" /> Private
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<Globe className="w-3 h-3 mr-2" /> Public
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center">
|
|
||||||
<Clock className="w-3 h-3 mr-2" /> {date}
|
|
||||||
</div>
|
</div>
|
||||||
|
{props.isAuthenticated && (
|
||||||
|
<ProjectCardDropdown
|
||||||
|
onVisibilityChange={handleVisibilityChange}
|
||||||
|
onDelete={handleDelete}
|
||||||
|
visibility={visibility}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<ProjectMetadata
|
||||||
|
visibility={visibility}
|
||||||
|
createdAt={createdAt}
|
||||||
|
likeCount={likeCount}
|
||||||
|
viewCount={viewCount}
|
||||||
|
/>
|
||||||
</Card>
|
</Card>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ProjectCardComponent.displayName = "ProjectCard"
|
||||||
|
|
||||||
|
const ProjectCard = memo(ProjectCardComponent)
|
||||||
|
|
||||||
|
export default ProjectCard
|
||||||
|
|
||||||
|
const colors: { [key: string]: number[][] } = {
|
||||||
|
react: [
|
||||||
|
[71, 207, 237],
|
||||||
|
[30, 126, 148],
|
||||||
|
],
|
||||||
|
node: [
|
||||||
|
[86, 184, 72],
|
||||||
|
[59, 112, 52],
|
||||||
|
],
|
||||||
|
}
|
||||||
|
@ -2,11 +2,11 @@
|
|||||||
|
|
||||||
import { deleteSandbox, updateSandbox } from "@/lib/actions"
|
import { deleteSandbox, updateSandbox } from "@/lib/actions"
|
||||||
import { Sandbox } from "@/lib/types"
|
import { Sandbox } from "@/lib/types"
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
import Link from "next/link"
|
import Link from "next/link"
|
||||||
import { useEffect, useState } from "react"
|
import { useEffect, useMemo, useState } from "react"
|
||||||
import { toast } from "sonner"
|
import { toast } from "sonner"
|
||||||
import ProjectCard from "./projectCard"
|
import ProjectCard from "./projectCard"
|
||||||
import { CanvasRevealEffect } from "./projectCard/revealEffect"
|
|
||||||
|
|
||||||
const colors: { [key: string]: number[][] } = {
|
const colors: { [key: string]: number[][] } = {
|
||||||
react: [
|
react: [
|
||||||
@ -28,11 +28,27 @@ export default function DashboardProjects({
|
|||||||
}) {
|
}) {
|
||||||
const [deletingId, setDeletingId] = useState<string>("")
|
const [deletingId, setDeletingId] = useState<string>("")
|
||||||
|
|
||||||
const onDelete = async (sandbox: Sandbox) => {
|
const onVisibilityChange = useMemo(
|
||||||
setDeletingId(sandbox.id)
|
() => async (sandbox: Pick<Sandbox, "id" | "name" | "visibility">) => {
|
||||||
toast(`Project ${sandbox.name} deleted.`)
|
const newVisibility =
|
||||||
await deleteSandbox(sandbox.id)
|
sandbox.visibility === "public" ? "private" : "public"
|
||||||
}
|
toast(`Project ${sandbox.name} is now ${newVisibility}.`)
|
||||||
|
await updateSandbox({
|
||||||
|
id: sandbox.id,
|
||||||
|
visibility: newVisibility,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
|
||||||
|
const onDelete = useMemo(
|
||||||
|
() => async (sandbox: Pick<Sandbox, "id" | "name">) => {
|
||||||
|
setDeletingId(sandbox.id)
|
||||||
|
toast(`Project ${sandbox.name} deleted.`)
|
||||||
|
await deleteSandbox(sandbox.id)
|
||||||
|
},
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (deletingId) {
|
if (deletingId) {
|
||||||
@ -40,15 +56,6 @@ export default function DashboardProjects({
|
|||||||
}
|
}
|
||||||
}, [sandboxes])
|
}, [sandboxes])
|
||||||
|
|
||||||
const onVisibilityChange = async (sandbox: Sandbox) => {
|
|
||||||
const newVisibility = sandbox.visibility === "public" ? "private" : "public"
|
|
||||||
toast(`Project ${sandbox.name} is now ${newVisibility}.`)
|
|
||||||
await updateSandbox({
|
|
||||||
id: sandbox.id,
|
|
||||||
visibility: newVisibility,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="grow p-4 flex flex-col">
|
<div className="grow p-4 flex flex-col">
|
||||||
<div className="text-xl font-medium mb-8">
|
<div className="text-xl font-medium mb-8">
|
||||||
@ -67,26 +74,20 @@ export default function DashboardProjects({
|
|||||||
<Link
|
<Link
|
||||||
key={sandbox.id}
|
key={sandbox.id}
|
||||||
href={`/code/${sandbox.id}`}
|
href={`/code/${sandbox.id}`}
|
||||||
className={`${
|
className={cn(
|
||||||
|
"transition-all focus-visible:outline-none focus-visible:ring-offset-2 focus-visible:ring-offset-background focus-visible:ring-2 focus-visible:ring-ring rounded-lg",
|
||||||
deletingId === sandbox.id
|
deletingId === sandbox.id
|
||||||
? "pointer-events-none opacity-50 cursor-events-none"
|
? "pointer-events-none opacity-50 cursor-events-none"
|
||||||
: "cursor-pointer"
|
: "cursor-pointer"
|
||||||
} transition-all focus-visible:outline-none focus-visible:ring-offset-2 focus-visible:ring-offset-background focus-visible:ring-2 focus-visible:ring-ring rounded-lg`}
|
)}
|
||||||
>
|
>
|
||||||
<ProjectCard
|
<ProjectCard
|
||||||
sandbox={sandbox}
|
|
||||||
onVisibilityChange={onVisibilityChange}
|
onVisibilityChange={onVisibilityChange}
|
||||||
onDelete={onDelete}
|
onDelete={onDelete}
|
||||||
deletingId={deletingId}
|
deletingId={deletingId}
|
||||||
>
|
isAuthenticated
|
||||||
<CanvasRevealEffect
|
{...sandbox}
|
||||||
animationSpeed={3}
|
/>
|
||||||
containerClassName="bg-black"
|
|
||||||
colors={colors[sandbox.type]}
|
|
||||||
dotSize={2}
|
|
||||||
/>
|
|
||||||
<div className="absolute inset-0 [mask-image:radial-gradient(400px_at_center,white,transparent)] bg-background/75" />
|
|
||||||
</ProjectCard>
|
|
||||||
</Link>
|
</Link>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import ProjectCard from "@/components/dashboard/projectCard/"
|
import ProjectCard from "@/components/dashboard/projectCard/"
|
||||||
import { CanvasRevealEffect } from "@/components/dashboard/projectCard/revealEffect"
|
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/components/ui/button"
|
||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
@ -9,22 +8,23 @@ import {
|
|||||||
CardDescription,
|
CardDescription,
|
||||||
CardTitle,
|
CardTitle,
|
||||||
} from "@/components/ui/card"
|
} from "@/components/ui/card"
|
||||||
|
import {
|
||||||
|
HoverCard,
|
||||||
|
HoverCardContent,
|
||||||
|
HoverCardTrigger,
|
||||||
|
} from "@/components/ui/hover-card"
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
|
||||||
import { updateSandbox } from "@/lib/actions"
|
import { deleteSandbox, updateSandbox } from "@/lib/actions"
|
||||||
|
import { MAX_FREE_GENERATION } from "@/lib/constant"
|
||||||
import { Sandbox, User } from "@/lib/types"
|
import { Sandbox, User } from "@/lib/types"
|
||||||
import { PlusCircle } from "lucide-react"
|
import { cn } from "@/lib/utils"
|
||||||
|
import { Heart, LucideIcon, Package2, PlusCircle, Sparkles } from "lucide-react"
|
||||||
import Link from "next/link"
|
import Link from "next/link"
|
||||||
|
import { useMemo, useState } from "react"
|
||||||
import { toast } from "sonner"
|
import { toast } from "sonner"
|
||||||
const colors: { [key: string]: number[][] } = {
|
import Avatar from "../ui/avatar"
|
||||||
react: [
|
import { Badge } from "../ui/badge"
|
||||||
[71, 207, 237],
|
import { Progress } from "../ui/progress"
|
||||||
[30, 126, 148],
|
|
||||||
],
|
|
||||||
node: [
|
|
||||||
[86, 184, 72],
|
|
||||||
[59, 112, 52],
|
|
||||||
],
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function ProfilePage({
|
export default function ProfilePage({
|
||||||
publicSandboxes,
|
publicSandboxes,
|
||||||
@ -35,54 +35,89 @@ export default function ProfilePage({
|
|||||||
publicSandboxes: Sandbox[]
|
publicSandboxes: Sandbox[]
|
||||||
privateSandboxes: Sandbox[]
|
privateSandboxes: Sandbox[]
|
||||||
user: User
|
user: User
|
||||||
currentUser: {
|
currentUser: User | null
|
||||||
id: string
|
|
||||||
firstName: string | null
|
|
||||||
lastName: string | null
|
|
||||||
} | null
|
|
||||||
}) {
|
}) {
|
||||||
const onVisibilityChange = async (sandbox: Sandbox) => {
|
const [deletingId, setDeletingId] = useState<string>("")
|
||||||
const newVisibility = sandbox.visibility === "public" ? "private" : "public"
|
|
||||||
toast(`Project ${sandbox.name} is now ${newVisibility}.`)
|
|
||||||
await updateSandbox({
|
|
||||||
id: sandbox.id,
|
|
||||||
visibility: newVisibility,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
const isLoggedIn = Boolean(currentUser)
|
const isLoggedIn = Boolean(currentUser)
|
||||||
const hasPublicSandboxes = publicSandboxes.length > 0
|
const hasPublicSandboxes = publicSandboxes.length > 0
|
||||||
const hasPrivateSandboxes = privateSandboxes.length > 0
|
const hasPrivateSandboxes = privateSandboxes.length > 0
|
||||||
|
|
||||||
|
const onVisibilityChange = useMemo(
|
||||||
|
() => async (sandbox: Pick<Sandbox, "id" | "name" | "visibility">) => {
|
||||||
|
const newVisibility =
|
||||||
|
sandbox.visibility === "public" ? "private" : "public"
|
||||||
|
toast(`Project ${sandbox.name} is now ${newVisibility}.`)
|
||||||
|
await updateSandbox({
|
||||||
|
id: sandbox.id,
|
||||||
|
visibility: newVisibility,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
|
||||||
|
const onDelete = useMemo(
|
||||||
|
() => async (sandbox: Pick<Sandbox, "id" | "name">) => {
|
||||||
|
setDeletingId(sandbox.id)
|
||||||
|
toast(`Project ${sandbox.name} deleted.`)
|
||||||
|
await deleteSandbox(sandbox.id)
|
||||||
|
setDeletingId("")
|
||||||
|
},
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
const stats = useMemo(() => {
|
||||||
|
const allSandboxes = isLoggedIn
|
||||||
|
? [...publicSandboxes, ...privateSandboxes]
|
||||||
|
: publicSandboxes
|
||||||
|
|
||||||
|
const totalSandboxes = allSandboxes.length
|
||||||
|
const totalLikes = allSandboxes.reduce(
|
||||||
|
(sum, sandbox) => sum + sandbox.likeCount,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
sandboxes:
|
||||||
|
totalSandboxes === 1 ? "1 sandbox" : `${totalSandboxes} sandboxes`,
|
||||||
|
likes: totalLikes === 1 ? "1 like" : `${totalLikes} likes`,
|
||||||
|
}
|
||||||
|
}, [isLoggedIn, publicSandboxes, privateSandboxes])
|
||||||
|
const joinDate = useMemo(
|
||||||
|
() =>
|
||||||
|
new Date(user.createdAt).toLocaleDateString("en-US", {
|
||||||
|
month: "long",
|
||||||
|
year: "numeric",
|
||||||
|
}),
|
||||||
|
[user.createdAt]
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="container mx-auto p-6 grid grid-cols-1 md:grid-cols-3 gap-6">
|
<div className="container mx-auto p-6 grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||||
<div className="md:col-span-1">
|
<div className="md:col-span-1">
|
||||||
<Card className="mb-6 md:mb-0 sticky top-6">
|
<Card className="mb-6 md:mb-0 sticky top-6">
|
||||||
<CardContent className="flex flex-col gap-3 items-center pt-6">
|
<CardContent className="flex flex-col gap-3 items-center pt-6">
|
||||||
<div className="w-16 h-16 font-mono rounded-full overflow-hidden bg-gradient-to-t from-neutral-800 to-neutral-600 flex items-center justify-center text-sm font-medium">
|
<Avatar
|
||||||
<span className="text-2xl text-background">
|
name={user.name}
|
||||||
{user.name &&
|
avatarUrl={user.avatarUrl}
|
||||||
user.name
|
className="size-36"
|
||||||
.split(" ")
|
/>
|
||||||
.slice(0, 2)
|
|
||||||
.map((name) => name[0].toUpperCase())}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<CardTitle className="text-2xl">{user.name}</CardTitle>
|
<CardTitle className="text-2xl">{user.name}</CardTitle>
|
||||||
<CardDescription>@janedoe</CardDescription>
|
<CardDescription>{`@${user.username}`}</CardDescription>
|
||||||
<p className="text-sm text-muted-foreground">
|
<div className="flex gap-6">
|
||||||
Full-stack developer | Open source enthusiast
|
<StatsItem icon={Package2} label={stats.sandboxes} />
|
||||||
</p>
|
<StatsItem icon={Heart} label={stats.likes} />
|
||||||
<p className="text-xs text-muted-foreground">
|
</div>
|
||||||
Joined January 2023
|
<div className="flex flex-col items-center gap-2">
|
||||||
</p>
|
<p className="text-xs text-muted-foreground">
|
||||||
|
{`Joined ${joinDate}`}
|
||||||
|
</p>
|
||||||
|
{isLoggedIn && <SubscriptionBadge user={currentUser!} />}
|
||||||
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
<div className="md:col-span-2">
|
<div className="md:col-span-2">
|
||||||
<div className="flex justify-between items-center mb-4">
|
|
||||||
<h2 className="text-2xl font-bold">Sandboxes</h2>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Tabs defaultValue="public">
|
<Tabs defaultValue="public">
|
||||||
<TabsList className="mb-4">
|
<TabsList className="mb-4">
|
||||||
<TabsTrigger value="public">Public</TabsTrigger>
|
<TabsTrigger value="public">Public</TabsTrigger>
|
||||||
@ -96,22 +131,24 @@ export default function ProfilePage({
|
|||||||
<Link
|
<Link
|
||||||
key={sandbox.id}
|
key={sandbox.id}
|
||||||
href={`/code/${sandbox.id}`}
|
href={`/code/${sandbox.id}`}
|
||||||
className={`cursor-pointer transition-all focus-visible:outline-none focus-visible:ring-offset-2 focus-visible:ring-offset-background focus-visible:ring-2 focus-visible:ring-ring rounded-lg`}
|
className={cn(
|
||||||
|
"transition-all focus-visible:outline-none focus-visible:ring-offset-2 focus-visible:ring-offset-background focus-visible:ring-2 focus-visible:ring-ring rounded-lg",
|
||||||
|
deletingId === sandbox.id
|
||||||
|
? "pointer-events-none opacity-50 cursor-events-none"
|
||||||
|
: "cursor-pointer"
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
<ProjectCard
|
{isLoggedIn ? (
|
||||||
sandbox={sandbox}
|
<ProjectCard
|
||||||
onVisibilityChange={onVisibilityChange}
|
onVisibilityChange={onVisibilityChange}
|
||||||
onDelete={() => {}}
|
onDelete={onDelete}
|
||||||
deletingId={"deletingId"}
|
deletingId={deletingId}
|
||||||
>
|
isAuthenticated
|
||||||
<CanvasRevealEffect
|
{...sandbox}
|
||||||
animationSpeed={3}
|
|
||||||
containerClassName="bg-black"
|
|
||||||
colors={colors[sandbox.type]}
|
|
||||||
dotSize={2}
|
|
||||||
/>
|
/>
|
||||||
<div className="absolute inset-0 [mask-image:radial-gradient(400px_at_center,white,transparent)] bg-background/75" />
|
) : (
|
||||||
</ProjectCard>
|
<ProjectCard isAuthenticated={false} {...sandbox} />
|
||||||
|
)}
|
||||||
</Link>
|
</Link>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
@ -119,7 +156,12 @@ export default function ProfilePage({
|
|||||||
) : (
|
) : (
|
||||||
<EmptyState
|
<EmptyState
|
||||||
title="No public sandboxes yet"
|
title="No public sandboxes yet"
|
||||||
description="Create your first public sandbox to share your work with the world!"
|
description={
|
||||||
|
isLoggedIn
|
||||||
|
? "Create your first public sandbox to share your work with the world!"
|
||||||
|
: "Login to create public sandboxes"
|
||||||
|
}
|
||||||
|
isLoggedIn={isLoggedIn}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
@ -131,29 +173,32 @@ export default function ProfilePage({
|
|||||||
<Link
|
<Link
|
||||||
key={sandbox.id}
|
key={sandbox.id}
|
||||||
href={`/code/${sandbox.id}`}
|
href={`/code/${sandbox.id}`}
|
||||||
className={`cursor-pointer transition-all focus-visible:outline-none focus-visible:ring-offset-2 focus-visible:ring-offset-background focus-visible:ring-2 focus-visible:ring-ring rounded-lg`}
|
className={cn(
|
||||||
|
"transition-all focus-visible:outline-none focus-visible:ring-offset-2 focus-visible:ring-offset-background focus-visible:ring-2 focus-visible:ring-ring rounded-lg",
|
||||||
|
deletingId === sandbox.id
|
||||||
|
? "pointer-events-none opacity-50 cursor-events-none"
|
||||||
|
: "cursor-pointer"
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
<ProjectCard
|
<ProjectCard
|
||||||
sandbox={sandbox}
|
|
||||||
onVisibilityChange={onVisibilityChange}
|
onVisibilityChange={onVisibilityChange}
|
||||||
onDelete={() => {}}
|
onDelete={onDelete}
|
||||||
deletingId={"deletingId"}
|
deletingId={deletingId}
|
||||||
>
|
isAuthenticated
|
||||||
<CanvasRevealEffect
|
{...sandbox}
|
||||||
animationSpeed={3}
|
/>
|
||||||
containerClassName="bg-black"
|
|
||||||
colors={colors[sandbox.type]}
|
|
||||||
dotSize={2}
|
|
||||||
/>
|
|
||||||
<div className="absolute inset-0 [mask-image:radial-gradient(400px_at_center,white,transparent)] bg-background/75" />
|
|
||||||
</ProjectCard>
|
|
||||||
</Link>
|
</Link>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<EmptyState
|
<EmptyState
|
||||||
title="No private sandboxes yet"
|
title="No private sandboxes yet"
|
||||||
description="Create your first private sandbox to start working on your personal projects!"
|
description={
|
||||||
|
isLoggedIn
|
||||||
|
? "Create your first private sandbox to start working on your personal projects!"
|
||||||
|
: "Login to create private sandboxes"
|
||||||
|
}
|
||||||
|
isLoggedIn={isLoggedIn}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
@ -168,19 +213,63 @@ export default function ProfilePage({
|
|||||||
function EmptyState({
|
function EmptyState({
|
||||||
title,
|
title,
|
||||||
description,
|
description,
|
||||||
|
isLoggedIn,
|
||||||
}: {
|
}: {
|
||||||
title: string
|
title: string
|
||||||
description: string
|
description: string
|
||||||
|
isLoggedIn: boolean
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<Card className="flex flex-col items-center justify-center p-6 text-center h-[300px]">
|
<Card className="flex flex-col items-center justify-center p-6 text-center h-[300px]">
|
||||||
<PlusCircle className="h-12 w-12 text-muted-foreground mb-4" />
|
<PlusCircle className="h-12 w-12 text-muted-foreground mb-4" />
|
||||||
<CardTitle className="text-xl mb-2">{title}</CardTitle>
|
<CardTitle className="text-xl mb-2">{title}</CardTitle>
|
||||||
<CardDescription className="mb-4">{description}</CardDescription>
|
<CardDescription className="mb-4">{description}</CardDescription>
|
||||||
<Button>
|
{isLoggedIn && (
|
||||||
<PlusCircle className="h-4 w-4 mr-2" />
|
<Button>
|
||||||
Create Sandbox
|
<PlusCircle className="h-4 w-4 mr-2" />
|
||||||
</Button>
|
Create Sandbox
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
</Card>
|
</Card>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface StatsItemProps {
|
||||||
|
icon: LucideIcon
|
||||||
|
label: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const StatsItem = ({ icon: Icon, label }: StatsItemProps) => (
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Icon size={18} />
|
||||||
|
<span className="text-sm text-muted-foreground">{label}</span>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
const SubscriptionBadge = ({ user }: { user: User }) => {
|
||||||
|
return (
|
||||||
|
<HoverCard>
|
||||||
|
<HoverCardTrigger>
|
||||||
|
<Badge variant="secondary" className="text-xs cursor-pointer">
|
||||||
|
Free
|
||||||
|
</Badge>
|
||||||
|
</HoverCardTrigger>
|
||||||
|
<HoverCardContent>
|
||||||
|
<div className="w-full space-y-2">
|
||||||
|
<div className="flex justify-between text-sm">
|
||||||
|
<span className="font-medium">AI Generations</span>
|
||||||
|
<span>{`${user.generations} / ${MAX_FREE_GENERATION}`}</span>
|
||||||
|
</div>
|
||||||
|
<Progress
|
||||||
|
value={user?.generations!}
|
||||||
|
max={MAX_FREE_GENERATION}
|
||||||
|
className="w-full"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<Button size="sm" className="w-full mt-4">
|
||||||
|
<Sparkles className="mr-2 h-4 w-4" /> Upgrade to Pro
|
||||||
|
</Button>
|
||||||
|
</HoverCardContent>
|
||||||
|
</HoverCard>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
26
frontend/components/profile/navbar.tsx
Normal file
26
frontend/components/profile/navbar.tsx
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import Logo from "@/assets/logo.svg"
|
||||||
|
import { ThemeSwitcher } from "@/components/ui/theme-switcher"
|
||||||
|
import UserButton from "@/components/ui/userButton"
|
||||||
|
import { User } from "@/lib/types"
|
||||||
|
import Image from "next/image"
|
||||||
|
import Link from "next/link"
|
||||||
|
|
||||||
|
export default function ProfileNavbar({ userData }: { userData: User }) {
|
||||||
|
return (
|
||||||
|
<nav className=" py-2 px-4 w-full flex items-center justify-between border-b border-border">
|
||||||
|
<div className="flex items-center space-x-4">
|
||||||
|
<Link
|
||||||
|
href="/"
|
||||||
|
className="ring-offset-2 ring-offset-background focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none rounded-sm"
|
||||||
|
>
|
||||||
|
<Image src={Logo} alt="Logo" width={36} height={36} />
|
||||||
|
</Link>
|
||||||
|
<div className="text-sm font-medium flex items-center">Sandbox</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center space-x-4">
|
||||||
|
<ThemeSwitcher />
|
||||||
|
{Boolean(userData?.id) ? <UserButton userData={userData} /> : null}
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
)
|
||||||
|
}
|
@ -1,5 +1,4 @@
|
|||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
import Image from "next/image"
|
|
||||||
|
|
||||||
export default function Avatar({
|
export default function Avatar({
|
||||||
name,
|
name,
|
||||||
@ -22,12 +21,12 @@ export default function Avatar({
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
className,
|
"size-9 font-mono rounded-full overflow-hidden bg-gradient-to-t from-neutral-800 to-neutral-600 flex items-center justify-center text-sm font-medium",
|
||||||
"w-9 h-9 font-mono rounded-full overflow-hidden bg-gradient-to-t from-neutral-800 to-neutral-600 flex items-center justify-center text-sm font-medium"
|
className
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{avatarUrl ? (
|
{avatarUrl ? (
|
||||||
<Image
|
<img
|
||||||
src={avatarUrl}
|
src={avatarUrl}
|
||||||
alt={name || "User"}
|
alt={name || "User"}
|
||||||
width={20}
|
width={20}
|
||||||
|
29
frontend/components/ui/hover-card.tsx
Normal file
29
frontend/components/ui/hover-card.tsx
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import * as React from "react"
|
||||||
|
import * as HoverCardPrimitive from "@radix-ui/react-hover-card"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
const HoverCard = HoverCardPrimitive.Root
|
||||||
|
|
||||||
|
const HoverCardTrigger = HoverCardPrimitive.Trigger
|
||||||
|
|
||||||
|
const HoverCardContent = React.forwardRef<
|
||||||
|
React.ElementRef<typeof HoverCardPrimitive.Content>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof HoverCardPrimitive.Content>
|
||||||
|
>(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
|
||||||
|
<HoverCardPrimitive.Content
|
||||||
|
ref={ref}
|
||||||
|
align={align}
|
||||||
|
sideOffset={sideOffset}
|
||||||
|
className={cn(
|
||||||
|
"z-50 w-64 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
HoverCardContent.displayName = HoverCardPrimitive.Content.displayName
|
||||||
|
|
||||||
|
export { HoverCard, HoverCardTrigger, HoverCardContent }
|
@ -1,7 +1,7 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import * as React from "react"
|
|
||||||
import * as ProgressPrimitive from "@radix-ui/react-progress"
|
import * as ProgressPrimitive from "@radix-ui/react-progress"
|
||||||
|
import * as React from "react"
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
@ -18,7 +18,7 @@ const Progress = React.forwardRef<
|
|||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<ProgressPrimitive.Indicator
|
<ProgressPrimitive.Indicator
|
||||||
className="h-full w-full flex-1 bg-primary transition-all"
|
className="h-full w-full flex-1 bg-primary transition-all rounded-full"
|
||||||
style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
|
style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
|
||||||
/>
|
/>
|
||||||
</ProgressPrimitive.Root>
|
</ProgressPrimitive.Root>
|
||||||
|
@ -17,7 +17,7 @@ export function ThemeSwitcher() {
|
|||||||
return (
|
return (
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
<Button variant="outline" size="icon" className="text-muted-foreground">
|
<Button variant="outline" size="icon" className="text-foreground">
|
||||||
<Sun className="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
|
<Sun className="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
|
||||||
<Moon className="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
|
<Moon className="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
|
||||||
<span className="sr-only">Toggle theme</span>
|
<span className="sr-only">Toggle theme</span>
|
||||||
|
@ -7,9 +7,16 @@ import {
|
|||||||
DropdownMenuSeparator,
|
DropdownMenuSeparator,
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from "@/components/ui/dropdown-menu"
|
} from "@/components/ui/dropdown-menu"
|
||||||
|
import { MAX_FREE_GENERATION } from "@/lib/constant"
|
||||||
import { User } from "@/lib/types"
|
import { User } from "@/lib/types"
|
||||||
import { useClerk } from "@clerk/nextjs"
|
import { useClerk } from "@clerk/nextjs"
|
||||||
import { LogOut, Sparkles } from "lucide-react"
|
import {
|
||||||
|
LayoutDashboard,
|
||||||
|
LogOut,
|
||||||
|
Sparkles,
|
||||||
|
User as UserIcon,
|
||||||
|
} from "lucide-react"
|
||||||
|
import Link from "next/link"
|
||||||
import { useRouter } from "next/navigation"
|
import { useRouter } from "next/navigation"
|
||||||
import Avatar from "./avatar"
|
import Avatar from "./avatar"
|
||||||
|
|
||||||
@ -33,31 +40,43 @@ export default function UserButton({ userData }: { userData: User }) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator />
|
||||||
<div className="py-1.5 px-2 w-full flex flex-col items-start text-sm">
|
<DropdownMenuItem>
|
||||||
<div className="flex items-center">
|
<Sparkles className="size-4 mr-2 text-indigo-500" />
|
||||||
<Sparkles className={`h-4 w-4 mr-2 text-indigo-500`} />
|
<div className="w-full flex flex-col items-start text-sm">
|
||||||
AI Usage: {userData.generations}/1000
|
<span className="text-sm">{`AI Usage: ${userData.generations}/${MAX_FREE_GENERATION}`}</span>
|
||||||
|
<div className="rounded-full w-full mt-1 h-1.5 overflow-hidden bg-secondary border border-muted-foreground">
|
||||||
|
<div
|
||||||
|
className="h-full bg-indigo-500 rounded-full"
|
||||||
|
style={{
|
||||||
|
width: `${(userData.generations * 100) / 1000}%`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="rounded-full w-full mt-2 h-2 overflow-hidden bg-secondary">
|
</DropdownMenuItem>
|
||||||
<div
|
<DropdownMenuItem className="cursor-pointer" asChild>
|
||||||
className="h-full bg-indigo-500 rounded-full"
|
<Link href={"/dashboard"}>
|
||||||
style={{
|
<LayoutDashboard className="mr-2 size-4" />
|
||||||
width: `${(userData.generations * 100) / 1000}%`,
|
<span>Dashboard</span>
|
||||||
}}
|
<DropdownMenuSeparator />
|
||||||
/>
|
</Link>
|
||||||
</div>
|
</DropdownMenuItem>
|
||||||
</div>
|
<DropdownMenuItem className="cursor-pointer" asChild>
|
||||||
<DropdownMenuSeparator />
|
<Link href={`/@${userData.username}`}>
|
||||||
|
<UserIcon className="mr-2 size-4" />
|
||||||
|
<span>Profile</span>
|
||||||
|
<DropdownMenuSeparator />
|
||||||
|
</Link>
|
||||||
|
</DropdownMenuItem>
|
||||||
{/* <DropdownMenuItem className="cursor-pointer">
|
{/* <DropdownMenuItem className="cursor-pointer">
|
||||||
<Pencil className="mr-2 h-4 w-4" />
|
<Pencil className="mr-2 size-4" />
|
||||||
<span>Edit Profile</span>
|
<span>Edit Profile</span>
|
||||||
</DropdownMenuItem> */}
|
</DropdownMenuItem> */}
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
onClick={() => signOut(() => router.push("/"))}
|
onClick={() => signOut(() => router.push("/"))}
|
||||||
className="!text-destructive cursor-pointer"
|
className="!text-destructive cursor-pointer"
|
||||||
>
|
>
|
||||||
<LogOut className="mr-2 h-4 w-4" />
|
<LogOut className="mr-2 size-4" />
|
||||||
<span>Log Out</span>
|
<span>Log Out</span>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
|
1
frontend/lib/constant.ts
Normal file
1
frontend/lib/constant.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export const MAX_FREE_GENERATION = 1000
|
@ -19,6 +19,8 @@ export type Sandbox = {
|
|||||||
visibility: "public" | "private"
|
visibility: "public" | "private"
|
||||||
createdAt: Date
|
createdAt: Date
|
||||||
userId: string
|
userId: string
|
||||||
|
likeCount: number
|
||||||
|
viewCount: number
|
||||||
usersToSandboxes: UsersToSandboxes[]
|
usersToSandboxes: UsersToSandboxes[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
912
frontend/package-lock.json
generated
912
frontend/package-lock.json
generated
@ -24,6 +24,7 @@
|
|||||||
"@radix-ui/react-context-menu": "^2.1.5",
|
"@radix-ui/react-context-menu": "^2.1.5",
|
||||||
"@radix-ui/react-dialog": "^1.0.5",
|
"@radix-ui/react-dialog": "^1.0.5",
|
||||||
"@radix-ui/react-dropdown-menu": "^2.0.6",
|
"@radix-ui/react-dropdown-menu": "^2.0.6",
|
||||||
|
"@radix-ui/react-hover-card": "^1.1.2",
|
||||||
"@radix-ui/react-icons": "^1.3.0",
|
"@radix-ui/react-icons": "^1.3.0",
|
||||||
"@radix-ui/react-label": "^2.0.2",
|
"@radix-ui/react-label": "^2.0.2",
|
||||||
"@radix-ui/react-popover": "^1.1.1",
|
"@radix-ui/react-popover": "^1.1.1",
|
||||||
@ -31,8 +32,8 @@
|
|||||||
"@radix-ui/react-select": "^2.0.0",
|
"@radix-ui/react-select": "^2.0.0",
|
||||||
"@radix-ui/react-slot": "^1.0.2",
|
"@radix-ui/react-slot": "^1.0.2",
|
||||||
"@radix-ui/react-switch": "^1.0.3",
|
"@radix-ui/react-switch": "^1.0.3",
|
||||||
"@radix-ui/react-tooltip": "^1.1.3",
|
|
||||||
"@radix-ui/react-tabs": "^1.1.1",
|
"@radix-ui/react-tabs": "^1.1.1",
|
||||||
|
"@radix-ui/react-tooltip": "^1.1.3",
|
||||||
"@react-three/fiber": "^8.16.6",
|
"@react-three/fiber": "^8.16.6",
|
||||||
"@uiw/codemirror-theme-vscode": "^4.23.5",
|
"@uiw/codemirror-theme-vscode": "^4.23.5",
|
||||||
"@uiw/react-codemirror": "^4.23.5",
|
"@uiw/react-codemirror": "^4.23.5",
|
||||||
@ -1696,6 +1697,348 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@radix-ui/react-hover-card": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-hover-card/-/react-hover-card-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-Y5w0qGhysvmqsIy6nQxaPa6mXNKznfoGjOfBgzOjocLxr2XlSjqBMYQQL+FfyogsMuX+m8cZyQGYhJxvxUzO4w==",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/primitive": "1.1.0",
|
||||||
|
"@radix-ui/react-compose-refs": "1.1.0",
|
||||||
|
"@radix-ui/react-context": "1.1.1",
|
||||||
|
"@radix-ui/react-dismissable-layer": "1.1.1",
|
||||||
|
"@radix-ui/react-popper": "1.2.0",
|
||||||
|
"@radix-ui/react-portal": "1.1.2",
|
||||||
|
"@radix-ui/react-presence": "1.1.1",
|
||||||
|
"@radix-ui/react-primitive": "2.0.0",
|
||||||
|
"@radix-ui/react-use-controllable-state": "1.1.0"
|
||||||
|
},
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-hover-card/node_modules/@radix-ui/primitive": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA=="
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-hover-card/node_modules/@radix-ui/react-arrow": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-FmlW1rCg7hBpEBwFbjHwCW6AmWLQM6g/v0Sn8XbP9NvmSZ2San1FpQeyPtufzOMSIx7Y4dzjlHoifhp+7NkZhw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-primitive": "2.0.0"
|
||||||
|
},
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-hover-card/node_modules/@radix-ui/react-compose-refs": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-b4inOtiaOnYf9KWyO3jAeeCG6FeyfY6ldiEPanbUjWd+xIk5wZeHa8yVwmrJ2vderhu/BQvzCrJI0lHd+wIiqw==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-hover-card/node_modules/@radix-ui/react-context": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-hover-card/node_modules/@radix-ui/react-dismissable-layer": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-QSxg29lfr/xcev6kSz7MAlmDnzbP1eI/Dwn3Tp1ip0KT5CUELsxkekFEMVBEoykI3oV39hKT4TKZzBNMbcTZYQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/primitive": "1.1.0",
|
||||||
|
"@radix-ui/react-compose-refs": "1.1.0",
|
||||||
|
"@radix-ui/react-primitive": "2.0.0",
|
||||||
|
"@radix-ui/react-use-callback-ref": "1.1.0",
|
||||||
|
"@radix-ui/react-use-escape-keydown": "1.1.0"
|
||||||
|
},
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-hover-card/node_modules/@radix-ui/react-popper": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-ZnRMshKF43aBxVWPWvbj21+7TQCvhuULWJ4gNIKYpRlQt5xGRhLx66tMp8pya2UkGHTSlhpXwmjqltDYHhw7Vg==",
|
||||||
|
"dependencies": {
|
||||||
|
"@floating-ui/react-dom": "^2.0.0",
|
||||||
|
"@radix-ui/react-arrow": "1.1.0",
|
||||||
|
"@radix-ui/react-compose-refs": "1.1.0",
|
||||||
|
"@radix-ui/react-context": "1.1.0",
|
||||||
|
"@radix-ui/react-primitive": "2.0.0",
|
||||||
|
"@radix-ui/react-use-callback-ref": "1.1.0",
|
||||||
|
"@radix-ui/react-use-layout-effect": "1.1.0",
|
||||||
|
"@radix-ui/react-use-rect": "1.1.0",
|
||||||
|
"@radix-ui/react-use-size": "1.1.0",
|
||||||
|
"@radix-ui/rect": "1.1.0"
|
||||||
|
},
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-hover-card/node_modules/@radix-ui/react-popper/node_modules/@radix-ui/react-context": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-OKrckBy+sMEgYM/sMmqmErVn0kZqrHPJze+Ql3DzYsDDp0hl0L62nx/2122/Bvps1qz645jlcu2tD9lrRSdf8A==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-hover-card/node_modules/@radix-ui/react-portal": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-WeDYLGPxJb/5EGBoedyJbT0MpoULmwnIPMJMSldkuiMsBAv7N1cRdsTWZWht9vpPOiN3qyiGAtbK2is47/uMFg==",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-primitive": "2.0.0",
|
||||||
|
"@radix-ui/react-use-layout-effect": "1.1.0"
|
||||||
|
},
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-hover-card/node_modules/@radix-ui/react-presence": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-IeFXVi4YS1K0wVZzXNrbaaUvIJ3qdY+/Ih4eHFhWA9SwGR9UDX7Ck8abvL57C4cv3wwMvUE0OG69Qc3NCcTe/A==",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-compose-refs": "1.1.0",
|
||||||
|
"@radix-ui/react-use-layout-effect": "1.1.0"
|
||||||
|
},
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-hover-card/node_modules/@radix-ui/react-primitive": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-ZSpFm0/uHa8zTvKBDjLFWLo8dkr4MBsiDLz0g3gMUwqgLHz9rTaRRGYDgvZPtBJgYCBKXkS9fzmoySgr8CO6Cw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-slot": "1.1.0"
|
||||||
|
},
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-hover-card/node_modules/@radix-ui/react-slot": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-compose-refs": "1.1.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-hover-card/node_modules/@radix-ui/react-use-callback-ref": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-hover-card/node_modules/@radix-ui/react-use-controllable-state": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-use-callback-ref": "1.1.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-hover-card/node_modules/@radix-ui/react-use-escape-keydown": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-use-callback-ref": "1.1.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-hover-card/node_modules/@radix-ui/react-use-layout-effect": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-hover-card/node_modules/@radix-ui/react-use-rect": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-0Fmkebhr6PiseyZlYAOtLS+nb7jLmpqTrJyv61Pe68MKYW6OWdRE2kI70TaYY27u7H0lajqM3hSMMLFq18Z7nQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/rect": "1.1.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-hover-card/node_modules/@radix-ui/react-use-size": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-XW3/vWuIXHa+2Uwcc2ABSfcCledmXhhQPlGbfcRXbiUQI5Icjcg19BGCZVKKInYbvUCut/ufbbLLPFC5cbb1hw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-use-layout-effect": "1.1.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-hover-card/node_modules/@radix-ui/rect": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg=="
|
||||||
|
},
|
||||||
"node_modules/@radix-ui/react-icons": {
|
"node_modules/@radix-ui/react-icons": {
|
||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-icons/-/react-icons-1.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-icons/-/react-icons-1.3.0.tgz",
|
||||||
@ -2304,7 +2647,6 @@
|
|||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-progress/-/react-progress-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-progress/-/react-progress-1.1.0.tgz",
|
||||||
"integrity": "sha512-aSzvnYpP725CROcxAOEBVZZSIQVQdHgBr2QQFKySsaD14u8dNT0batuXI+AAGDdAHfXH8rbnHmjYFqVJ21KkRg==",
|
"integrity": "sha512-aSzvnYpP725CROcxAOEBVZZSIQVQdHgBr2QQFKySsaD14u8dNT0batuXI+AAGDdAHfXH8rbnHmjYFqVJ21KkRg==",
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@radix-ui/react-context": "1.1.0",
|
"@radix-ui/react-context": "1.1.0",
|
||||||
"@radix-ui/react-primitive": "2.0.0"
|
"@radix-ui/react-primitive": "2.0.0"
|
||||||
@ -2516,6 +2858,289 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@radix-ui/react-tabs": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-3GBUDmP2DvzmtYLMsHmpA1GtR46ZDZ+OreXM/N+kkQJOPIgytFWWTfDQmBQKBvaFS0Vno0FktdbVzN28KGrMdw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/primitive": "1.1.0",
|
||||||
|
"@radix-ui/react-context": "1.1.1",
|
||||||
|
"@radix-ui/react-direction": "1.1.0",
|
||||||
|
"@radix-ui/react-id": "1.1.0",
|
||||||
|
"@radix-ui/react-presence": "1.1.1",
|
||||||
|
"@radix-ui/react-primitive": "2.0.0",
|
||||||
|
"@radix-ui/react-roving-focus": "1.1.0",
|
||||||
|
"@radix-ui/react-use-controllable-state": "1.1.0"
|
||||||
|
},
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/primitive": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA=="
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-collection": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-GZsZslMJEyo1VKm5L1ZJY8tGDxZNPAoUeQUIbKeJfoi7Q4kmig5AsgLMYYuyYbfjd8fBmFORAIwYAkXMnXZgZw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-compose-refs": "1.1.0",
|
||||||
|
"@radix-ui/react-context": "1.1.0",
|
||||||
|
"@radix-ui/react-primitive": "2.0.0",
|
||||||
|
"@radix-ui/react-slot": "1.1.0"
|
||||||
|
},
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-collection/node_modules/@radix-ui/react-context": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-OKrckBy+sMEgYM/sMmqmErVn0kZqrHPJze+Ql3DzYsDDp0hl0L62nx/2122/Bvps1qz645jlcu2tD9lrRSdf8A==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-compose-refs": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-b4inOtiaOnYf9KWyO3jAeeCG6FeyfY6ldiEPanbUjWd+xIk5wZeHa8yVwmrJ2vderhu/BQvzCrJI0lHd+wIiqw==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-context": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-direction": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-id": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-use-layout-effect": "1.1.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-presence": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-IeFXVi4YS1K0wVZzXNrbaaUvIJ3qdY+/Ih4eHFhWA9SwGR9UDX7Ck8abvL57C4cv3wwMvUE0OG69Qc3NCcTe/A==",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-compose-refs": "1.1.0",
|
||||||
|
"@radix-ui/react-use-layout-effect": "1.1.0"
|
||||||
|
},
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-primitive": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-ZSpFm0/uHa8zTvKBDjLFWLo8dkr4MBsiDLz0g3gMUwqgLHz9rTaRRGYDgvZPtBJgYCBKXkS9fzmoySgr8CO6Cw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-slot": "1.1.0"
|
||||||
|
},
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-roving-focus": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-EA6AMGeq9AEeQDeSH0aZgG198qkfHSbvWTf1HvoDmOB5bBG/qTxjYMWUKMnYiV6J/iP/J8MEFSuB2zRU2n7ODA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/primitive": "1.1.0",
|
||||||
|
"@radix-ui/react-collection": "1.1.0",
|
||||||
|
"@radix-ui/react-compose-refs": "1.1.0",
|
||||||
|
"@radix-ui/react-context": "1.1.0",
|
||||||
|
"@radix-ui/react-direction": "1.1.0",
|
||||||
|
"@radix-ui/react-id": "1.1.0",
|
||||||
|
"@radix-ui/react-primitive": "2.0.0",
|
||||||
|
"@radix-ui/react-use-callback-ref": "1.1.0",
|
||||||
|
"@radix-ui/react-use-controllable-state": "1.1.0"
|
||||||
|
},
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-roving-focus/node_modules/@radix-ui/react-context": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-OKrckBy+sMEgYM/sMmqmErVn0kZqrHPJze+Ql3DzYsDDp0hl0L62nx/2122/Bvps1qz645jlcu2tD9lrRSdf8A==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-slot": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-compose-refs": "1.1.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-use-callback-ref": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-use-controllable-state": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-use-callback-ref": "1.1.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-use-layout-effect": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@radix-ui/react-tooltip": {
|
"node_modules/@radix-ui/react-tooltip": {
|
||||||
"version": "1.1.3",
|
"version": "1.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.1.3.tgz",
|
||||||
@ -2921,289 +3546,6 @@
|
|||||||
"integrity": "sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==",
|
"integrity": "sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@radix-ui/react-tabs": {
|
|
||||||
"version": "1.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.1.tgz",
|
|
||||||
"integrity": "sha512-3GBUDmP2DvzmtYLMsHmpA1GtR46ZDZ+OreXM/N+kkQJOPIgytFWWTfDQmBQKBvaFS0Vno0FktdbVzN28KGrMdw==",
|
|
||||||
"dependencies": {
|
|
||||||
"@radix-ui/primitive": "1.1.0",
|
|
||||||
"@radix-ui/react-context": "1.1.1",
|
|
||||||
"@radix-ui/react-direction": "1.1.0",
|
|
||||||
"@radix-ui/react-id": "1.1.0",
|
|
||||||
"@radix-ui/react-presence": "1.1.1",
|
|
||||||
"@radix-ui/react-primitive": "2.0.0",
|
|
||||||
"@radix-ui/react-roving-focus": "1.1.0",
|
|
||||||
"@radix-ui/react-use-controllable-state": "1.1.0"
|
|
||||||
},
|
|
||||||
"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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/primitive": {
|
|
||||||
"version": "1.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.0.tgz",
|
|
||||||
"integrity": "sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA=="
|
|
||||||
},
|
|
||||||
"node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-collection": {
|
|
||||||
"version": "1.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.0.tgz",
|
|
||||||
"integrity": "sha512-GZsZslMJEyo1VKm5L1ZJY8tGDxZNPAoUeQUIbKeJfoi7Q4kmig5AsgLMYYuyYbfjd8fBmFORAIwYAkXMnXZgZw==",
|
|
||||||
"dependencies": {
|
|
||||||
"@radix-ui/react-compose-refs": "1.1.0",
|
|
||||||
"@radix-ui/react-context": "1.1.0",
|
|
||||||
"@radix-ui/react-primitive": "2.0.0",
|
|
||||||
"@radix-ui/react-slot": "1.1.0"
|
|
||||||
},
|
|
||||||
"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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-collection/node_modules/@radix-ui/react-context": {
|
|
||||||
"version": "1.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.0.tgz",
|
|
||||||
"integrity": "sha512-OKrckBy+sMEgYM/sMmqmErVn0kZqrHPJze+Ql3DzYsDDp0hl0L62nx/2122/Bvps1qz645jlcu2tD9lrRSdf8A==",
|
|
||||||
"peerDependencies": {
|
|
||||||
"@types/react": "*",
|
|
||||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
|
||||||
},
|
|
||||||
"peerDependenciesMeta": {
|
|
||||||
"@types/react": {
|
|
||||||
"optional": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-compose-refs": {
|
|
||||||
"version": "1.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.0.tgz",
|
|
||||||
"integrity": "sha512-b4inOtiaOnYf9KWyO3jAeeCG6FeyfY6ldiEPanbUjWd+xIk5wZeHa8yVwmrJ2vderhu/BQvzCrJI0lHd+wIiqw==",
|
|
||||||
"peerDependencies": {
|
|
||||||
"@types/react": "*",
|
|
||||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
|
||||||
},
|
|
||||||
"peerDependenciesMeta": {
|
|
||||||
"@types/react": {
|
|
||||||
"optional": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-context": {
|
|
||||||
"version": "1.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.1.tgz",
|
|
||||||
"integrity": "sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==",
|
|
||||||
"peerDependencies": {
|
|
||||||
"@types/react": "*",
|
|
||||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
|
||||||
},
|
|
||||||
"peerDependenciesMeta": {
|
|
||||||
"@types/react": {
|
|
||||||
"optional": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-direction": {
|
|
||||||
"version": "1.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.0.tgz",
|
|
||||||
"integrity": "sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg==",
|
|
||||||
"peerDependencies": {
|
|
||||||
"@types/react": "*",
|
|
||||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
|
||||||
},
|
|
||||||
"peerDependenciesMeta": {
|
|
||||||
"@types/react": {
|
|
||||||
"optional": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-id": {
|
|
||||||
"version": "1.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.0.tgz",
|
|
||||||
"integrity": "sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==",
|
|
||||||
"dependencies": {
|
|
||||||
"@radix-ui/react-use-layout-effect": "1.1.0"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"@types/react": "*",
|
|
||||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
|
||||||
},
|
|
||||||
"peerDependenciesMeta": {
|
|
||||||
"@types/react": {
|
|
||||||
"optional": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-presence": {
|
|
||||||
"version": "1.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.1.tgz",
|
|
||||||
"integrity": "sha512-IeFXVi4YS1K0wVZzXNrbaaUvIJ3qdY+/Ih4eHFhWA9SwGR9UDX7Ck8abvL57C4cv3wwMvUE0OG69Qc3NCcTe/A==",
|
|
||||||
"dependencies": {
|
|
||||||
"@radix-ui/react-compose-refs": "1.1.0",
|
|
||||||
"@radix-ui/react-use-layout-effect": "1.1.0"
|
|
||||||
},
|
|
||||||
"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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-primitive": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.0.tgz",
|
|
||||||
"integrity": "sha512-ZSpFm0/uHa8zTvKBDjLFWLo8dkr4MBsiDLz0g3gMUwqgLHz9rTaRRGYDgvZPtBJgYCBKXkS9fzmoySgr8CO6Cw==",
|
|
||||||
"dependencies": {
|
|
||||||
"@radix-ui/react-slot": "1.1.0"
|
|
||||||
},
|
|
||||||
"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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-roving-focus": {
|
|
||||||
"version": "1.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.0.tgz",
|
|
||||||
"integrity": "sha512-EA6AMGeq9AEeQDeSH0aZgG198qkfHSbvWTf1HvoDmOB5bBG/qTxjYMWUKMnYiV6J/iP/J8MEFSuB2zRU2n7ODA==",
|
|
||||||
"dependencies": {
|
|
||||||
"@radix-ui/primitive": "1.1.0",
|
|
||||||
"@radix-ui/react-collection": "1.1.0",
|
|
||||||
"@radix-ui/react-compose-refs": "1.1.0",
|
|
||||||
"@radix-ui/react-context": "1.1.0",
|
|
||||||
"@radix-ui/react-direction": "1.1.0",
|
|
||||||
"@radix-ui/react-id": "1.1.0",
|
|
||||||
"@radix-ui/react-primitive": "2.0.0",
|
|
||||||
"@radix-ui/react-use-callback-ref": "1.1.0",
|
|
||||||
"@radix-ui/react-use-controllable-state": "1.1.0"
|
|
||||||
},
|
|
||||||
"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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-roving-focus/node_modules/@radix-ui/react-context": {
|
|
||||||
"version": "1.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.0.tgz",
|
|
||||||
"integrity": "sha512-OKrckBy+sMEgYM/sMmqmErVn0kZqrHPJze+Ql3DzYsDDp0hl0L62nx/2122/Bvps1qz645jlcu2tD9lrRSdf8A==",
|
|
||||||
"peerDependencies": {
|
|
||||||
"@types/react": "*",
|
|
||||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
|
||||||
},
|
|
||||||
"peerDependenciesMeta": {
|
|
||||||
"@types/react": {
|
|
||||||
"optional": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-slot": {
|
|
||||||
"version": "1.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.0.tgz",
|
|
||||||
"integrity": "sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==",
|
|
||||||
"dependencies": {
|
|
||||||
"@radix-ui/react-compose-refs": "1.1.0"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"@types/react": "*",
|
|
||||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
|
||||||
},
|
|
||||||
"peerDependenciesMeta": {
|
|
||||||
"@types/react": {
|
|
||||||
"optional": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-use-callback-ref": {
|
|
||||||
"version": "1.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz",
|
|
||||||
"integrity": "sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==",
|
|
||||||
"peerDependencies": {
|
|
||||||
"@types/react": "*",
|
|
||||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
|
||||||
},
|
|
||||||
"peerDependenciesMeta": {
|
|
||||||
"@types/react": {
|
|
||||||
"optional": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-use-controllable-state": {
|
|
||||||
"version": "1.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.1.0.tgz",
|
|
||||||
"integrity": "sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw==",
|
|
||||||
"dependencies": {
|
|
||||||
"@radix-ui/react-use-callback-ref": "1.1.0"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"@types/react": "*",
|
|
||||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
|
||||||
},
|
|
||||||
"peerDependenciesMeta": {
|
|
||||||
"@types/react": {
|
|
||||||
"optional": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-use-layout-effect": {
|
|
||||||
"version": "1.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.0.tgz",
|
|
||||||
"integrity": "sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==",
|
|
||||||
"peerDependencies": {
|
|
||||||
"@types/react": "*",
|
|
||||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
|
||||||
},
|
|
||||||
"peerDependenciesMeta": {
|
|
||||||
"@types/react": {
|
|
||||||
"optional": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@radix-ui/react-use-callback-ref": {
|
"node_modules/@radix-ui/react-use-callback-ref": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.1.tgz",
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
"@radix-ui/react-context-menu": "^2.1.5",
|
"@radix-ui/react-context-menu": "^2.1.5",
|
||||||
"@radix-ui/react-dialog": "^1.0.5",
|
"@radix-ui/react-dialog": "^1.0.5",
|
||||||
"@radix-ui/react-dropdown-menu": "^2.0.6",
|
"@radix-ui/react-dropdown-menu": "^2.0.6",
|
||||||
|
"@radix-ui/react-hover-card": "^1.1.2",
|
||||||
"@radix-ui/react-icons": "^1.3.0",
|
"@radix-ui/react-icons": "^1.3.0",
|
||||||
"@radix-ui/react-label": "^2.0.2",
|
"@radix-ui/react-label": "^2.0.2",
|
||||||
"@radix-ui/react-popover": "^1.1.1",
|
"@radix-ui/react-popover": "^1.1.1",
|
||||||
@ -32,8 +33,8 @@
|
|||||||
"@radix-ui/react-select": "^2.0.0",
|
"@radix-ui/react-select": "^2.0.0",
|
||||||
"@radix-ui/react-slot": "^1.0.2",
|
"@radix-ui/react-slot": "^1.0.2",
|
||||||
"@radix-ui/react-switch": "^1.0.3",
|
"@radix-ui/react-switch": "^1.0.3",
|
||||||
"@radix-ui/react-tooltip": "^1.1.3",
|
|
||||||
"@radix-ui/react-tabs": "^1.1.1",
|
"@radix-ui/react-tabs": "^1.1.1",
|
||||||
|
"@radix-ui/react-tooltip": "^1.1.3",
|
||||||
"@react-three/fiber": "^8.16.6",
|
"@react-three/fiber": "^8.16.6",
|
||||||
"@uiw/codemirror-theme-vscode": "^4.23.5",
|
"@uiw/codemirror-theme-vscode": "^4.23.5",
|
||||||
"@uiw/react-codemirror": "^4.23.5",
|
"@uiw/react-codemirror": "^4.23.5",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user