98 lines
3.2 KiB
TypeScript
98 lines
3.2 KiB
TypeScript
import { Link, useNavigate, useRouter } from '@tanstack/react-router'
|
|
import { LogOut, UserCog } from 'lucide-react'
|
|
import { useState, type ReactNode } from 'react'
|
|
import { Button } from '#/components/ui/button.tsx'
|
|
import type { Session } from '#/lib/auth.ts'
|
|
import { authClient } from '#/lib/auth-client.ts'
|
|
|
|
type AppShellProps = {
|
|
session: Session
|
|
nav?: ReactNode
|
|
children: ReactNode
|
|
}
|
|
|
|
export function AppShell({ session, nav, children }: AppShellProps) {
|
|
const router = useRouter()
|
|
const navigate = useNavigate()
|
|
const [stopping, setStopping] = useState(false)
|
|
const impersonating = Boolean(session.session.impersonatedBy)
|
|
|
|
async function signOut() {
|
|
await authClient.signOut()
|
|
await router.invalidate()
|
|
await navigate({ to: '/login' })
|
|
}
|
|
|
|
async function stopImpersonation() {
|
|
setStopping(true)
|
|
await authClient.admin.stopImpersonating()
|
|
await router.invalidate()
|
|
setStopping(false)
|
|
await navigate({ to: '/admin' })
|
|
}
|
|
|
|
return (
|
|
<div className="flex min-h-screen flex-col bg-background">
|
|
{impersonating ? (
|
|
<div className="border-b border-amber-500/40 bg-amber-500/10 text-amber-900 dark:text-amber-200">
|
|
<div className="mx-auto flex max-w-6xl items-center justify-between gap-4 px-4 py-2 md:px-6">
|
|
<p className="flex items-center gap-2 text-sm">
|
|
<UserCog className="size-4" />
|
|
Sie sind als <strong>{session.user.name}</strong> ({session.user.email}) eingeloggt.
|
|
</p>
|
|
<Button
|
|
type="button"
|
|
variant="outline"
|
|
size="sm"
|
|
onClick={stopImpersonation}
|
|
disabled={stopping}
|
|
>
|
|
{stopping ? 'Beende ...' : 'Impersonation beenden'}
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
) : null}
|
|
<header className="border-b border-border bg-card">
|
|
<div className="mx-auto flex max-w-6xl items-center justify-between gap-4 px-4 py-3 md:px-6">
|
|
<Link
|
|
to={session.user.role === 'admin' ? '/admin' : '/dashboard'}
|
|
className="flex items-center gap-2 no-underline"
|
|
>
|
|
<img
|
|
alt="Logo Webkulisse"
|
|
src="/img/logo.png"
|
|
className="h-8 w-8 rounded-md"
|
|
/>
|
|
<span className="font-heading text-base font-bold">webkulisse</span>
|
|
</Link>
|
|
|
|
{nav ? <nav className="flex items-center gap-1">{nav}</nav> : null}
|
|
|
|
<div className="flex items-center gap-3">
|
|
<div className="hidden text-right sm:block">
|
|
<div className="text-sm font-medium leading-tight">
|
|
{session.user.name}
|
|
</div>
|
|
<div className="text-xs text-muted-foreground leading-tight">
|
|
{session.user.email}
|
|
</div>
|
|
</div>
|
|
<Button
|
|
type="button"
|
|
variant="outline"
|
|
size="sm"
|
|
onClick={signOut}
|
|
>
|
|
<LogOut />
|
|
Abmelden
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
<main className="flex-1">
|
|
<div className="mx-auto max-w-6xl px-4 py-8 md:px-6">{children}</div>
|
|
</main>
|
|
</div>
|
|
)
|
|
}
|