199 lines
8.8 KiB
TypeScript
199 lines
8.8 KiB
TypeScript
'use client'
|
||
|
||
import { useEffect, useState } from 'react'
|
||
import { useSession, signIn, signOut } from 'next-auth/react'
|
||
import { signOutWithStatus } from '@/app/lib/signOutWithStatus'
|
||
import { useRouter, usePathname } from 'next/navigation'
|
||
import { AnimatePresence, motion } from 'framer-motion'
|
||
import Image from 'next/image'
|
||
import LoadingSpinner from '@/app/components/LoadingSpinner'
|
||
import Button from './Button'
|
||
import UserAvatarWithStatus from './UserAvatarWithStatus'
|
||
import PremierRankBadge from './PremierRankBadge'
|
||
|
||
export default function SidebarFooter() {
|
||
const router = useRouter()
|
||
const pathname = usePathname()
|
||
const { data: session, status } = useSession()
|
||
|
||
const [isOpen, setIsOpen] = useState(false)
|
||
const [teamName, setTeamName] = useState<string | null>(null)
|
||
const [premierRank, setPremierRank] = useState<number>(0)
|
||
|
||
// ➜ Nach Login: User aus DB laden (inkl. premierRank & Teamname)
|
||
useEffect(() => {
|
||
if (status !== 'authenticated') {
|
||
setTeamName(null)
|
||
setPremierRank(0) // ← immer 0, nicht null
|
||
return
|
||
}
|
||
(async () => {
|
||
try {
|
||
const res = await fetch('/api/user', { cache: 'no-store' })
|
||
if (!res.ok) return
|
||
const user = await res.json()
|
||
const rank = typeof user?.premierRank === 'number' ? user.premierRank : 0
|
||
setPremierRank(rank)
|
||
setTeamName(user?.team?.name ?? null)
|
||
} catch (e) {
|
||
console.error('[SidebarFooter] /api/user fehlgeschlagen:', e)
|
||
setPremierRank(0)
|
||
}
|
||
})()
|
||
}, [status])
|
||
|
||
if (status === 'loading') return <LoadingSpinner />
|
||
|
||
if (status === 'unauthenticated') {
|
||
return (
|
||
<button
|
||
onClick={() => signIn('steam')}
|
||
className="flex items-center justify-center gap-2 w-full py-4 px-6 bg-green-800 text-white text-md font-medium hover:bg-green-900 transition"
|
||
>
|
||
<i className="fab fa-steam" />
|
||
<span>Mit Steam anmelden</span>
|
||
</button>
|
||
)
|
||
}
|
||
|
||
const subline = teamName ?? session?.user?.steamId
|
||
const userName = session?.user?.name || 'Profil'
|
||
const avatarSrc = (session?.user as any)?.avatar || session?.user?.image || '/default-avatar.png'
|
||
|
||
const linkClass = (active: boolean) =>
|
||
`flex items-center gap-x-3.5 py-2 px-2.5 text-sm rounded-lg transition-colors ${
|
||
active
|
||
? 'bg-gray-100 dark:bg-neutral-700 text-gray-900 dark:text-white'
|
||
: 'text-gray-800 hover:bg-gray-100 dark:text-neutral-300 dark:hover:bg-neutral-700'
|
||
}`
|
||
|
||
return (
|
||
<div className="relative w-full min-h-[65px]">
|
||
{/* Kopf / Toggle */}
|
||
<button
|
||
onClick={() => setIsOpen(v => !v)}
|
||
className={`w-full min-h-[65px] inline-flex items-center gap-x-2 px-4 py-3 text-sm text-left text-gray-800 transition-all duration-100
|
||
${isOpen ? 'bg-gray-100 dark:bg-neutral-700' : 'hover:bg-gray-100 dark:hover:bg-neutral-700'}
|
||
`}
|
||
>
|
||
{/* Linker Block: Avatar + Name/Subline + Rank */}
|
||
<div className="flex items-center flex-1 min-w-0">
|
||
<UserAvatarWithStatus
|
||
src={avatarSrc}
|
||
alt={userName}
|
||
size={30}
|
||
steamId={session?.user?.steamId}
|
||
/>
|
||
<div className="ms-3 flex-1 min-w-0">
|
||
<h3 className="font-semibold text-gray-800 dark:text-white truncate">
|
||
{userName}
|
||
</h3>
|
||
<p className="text-xs font-medium text-gray-400 dark:text-neutral-500 truncate">
|
||
{subline}
|
||
</p>
|
||
</div>
|
||
|
||
{/* Badge darf nicht schrumpfen */}
|
||
<div className="ml-2 flex-shrink-0">
|
||
<PremierRankBadge rank={premierRank} />
|
||
</div>
|
||
</div>
|
||
|
||
{/* Pfeil – ebenfalls nicht schrumpfen */}
|
||
<svg
|
||
className={`ms-2 size-4 shrink-0 ${isOpen ? 'rotate-180' : ''} text-gray-600 dark:text-neutral-400`}
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
fill="none"
|
||
viewBox="0 0 24 24"
|
||
stroke="currentColor"
|
||
>
|
||
<path strokeWidth={2} d="m5 15 7-7 7 7" />
|
||
</svg>
|
||
</button>
|
||
|
||
{/* Dropdown */}
|
||
<AnimatePresence>
|
||
{isOpen && (
|
||
<motion.div
|
||
initial={{ opacity: 0, height: 0 }}
|
||
animate={{ opacity: 1, height: 'auto' }}
|
||
exit={{ opacity: 0, height: 0 }}
|
||
transition={{ duration: 0.2 }}
|
||
className="overflow-hidden w-full bg-white shadow-lg dark:bg-neutral-800 dark:border-neutral-600 z-20"
|
||
>
|
||
<div className="p-2 flex flex-col gap-1">
|
||
<Button
|
||
onClick={() => router.push(`/profile/${session?.user?.steamId}`)}
|
||
size="sm"
|
||
variant="link"
|
||
className={linkClass(pathname === `/profile/${session?.user?.steamId}`)}
|
||
>
|
||
<svg className="size-4" xmlns="http://www.w3.org/2000/svg" fill="none"
|
||
viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||
<path d="M15 9h3m-3 3h3m-3 3h3m-6 1c-.306-.613-.933-1-1.618-1H7.618c-.685 0-1.312.387-1.618 1M4 5h16a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V6a1 1 0 0 1 1-1Zm7 5a2 2 0 1 1-4 0 2 2 0 0 1 4 0Z"/>
|
||
</svg>
|
||
Profil
|
||
</Button>
|
||
|
||
<Button
|
||
onClick={() => router.push(`/team`)}
|
||
size="sm"
|
||
variant="link"
|
||
className={linkClass(pathname === '/team')}
|
||
>
|
||
<svg className="size-4" xmlns="http://www.w3.org/2000/svg" fill="none"
|
||
viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||
<path d="M15 9h3m-3 3h3m-3 3h3m-6 1c-.306-.613-.933-1-1.618-1H7.618c-.685 0-1.312.387-1.618 1M4 5h16a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V6a1 1 0 0 1 1-1Zm7 5a2 2 0 1 1-4 0 2 2 0 0 1 4 0Z"/>
|
||
</svg>
|
||
Team
|
||
</Button>
|
||
|
||
<Button
|
||
onClick={() => router.push('/settings')}
|
||
size="sm"
|
||
variant="link"
|
||
className={linkClass(pathname.startsWith('/settings'))}
|
||
>
|
||
<svg className="size-4" xmlns="http://www.w3.org/2000/svg" fill="none"
|
||
viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||
<path d="M10 19H5a1 1 0 0 1-1-1v-1a3 3 0 0 1 3-3h2m10 1a3 3 0 0 1-3 3m3-3a3 3 0 0 0-3-3m3 3h1m-4 3a3 3 0 0 1-3-3m3 3v1m-3-4a3 3 0 0 1 3-3m-3 3h-1m4-3v-1m-2.121 1.879-.707-.707m5.656 5.656-.707-.707m-4.242 0-.707.707m5.656-5.656-.707.707M12 8a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z"/>
|
||
</svg>
|
||
Einstellungen
|
||
</Button>
|
||
|
||
{session?.user?.isAdmin && (
|
||
<Button
|
||
onClick={() => router.push('/admin')}
|
||
size="sm"
|
||
variant="link"
|
||
className={linkClass(pathname.startsWith('/admin'))}
|
||
>
|
||
<svg className="size-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"
|
||
fill="currentColor">
|
||
<path transform="scale(0.046875)" d="M78.6 5C69.1-2.4 55.6-1.5 47 7L7 47c-8.5 8.5-9.4 22-2.1 31.6l80 104c4.5 5.9 11.6 9.4 19 9.4l54.1 0 109 109c-14.7 29-10 65.4 14.3 89.6l112 112c12.5 12.5 32.8 12.5 45.3 0l64-64c12.5-12.5 12.5-32.8 0-45.3l-112-112c-24.2-24.2-60.6-29-89.6-14.3l-109-109 0-54.1c0-7.5-3.5-14.5-9.4-19L78.6 5zM19.9 396.1C7.2 408.8 0 426.1 0 444.1C0 481.6 30.4 512 67.9 512c18 0 35.3-7.2 48-19.9L233.7 374.3c-7.8-20.9-9-43.6-3.6-65.1l-61.7-61.7L19.9 396.1zM512 144c0-10.5-1.1-20.7-3.2-30.5c-2.4-11.2-16.1-14.1-24.2-6l-63.9 63.9c-3 3-7.1 4.7-11.3 4.7L352 176c-8.8 0-16-7.2-16-16l0-57.4c0-4.2 1.7-8.3 4.7-11.3l63.9-63.9c8.1-8.1 5.2-21.8-6-24.2C388.7 1.1 378.5 0 368 0C288.5 0 224 64.5 224 144l0 .8 85.3 85.3c36-9.1 75.8 .5 104 28.7L429 274.5c49-23 83-72.8 83-130.5zM56 432a24 24 0 1 1 48 0 24 24 0 1 1 -48 0z"/>
|
||
</svg>
|
||
Administration
|
||
</Button>
|
||
)}
|
||
|
||
<Button
|
||
onClick={() => signOutWithStatus()}
|
||
size="sm"
|
||
variant="link"
|
||
color="red"
|
||
className="flex items-center gap-x-3.5 py-2 px-2.5 text-sm rounded-lg transition-colors text-gray-800 hover:bg-red-100 dark:text-neutral-300 dark:hover:bg-red-700"
|
||
>
|
||
<svg className="size-4" xmlns="http://www.w3.org/2000/svg" fill="none"
|
||
viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||
<path d="M20 12H8m12 0-4 4m4-4-4-4M9 4H7a3 3 0 0 0-3 3v10a3 3 0 0 0 3 3h2"/>
|
||
</svg>
|
||
Abmelden
|
||
</Button>
|
||
</div>
|
||
</motion.div>
|
||
)}
|
||
</AnimatePresence>
|
||
</div>
|
||
)
|
||
}
|