-
Notifications
You must be signed in to change notification settings - Fork 63
Expand file tree
/
Copy pathUserMenu.tsx
More file actions
83 lines (76 loc) · 2.88 KB
/
UserMenu.tsx
File metadata and controls
83 lines (76 loc) · 2.88 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
import { useState, useRef, useEffect, useCallback } from 'react'
import { User, LogOut } from 'lucide-react'
import { useAuthMe } from '../api/client'
import { useQueryClient } from '@tanstack/react-query'
import { computeUserInitials } from '@skyhook-io/k8s-ui/utils/user-initials'
export function UserMenu() {
const { data: authMe } = useAuthMe()
const [isOpen, setIsOpen] = useState(false)
const menuRef = useRef<HTMLDivElement>(null)
const queryClient = useQueryClient()
// Close on click outside
useEffect(() => {
if (!isOpen) return
function handleClick(e: MouseEvent) {
if (menuRef.current && !menuRef.current.contains(e.target as Node)) {
setIsOpen(false)
}
}
document.addEventListener('mousedown', handleClick)
return () => document.removeEventListener('mousedown', handleClick)
}, [isOpen])
const handleLogout = useCallback(async () => {
let redirectTo = '/'
try {
const res = await fetch('/auth/logout', { credentials: 'same-origin' })
const data = await res.json()
if (data.redirectTo) {
redirectTo = data.redirectTo
}
} catch (err) {
console.error('[logout] Failed to complete server-side logout:', err)
}
queryClient.clear()
window.location.href = redirectTo
}, [queryClient])
if (!authMe?.authEnabled || !authMe?.username) {
return null
}
const initials = computeUserInitials(authMe.username)
return (
<div ref={menuRef} className="relative">
<button
onClick={() => setIsOpen(!isOpen)}
className="w-7 h-7 rounded-full bg-blue-500/15 text-blue-500 flex items-center justify-center text-xs font-medium hover:bg-blue-500/25 transition-colors"
title={authMe.username}
>
{initials || <User className="w-3.5 h-3.5" />}
</button>
{isOpen && (
<div className="absolute right-0 top-full mt-1.5 w-56 bg-theme-surface border border-theme-border rounded-lg shadow-lg z-50 py-1">
<div className="px-3 py-2 border-b border-theme-border">
<p className="text-sm font-medium text-theme-text-primary truncate">{authMe.username}</p>
{authMe.groups && authMe.groups.length > 0 && (
<p className="text-[11px] text-theme-text-tertiary mt-0.5 truncate">
{authMe.groups.join(', ')}
</p>
)}
</div>
{authMe.authMode === 'proxy' ? (
<p className="px-3 py-1.5 text-[11px] text-theme-text-tertiary">
Session managed by auth proxy
</p>
) : (
<button
onClick={handleLogout}
className="w-full flex items-center gap-2 px-3 py-1.5 text-sm text-theme-text-secondary hover:bg-theme-hover transition-colors"
>
<LogOut className="w-3.5 h-3.5" />
Logout
</button>
)}
</div>
)}
</div>
)
}