simpse / src /app /dashboard /layout.tsx
canboi99's picture
Deploy Simpse real estate platform with Vancouver multifamily building interactive map
0d2425e verified
Raw
History Blame Contribute Delete
10.4 kB
"use client"
import { useState, useEffect } from "react"
import { useRouter } from "next/navigation"
import Link from "next/link"
import { cn } from "@/lib/utils"
import { Button } from "@/components/ui/button"
import { Badge } from "@/components/ui/badge"
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"
import {
Building2,
Home,
TrendingUp,
Map,
BarChart3,
Search,
Settings,
Bell,
User,
LogOut,
Menu,
X,
Code2,
Database,
Wrench,
FileText,
LineChart
} from "lucide-react"
import DebugPanel from "@/components/debug-panel"
const navigation = [
{ name: "Overview", href: "/dashboard", icon: Home, current: true },
{ name: "Professional Analytics", href: "/dashboard/professional", icon: LineChart },
{ name: "Property Search", href: "/dashboard/search", icon: Search },
{ name: "Market Analytics", href: "/dashboard/analytics", icon: BarChart3 },
{ name: "Reports", href: "/dashboard/reports", icon: FileText },
{ name: "Map View", href: "/dashboard/map", icon: Map },
{ name: "Market Trends", href: "/dashboard/trends", icon: TrendingUp },
]
const developerNavigation = [
{ name: "Developer Settings", href: "/dashboard/dev-settings", icon: Wrench },
{ name: "API Explorer", href: "/dashboard/api-explorer", icon: Code2 },
{ name: "Data Browser", href: "/dashboard/data-browser", icon: Database },
]
export default function DashboardLayout({
children,
}: {
children: React.ReactNode
}) {
const [user, setUser] = useState<any>(null)
const [sidebarOpen, setSidebarOpen] = useState(false)
const router = useRouter()
useEffect(() => {
// Check authentication
const authToken = localStorage.getItem("simpse-auth")
const userData = localStorage.getItem("simpse-user")
if (!authToken) {
router.push("/")
return
}
if (userData) {
setUser(JSON.parse(userData))
}
}, [router])
const handleLogout = () => {
localStorage.removeItem("simpse-auth")
localStorage.removeItem("simpse-user")
router.push("/")
}
if (!user) {
return (
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
<div className="text-center">
<Building2 className="h-12 w-12 text-blue-600 mx-auto mb-4" />
<h2 className="text-lg font-medium text-gray-900">Loading...</h2>
</div>
</div>
)
}
return (
<div className="h-screen flex">
{/* Mobile sidebar overlay */}
{sidebarOpen && (
<div className="fixed inset-0 flex z-40 md:hidden">
<div
className="fixed inset-0 bg-gray-600 bg-opacity-75"
onClick={() => setSidebarOpen(false)}
/>
<div className="relative flex-1 flex flex-col max-w-xs w-full bg-white">
<div className="absolute top-0 right-0 -mr-12 pt-2">
<button
type="button"
className="ml-1 flex items-center justify-center h-10 w-10 rounded-full focus:outline-none focus:ring-2 focus:ring-inset focus:ring-white"
onClick={() => setSidebarOpen(false)}
>
<X className="h-6 w-6 text-white" />
</button>
</div>
<SidebarContent />
</div>
</div>
)}
{/* Static sidebar for desktop */}
<div className="hidden md:flex md:flex-shrink-0">
<div className="flex flex-col w-64">
<SidebarContent />
</div>
</div>
{/* Main content */}
<div className="flex flex-col w-0 flex-1 overflow-hidden">
{/* Top navigation */}
<div className="relative z-10 flex-shrink-0 flex h-16 bg-white shadow">
<button
type="button"
className="px-4 border-r border-gray-200 text-gray-500 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-blue-500 md:hidden"
onClick={() => setSidebarOpen(true)}
>
<Menu className="h-6 w-6" />
</button>
<div className="flex-1 px-4 flex justify-between">
<div className="flex-1 flex">
<div className="w-full flex md:ml-0">
<div className="relative w-full text-gray-400 focus-within:text-gray-600 max-w-lg">
<div className="absolute inset-y-0 left-0 flex items-center pointer-events-none">
<Search className="h-5 w-5" />
</div>
<input
className="block w-full h-full pl-8 pr-3 py-2 border-transparent text-gray-900 placeholder-gray-500 focus:outline-none focus:placeholder-gray-400 focus:ring-0 focus:border-transparent text-sm md:text-base"
placeholder="Search properties..."
/>
</div>
</div>
</div>
<div className="ml-4 flex items-center md:ml-6">
<Button variant="ghost" size="icon" className="relative">
<Bell className="h-5 w-5" />
<span className="absolute -top-1 -right-1 h-4 w-4 bg-red-500 rounded-full text-xs text-white flex items-center justify-center">
3
</span>
</Button>
{/* Profile dropdown */}
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="relative ml-3 flex items-center text-sm">
<Avatar className="h-8 w-8 mr-2">
<AvatarImage src="/avatars/demo.jpg" />
<AvatarFallback>DU</AvatarFallback>
</Avatar>
<div className="hidden md:block text-left">
<div className="text-sm font-medium text-gray-900">{user.name}</div>
<div className="text-xs text-gray-500">
<Badge variant="outline" className="text-xs">
{user.subscription}
</Badge>
</div>
</div>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-56">
<DropdownMenuLabel>My Account</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuItem>
<User className="mr-2 h-4 w-4" />
Profile
</DropdownMenuItem>
<DropdownMenuItem>
<Settings className="mr-2 h-4 w-4" />
Settings
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem onClick={handleLogout}>
<LogOut className="mr-2 h-4 w-4" />
Sign out
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
</div>
</div>
{/* Page content */}
<main className="flex-1 relative overflow-y-auto focus:outline-none">
{children}
</main>
</div>
{/* Debug Panel - Only show for enterprise users */}
{user?.subscription === "enterprise" && <DebugPanel />}
</div>
)
function SidebarContent() {
return (
<div className="flex flex-col h-full bg-gray-800">
{/* Logo */}
<div className="flex items-center h-16 flex-shrink-0 px-4 bg-gray-900">
<Building2 className="h-8 w-8 text-blue-400 mr-3" />
<h1 className="text-xl font-bold text-white">Simpse</h1>
</div>
{/* Navigation */}
<div className="flex-1 flex flex-col overflow-y-auto">
<nav className="flex-1 px-2 py-4 space-y-1">
{navigation.map((item) => (
<Link
key={item.name}
href={item.href}
className={cn(
"group flex items-center px-2 py-2 text-sm font-medium rounded-md transition-colors",
"text-gray-300 hover:bg-gray-700 hover:text-white"
)}
>
<item.icon
className="text-gray-400 group-hover:text-gray-300 mr-3 flex-shrink-0 h-5 w-5"
/>
{item.name}
</Link>
))}
{/* Developer Navigation - Only show for enterprise users */}
{user?.subscription === "enterprise" && (
<>
<div className="px-2 py-2">
<div className="flex items-center">
<div className="flex-grow border-t border-gray-600"></div>
<span className="flex-shrink mx-2 text-xs text-gray-400 uppercase tracking-wider">Developer</span>
<div className="flex-grow border-t border-gray-600"></div>
</div>
</div>
{developerNavigation.map((item) => (
<Link
key={item.name}
href={item.href}
className={cn(
"group flex items-center px-2 py-2 text-sm font-medium rounded-md transition-colors",
"text-purple-300 hover:bg-purple-800 hover:text-white"
)}
>
<item.icon
className="text-purple-400 group-hover:text-purple-300 mr-3 flex-shrink-0 h-5 w-5"
/>
{item.name}
</Link>
))}
</>
)}
</nav>
</div>
{/* User info */}
<div className="flex-shrink-0 flex bg-gray-700 p-4">
<div className="flex items-center">
<Avatar className="h-8 w-8">
<AvatarImage src="/avatars/demo.jpg" />
<AvatarFallback>DU</AvatarFallback>
</Avatar>
<div className="ml-3">
<p className="text-sm font-medium text-white">{user.name}</p>
<p className="text-xs text-gray-300">{user.email}</p>
</div>
</div>
</div>
</div>
)
}
}