Files
tunnl_please_frontend/components/user-menu.tsx
bagas 018a980dcb
All checks were successful
Docker Build and Push / build-and-push (push) Successful in 15m8s
feat: add auth
2025-10-01 17:16:58 +07:00

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