144 lines
5.5 KiB
TypeScript
144 lines
5.5 KiB
TypeScript
import type {ReactNode} from 'react';
|
|
import {Link, NavLink} from 'react-router';
|
|
import {cn} from '@/lib/utils';
|
|
import {useAuthStore} from '@/stores/auth';
|
|
|
|
/**
|
|
* 로그인 전 화면(랜딩·요금·쇼케이스)의 껍데기.
|
|
*
|
|
* ★ AppShell 과 나눠 둔 이유: 저쪽은 **사이드바가 있는 작업 화면**이다. 아직 아무것도 만들지
|
|
* 않은 사람에게 사이드바를 보여 주면 비어 있는 메뉴만 남는다(b07ade2 가 온보딩에서 사이드바를
|
|
* 뺀 것과 같은 판단).
|
|
*
|
|
* ★ 메뉴는 셋을 넘기지 않는다. 소상공인 대상 화면에서 드롭다운은 "못 찾는 메뉴"가 된다.
|
|
*/
|
|
const NAV = [
|
|
{to: '/showcase', label: '이렇게 나옵니다'},
|
|
{to: '/pricing', label: '요금'},
|
|
];
|
|
|
|
export function MarketingShell({
|
|
children,
|
|
showAuthCta = true,
|
|
showBusinessInfo = false,
|
|
}: {
|
|
children: ReactNode;
|
|
showAuthCta?: boolean;
|
|
showBusinessInfo?: boolean;
|
|
}) {
|
|
const user = useAuthStore((s) => s.user);
|
|
|
|
return (
|
|
<div className="min-h-screen bg-background text-foreground">
|
|
<header className="sticky top-0 z-20 border-b border-border bg-background/85 backdrop-blur">
|
|
<div className="mx-auto flex h-16 w-full max-w-6xl items-center gap-8 px-5">
|
|
<Link to="/" className="flex items-center">
|
|
<img src="/brand/web4ai-wordmark.svg" alt="Web4Ai" className="h-8 w-auto" />
|
|
</Link>
|
|
|
|
<nav className="hidden items-center gap-6 sm:flex">
|
|
{NAV.map(({to, label}) => (
|
|
<NavLink
|
|
key={to}
|
|
to={to}
|
|
className={({isActive}) =>
|
|
cn(
|
|
'text-sm font-medium transition-colors',
|
|
isActive ? 'text-foreground' : 'text-muted-foreground hover:text-foreground',
|
|
)
|
|
}
|
|
>
|
|
{label}
|
|
</NavLink>
|
|
))}
|
|
</nav>
|
|
|
|
<div className="ml-auto flex items-center gap-3">
|
|
{/* ★ 이미 사이트를 가진 사장님에게 [로그인] 을 다시 보여주지 않는다 — 갈 곳은 내 사이트다. */}
|
|
{/*
|
|
★ [무료로 만들기] 를 뺐다 (2026-09-04, 사장님 지시)
|
|
히어로의 큰 입력 카드가 이미 그 자리다 — 시작하는 문이 한 화면에 둘이면
|
|
어느 쪽이 진짜인지 고르게 만든다. 헤더는 로그인만 받는다.
|
|
*/}
|
|
{showAuthCta && (user ? (
|
|
<Link
|
|
to="/sites"
|
|
className="rounded-md bg-primary px-4 py-2 text-[13px] font-medium text-primary-foreground transition-colors hover:bg-primary/90"
|
|
>
|
|
내 사이트
|
|
</Link>
|
|
) : (
|
|
<Link
|
|
to="/login"
|
|
className="rounded-md border border-border px-4 py-2 text-[13px] font-medium transition-colors hover:bg-muted"
|
|
>
|
|
로그인
|
|
</Link>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
<main>{children}</main>
|
|
|
|
<footer className="mt-24 border-t border-border">
|
|
<div className="mx-auto flex w-full max-w-6xl flex-col gap-6 px-5 py-10 text-xs text-muted-foreground">
|
|
<div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
|
<img src="/brand/web4ai-wordmark.svg" alt="Web4Ai" className="h-6 w-auto opacity-60" />
|
|
<div className="flex gap-5">
|
|
{NAV.map(({to, label}) => (
|
|
<Link key={to} to={to} className="transition-colors hover:text-foreground">
|
|
{label}
|
|
</Link>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
{showBusinessInfo && (
|
|
<address className="space-y-1 leading-relaxed not-italic">
|
|
<p className="font-medium text-foreground/80">㈜에이아이오투오</p>
|
|
<p>사업자 등록번호 : 620-87-00810 | 대표 : 안성민</p>
|
|
<p>본사 : 대구광역시 북구 옥산로 111, 5층 유니콘랩 대구 A05호</p>
|
|
<p>연구소 : 경기 성남시 수정구 금토로 32 (금토동) (주)KT 판교빌딩 504호~505호 (East)</p>
|
|
<p>전화 : 070-4260-8310 | 010-2755-6463</p>
|
|
<p>
|
|
이메일 :{' '}
|
|
<a href="mailto:o2oteam@o2o.kr" className="transition-colors hover:text-foreground">
|
|
o2oteam@o2o.kr
|
|
</a>
|
|
</p>
|
|
</address>
|
|
)}
|
|
</div>
|
|
</footer>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
/** 랜딩의 한 켜. 섹션마다 여백을 손으로 적지 않게 한 곳에 모은다. */
|
|
export function Section({
|
|
children,
|
|
className,
|
|
muted = false,
|
|
}: {
|
|
children: ReactNode;
|
|
className?: string;
|
|
muted?: boolean;
|
|
}) {
|
|
return (
|
|
<section className={cn('border-b border-border', muted && 'bg-muted/40')}>
|
|
<div className={cn('mx-auto w-full max-w-6xl px-5 py-16 sm:py-20', className)}>{children}</div>
|
|
</section>
|
|
);
|
|
}
|
|
|
|
export function SectionHead({eyebrow, title, description}: {eyebrow?: string; title: string; description?: string}) {
|
|
return (
|
|
<header className="mb-10 max-w-2xl">
|
|
{eyebrow && <p className="mb-2 text-xs font-medium tracking-wide text-muted-foreground">{eyebrow}</p>}
|
|
<h2 className="text-2xl font-bold tracking-tight text-balance sm:text-3xl">{title}</h2>
|
|
{description && <p className="mt-3 text-sm leading-relaxed text-muted-foreground">{description}</p>}
|
|
</header>
|
|
);
|
|
}
|