| "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(() => { |
| |
| 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> |
|
|
| {} |
| <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> |
| ) |
| } |
| } |