// components/UserMenu.tsx 'use client'; import { useEffect, useRef, useState } from 'react'; import { ChevronDownIcon } from '@heroicons/react/20/solid'; import { signOut, useSession } from 'next-auth/react'; import PersonAvatar from '@/components/ui/UserAvatar'; import ProfileAvatarModal from '@/components/ProfileAvatarModal'; export type UserMenuProps = { displayName: string; avatarName: string; avatarUrl?: string | null; }; const userNavigation = [ { name: 'Profilbild ändern', href: '#' }, { name: 'Abmelden', href: '#' }, ]; export default function UserMenu({ displayName, avatarName, avatarUrl, }: UserMenuProps) { const [open, setOpen] = useState(false); const [avatarModalOpen, setAvatarModalOpen] = useState(false); const { update } = useSession(); const [currentAvatarUrl, setCurrentAvatarUrl] = useState(avatarUrl ?? null); const buttonRef = useRef(null); const menuRef = useRef(null); // Klick außerhalb / Escape => Menü schließen useEffect(() => { if (!open) return; function handleClickOutside(event: MouseEvent) { const target = event.target as Node | null; if ( menuRef.current && !menuRef.current.contains(target) && buttonRef.current && !buttonRef.current.contains(target) ) { setOpen(false); } } function handleKeyDown(event: KeyboardEvent) { if (event.key === 'Escape') { setOpen(false); buttonRef.current?.focus(); } } document.addEventListener('mousedown', handleClickOutside); document.addEventListener('keydown', handleKeyDown); return () => { document.removeEventListener('mousedown', handleClickOutside); document.removeEventListener('keydown', handleKeyDown); }; }, [open]); const handleToggle = () => { setOpen((prev) => !prev); }; const handleItemClick = (itemName: string) => { setOpen(false); if (itemName === 'Profilbild ändern') { setAvatarModalOpen(true); return; } if (itemName === 'Abmelden') { void signOut({ callbackUrl: '/login' }); return; } }; const handleAvatarSelected = async (file: File) => { const formData = new FormData(); formData.append('avatar', file); const res = await fetch('/api/profile/avatar', { method: 'POST', body: formData, }); if (!res.ok) { console.error('Avatar-Upload fehlgeschlagen'); return; } const data = await res.json(); const newUrl = data.avatarUrl as string | null; // 1) Lokal sofort aktualisieren setCurrentAvatarUrl(newUrl); // 2) NextAuth-Session aktualisieren → layout.tsx bekommt neue avatarUrl await update({ avatarUrl: newUrl }); }; const handleAvatarDelete = async () => { const res = await fetch('/api/profile/avatar', { method: 'DELETE', }); if (!res.ok) { console.error('Avatar-Löschung fehlgeschlagen'); return; } // 1) Lokal zurück auf null setCurrentAvatarUrl(null); // 2) Session updaten → überall Fallback-Initialen await update({ avatarUrl: null }); }; return ( <>
{open && ( )}
{/* Profilbild-Modal */} setAvatarModalOpen(false)} avatarName={avatarName} avatarUrl={currentAvatarUrl ?? undefined} onAvatarSelected={handleAvatarSelected} onAvatarDelete={handleAvatarDelete} /> ); }