Files
OpenFrame/components/layout/header.tsx
T
Yusuf İpek 264392c2ec chore(init): scaffold OpenFrame — Next.js + Bun + shadcn
Initialize OpenFrame project with core scaffold and UI foundation.

- Project scaffold: Next.js 16.1 (App Router) + Bun runtime
- UI: shadcn/ui + TailwindCSS; landing, dashboard, project, and auth UIs
- Video support: provider abstraction (YouTube first), video player page with timestamped comments and custom timeline
- Auth & DB: NextAuth skeleton and Prisma schema (Postgres) included
- UX: dark-mode toggle, full-width layouts, comments sidebar, video route moved to /watch/[videoId]
- Dev: added YouTube iframe integration, custom controls, and TypeScript types
- Next steps: DB migrations, API routes (CRUD), real data wiring, voice-recording & sharing
2026-02-05 22:11:20 +03:00

174 lines
5.6 KiB
TypeScript

'use client';
import Link from 'next/link';
import { usePathname } from 'next/navigation';
import {
Video,
FolderOpen,
Plus,
Settings,
LogOut,
User,
Menu
} from 'lucide-react';
import { Button } from '@/components/ui/button';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
import { Sheet, SheetContent, SheetTrigger } from '@/components/ui/sheet';
import { ThemeToggle } from '@/components/theme-toggle';
import { cn } from '@/lib/utils';
interface NavItem {
href: string;
label: string;
icon: React.ComponentType<{ className?: string }>;
}
const navItems: NavItem[] = [
{ href: '/dashboard', label: 'Projects', icon: FolderOpen },
];
interface HeaderProps {
user?: {
name?: string | null;
email?: string | null;
image?: string | null;
} | null;
}
export function Header({ user }: HeaderProps) {
const pathname = usePathname();
return (
<header className="sticky top-0 z-50 w-full border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
<div className="px-6 lg:px-8 flex h-14 items-center w-full">
{/* Mobile menu */}
<Sheet>
<SheetTrigger asChild>
<Button variant="ghost" size="icon" className="md:hidden mr-2">
<Menu className="h-5 w-5" />
<span className="sr-only">Toggle menu</span>
</Button>
</SheetTrigger>
<SheetContent side="left" className="w-64">
<nav className="flex flex-col gap-2 mt-4">
{navItems.map((item) => (
<Link
key={item.href}
href={item.href}
className={cn(
'flex items-center gap-2 px-3 py-2 text-sm font-medium rounded-md transition-colors',
pathname === item.href
? 'bg-accent text-accent-foreground'
: 'hover:bg-accent hover:text-accent-foreground'
)}
>
<item.icon className="h-4 w-4" />
{item.label}
</Link>
))}
</nav>
</SheetContent>
</Sheet>
{/* Logo */}
<Link href="/" className="flex items-center gap-2 mr-6">
<Video className="h-6 w-6 text-primary" />
<span className="font-bold text-lg hidden sm:inline-block">OpenFrame</span>
</Link>
{/* Desktop nav */}
<nav className="hidden md:flex items-center gap-1">
{navItems.map((item) => (
<Link
key={item.href}
href={item.href}
className={cn(
'flex items-center gap-2 px-3 py-2 text-sm font-medium rounded-md transition-colors',
pathname === item.href
? 'bg-accent text-accent-foreground'
: 'hover:bg-accent/50'
)}
>
<item.icon className="h-4 w-4" />
{item.label}
</Link>
))}
</nav>
{/* Right side */}
<div className="flex items-center gap-2 ml-auto">
<Button asChild size="sm" className="hidden sm:flex">
<Link href="/projects/new">
<Plus className="h-4 w-4 mr-1" />
New Project
</Link>
</Button>
<Button asChild size="icon" variant="ghost" className="sm:hidden">
<Link href="/projects/new">
<Plus className="h-5 w-5" />
</Link>
</Button>
<ThemeToggle />
{user ? (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="relative h-8 w-8 rounded-full">
<Avatar className="h-8 w-8">
<AvatarImage src={user.image ?? undefined} alt={user.name ?? ''} />
<AvatarFallback>
{user.name?.charAt(0).toUpperCase() ?? 'U'}
</AvatarFallback>
</Avatar>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-56">
<div className="flex items-center justify-start gap-2 p-2">
<div className="flex flex-col space-y-1 leading-none">
{user.name && <p className="font-medium">{user.name}</p>}
{user.email && (
<p className="w-[200px] truncate text-sm text-muted-foreground">
{user.email}
</p>
)}
</div>
</div>
<DropdownMenuSeparator />
<DropdownMenuItem asChild>
<Link href="/settings">
<Settings className="h-4 w-4 mr-2" />
Settings
</Link>
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem asChild>
<Link href="/api/auth/signout">
<LogOut className="h-4 w-4 mr-2" />
Sign out
</Link>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
) : (
<Button asChild variant="outline" size="sm">
<Link href="/login">
<User className="h-4 w-4 mr-1" />
Sign in
</Link>
</Button>
)}
</div>
</div>
</header>
);
}