All checks were successful
Docker Build and Push / build-and-push (push) Successful in 15m8s
151 lines
5.3 KiB
TypeScript
151 lines
5.3 KiB
TypeScript
"use client"
|
|
|
|
import { useState, useRef, useEffect } from "react"
|
|
import Link from "next/link"
|
|
|
|
interface UserMenuProps {
|
|
user: {
|
|
name: string
|
|
email: string
|
|
image?: string
|
|
}
|
|
onSignOut?: () => void
|
|
}
|
|
|
|
export default function UserMenu({ user, onSignOut }: UserMenuProps) {
|
|
const [isOpen, setIsOpen] = useState(false)
|
|
const menuRef = useRef<HTMLDivElement>(null)
|
|
|
|
useEffect(() => {
|
|
function handleClickOutside(event: MouseEvent) {
|
|
if (menuRef.current && !menuRef.current.contains(event.target as Node)) {
|
|
setIsOpen(false)
|
|
}
|
|
}
|
|
|
|
document.addEventListener("mousedown", handleClickOutside)
|
|
return () => document.removeEventListener("mousedown", handleClickOutside)
|
|
}, [])
|
|
|
|
return (
|
|
<div className="relative" ref={menuRef}>
|
|
<button
|
|
onClick={() => setIsOpen(!isOpen)}
|
|
className="flex items-center gap-3 hover:opacity-80 transition-opacity"
|
|
>
|
|
{user.image ? (
|
|
<img
|
|
src={user.image || "/placeholder.svg"}
|
|
alt={user.name}
|
|
className="w-9 h-9 rounded-full border-2 border-emerald-500"
|
|
/>
|
|
) : (
|
|
<div className="w-9 h-9 rounded-full bg-emerald-600 border-2 border-emerald-500 flex items-center justify-center text-white font-medium text-sm">
|
|
{user.name}
|
|
</div>
|
|
)}
|
|
<div className="hidden md:block text-left">
|
|
<p className="text-sm font-medium text-white">{user.name}</p>
|
|
<p className="text-xs text-gray-400">{user.email}</p>
|
|
</div>
|
|
<svg
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
width="16"
|
|
height="16"
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
strokeWidth="2"
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
className={`text-gray-400 transition-transform ${isOpen ? "rotate-180" : ""}`}
|
|
>
|
|
<path d="m6 9 6 6 6-6" />
|
|
</svg>
|
|
</button>
|
|
|
|
{isOpen && (
|
|
<div className="absolute right-0 mt-2 w-64 bg-gray-800 rounded-lg border border-gray-700 shadow-xl py-2 z-50">
|
|
<div className="md:hidden px-4 py-3 border-b border-gray-700">
|
|
<p className="text-sm font-medium text-white">{user.name}</p>
|
|
<p className="text-xs text-gray-400">{user.email}</p>
|
|
</div>
|
|
|
|
<Link
|
|
href="/dashboard"
|
|
onClick={() => setIsOpen(false)}
|
|
className="flex items-center gap-3 px-4 py-2 text-sm text-gray-300 hover:bg-gray-700 transition-colors"
|
|
>
|
|
<svg
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
width="16"
|
|
height="16"
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
strokeWidth="2"
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
>
|
|
<rect width="7" height="9" x="3" y="3" rx="1" />
|
|
<rect width="7" height="5" x="14" y="3" rx="1" />
|
|
<rect width="7" height="9" x="14" y="12" rx="1" />
|
|
<rect width="7" height="5" x="3" y="16" rx="1" />
|
|
</svg>
|
|
Dashboard
|
|
</Link>
|
|
|
|
<Link
|
|
href="/settings"
|
|
onClick={() => setIsOpen(false)}
|
|
className="flex items-center gap-3 px-4 py-2 text-sm text-gray-300 hover:bg-gray-700 transition-colors"
|
|
>
|
|
<svg
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
width="16"
|
|
height="16"
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
strokeWidth="2"
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
>
|
|
<path d="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z" />
|
|
<circle cx="12" cy="12" r="3" />
|
|
</svg>
|
|
Settings
|
|
</Link>
|
|
|
|
<div className="border-t border-gray-700 my-2"></div>
|
|
|
|
<button
|
|
onClick={() => {
|
|
setIsOpen(false)
|
|
onSignOut?.()
|
|
}}
|
|
className="flex items-center gap-3 px-4 py-2 text-sm text-red-400 hover:bg-gray-700 transition-colors w-full"
|
|
>
|
|
<svg
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
width="16"
|
|
height="16"
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
strokeWidth="2"
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
>
|
|
<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4" />
|
|
<polyline points="16 17 21 12 16 7" />
|
|
<line x1="21" x2="9" y1="12" y2="12" />
|
|
</svg>
|
|
Sign Out
|
|
</button>
|
|
</div>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|