Fix basepath ipv6, fix sponsor.

This commit is contained in:
2026-05-14 21:17:50 +07:00
parent 219b420f48
commit cce0c675f9
12 changed files with 177 additions and 125 deletions
+1 -1
View File
@@ -95,7 +95,7 @@ export default function FeedbackForm({ data }: { data: FeedbackJson }) {
setSubmitState("sending");
try {
const res = await fetch("/api/submissions", {
const res = await fetch("/ipv6/api/submissions", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
+1 -1
View File
@@ -40,7 +40,7 @@ export default function RegistrationForm({
setSubmitState("sending");
const industryLabel = industries.find((o) => o.value === form.industry)?.label ?? "";
try {
const res = await fetch("/api/submissions", {
const res = await fetch("/ipv6/api/submissions", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
@@ -20,9 +20,14 @@ export default function HeaderClient({
const { lang, setLang } = useLanguage();
const { site } = useTranslation();
const isHome = pathname === "/";
const isHome = pathname === "/" || pathname === "/ipv6" || pathname === "/ipv6/";
const navItems = useMemo(() => site.nav.filter((x) => x.href !== "/"), [site.nav]);
const isActive = (href: string) => {
if (href === "/") return isHome;
return pathname === href || pathname === `/ipv6${href}` || pathname?.startsWith(`${href}/`) || pathname?.startsWith(`/ipv6${href}/`);
};
const registerLabel = lang === "vi" ? "Đăng Ký" : "Register";
const registerNowLabel = lang === "vi" ? "Đăng Ký Ngay" : "Register Now";
const backLabel = lang === "vi" ? "Trang Chủ" : "Home";
@@ -56,7 +61,7 @@ export default function HeaderClient({
{backLabel}
</Link>
{navItems.map((item) => {
const active = pathname === item.href;
const active = isActive(item.href);
return (
<Link
key={item.href}
@@ -157,7 +162,7 @@ export default function HeaderClient({
{backLabel}
</Link>
{navItems.map((item) => {
const active = pathname === item.href;
const active = isActive(item.href);
return (
<Link
key={item.href}
+57 -37
View File
@@ -10,67 +10,87 @@ type SponsorContactData = {
export default function SponsorContact({ data }: { data: SponsorContactData }) {
return (
<section id={data.id} className="py-16 lg:py-32 relative">
<div className="max-w-[1280px] mx-auto px-gutter">
<section id={data.id} className="py-[var(--spacing-section-gap)] relative px-[var(--spacing-gutter)]">
<div className="max-w-hd mx-auto">
<div className="glass-panel gold-border p-6 sm:p-10 lg:p-24 rounded-[2.5rem] lg:rounded-[4rem] overflow-hidden relative">
{/* Background Decor */}
<div className="absolute top-0 right-0 w-1/2 h-full bg-gradient-to-l from-primary/5 to-transparent pointer-events-none" />
<div className="absolute -bottom-24 -right-24 w-96 h-96 bg-primary/10 blur-[120px] rounded-full pointer-events-none" />
{/* glass-panel gold-border — p-stack-lg lg:p-section-gap */}
<div className="glass-panel gold-border p-stack-lg lg:p-section-gap rounded-3xl overflow-hidden relative">
{/* Glow */}
<div className="absolute -bottom-24 -right-24 w-64 h-64 bg-primary/20 blur-[100px] rounded-full" />
<div className="relative z-10 grid grid-cols-1 lg:grid-cols-2 gap-12 lg:gap-24 items-center">
{/* Left Content */}
<div className="space-y-12">
<div>
<h2 className="font-[var(--font-display-lg)] text-[clamp(2rem,4vw,4rem)] text-on-surface mb-6 lg:mb-8 leading-[1.1]">
{data.title}
</h2>
<p className="font-body-base text-base lg:text-[20px] text-on-surface-variant max-w-xl leading-relaxed opacity-80">
{data.description}
</p>
</div>
<div className="relative z-10 grid grid-cols-1 lg:grid-cols-2 gap-stack-lg items-center">
{/* Left */}
<div>
<h2 className="font-display-lg text-display-lg mb-6">{data.title}</h2>
<p className="font-body-base text-body-base text-on-surface-variant/80 mb-8">
{data.description}
</p>
<div className="space-y-6 mb-12">
<div className="grid grid-cols-1 sm:grid-cols-2 gap-8 lg:gap-12">
{/* Phone */}
<div className="flex items-center gap-4">
<div className="w-10 h-10 border border-primary/30 rounded-full flex items-center justify-center">
<span className="material-symbols-outlined text-primary">call</span>
</div>
<div>
<p className="text-label-caps opacity-60">{data.phone.label}</p>
<p className="font-title-sm">{data.phone.value}</p>
<div className="group">
<div className="flex items-center gap-6">
<div className="w-16 h-16 border border-primary/20 bg-primary/5 rounded-2xl flex items-center justify-center group-hover:border-primary/50 group-hover:bg-primary/10 transition-all duration-300">
<span className="material-symbols-outlined text-primary text-3xl">call</span>
</div>
<div>
<p className="font-[var(--font-label-caps)] text-[10px] lg:text-xs font-bold tracking-[0.25em] uppercase text-primary mb-2 opacity-60">
{data.phone.label}
</p>
<p className="font-[var(--font-display-lg)] text-lg lg:text-2xl text-on-surface">
{data.phone.value}
</p>
</div>
</div>
</div>
{/* Email */}
<div className="flex items-center gap-4">
<div className="w-10 h-10 border border-primary/30 rounded-full flex items-center justify-center">
<span className="material-symbols-outlined text-primary">mail</span>
</div>
<div>
<p className="text-label-caps opacity-60">{data.email.label}</p>
<p className="font-title-sm">{data.email.value}</p>
<div className="group">
<div className="flex items-center gap-6">
<div className="w-16 h-16 border border-primary/20 bg-primary/5 rounded-2xl flex items-center justify-center group-hover:border-primary/50 group-hover:bg-primary/10 transition-all duration-300">
<span className="material-symbols-outlined text-primary text-3xl">mail</span>
</div>
<div>
<p className="font-[var(--font-label-caps)] text-[10px] lg:text-xs font-bold tracking-[0.25em] uppercase text-primary mb-2 opacity-60">
{data.email.label}
</p>
<p className="font-[var(--font-display-lg)] text-lg lg:text-2xl text-on-surface break-all">
{data.email.value}
</p>
</div>
</div>
</div>
</div>
<button
type="button"
className="gold-gradient-bg text-on-primary font-title-sm px-12 py-5 rounded-lg hover:opacity-90 transition-all active:scale-95 flex items-center gap-3"
className="gold-gradient-bg text-on-primary w-full sm:w-auto px-12 lg:px-16 py-5 lg:py-6 rounded-2xl font-bold uppercase tracking-widest text-sm lg:text-base shadow-2xl shadow-primary/20 hover:shadow-primary/40 transition-all duration-500 active:scale-95 flex items-center justify-center gap-4 group"
>
{data.cta}
<span className="material-symbols-outlined">arrow_forward</span>
<span className="material-symbols-outlined group-hover:translate-x-2 transition-transform">
arrow_forward
</span>
</button>
</div>
{/* Right: image */}
<div className="hidden lg:block">
<div className="relative aspect-square">
{/* Right Image */}
<div className="relative group lg:mt-0 mt-8">
<div className="relative aspect-[4/5] rounded-[2rem] lg:rounded-[3rem] overflow-hidden">
{/* eslint-disable-next-line @next/next/no-img-element */}
<img
src={data.image.src}
alt={data.image.alt}
className="w-full h-full object-cover rounded-2xl opacity-60 mix-blend-luminosity"
className="w-full h-full object-cover opacity-70 mix-blend-luminosity grayscale group-hover:scale-110 group-hover:grayscale-0 group-hover:opacity-100 transition-all duration-1000"
/>
<div className="absolute inset-0 ring-1 ring-inset ring-white/10 rounded-2xl" />
<div className="absolute inset-0 bg-gradient-to-t from-background via-transparent to-transparent opacity-80" />
<div className="absolute inset-0 ring-1 ring-inset ring-white/10 rounded-[2rem] lg:rounded-[3rem]" />
</div>
{/* Decorative Frame */}
<div className="absolute -inset-4 lg:-inset-6 border border-primary/10 rounded-[2.5rem] lg:rounded-[3.5rem] -z-10 pointer-events-none group-hover:border-primary/30 transition-colors duration-500" />
</div>
</div>
</div>
+16 -13
View File
@@ -11,36 +11,39 @@ type SponsorHeroData = {
export default function SponsorHero({ data }: { data: SponsorHeroData }) {
return (
<section className="relative py-16 lg:py-32 overflow-hidden">
<div className="absolute inset-0 bg-[radial-gradient(circle_at_50%_50%,rgba(197,160,89,0.08),transparent_70%)]" />
<div className="max-w-[1280px] mx-auto px-gutter relative z-10 text-center">
<span className="font-label-caps text-label-caps text-primary mb-4 block">
<section className="relative py-16 md:py-32 overflow-hidden text-center px-[var(--spacing-gutter)]">
<div className="relative z-10 max-w-4xl mx-auto w-full">
{/* Eyebrow */}
<p className="font-[var(--font-label-caps)] text-primary text-[10px] md:text-xs font-bold tracking-[0.3em] uppercase mb-4 md:mb-6">
{data.eyebrow}
</span>
</p>
<h1 className="font-display-lg text-display-lg mb-6">
{/* Title */}
<h1 className="font-[var(--font-display-lg)] text-on-surface mb-6 md:mb-8 leading-[1.2] text-3xl sm:text-4xl md:text-6xl lg:text-7xl break-words">
{data.title}
</h1>
<h2 className="font-headline-md text-headline-md text-on-surface-variant mb-8 max-w-3xl mx-auto">
{/* Subtitle */}
<p className="text-primary/90 font-[var(--font-display-lg)] text-[11px] md:text-sm lg:text-base mb-8 md:mb-10 uppercase tracking-[0.2em] max-w-2xl mx-auto leading-relaxed">
{data.subtitle}
</h2>
</p>
<p className="font-body-base text-body-base text-on-surface-variant/80 max-w-2xl mx-auto mb-12">
{/* Description */}
<p className="font-body-base text-on-surface-variant text-sm md:text-lg max-w-2xl mx-auto mb-12 md:mb-16 leading-relaxed opacity-80">
{data.description}
</p>
<div className="flex flex-col sm:flex-row justify-center gap-stack-md">
{/* CTAs */}
<div className="flex flex-col sm:flex-row items-center justify-center gap-4 md:gap-6">
<Link
href={data.primaryCta.href}
className="gold-gradient-bg text-on-primary font-title-sm px-10 py-4 rounded-lg hover:opacity-90 transition-all active:scale-95 text-center"
className="gold-gradient-bg text-on-primary px-10 py-4 rounded-xl font-bold uppercase tracking-widest text-xs md:text-sm shadow-xl shadow-primary/20 hover:shadow-primary/30 transition-all duration-300 active:scale-95 w-full sm:w-auto"
>
{data.primaryCta.label}
</Link>
<Link
href={data.secondaryCta.href}
className="border border-primary text-primary font-title-sm px-10 py-4 rounded-lg hover:bg-primary/5 transition-all active:scale-95 text-center"
className="border border-white/10 text-on-surface px-10 py-4 rounded-xl font-bold uppercase tracking-widest text-xs md:text-sm hover:border-primary hover:bg-primary/5 transition-all duration-300 active:scale-95 w-full sm:w-auto"
>
{data.secondaryCta.label}
</Link>
+38 -23
View File
@@ -20,45 +20,60 @@ type SponsorItemBasedData = {
export default function SponsorItemBased({ data }: { data: SponsorItemBasedData }) {
return (
<section className="py-16 lg:py-32">
<div className="max-w-[1280px] mx-auto px-gutter">
{/* Header — text-center mb-stack-lg */}
<div className="text-center mb-stack-lg">
<h2 className="font-headline-md text-headline-md mb-2">{data.title}</h2>
<p className="text-on-surface-variant opacity-70">{data.description}</p>
<section className="py-[var(--spacing-section-gap)] px-[var(--spacing-gutter)] bg-background">
<div className="max-w-hd mx-auto">
{/* Header */}
<div className="text-center mb-16 lg:mb-28">
<h2 className="font-[var(--font-display-lg)] text-[clamp(1.75rem,3.5vw,3.5rem)] mb-4 md:mb-6 text-on-surface leading-tight">
{data.title}
</h2>
<p className="text-on-surface-variant text-base lg:text-[20px] max-w-3xl mx-auto opacity-70 leading-relaxed">
{data.description}
</p>
</div>
{/* 4-col cards — gap-gutter = 24px, mb-stack-lg = 32px */}
<div className="grid grid-cols-2 lg:grid-cols-4 gap-gutter mb-stack-lg">
{/* 4-col cards */}
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6 lg:gap-10 mb-16 lg:mb-24">
{data.items.map((item) => (
<div
key={item.title}
className="glass-panel p-stack-md rounded-lg text-center group hover:bg-surface-container transition-colors"
className="glass-panel p-8 lg:p-12 rounded-3xl text-center group hover:bg-surface-container-high transition-all duration-500 transform hover:-translate-y-2 border-primary/10"
>
<div className="w-12 h-12 gold-gradient-bg rounded-full flex items-center justify-center mx-auto mb-4">
<span className="material-symbols-outlined text-on-primary">{item.icon}</span>
<div className="w-16 h-16 gold-gradient-bg rounded-2xl flex items-center justify-center mx-auto mb-8 rotate-3 group-hover:rotate-0 transition-transform duration-500 shadow-xl shadow-primary/20">
<span className="material-symbols-outlined text-on-primary text-3xl lg:text-4xl">
{item.icon}
</span>
</div>
<h4 className="font-title-sm text-body-base mb-1">{item.title}</h4>
<p className="text-label-caps text-primary">{item.status}</p>
<h4 className="font-[var(--font-display-lg)] text-xl lg:text-2xl mb-3 text-on-surface">
{item.title}
</h4>
<p className="text-[11px] font-bold tracking-[0.3em] uppercase text-primary">
{item.status}
</p>
</div>
))}
</div>
{/* Highlights — gap-gutter = 24px */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-gutter">
{/* Highlights */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 lg:gap-10">
{data.highlights.map((h) => (
<div
key={h.eyebrow}
className="bg-primary/5 border border-primary/20 p-stack-lg rounded-xl flex items-center justify-between"
className="glass-panel border-primary/20 bg-surface-container-high/40 p-8 lg:p-14 rounded-[2rem] flex items-center justify-between group hover:bg-surface-container-highest transition-all duration-500"
>
<div>
<p className="font-label-caps text-primary mb-1">{h.eyebrow}</p>
<p className="font-title-sm">{h.title}</p>
<div className="space-y-2 lg:space-y-4">
<p className="font-[var(--font-label-caps)] text-xs lg:text-sm tracking-[0.2em] text-primary uppercase opacity-80">
{h.eyebrow}
</p>
<p className="font-[var(--font-display-lg)] text-xl lg:text-3xl text-on-surface leading-tight">
{h.title}
</p>
</div>
<div className="w-16 h-16 lg:w-24 lg:h-24 rounded-2xl bg-primary/10 flex items-center justify-center group-hover:scale-110 group-hover:rotate-6 transition-all duration-500 flex-shrink-0 ml-6">
<span className="material-symbols-outlined text-primary text-3xl lg:text-5xl">
{h.icon}
</span>
</div>
<span className="material-symbols-outlined text-primary text-4xl">
{h.icon}
</span>
</div>
))}
</div>
+24 -40
View File
@@ -17,60 +17,48 @@ type SponsorPackagesData = {
export default function SponsorPackages({ data }: { data: SponsorPackagesData }) {
return (
<section className="py-16 lg:py-32 bg-surface-container-lowest">
<div className="max-w-[1280px] mx-auto px-gutter">
{/* Header — flex items-center justify-between mb-stack-lg */}
<div className="flex items-center justify-between mb-stack-lg">
<h2 className="font-headline-md text-headline-md">{data.title}</h2>
<div className="hidden lg:block h-px flex-grow mx-stack-lg bg-outline-variant/30" />
<section className="py-[var(--spacing-section-gap)] bg-surface-container-low/30 px-[var(--spacing-gutter)]">
<div className="max-w-[1280px] mx-auto w-full">
{/* Header */}
<div className="flex flex-col md:flex-row items-start md:items-center gap-4 md:gap-8 mb-12 md:mb-20">
<h2 className="font-[var(--font-display-lg)] text-2xl md:text-4xl text-primary leading-tight">
{data.title}
</h2>
<div className="hidden md:block h-px w-full bg-primary/20" />
</div>
{/* Grid — gap-stack-lg = 32px */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-stack-lg">
{/* Grid */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-8 lg:gap-12">
{data.items.map((pkg) => (
<div
key={pkg.id}
className={`glass-panel p-stack-lg rounded-xl flex flex-col h-full ${
pkg.featured ? "gold-border gold-glow ring-2 ring-primary/20" : ""
className={`glass-panel p-6 sm:p-10 rounded-[2rem] flex flex-col h-full transition-all duration-500 hover:-translate-y-2 hover:border-primary hover:shadow-[0_0_20px_rgba(197,160,89,0.3)] ${
pkg.featured ? "bg-surface-container-high/40" : ""
}`}
>
<div className="mb-stack-lg">
<div className="mb-10">
{pkg.badge && (
<span className="bg-primary/20 text-primary px-3 py-1 rounded text-label-caps mb-4 inline-block">
<span className="bg-primary/10 text-primary border border-primary/20 px-5 py-2 rounded-full text-[10px] font-bold tracking-[0.25em] uppercase mb-6 inline-block">
{pkg.badge}
</span>
)}
<h3
className={`font-display-lg text-headline-md ${
pkg.featured ? "text-primary" : ""
}`}
>
<h3 className={`font-[var(--font-display-lg)] text-2xl md:text-3xl mb-3 ${pkg.featured ? "text-primary" : "text-on-surface"}`}>
{pkg.tier}
</h3>
<div className="text-on-surface font-display-lg text-title-sm mt-2">
<div className="text-primary/90 font-[var(--font-mono)] text-xl tracking-tight">
{pkg.price}
</div>
</div>
<ul className="space-y-4 mb-stack-lg flex-grow">
<div className="h-px w-full bg-white/5 mb-10" />
<ul className="space-y-6 mb-12 flex-grow">
{pkg.benefits.map((benefit) => (
<li key={benefit} className="flex items-start gap-3">
<span
className="material-symbols-outlined text-primary text-[20px]"
style={
pkg.featured
? { fontVariationSettings: "'FILL' 1" }
: undefined
}
>
check_circle
<li key={benefit} className="flex items-start gap-5">
<span className="material-symbols-outlined text-primary text-[22px] mt-0.5">
{pkg.featured ? "verified" : "check_circle"}
</span>
<span
className={`text-on-surface-variant ${
benefit === pkg.boldBenefit ? "font-bold" : ""
}`}
>
<span className={`text-on-surface-variant text-base leading-relaxed ${benefit === pkg.boldBenefit ? "font-bold text-primary" : ""}`}>
{benefit}
</span>
</li>
@@ -79,11 +67,7 @@ export default function SponsorPackages({ data }: { data: SponsorPackagesData })
<button
type="button"
className={`w-full py-4 rounded font-title-sm transition-all active:scale-95 ${
pkg.featured
? "gold-gradient-bg text-on-primary hover:opacity-90"
: "border border-outline-variant text-on-surface hover:border-primary transition-colors"
}`}
className="w-full py-5 rounded-xl font-bold uppercase tracking-widest text-sm transition-all duration-300 bg-surface-container-high border border-white/10 text-on-surface hover:bg-primary hover:text-on-primary hover:border-primary hover:shadow-lg hover:shadow-primary/20 active:scale-95"
>
{pkg.cta}
</button>
+1 -1
View File
@@ -10,7 +10,7 @@ export default function SponsorPageClient() {
const { sponsor } = useTranslation();
return (
<main className="pt-20">
<main className="pt-16 lg:pt-20">
<SponsorHero data={sponsor.hero} />
<SponsorPackages data={sponsor.packages} />
<SponsorItemBased data={sponsor.itemBased} />