Files
auto-trade/features/layout/components/header.tsx

154 lines
5.4 KiB
TypeScript

/**
* @file features/layout/components/header.tsx
* @description 애플리케이션 상단 헤더 컴포넌트
*/
import Link from "next/link";
import { User } from "@supabase/supabase-js";
import { AUTH_ROUTES } from "@/features/auth/constants";
import { UserMenu } from "@/features/layout/components/user-menu";
import { ThemeToggle } from "@/components/theme-toggle";
import { Button } from "@/components/ui/button";
import { SessionTimer } from "@/features/auth/components/session-timer";
import { cn } from "@/lib/utils";
interface HeaderProps {
/** 현재 로그인 사용자 정보(null 가능) */
user: User | null;
/** 대시보드 링크 버튼 노출 여부 */
showDashboardLink?: boolean;
/** 홈 랜딩에서 배경과 자연스럽게 섞이는 헤더 모드 */
blendWithBackground?: boolean;
}
/**
* 글로벌 헤더 컴포넌트
* @param user Supabase User 객체
* @param showDashboardLink 대시보드 버튼 노출 여부
* @param blendWithBackground 홈 랜딩 전용 반투명 모드
* @returns Header JSX
* @see app/(home)/page.tsx 홈 랜딩에서 blendWithBackground=true로 호출
*/
export function Header({
user,
showDashboardLink = false,
blendWithBackground = false,
}: HeaderProps) {
return (
<header
className={cn(
"fixed inset-x-0 top-0 z-50 w-full",
blendWithBackground
? "text-white"
: "border-b border-border/40 bg-background/80 backdrop-blur-xl supports-backdrop-filter:bg-background/60",
)}
>
{blendWithBackground && (
<div
aria-hidden="true"
className="pointer-events-none absolute inset-x-0 top-0 h-24 bg-linear-to-b from-black/70 via-black/35 to-transparent"
/>
)}
<div
className={cn(
"relative z-10 flex h-16 w-full items-center justify-between px-4 md:px-6",
blendWithBackground
? "bg-black/30 backdrop-blur-xl supports-backdrop-filter:bg-black/20"
: "",
)}
>
{/* ========== LEFT: LOGO SECTION ========== */}
<Link
href={AUTH_ROUTES.HOME}
className={cn("group flex items-center gap-2", blendWithBackground ? "text-white" : "")}
>
<div
className={cn(
"flex h-9 w-9 items-center justify-center rounded-xl transition-transform duration-200 group-hover:scale-110",
blendWithBackground ? "bg-brand-500/45 ring-1 ring-white/55" : "bg-primary/10",
)}
>
<div className="h-5 w-5 rounded-lg bg-linear-to-br from-brand-500 to-brand-700" />
</div>
<span
className={cn(
"text-xl font-bold tracking-tight transition-colors",
blendWithBackground
? "!text-white [text-shadow:0_2px_18px_rgba(0,0,0,0.75)] group-hover:text-brand-100"
: "text-foreground group-hover:text-primary",
)}
>
AutoTrade
</span>
</Link>
{/* ========== RIGHT: ACTION SECTION ========== */}
<div className={cn("flex items-center gap-2 sm:gap-3", blendWithBackground ? "text-white" : "")}
>
<ThemeToggle
className={cn(
blendWithBackground
? "rounded-full border border-white/40 bg-black/50 !text-white backdrop-blur-md hover:bg-black/65 focus-visible:ring-white/80"
: "",
)}
iconClassName={blendWithBackground ? "!text-white" : undefined}
/>
{user ? (
<>
<SessionTimer blendWithBackground={blendWithBackground} />
{showDashboardLink && (
<Button
asChild
variant="ghost"
size="sm"
className={cn(
"hidden font-medium sm:inline-flex",
blendWithBackground
? "rounded-full border border-white/40 bg-black/50 !text-white backdrop-blur-md [text-shadow:0_1px_8px_rgba(0,0,0,0.45)] hover:bg-black/65 hover:!text-white"
: "",
)}
>
<Link href={AUTH_ROUTES.DASHBOARD}></Link>
</Button>
)}
<UserMenu user={user} blendWithBackground={blendWithBackground} />
</>
) : (
<div className="flex items-center gap-2">
<Button
asChild
variant="ghost"
size="sm"
className={cn(
"hidden sm:inline-flex",
blendWithBackground
? "rounded-full border border-white/40 bg-black/50 !text-white backdrop-blur-md hover:bg-black/65 hover:!text-white"
: "",
)}
>
<Link href={AUTH_ROUTES.LOGIN}></Link>
</Button>
<Button
asChild
size="sm"
className={cn(
"rounded-full px-6",
blendWithBackground
? "bg-brand-500/90 text-white shadow-lg shadow-brand-700/40 hover:bg-brand-400"
: "",
)}
>
<Link href={AUTH_ROUTES.SIGNUP}></Link>
</Button>
</div>
)}
</div>
</div>
</header>
);
}