Spaces:
Paused
Paused
| "use client" | |
| import { useAuth } from "@/contexts/auth-context" | |
| import { useRouter, usePathname } from "next/navigation" | |
| import { useEffect, useState } from "react" | |
| import Image from "next/image" | |
| import { | |
| Home, | |
| Settings, | |
| Users, | |
| BarChart3, | |
| FileText, | |
| LogOut, | |
| User, | |
| Film, | |
| Video, | |
| Menu, | |
| X, | |
| Key, | |
| Tv, | |
| Camera, | |
| MonitorPlay, | |
| Clapperboard, | |
| HardDrive, | |
| } from "lucide-react" | |
| import { | |
| Sidebar, | |
| SidebarContent, | |
| SidebarFooter, | |
| SidebarGroup, | |
| SidebarGroupContent, | |
| SidebarGroupLabel, | |
| SidebarHeader, | |
| SidebarInset, | |
| SidebarMenu, | |
| SidebarMenuButton, | |
| SidebarMenuItem, | |
| SidebarProvider, | |
| SidebarTrigger, | |
| } from "@/components/ui/sidebar" | |
| import { Button } from "@/components/ui/button" | |
| import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar" | |
| import { | |
| DropdownMenu, | |
| DropdownMenuContent, | |
| DropdownMenuItem, | |
| DropdownMenuLabel, | |
| DropdownMenuSeparator, | |
| DropdownMenuTrigger, | |
| } from "@/components/ui/dropdown-menu" | |
| import { signOut } from "@/lib/auth" | |
| import { ThemeToggle } from "@/components/theme-toggle" | |
| import { Loader } from "@/components/ui/loader" | |
| import { Toaster } from "@/components/ui/sonner" | |
| import { GlobalSearch } from "@/components/global-search" | |
| import { title } from "process" | |
| // Navigation items | |
| const navItems = [ | |
| { | |
| title: "Anime", | |
| url: "/dashboard/anime", | |
| icon: Tv, | |
| }, | |
| { | |
| title: "Movies", | |
| url: "/dashboard/movies", | |
| icon: Film, | |
| }, | |
| { | |
| title: "KM Movies", | |
| url: "/dashboard/kmmovies", | |
| icon: Camera, | |
| }, | |
| { | |
| title: "DesireMovies", | |
| url: "/dashboard/desiremovies", | |
| icon: Clapperboard, | |
| }, | |
| { | |
| title: "10 Bit", | |
| url: "/dashboard/10bitclub", | |
| icon: HardDrive, | |
| }, | |
| { | |
| title: "VegaMovies", | |
| url: "/dashboard/vegamovies", | |
| icon: MonitorPlay, | |
| }, | |
| { | |
| title: "API Keys", | |
| url: "/dashboard/api-keys", | |
| icon: Key, | |
| }, | |
| { | |
| title: "Docs", | |
| url: "/dashboard/docs", | |
| icon: FileText, | |
| } | |
| ] | |
| function AppSidebar() { | |
| const pathname = usePathname() | |
| return ( | |
| <Sidebar collapsible="icon"> | |
| <SidebarHeader className="border-b border-sidebar-border"> | |
| <div className="flex items-center gap-2 px-2 py-2"> | |
| <div className="flex items-center gap-2"> | |
| <div className="h-8 w-8 rounded-lg overflow-hidden flex items-center justify-center"> | |
| <Image | |
| src="/logo.jpg" | |
| alt="ScreenScape Logo" | |
| width={32} | |
| height={32} | |
| className="object-contain" | |
| /> | |
| </div> | |
| <span | |
| className="font-bold group-data-[collapsible=icon]:hidden cursor-pointer hover:text-primary transition-colors" | |
| onClick={() => window.open('https://screenscape.fun', '_blank')} | |
| > | |
| ScreenScape | |
| </span> | |
| </div> | |
| </div> | |
| </SidebarHeader> | |
| <SidebarContent> | |
| <SidebarGroup> | |
| <SidebarGroupLabel className="data-[state=collapsed]:hidden font-medium">Navigation</SidebarGroupLabel> | |
| <SidebarGroupContent> | |
| <SidebarMenu> | |
| {navItems.map((item) => { | |
| const isActive = pathname === item.url || (item.url !== "/dashboard" && pathname.startsWith(item.url)) | |
| return ( | |
| <SidebarMenuItem key={item.title}> | |
| <SidebarMenuButton asChild tooltip={item.title}> | |
| <a | |
| href={item.url} | |
| className={`group-data-[state=collapsed]:justify-center font-medium transition-colors ${ | |
| isActive | |
| ? "bg-primary text-primary-foreground shadow-sm" | |
| : "hover:bg-sidebar-accent hover:text-sidebar-accent-foreground" | |
| }`} | |
| > | |
| <item.icon className={isActive ? "text-primary-foreground" : ""} /> | |
| <span className="data-[state=collapsed]:hidden font-medium">{item.title}</span> | |
| </a> | |
| </SidebarMenuButton> | |
| </SidebarMenuItem> | |
| ) | |
| })} | |
| </SidebarMenu> | |
| </SidebarGroupContent> | |
| </SidebarGroup> | |
| </SidebarContent> | |
| <SidebarFooter className="border-t border-sidebar-border"> | |
| <div className="p-2 text-center"> | |
| <p className="text-xs text-muted-foreground group-data-[collapsible=icon]:hidden"> | |
| Created and managed by | |
| </p> | |
| <p className="text-xs font-medium text-primary group-data-[collapsible=icon]:hidden"> | |
| Team ScreenScape | |
| </p> | |
| <div className="group-data-[collapsible=icon]:block hidden"> | |
| <div className="h-6 w-6 mx-auto rounded overflow-hidden flex items-center justify-center"> | |
| <Image | |
| src="/logo.jpg" | |
| alt="ScreenScape Logo" | |
| width={24} | |
| height={24} | |
| className="object-contain" | |
| /> | |
| </div> | |
| </div> | |
| </div> | |
| </SidebarFooter> | |
| </Sidebar> | |
| ) | |
| } | |
| function UserMenu() { | |
| const { user } = useAuth() | |
| const router = useRouter() | |
| const handleSignOut = async () => { | |
| await signOut() | |
| router.push("/login") | |
| } | |
| return ( | |
| <DropdownMenu> | |
| <DropdownMenuTrigger asChild> | |
| <Button variant="ghost" className="h-8 w-8 rounded-full"> | |
| <Avatar className="h-8 w-8"> | |
| <AvatarImage src={user?.photoURL || "/avatars/01.png"} alt={user?.email} /> | |
| <AvatarFallback className="bg-primary text-primary-foreground text-xs"> | |
| {user?.email?.charAt(0).toUpperCase()} | |
| </AvatarFallback> | |
| </Avatar> | |
| </Button> | |
| </DropdownMenuTrigger> | |
| <DropdownMenuContent className="w-56" align="end" forceMount> | |
| <DropdownMenuLabel className="font-normal"> | |
| <div className="flex flex-col space-y-1"> | |
| <p className="text-sm font-medium leading-none"> | |
| {user?.displayName?.split(' ')[0] || user?.email?.split('@')[0] || "User"} | |
| </p> | |
| <p className="text-xs leading-none text-muted-foreground"> | |
| {user?.email} | |
| </p> | |
| </div> | |
| </DropdownMenuLabel> | |
| <DropdownMenuSeparator /> | |
| <DropdownMenuItem> | |
| <User className="mr-2 h-4 w-4" /> | |
| <span>Profile</span> | |
| </DropdownMenuItem> | |
| <DropdownMenuItem> | |
| <Settings className="mr-2 h-4 w-4" /> | |
| <span>Settings</span> | |
| </DropdownMenuItem> | |
| <DropdownMenuSeparator /> | |
| <DropdownMenuItem onClick={handleSignOut}> | |
| <LogOut className="mr-2 h-4 w-4" /> | |
| <span>Log out</span> | |
| </DropdownMenuItem> | |
| </DropdownMenuContent> | |
| </DropdownMenu> | |
| ) | |
| } | |
| // Global navbar component - now exported | |
| export function DashboardNavbar({ title, children }: { title?: string; children?: React.ReactNode }) { | |
| return ( | |
| <header className="flex h-16 shrink-0 items-center gap-2 border-b px-4 sticky top-0 bg-background z-10"> | |
| <SidebarTrigger className="-ml-1" /> | |
| <div className="flex flex-1 items-center justify-between"> | |
| {title && ( | |
| <div className="flex items-center gap-2"> | |
| <h1 className="text-lg font-semibold">{title}</h1> | |
| </div> | |
| )} | |
| {children} | |
| <div className="flex items-center gap-2 ml-auto"> | |
| <GlobalSearch /> | |
| <ThemeToggle /> | |
| <UserMenu /> | |
| </div> | |
| </div> | |
| </header> | |
| ) | |
| } | |
| export default function DashboardLayout({ | |
| children, | |
| }: { | |
| children: React.ReactNode | |
| }) { | |
| const { user, loading } = useAuth() | |
| const router = useRouter() | |
| const [isMobileSidebarOpen, setIsMobileSidebarOpen] = useState(false) | |
| useEffect(() => { | |
| if (!loading && !user) { | |
| router.push("/login") | |
| } | |
| }, [user, loading, router]) | |
| if (loading) { | |
| return ( | |
| <div className="flex min-h-svh items-center justify-center"> | |
| <Loader size="lg" text="Loading dashboard..." /> | |
| </div> | |
| ) | |
| } | |
| if (!user) { | |
| return null | |
| } | |
| const toggleMobileSidebar = () => { | |
| setIsMobileSidebarOpen(!isMobileSidebarOpen) | |
| } | |
| return ( | |
| <SidebarProvider> | |
| <AppSidebar /> | |
| {/* Mobile sidebar overlay */} | |
| {isMobileSidebarOpen && ( | |
| <div className="fixed inset-0 z-50 md:hidden"> | |
| <div | |
| className="fixed inset-0 bg-black/20" | |
| onClick={() => setIsMobileSidebarOpen(false)} | |
| /> | |
| <div className="fixed left-0 top-0 h-full w-64 bg-background border-r shadow-lg"> | |
| <div className="p-4"> | |
| <Button | |
| variant="ghost" | |
| size="icon" | |
| onClick={() => setIsMobileSidebarOpen(false)} | |
| className="mb-4" | |
| > | |
| <X className="h-4 w-4" /> | |
| </Button> | |
| <div className="space-y-2"> | |
| {navItems.map((item) => ( | |
| <a | |
| key={item.title} | |
| href={item.url} | |
| className="flex items-center gap-2 p-2 hover:bg-accent rounded" | |
| onClick={() => setIsMobileSidebarOpen(false)} | |
| > | |
| <item.icon className="h-4 w-4" /> | |
| {item.title} | |
| </a> | |
| ))} | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| )} | |
| <SidebarInset className="flex flex-col"> | |
| <DashboardNavbar /> | |
| {children} | |
| </SidebarInset> | |
| <Toaster /> | |
| </SidebarProvider> | |
| ) | |
| } | |