- Split landing pages into Server/Client Components to enable Next.js metadata exports - Add robots.ts, sitemap.ts, JSON-LD structured data, OpenGraph and viewport config - Fix i18n hydration mismatch: detect locale server-side via cookie/Accept-Language header - Replace localStorage with cookie for locale persistence (SSR-readable) - Dynamic <html lang> based on locale cookie - Optimize images with next/image (avif/webp formats, quality config) - Add /homepage route as always-visible landing page (no auth redirect) - Auth-aware CTA buttons: show "Dashboard"/"进入工作台" for logged-in users - Login page redirects authenticated users to dashboard - Unify logout/401 redirect to "/" instead of "/login" - Fix title template to avoid double "Multica" suffix on homepage Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
110 lines
3.3 KiB
TypeScript
110 lines
3.3 KiB
TypeScript
"use client";
|
|
|
|
import Image from "next/image";
|
|
import Link from "next/link";
|
|
import { useAuthStore } from "@/features/auth";
|
|
import { useLocale } from "../i18n";
|
|
import {
|
|
ClaudeCodeLogo,
|
|
CodexLogo,
|
|
GitHubMark,
|
|
githubUrl,
|
|
heroButtonClassName,
|
|
} from "./shared";
|
|
|
|
export function LandingHero() {
|
|
const { t } = useLocale();
|
|
const user = useAuthStore((s) => s.user);
|
|
|
|
return (
|
|
<div className="relative min-h-full overflow-hidden bg-[#05070b] text-white">
|
|
<LandingBackdrop />
|
|
|
|
<main className="relative z-10">
|
|
<section
|
|
id="product"
|
|
className="mx-auto max-w-[1320px] px-4 pb-16 pt-28 sm:px-6 sm:pt-32 lg:px-8 lg:pb-24 lg:pt-36"
|
|
>
|
|
<div className="mx-auto max-w-[1120px] text-center">
|
|
<h1 className="font-[family-name:var(--font-serif)] text-[3.65rem] leading-[0.93] tracking-[-0.038em] text-white drop-shadow-[0_10px_34px_rgba(0,0,0,0.32)] sm:text-[4.85rem] lg:text-[6.4rem]">
|
|
{t.hero.headlineLine1}
|
|
<br />
|
|
{t.hero.headlineLine2}
|
|
</h1>
|
|
|
|
<p className="mx-auto mt-7 max-w-[820px] text-[15px] leading-7 text-white/84 sm:text-[17px]">
|
|
{t.hero.subheading}
|
|
</p>
|
|
|
|
<div className="mt-8 flex flex-wrap items-center justify-center gap-3">
|
|
<Link href={user ? "/issues" : "/login"} className={heroButtonClassName("solid")}>
|
|
{user ? t.header.dashboard : t.hero.cta}
|
|
</Link>
|
|
<Link
|
|
href={githubUrl}
|
|
target="_blank"
|
|
rel="noreferrer"
|
|
className={heroButtonClassName("ghost")}
|
|
>
|
|
<GitHubMark className="size-4" />
|
|
GitHub
|
|
</Link>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="mt-10 flex items-center justify-center gap-8">
|
|
<span className="text-[15px] text-white/50">
|
|
{t.hero.worksWith}
|
|
</span>
|
|
<div className="flex items-center gap-6">
|
|
<div className="flex items-center gap-2.5 text-white/80">
|
|
<ClaudeCodeLogo className="size-5" />
|
|
<span className="text-[15px] font-medium">Claude Code</span>
|
|
</div>
|
|
<div className="flex items-center gap-2.5 text-white/80">
|
|
<CodexLogo className="size-5" />
|
|
<span className="text-[15px] font-medium">Codex</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="preview" className="mt-10 sm:mt-12">
|
|
<ProductImage alt={t.hero.imageAlt} />
|
|
</div>
|
|
</section>
|
|
</main>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function LandingBackdrop() {
|
|
return (
|
|
<div className="pointer-events-none absolute inset-0">
|
|
<Image
|
|
src="/images/landing-bg.jpg"
|
|
alt=""
|
|
fill
|
|
priority
|
|
className="object-cover object-center"
|
|
/>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function ProductImage({ alt }: { alt: string }) {
|
|
return (
|
|
<div>
|
|
<div className="relative overflow-hidden border border-white/14">
|
|
<Image
|
|
src="/images/landing-hero.png"
|
|
alt={alt}
|
|
width={3532}
|
|
height={2382}
|
|
className="block h-auto w-full"
|
|
sizes="(max-width: 1320px) 100vw, 1320px"
|
|
quality={85}
|
|
/>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|