Initial commit: Complete NodeJS-native setup
- Migrated from Python pre-commit to NodeJS-native solution - Reorganized documentation structure - Set up Husky + lint-staged for efficient pre-commit hooks - Fixed Dockerfile healthcheck issue - Added comprehensive documentation index
This commit is contained in:
112
components/auth/AvatarDropdown.tsx
Normal file
112
components/auth/AvatarDropdown.tsx
Normal file
@@ -0,0 +1,112 @@
|
||||
import React, { useState, useRef, useEffect } from 'react';
|
||||
import { User, UserRole } from '../../types';
|
||||
|
||||
interface AvatarDropdownProps {
|
||||
user: User;
|
||||
onLogout: () => void;
|
||||
onAdmin?: () => void;
|
||||
onChangePassword?: () => void;
|
||||
}
|
||||
|
||||
const getInitials = (name: string) => {
|
||||
return name ? name.charAt(0).toUpperCase() : '?';
|
||||
};
|
||||
|
||||
const AvatarDropdown: React.FC<AvatarDropdownProps> = ({
|
||||
user,
|
||||
onLogout,
|
||||
onAdmin,
|
||||
onChangePassword,
|
||||
}) => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const dropdownRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const handleClickOutside = (event: MouseEvent) => {
|
||||
if (
|
||||
dropdownRef.current &&
|
||||
!dropdownRef.current.contains(event.target as Node)
|
||||
) {
|
||||
setIsOpen(false);
|
||||
}
|
||||
};
|
||||
document.addEventListener('mousedown', handleClickOutside);
|
||||
return () => document.removeEventListener('mousedown', handleClickOutside);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className='relative' ref={dropdownRef}>
|
||||
<button
|
||||
onClick={() => setIsOpen(!isOpen)}
|
||||
className='w-10 h-10 rounded-full bg-slate-200 dark:bg-slate-700 flex items-center justify-center text-lg font-bold text-slate-600 dark:text-slate-300 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:focus:ring-offset-slate-900'
|
||||
aria-label='User menu'
|
||||
>
|
||||
{user.avatar ? (
|
||||
<img
|
||||
src={user.avatar}
|
||||
alt='User avatar'
|
||||
className='w-full h-full rounded-full object-cover'
|
||||
/>
|
||||
) : (
|
||||
<span>{getInitials(user.username)}</span>
|
||||
)}
|
||||
</button>
|
||||
|
||||
{isOpen && (
|
||||
<div className='absolute right-0 mt-2 w-48 bg-white dark:bg-slate-800 rounded-md shadow-lg ring-1 ring-black ring-opacity-5 py-1 z-30 border dark:border-slate-700'>
|
||||
<div className='px-4 py-2 border-b border-slate-200 dark:border-slate-700'>
|
||||
<p className='text-sm text-slate-500 dark:text-slate-400'>
|
||||
Signed in as
|
||||
</p>
|
||||
<p className='text-sm font-medium text-slate-800 dark:text-slate-200 truncate'>
|
||||
{user.username}
|
||||
</p>
|
||||
{user.role === UserRole.ADMIN && (
|
||||
<p className='text-xs text-purple-600 dark:text-purple-400 font-medium'>
|
||||
Administrator
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Password Change Option - Only for password-based accounts */}
|
||||
{user.password && onChangePassword && (
|
||||
<button
|
||||
onClick={() => {
|
||||
onChangePassword();
|
||||
setIsOpen(false);
|
||||
}}
|
||||
className='w-full text-left px-4 py-2 text-sm text-slate-700 dark:text-slate-200 hover:bg-slate-100 dark:hover:bg-slate-700'
|
||||
>
|
||||
Change Password
|
||||
</button>
|
||||
)}
|
||||
|
||||
{/* Admin Interface - Only for admins */}
|
||||
{user.role === UserRole.ADMIN && onAdmin && (
|
||||
<button
|
||||
onClick={() => {
|
||||
onAdmin();
|
||||
setIsOpen(false);
|
||||
}}
|
||||
className='w-full text-left px-4 py-2 text-sm text-purple-700 dark:text-purple-300 hover:bg-slate-100 dark:hover:bg-slate-700 font-medium'
|
||||
>
|
||||
Admin Interface
|
||||
</button>
|
||||
)}
|
||||
|
||||
<button
|
||||
onClick={() => {
|
||||
onLogout();
|
||||
setIsOpen(false);
|
||||
}}
|
||||
className='w-full text-left px-4 py-2 text-sm text-slate-700 dark:text-slate-200 hover:bg-slate-100 dark:hover:bg-slate-700'
|
||||
>
|
||||
Logout
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AvatarDropdown;
|
||||
Reference in New Issue
Block a user