beautify: register #1

This commit is contained in:
DeathKaioken 2025-10-03 22:40:25 +02:00
parent d5cb8e673d
commit 2bddd8360b
3 changed files with 152 additions and 167 deletions

View File

@ -45,6 +45,7 @@ const navLinks = [
export default function Header() {
const [mobileMenuOpen, setMobileMenuOpen] = useState(false)
const [isDark, setIsDark] = useState(false)
const [mounted, setMounted] = useState(false) // added
const user = useAuthStore((s) => s.user);
const logout = useAuthStore((s) => s.logout);
const router = useRouter();
@ -83,6 +84,7 @@ export default function Header() {
document.documentElement.classList.remove('dark')
setIsDark(false)
}
setMounted(true) // hydration complete
}, [])
const toggleTheme = useCallback(() => {
@ -99,6 +101,9 @@ export default function Header() {
})
}, [])
const isLoggedIn = !!user
const userPresent = mounted && isLoggedIn
return (
<header
className="relative isolate z-10 border-b border-white/10 shadow-lg shadow-black/30 after:pointer-events-none after:absolute after:inset-0 after:-z-10 after:bg-[radial-gradient(circle_at_20%_20%,rgba(56,124,255,0.18),transparent_55%),radial-gradient(circle_at_80%_35%,rgba(139,92,246,0.16),transparent_60%)]"
@ -190,60 +195,67 @@ export default function Header() {
{/* Removed user profile Popover from here (now on right side) */}
</PopoverGroup>
<div className="hidden lg:flex lg:flex-1 lg:justify-end lg:items-center lg:gap-x-4">
{/* Avatar first (when logged in) - repositioned dropdown */}
{user && (
<Popover className="relative">
<PopoverButton className="flex items-center gap-x-1 text-sm font-semibold text-gray-900 dark:text-white">
<Avatar
src=""
initials={getUserInitials()}
className="size-8 bg-gradient-to-br from-indigo-500/40 to-indigo-600/60 text-white"
/>
<ChevronDownIcon aria-hidden="true" className="size-5 flex-none text-gray-500" />
</PopoverButton>
<PopoverPanel
transition
className="absolute left-0 top-full mt-2 w-64 rounded-md bg-white dark:bg-gray-900 ring-1 ring-black/10 dark:ring-white/10 shadow-lg data-closed:-translate-y-1 data-closed:opacity-0 data-enter:duration-200 data-leave:duration-150"
>
<div className="p-4">
<div className="flex flex-col border-b border-gray-200 dark:border-white/10 pb-4 mb-4">
<div className="font-medium text-gray-900 dark:text-white">
{user?.firstName && user?.lastName ? `${user.firstName} ${user.lastName}` : (user?.email || 'User')}
</div>
<div className="text-sm text-gray-500 dark:text-gray-400">
{user?.email || 'user@example.com'}
{/* Stable auth slot to avoid SSR/CSR structural drift */}
<div className="flex items-center">
{userPresent ? (
<Popover className="relative">
<PopoverButton className="flex items-center gap-x-1 text-sm font-semibold text-gray-900 dark:text-white">
<Avatar
src=""
initials={(() => {
if (!user) return 'U'
if (user.firstName || user.lastName) {
return ((user.firstName?.[0] || '') + (user.lastName?.[0] || '')).toUpperCase()
}
return user.email ? user.email[0].toUpperCase() : 'U'
})()}
className="size-8 bg-gradient-to-br from-indigo-500/40 to-indigo-600/60 text-white"
/>
<ChevronDownIcon aria-hidden="true" className="size-5 flex-none text-gray-500" />
</PopoverButton>
<PopoverPanel
transition
className="absolute left-0 top-full mt-2 w-64 rounded-md bg-white dark:bg-gray-900 ring-1 ring-black/10 dark:ring-white/10 shadow-lg data-closed:-translate-y-1 data-closed:opacity-0 data-enter:duration-200 data-leave:duration-150"
>
<div className="p-4">
<div className="flex flex-col border-b border-gray-200 dark:border-white/10 pb-4 mb-4">
<div className="font-medium text-gray-900 dark:text-white">
{user?.firstName && user?.lastName ? `${user.firstName} ${user.lastName}` : (user?.email || 'User')}
</div>
<div className="text-sm text-gray-500 dark:text-gray-400">
{user?.email || 'user@example.com'}
</div>
</div>
<button
onClick={() => router.push('/profile')}
className="flex items-center gap-x-2 w-full text-left p-2 text-sm text-gray-800 dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-white/5 rounded-md"
>
<UserCircleIcon className="size-5 text-gray-400" />
Profile
</button>
<button
onClick={handleLogout}
className="flex items-center gap-x-2 w-full text-left p-2 text-sm text-gray-800 dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-white/5 rounded-md"
>
<ArrowRightOnRectangleIcon className="size-5 text-gray-400" />
Logout
</button>
</div>
<button
onClick={() => router.push('/profile')}
className="flex items-center gap-x-2 w-full text-left p-2 text-sm text-gray-800 dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-white/5 rounded-md"
>
<UserCircleIcon className="size-5 text-gray-400" />
Profile
</button>
<button
onClick={handleLogout}
className="flex items-center gap-x-2 w-full text-left p-2 text-sm text-gray-800 dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-white/5 rounded-md"
>
<ArrowRightOnRectangleIcon className="size-5 text-gray-400" />
Logout
</button>
</div>
</PopoverPanel>
</Popover>
)}
{/* Login button (only when not logged in) */}
{!user && (
<button
onClick={() => router.push('/login')}
className="text-sm/6 font-semibold text-gray-900 dark:text-white hover:text-indigo-600 dark:hover:text-indigo-400 transition-colors"
>
Log in <span aria-hidden="true">&rarr;</span>
</button>
)}
{/* Language after avatar/login */}
</PopoverPanel>
</Popover>
) : mounted ? (
<button
onClick={() => router.push('/login')}
className="text-sm/6 font-semibold text-gray-900 dark:text-white hover:text-indigo-600 dark:hover:text-indigo-400 transition-colors"
>
Log in <span aria-hidden="true">&rarr;</span>
</button>
) : (
<div aria-hidden="true" className="w-20 h-8 rounded-md bg-gray-200 dark:bg-gray-700/70 animate-pulse" />
)}
</div>
{/* Language & theme remain after auth slot */}
<LanguageSwitcher variant={isDark ? 'dark' : 'light'} />
{/* Theme toggle last */}
<button
onClick={toggleTheme}
aria-label="Toggle theme"
@ -254,7 +266,6 @@ export default function Header() {
</div>
</nav>
<Dialog open={mobileMenuOpen} onClose={setMobileMenuOpen} className="lg:hidden">
{/* Overlay with fade */}
<Transition
appear
show={mobileMenuOpen}
@ -308,7 +319,11 @@ export default function Header() {
</div>
<div className="mt-6 flow-root">
<div className="-my-6 divide-y divide-gray-200 dark:divide-white/10">
{user ? (
{!mounted ? (
<div className="py-6 px-3">
<div className="h-28 w-full rounded-lg bg-gray-200 dark:bg-gray-700 animate-pulse" />
</div>
) : user ? (
<>
{/* User info now FIRST under logo */}
<div className="pt-6 space-y-2">

View File

@ -8,13 +8,54 @@ interface SessionDetectedModalProps {
open: boolean
onLogout: () => void
onCancel: () => void
inline?: boolean // added
}
export default function SessionDetectedModal({
open,
onLogout,
onCancel
onCancel,
inline = false
}: SessionDetectedModalProps) {
if (inline) {
return (
<div className="w-full flex justify-center items-center flex-1 min-h-[50vh]">
<div className="bg-white px-6 py-6 rounded-xl shadow-xl max-w-lg w-full border border-gray-200">
<div className="flex gap-4">
<div className="flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-full bg-orange-100">
<ExclamationTriangleIcon className="h-6 w-6 text-orange-600" aria-hidden="true" />
</div>
<div>
<h3 className="text-base font-semibold leading-6 text-[#0F172A]">
Aktive Sitzung erkannt
</h3>
<p className="mt-2 text-sm text-[#4A4A4A]">
Du bist bereits angemeldet. Um dich zu registrieren, musst du dich zuerst abmelden
oder du kannst zum Dashboard gehen.
</p>
<div className="mt-5 flex flex-col sm:flex-row gap-3 sm:justify-end">
<button
type="button"
onClick={onCancel}
className="inline-flex justify-center rounded-md bg-white px-4 py-2 text-sm font-medium text-[#4A4A4A] shadow-sm ring-1 ring-gray-300 hover:bg-gray-50 transition-colors"
>
Zum Dashboard
</button>
<button
type="button"
onClick={onLogout}
className="inline-flex justify-center rounded-md bg-[#8D6B1D] px-4 py-2 text-sm font-semibold text-white shadow-sm hover:bg-[#7A5E1A] transition-colors"
>
Abmelden und registrieren
</button>
</div>
</div>
</div>
</div>
</div>
)
}
return (
<Transition.Root show={open} as={Fragment}>
<Dialog as="div" className="relative z-50" onClose={onCancel}>

View File

@ -2,8 +2,10 @@
import { useEffect, useState } from 'react'
import { useSearchParams, useRouter } from 'next/navigation'
import PageLayout from '../components/PageLayout'
import useAuthStore from '../store/authStore'
import RegisterForm from './components/RegisterForm'
import PageLayout from '../components/PageLayout'
import SessionDetectedModal from './components/SessionDetectedModal'
export default function RegisterPage() {
const searchParams = useSearchParams()
@ -20,19 +22,17 @@ export default function RegisterPage() {
const [showSessionModal, setShowSessionModal] = useState(false)
const [sessionCleared, setSessionCleared] = useState(false)
// Redirect to login on successful registration
// Redirect to login after simulated registration
useEffect(() => {
if (registered) {
// Show success toast would go here
setTimeout(() => router.push('/login'), 1200)
const t = setTimeout(() => router.push('/login'), 1200)
return () => clearTimeout(t)
}
}, [registered, router])
// Check for existing session
// Detect existing logged-in session
useEffect(() => {
if (user && !sessionCleared) {
setShowSessionModal(true)
}
if (user && !sessionCleared) setShowSessionModal(true)
}, [user, sessionCleared])
const handleLogout = async () => {
@ -46,107 +46,36 @@ export default function RegisterPage() {
router.push('/dashboard')
}
// Block registration if session detected and not cleared
if (showSessionModal) {
return (
<PageLayout>
<div className="min-h-screen flex items-center justify-center">
<div className="bg-white p-8 rounded-lg shadow-lg max-w-md w-full">
<h3 className="text-lg font-semibold text-[#0F172A] mb-4">
Aktive Sitzung erkannt
</h3>
<p className="text-[#4A4A4A] mb-6">
Du bist bereits angemeldet. Um dich zu registrieren, musst du dich zuerst abmelden.
</p>
<div className="flex gap-3">
<button
onClick={handleLogout}
className="flex-1 bg-[#8D6B1D] text-white px-4 py-2 rounded-lg hover:bg-[#7A5E1A] transition-colors"
>
Abmelden und registrieren
</button>
<button
onClick={handleCancel}
className="flex-1 bg-gray-100 text-[#4A4A4A] px-4 py-2 rounded-lg hover:bg-gray-200 transition-colors"
>
Zum Dashboard
</button>
</div>
</div>
</div>
</PageLayout>
)
}
// Prevent form rendering until session is cleared
if (!sessionCleared && user) {
return null
}
return (
<PageLayout>
<div className="min-h-screen w-full flex flex-col px-4 py-8">
<div className="flex-1 flex items-start justify-center w-full pt-8">
<div className="w-full max-w-4xl mx-auto bg-white rounded-2xl shadow-2xl px-6 py-8 sm:px-12 sm:py-10">
<div className="text-center">
<h2 className="text-2xl sm:text-3xl font-extrabold text-[#0F172A] mb-4">
Registrierung für Profit Planet
</h2>
{refToken && (
<p className="text-base sm:text-sm text-[#8D6B1D] font-medium mb-8">
Du wurdest eingeladen! Referral-Token: {refToken}
</p>
)}
{/* Mode Toggle */}
<div className="flex justify-center mb-8">
<div className="bg-gray-100 p-1 rounded-lg">
<button
className={`px-6 py-2 rounded-md font-semibold text-sm transition-all duration-200 ${
mode === 'personal'
? 'bg-[#8D6B1D] text-white shadow-sm'
: 'bg-transparent text-[#4A4A4A] hover:text-[#8D6B1D]'
}`}
onClick={() => setMode('personal')}
>
Privatperson
</button>
<button
className={`px-6 py-2 rounded-md font-semibold text-sm transition-all duration-200 ${
mode === 'company'
? 'bg-[#8D6B1D] text-white shadow-sm'
: 'bg-transparent text-[#4A4A4A] hover:text-[#8D6B1D]'
}`}
onClick={() => setMode('company')}
>
Unternehmen
</button>
</div>
</div>
<div className="text-center">
<p className="text-[#4A4A4A] mb-4">
Registrierungsmodus: <strong>{mode === 'personal' ? 'Privatperson' : 'Unternehmen'}</strong>
</p>
<button
onClick={() => setRegistered(true)}
className="bg-[#8D6B1D] text-white px-6 py-3 rounded-lg hover:bg-[#7A5E1A] transition-colors font-semibold"
>
Test: Registrierung erfolgreich simulieren
</button>
<div className="mt-8">
<p className="text-[#4A4A4A]">
Bereits registriert?{' '}
<a href="/login" className="text-[#8D6B1D] hover:text-[#7A5E1A] font-medium transition-colors">
Hier anmelden
</a>
</p>
</div>
</div>
</div>
<main className="w-full flex flex-col flex-1 px-4 pt-12 pb-16 gap-10 min-h-[70vh]">
{showSessionModal ? (
<div className="flex flex-1 items-center justify-center">
<SessionDetectedModal
inline
open
onLogout={handleLogout}
onCancel={handleCancel}
/>
</div>
</div>
</div>
) : (
<>
{(!user || sessionCleared) && (
<RegisterForm
mode={mode}
setMode={setMode}
refToken={refToken}
onRegistered={() => setRegistered(true)}
/>
)}
{registered && (
<div className="mt-6 mx-auto text-center text-sm text-gray-600">
Registrierung erfolgreich Weiterleitung...
</div>
)}
</>
)}
</main>
</PageLayout>
)
}