first commit
This commit is contained in:
51
app/components/ThemeToggle.tsx
Normal file
51
app/components/ThemeToggle.tsx
Normal file
@@ -0,0 +1,51 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect, useSyncExternalStore } from 'react'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Moon, Sun } from 'lucide-react'
|
||||
|
||||
type Theme = 'light' | 'dark'
|
||||
|
||||
function getSnapshot(): Theme {
|
||||
const saved = localStorage.getItem('theme')
|
||||
if (saved === 'dark' || saved === 'light') return saved
|
||||
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
|
||||
}
|
||||
|
||||
function getServerSnapshot(): Theme {
|
||||
return 'light'
|
||||
}
|
||||
|
||||
function subscribe(cb: () => void): () => void {
|
||||
window.addEventListener('storage', cb)
|
||||
return () => window.removeEventListener('storage', cb)
|
||||
}
|
||||
|
||||
export default function ThemeToggle() {
|
||||
const theme = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot)
|
||||
|
||||
useEffect(() => {
|
||||
document.documentElement.classList.toggle('dark', theme === 'dark')
|
||||
}, [theme])
|
||||
|
||||
function toggleTheme() {
|
||||
const next: Theme = theme === 'dark' ? 'light' : 'dark'
|
||||
localStorage.setItem('theme', next)
|
||||
window.dispatchEvent(new StorageEvent('storage', { key: 'theme', newValue: next }))
|
||||
}
|
||||
|
||||
return (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={toggleTheme}
|
||||
suppressHydrationWarning
|
||||
aria-label="Tema değiştir"
|
||||
>
|
||||
{theme === 'dark'
|
||||
? <Sun className="size-4" />
|
||||
: <Moon className="size-4" />}
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
||||
59
app/components/TopBar.tsx
Normal file
59
app/components/TopBar.tsx
Normal file
@@ -0,0 +1,59 @@
|
||||
'use client'
|
||||
|
||||
import Link from 'next/link'
|
||||
import ThemeToggle from './ThemeToggle'
|
||||
import { signOut } from 'next-auth/react'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { LogOut, Zap } from 'lucide-react'
|
||||
|
||||
type Props = {
|
||||
isLoggedIn: boolean
|
||||
}
|
||||
|
||||
export default function TopBar({ isLoggedIn }: Props) {
|
||||
async function onLogout() {
|
||||
await signOut({ callbackUrl: '/auth/login' })
|
||||
}
|
||||
|
||||
return (
|
||||
<header className="sticky top-0 z-50 border-b bg-background/80 backdrop-blur-sm">
|
||||
<div className="mx-auto flex w-full max-w-7xl items-center justify-between px-4 py-2.5">
|
||||
{/* Logo */}
|
||||
<Link href="/" className="flex items-center gap-2">
|
||||
<div className="flex h-7 w-7 items-center justify-center rounded-lg bg-primary text-primary-foreground">
|
||||
<Zap className="size-4" />
|
||||
</div>
|
||||
<span className="text-base font-bold tracking-tight">NextGo</span>
|
||||
</Link>
|
||||
|
||||
{/* Actions */}
|
||||
<nav className="flex items-center gap-1">
|
||||
<ThemeToggle />
|
||||
|
||||
{isLoggedIn ? (
|
||||
<>
|
||||
<Button variant="ghost" size="sm" asChild>
|
||||
<Link href="/admin/users">Admin</Link>
|
||||
</Button>
|
||||
|
||||
<Button type="button" onClick={onLogout} variant="destructive" size="sm">
|
||||
<LogOut className="size-4" />
|
||||
Çıkış
|
||||
</Button>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Button variant="ghost" size="sm" asChild>
|
||||
<Link href="/auth/login">Giriş</Link>
|
||||
</Button>
|
||||
<Button size="sm" asChild>
|
||||
<Link href="/auth/register">Kayıt Ol</Link>
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user