128 lines
5.0 KiB
TypeScript
128 lines
5.0 KiB
TypeScript
import { useEffect, useRef, useState } from 'react'
|
|
import { User } from '../types'
|
|
import { useT, toggleLang } from '../i18n'
|
|
|
|
interface Props {
|
|
user: User
|
|
sbUsers?: any[]
|
|
onLogout: () => void
|
|
onViewProfile?: () => void
|
|
}
|
|
|
|
const ROLE_COLOR: Record<string, string> = {
|
|
Manager: '#2563eb',
|
|
Auditor: '#d97706',
|
|
Employee: '#6b7280',
|
|
Admin: '#7c3aed',
|
|
'X-Auditor': '#0f766e',
|
|
Chairman: '#0f766e',
|
|
}
|
|
|
|
export default function UserMenu({ user, sbUsers = [], onLogout, onViewProfile }: Props) {
|
|
const [open, setOpen] = useState(false)
|
|
const ref = useRef<HTMLDivElement>(null)
|
|
const t = useT()
|
|
|
|
useEffect(() => {
|
|
const handler = (e: MouseEvent) => {
|
|
if (ref.current && !ref.current.contains(e.target as Node)) setOpen(false)
|
|
}
|
|
document.addEventListener('mousedown', handler)
|
|
return () => document.removeEventListener('mousedown', handler)
|
|
}, [])
|
|
|
|
const sbUser = sbUsers.find(u => u.name === user.name || u.username === user.name)
|
|
const category = sbUser?.custom_category || user.role || 'User'
|
|
const initials = (user.full_name || user.name || 'U').charAt(0).toUpperCase()
|
|
const roleColor = ROLE_COLOR[user.role] || '#6b7280'
|
|
|
|
return (
|
|
<div ref={ref} style={{ position: 'relative', display: 'flex', alignItems: 'center', gap: 8 }}>
|
|
<button
|
|
onClick={() => setOpen(o => !o)}
|
|
style={{
|
|
display: 'flex', alignItems: 'center', gap: 7,
|
|
background: open ? '#f1f5f9' : 'transparent',
|
|
border: '1px solid transparent',
|
|
borderRadius: 20, padding: '4px 10px 4px 5px',
|
|
cursor: 'pointer', transition: 'background 0.15s',
|
|
}}>
|
|
<div style={{
|
|
width: 28, height: 28, borderRadius: '50%',
|
|
background: roleColor, color: 'white',
|
|
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
|
fontSize: 12, fontWeight: 700, flexShrink: 0,
|
|
boxShadow: '0 1px 3px rgba(0,0,0,0.2)',
|
|
}}>
|
|
{initials}
|
|
</div>
|
|
<span style={{ fontSize: 12, fontWeight: 600, color: '#374151', whiteSpace: 'nowrap', maxWidth: 90, overflow: 'hidden', textOverflow: 'ellipsis' }}>
|
|
{user.full_name || user.name}
|
|
</span>
|
|
<span style={{ fontSize: 9, color: '#9ca3af' }}>{open ? '▲' : '▼'}</span>
|
|
</button>
|
|
|
|
{open && (
|
|
<div style={{
|
|
position: 'absolute', right: 0, top: 'calc(100% + 6px)',
|
|
width: 220, background: 'white', borderRadius: 10,
|
|
boxShadow: '0 8px 32px rgba(0,0,0,0.15)', zIndex: 10000,
|
|
border: '1px solid #e5e7eb', overflow: 'hidden',
|
|
}}>
|
|
<div style={{ padding: '14px 16px', borderBottom: '1px solid #f0f0f0', background: '#f8fafc', display: 'flex', alignItems: 'center', gap: 10 }}>
|
|
<div style={{
|
|
width: 36, height: 36, borderRadius: '50%', background: roleColor,
|
|
color: 'white', display: 'flex', alignItems: 'center', justifyContent: 'center',
|
|
fontSize: 15, fontWeight: 700, flexShrink: 0,
|
|
}}>
|
|
{initials}
|
|
</div>
|
|
<div style={{ minWidth: 0 }}>
|
|
<div style={{ fontSize: 13, fontWeight: 700, color: '#1a2332', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
|
|
{user.full_name || user.name}
|
|
</div>
|
|
<div style={{ fontSize: 10, color: '#888', marginTop: 1, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
|
|
{user.name}
|
|
</div>
|
|
<div style={{
|
|
display: 'inline-block', marginTop: 4, padding: '1px 8px',
|
|
background: roleColor + '18', color: roleColor,
|
|
fontSize: 10, fontWeight: 700, borderRadius: 8, border: '1px solid ' + roleColor + '44',
|
|
}}>
|
|
{category}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div style={{ padding: '4px 0' }}>
|
|
<MenuRow icon="👤" label={t.myProfile} onClick={() => { onViewProfile?.(); setOpen(false) }} />
|
|
<MenuRow
|
|
icon="🌐"
|
|
label={t.switchLang}
|
|
onClick={() => { toggleLang(); setOpen(false) }}
|
|
/>
|
|
<div style={{ height: 1, background: '#f0f0f0', margin: '4px 8px' }} />
|
|
<MenuRow icon="↩" label={t.signOut} color="#ef4444" onClick={() => { onLogout(); setOpen(false) }} />
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
function MenuRow({ icon, label, color = '#374151', onClick }: { icon: string; label: string; color?: string; onClick: () => void }) {
|
|
const [hover, setHover] = useState(false)
|
|
return (
|
|
<div onClick={onClick} onMouseEnter={() => setHover(true)} onMouseLeave={() => setHover(false)}
|
|
style={{
|
|
padding: '8px 14px', fontSize: 13, color, cursor: 'pointer',
|
|
display: 'flex', alignItems: 'center', gap: 9,
|
|
background: hover ? (color === '#ef4444' ? '#fef2f2' : '#f9fafb') : 'white',
|
|
transition: 'background 0.1s',
|
|
}}>
|
|
<span style={{ fontSize: 14, width: 18, textAlign: 'center' }}>{icon}</span>
|
|
{label}
|
|
</div>
|
|
)
|
|
}
|