beautify: register #1
This commit is contained in:
parent
d5cb8e673d
commit
2bddd8360b
@ -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">→</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">→</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">
|
||||
|
||||
@ -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}>
|
||||
|
||||
@ -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()
|
||||
@ -11,142 +13,69 @@ export default function RegisterPage() {
|
||||
const [registered, setRegistered] = useState(false)
|
||||
const [mode, setMode] = useState<'personal' | 'company'>('personal')
|
||||
const router = useRouter()
|
||||
|
||||
|
||||
// Auth state
|
||||
const user = useAuthStore(state => state.user)
|
||||
const logout = useAuthStore(state => state.logout)
|
||||
|
||||
|
||||
// Session management
|
||||
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 () => {
|
||||
await logout()
|
||||
setSessionCleared(true)
|
||||
setShowSessionModal(false)
|
||||
}
|
||||
|
||||
|
||||
const handleCancel = () => {
|
||||
setShowSessionModal(false)
|
||||
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>
|
||||
)
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user