morethanadiagnosis-hub/web/components/layouts/DashboardLayout.tsx

155 lines
5.9 KiB
TypeScript

'use client'
import React, { useState } from 'react'
import { Header } from '../common/Header'
import { useAuth } from '@/lib/hooks/useAuth'
import { useRouter, usePathname } from 'next/navigation'
import { Link } from '../common/Link'
export interface DashboardLayoutProps {
children: React.ReactNode
}
export const DashboardLayout = ({ children }: DashboardLayoutProps) => {
const { user, isAuthenticated, logout } = useAuth()
const router = useRouter()
const pathname = usePathname()
const [sidebarOpen, setSidebarOpen] = useState(false)
// Redirect to login if not authenticated
React.useEffect(() => {
if (!isAuthenticated) {
router.push('/login')
}
}, [isAuthenticated, router])
const handleLogin = () => {
router.push('/login')
}
const sidebarItems = [
{ label: 'Overview', href: '/dashboard', icon: '📊' },
{ label: 'Profile', href: '/dashboard/profile', icon: '👤' },
{ label: 'Preferences', href: '/dashboard/preferences', icon: '⚙️' },
{ label: 'Security', href: '/dashboard/security', icon: '🔒' },
{ label: 'Privacy', href: '/dashboard/privacy', icon: '🛡️' },
]
if (!isAuthenticated) {
return null // or a loading spinner
}
return (
<div className="min-h-screen flex flex-col bg-gray-50 dark:bg-gray-900">
<Header
isAuthenticated={isAuthenticated}
userDisplayName={user?.display_name || user?.email}
onLogin={handleLogin}
onLogout={logout}
/>
<div className="flex-1 flex">
{/* Sidebar for desktop */}
<aside className="hidden md:flex md:flex-shrink-0">
<div className="w-64 flex flex-col border-r border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800">
<nav className="flex-1 px-4 py-6 space-y-1" aria-label="Dashboard navigation">
{sidebarItems.map((item) => {
const isActive = pathname === item.href
return (
<Link
key={item.href}
href={item.href}
variant="neutral"
className={`
flex items-center px-4 py-2 text-sm font-medium rounded-md no-underline
${isActive
? 'bg-primary-100 text-primary-900 dark:bg-primary-900 dark:text-primary-100'
: 'text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-700'
}
`}
>
<span className="mr-3 text-xl" aria-hidden="true">
{item.icon}
</span>
{item.label}
</Link>
)
})}
</nav>
</div>
</aside>
{/* Mobile sidebar */}
{sidebarOpen && (
<div className="fixed inset-0 z-40 md:hidden">
<div
className="fixed inset-0 bg-gray-600 bg-opacity-75"
onClick={() => setSidebarOpen(false)}
/>
<div className="fixed inset-y-0 left-0 flex flex-col w-64 bg-white dark:bg-gray-800 border-r border-gray-200 dark:border-gray-700">
<div className="flex items-center justify-between h-16 px-4 border-b border-gray-200 dark:border-gray-700">
<span className="text-lg font-semibold text-gray-900 dark:text-white">
Dashboard
</span>
<button
onClick={() => setSidebarOpen(false)}
className="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"
aria-label="Close sidebar"
>
<svg className="h-6 w-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
<nav className="flex-1 px-4 py-6 space-y-1">
{sidebarItems.map((item) => {
const isActive = pathname === item.href
return (
<Link
key={item.href}
href={item.href}
variant="neutral"
className={`
flex items-center px-4 py-2 text-sm font-medium rounded-md no-underline
${isActive
? 'bg-primary-100 text-primary-900 dark:bg-primary-900 dark:text-primary-100'
: 'text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-700'
}
`}
onClick={() => setSidebarOpen(false)}
>
<span className="mr-3 text-xl" aria-hidden="true">
{item.icon}
</span>
{item.label}
</Link>
)
})}
</nav>
</div>
</div>
)}
{/* Main content */}
<main className="flex-1">
{/* Mobile sidebar toggle */}
<div className="md:hidden px-4 py-4 border-b border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800">
<button
onClick={() => setSidebarOpen(true)}
className="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"
aria-label="Open sidebar"
>
<svg className="h-6 w-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6h16M4 12h16M4 18h16" />
</svg>
</button>
</div>
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
{children}
</div>
</main>
</div>
</div>
)
}