feat: complete profile page

This commit is contained in:
Hamzat Victor
2024-11-11 22:02:34 +01:00
parent 00e51205cf
commit 105eab9bad
16 changed files with 1172 additions and 485 deletions

View File

@ -1,5 +1,4 @@
import { cn } from "@/lib/utils"
import Image from "next/image"
export default function Avatar({
name,
@ -22,12 +21,12 @@ export default function Avatar({
return (
<div
className={cn(
className,
"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"
"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",
className
)}
>
{avatarUrl ? (
<Image
<img
src={avatarUrl}
alt={name || "User"}
width={20}

View 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 }

View File

@ -1,7 +1,7 @@
"use client"
import * as React from "react"
import * as ProgressPrimitive from "@radix-ui/react-progress"
import * as React from "react"
import { cn } from "@/lib/utils"
@ -18,7 +18,7 @@ const Progress = React.forwardRef<
{...props}
>
<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)}%)` }}
/>
</ProgressPrimitive.Root>

View File

@ -17,7 +17,7 @@ export function ThemeSwitcher() {
return (
<DropdownMenu>
<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" />
<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>

View File

@ -7,9 +7,16 @@ import {
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"
import { MAX_FREE_GENERATION } from "@/lib/constant"
import { User } from "@/lib/types"
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 Avatar from "./avatar"
@ -33,31 +40,43 @@ export default function UserButton({ userData }: { userData: User }) {
</div>
<DropdownMenuSeparator />
<div className="py-1.5 px-2 w-full flex flex-col items-start text-sm">
<div className="flex items-center">
<Sparkles className={`h-4 w-4 mr-2 text-indigo-500`} />
AI Usage: {userData.generations}/1000
<DropdownMenuItem>
<Sparkles className="size-4 mr-2 text-indigo-500" />
<div className="w-full flex flex-col items-start text-sm">
<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 className="rounded-full w-full mt-2 h-2 overflow-hidden bg-secondary">
<div
className="h-full bg-indigo-500 rounded-full"
style={{
width: `${(userData.generations * 100) / 1000}%`,
}}
/>
</div>
</div>
<DropdownMenuSeparator />
</DropdownMenuItem>
<DropdownMenuItem className="cursor-pointer" asChild>
<Link href={"/dashboard"}>
<LayoutDashboard className="mr-2 size-4" />
<span>Dashboard</span>
<DropdownMenuSeparator />
</Link>
</DropdownMenuItem>
<DropdownMenuItem className="cursor-pointer" asChild>
<Link href={`/@${userData.username}`}>
<UserIcon className="mr-2 size-4" />
<span>Profile</span>
<DropdownMenuSeparator />
</Link>
</DropdownMenuItem>
{/* <DropdownMenuItem className="cursor-pointer">
<Pencil className="mr-2 h-4 w-4" />
<Pencil className="mr-2 size-4" />
<span>Edit Profile</span>
</DropdownMenuItem> */}
<DropdownMenuItem
onClick={() => signOut(() => router.push("/"))}
className="!text-destructive cursor-pointer"
>
<LogOut className="mr-2 h-4 w-4" />
<LogOut className="mr-2 size-4" />
<span>Log Out</span>
</DropdownMenuItem>
</DropdownMenuContent>