Files
Next.js/src/components/ui/UserDropdown.tsx
David Melendez c3868062e6 init commit
2026-01-14 22:44:08 +01:00

153 lines
4.7 KiB
TypeScript

/**
* User Dropdown Component
* Reusable header dropdown with user information and navigation
*
* @author David Valera Melendez <david@valera-melendez.de>
* @created 2025-08-07
* @location Made in Germany 🇩🇪
*/
'use client';
import { useState } from 'react';
import { useRouter } from 'next/navigation';
import { User, FileText, ChevronDown, Settings, LogOut } from 'lucide-react';
interface UserDropdownProps {
/** Current page identifier to highlight in dropdown */
currentPage?: 'dashboard' | 'settings';
/** Whether to show the logo icon */
showLogo?: boolean;
/** Custom CSS classes */
className?: string;
}
/**
* UserDropdown Component - Reusable header dropdown
*
* Professional user dropdown component with user information display
* and navigation options. Can be customized for different pages.
*
* Features:
* - User information display (name + email)
* - Navigation menu with current page highlighting
* - Settings and logout functionality
* - Optional logo display
* - Responsive design
*
* @param currentPage - Current page to highlight in dropdown
* @param showLogo - Whether to display the logo icon
* @param className - Additional CSS classes
* @returns Reusable user dropdown component
*/
export function UserDropdown({
currentPage,
showLogo = false,
className = '',
}: UserDropdownProps) {
const router = useRouter();
const [isUserMenuOpen, setIsUserMenuOpen] = useState(false);
/**
* Handles navigation to settings page
*/
const handleAccountSettings = () => {
setIsUserMenuOpen(false);
router.push('/settings');
};
/**
* Handles logout functionality
*/
const handleLogout = () => {
setIsUserMenuOpen(false);
// Here you would typically clear user session/tokens
console.log('Logging out...');
// Redirect to login or home page
router.push('/');
};
/**
* Closes dropdown when clicking outside
*/
const handleDropdownToggle = () => {
setIsUserMenuOpen(!isUserMenuOpen);
};
return (
<div className={`flex items-center gap-3 ${className}`}>
{/* Optional Logo */}
{showLogo && (
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-gradient-to-br from-primary-600 to-primary-700 shadow-sm">
<FileText className="h-6 w-6 text-white" />
</div>
)}
{/* User Info Display */}
<div className="text-right">
<h1 className="text-lg font-bold text-primary-700">
David Valera Melendez
</h1>
<p className="text-xs text-gray-500">david@valera-melendez.de</p>
</div>
{/* User Dropdown */}
<div className="relative">
<button
onClick={handleDropdownToggle}
className="flex items-center gap-2 rounded-lg px-3 py-2 text-sm font-medium text-gray-700 transition-colors hover:bg-gray-100"
aria-label="User menu"
aria-expanded={isUserMenuOpen}
>
<div className="flex h-8 w-8 items-center justify-center rounded-full bg-gradient-to-br from-primary-100 to-primary-200">
<User className="h-4 w-4 text-primary-600" />
</div>
<ChevronDown className="h-4 w-4" />
</button>
{/* Dropdown Menu */}
{isUserMenuOpen && (
<>
{/* Backdrop for mobile */}
<div
className="fixed inset-0 z-40 lg:hidden"
onClick={() => setIsUserMenuOpen(false)}
/>
<div className="absolute right-0 top-full z-50 mt-2 w-56 rounded-lg border border-gray-200 bg-white shadow-lg">
<div className="py-1">
{/* Settings Option */}
<button
onClick={handleAccountSettings}
className={`flex w-full items-center gap-2 px-4 py-2 text-left text-sm transition-colors ${
currentPage === 'settings'
? 'bg-primary-50 text-primary-600'
: 'text-gray-700 hover:bg-gray-50'
}`}
>
<Settings className="h-4 w-4" />
Konto-Einstellungen
</button>
{/* Divider */}
<div className="mx-1 my-1 border-t border-gray-100" />
{/* Logout Option */}
<button
onClick={handleLogout}
className="flex w-full items-center gap-2 px-4 py-2 text-left text-sm text-red-600 transition-colors hover:bg-red-50"
>
<LogOut className="h-4 w-4" />
Abmelden
</button>
</div>
</div>
</>
)}
</div>
</div>
);
}
export default UserDropdown;