Merge pull request 'Adding speakers section and gallery section' (#15) from feat/duong-11052026-IPV6SUBMIT into develop
Reviewed-on: UKSOURCE/ipv6#15
@@ -0,0 +1,80 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
const BASE = process.env.NEXT_PUBLIC_BASE_PATH || "";
|
||||||
|
|
||||||
|
type GalleryItem = {
|
||||||
|
src: string;
|
||||||
|
alt: string;
|
||||||
|
span: "wide" | "normal" | "full";
|
||||||
|
};
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
data: {
|
||||||
|
eyebrow: string;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
items: GalleryItem[];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Map span → Tailwind col-span classes
|
||||||
|
const spanClass: Record<GalleryItem["span"], string> = {
|
||||||
|
wide: "md:col-span-2 lg:col-span-2",
|
||||||
|
normal: "",
|
||||||
|
full: "md:col-span-2 lg:col-span-3",
|
||||||
|
};
|
||||||
|
|
||||||
|
// Map span → chiều cao ảnh
|
||||||
|
const heightClass: Record<GalleryItem["span"], string> = {
|
||||||
|
wide: "h-[240px] md:h-[340px] lg:h-[420px]",
|
||||||
|
normal: "h-[240px] md:h-[340px] lg:h-[420px]",
|
||||||
|
full: "h-[260px] md:h-[360px] lg:h-[480px]",
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function GallerySection({ data }: Props) {
|
||||||
|
return (
|
||||||
|
<section
|
||||||
|
className="py-16 lg:py-32 max-w-hd mx-auto px-[var(--spacing-gutter)]"
|
||||||
|
id="gallery"
|
||||||
|
>
|
||||||
|
{/* Header */}
|
||||||
|
<div className="text-center mb-12 lg:mb-20">
|
||||||
|
<span className="font-[var(--font-label-caps)] text-xs lg:text-sm text-primary block mb-4 lg:mb-6 tracking-[0.25em] uppercase">
|
||||||
|
{data.eyebrow}
|
||||||
|
</span>
|
||||||
|
<h2 className="font-[var(--font-display-lg)] text-on-surface mb-4 lg:mb-6 text-[clamp(2rem,4vw+1rem,4.5rem)] leading-tight">
|
||||||
|
{data.title}
|
||||||
|
</h2>
|
||||||
|
<p className="font-[var(--font-body-base)] text-on-surface-variant text-base lg:text-[20px] max-w-2xl mx-auto opacity-70 leading-relaxed">
|
||||||
|
{data.description}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Grid masonry-style */}
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 lg:gap-6">
|
||||||
|
{data.items.map((item, i) => (
|
||||||
|
<div
|
||||||
|
key={i}
|
||||||
|
className={[
|
||||||
|
spanClass[item.span],
|
||||||
|
"overflow-hidden rounded-2xl border border-primary/20 hover:border-primary/50",
|
||||||
|
"shadow-lg group transition-all duration-500",
|
||||||
|
].join(" ")}
|
||||||
|
>
|
||||||
|
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||||
|
<img
|
||||||
|
src={`${BASE}${item.src}`}
|
||||||
|
alt={item.alt}
|
||||||
|
className={[
|
||||||
|
"w-full object-cover",
|
||||||
|
heightClass[item.span],
|
||||||
|
"transition-transform duration-700 group-hover:scale-105",
|
||||||
|
].join(" ")}
|
||||||
|
loading={i < 3 ? "eager" : "lazy"}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -5,6 +5,8 @@ import HeroSection from "@/app/components/home/HeroSection";
|
|||||||
import VisionSection from "@/app/components/home/VisionSection";
|
import VisionSection from "@/app/components/home/VisionSection";
|
||||||
import StatsSection from "@/app/components/home/StatsSection";
|
import StatsSection from "@/app/components/home/StatsSection";
|
||||||
import AgendaSection from "@/app/components/home/AgendaSection";
|
import AgendaSection from "@/app/components/home/AgendaSection";
|
||||||
|
import SpeakerSpotlightSection from "@/app/components/home/SpeakerSpotlightSection";
|
||||||
|
import GallerySection from "@/app/components/home/GallerySection";
|
||||||
import PartnersCarousel from "@/app/components/home/PartnersCarousel";
|
import PartnersCarousel from "@/app/components/home/PartnersCarousel";
|
||||||
|
|
||||||
export default function HomePageClient() {
|
export default function HomePageClient() {
|
||||||
@@ -16,7 +18,9 @@ export default function HomePageClient() {
|
|||||||
<VisionSection data={home.vision} />
|
<VisionSection data={home.vision} />
|
||||||
<StatsSection data={home.stats} />
|
<StatsSection data={home.stats} />
|
||||||
<AgendaSection data={home.agenda} />
|
<AgendaSection data={home.agenda} />
|
||||||
<PartnersCarousel />
|
<SpeakerSpotlightSection data={home.speakers} />
|
||||||
|
<PartnersCarousel eyebrow={home.partners.eyebrow} title={home.partners.title} />
|
||||||
|
<GallerySection data={home.gallery} />
|
||||||
</main>
|
</main>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,17 +73,23 @@ function CarouselRow({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function PartnersCarousel() {
|
export default function PartnersCarousel({
|
||||||
|
eyebrow = "Partners & Sponsors",
|
||||||
|
title = "Trusted by Industry Leaders",
|
||||||
|
}: {
|
||||||
|
eyebrow?: string;
|
||||||
|
title?: string;
|
||||||
|
}) {
|
||||||
const base = process.env.NEXT_PUBLIC_BASE_PATH || "";
|
const base = process.env.NEXT_PUBLIC_BASE_PATH || "";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="w-full py-16 lg:py-24 overflow-hidden border-t border-white/5">
|
<section className="w-full py-16 lg:py-24 overflow-hidden border-t border-white/5">
|
||||||
<div className="mb-10 px-[var(--spacing-gutter)] max-w-hd mx-auto">
|
<div className="mb-10 px-[var(--spacing-gutter)] max-w-hd mx-auto">
|
||||||
<p className="text-primary font-[var(--font-label-caps)] text-xs tracking-[0.3em] uppercase mb-3">
|
<p className="text-primary font-[var(--font-label-caps)] text-xs tracking-[0.3em] uppercase mb-3">
|
||||||
Partners & Sponsors
|
{eyebrow}
|
||||||
</p>
|
</p>
|
||||||
<h2 className="text-on-surface font-[var(--font-display-lg)] text-2xl lg:text-4xl font-bold">
|
<h2 className="text-on-surface font-[var(--font-display-lg)] text-2xl lg:text-4xl font-bold">
|
||||||
Trusted by Industry Leaders
|
{title}
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,93 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
const BASE = process.env.NEXT_PUBLIC_BASE_PATH || "";
|
||||||
|
|
||||||
|
type SpeakerItem = {
|
||||||
|
name: string;
|
||||||
|
title: string;
|
||||||
|
photo: string;
|
||||||
|
quote: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
data: {
|
||||||
|
eyebrow: string;
|
||||||
|
title: string;
|
||||||
|
items: SpeakerItem[];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function SpeakerSpotlightSection({ data }: Props) {
|
||||||
|
const speaker = data.items[0];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className="py-16 lg:py-32 max-w-hd mx-auto px-[var(--spacing-gutter)]" id="speakers">
|
||||||
|
{/* Section header */}
|
||||||
|
<div className="mb-10 lg:mb-16">
|
||||||
|
<span className="font-[var(--font-label-caps)] text-xs lg:text-sm text-primary block mb-3 lg:mb-4 tracking-[0.25em] uppercase">
|
||||||
|
{data.eyebrow}
|
||||||
|
</span>
|
||||||
|
<h2 className="font-[var(--font-display-lg)] text-on-surface text-[clamp(2rem,4vw+1rem,4.5rem)] leading-tight">
|
||||||
|
{data.title}
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Card: 3/10 image — 7/10 quote */}
|
||||||
|
<div className="glass-panel rounded-[2rem] overflow-hidden shadow-2xl shadow-primary/5 flex flex-col lg:flex-row items-stretch">
|
||||||
|
|
||||||
|
{/* LEFT — 30%: ảnh + tên + chức danh */}
|
||||||
|
<div className="lg:w-[30%] flex flex-col items-center justify-center p-8 lg:p-12 text-center border-b lg:border-b-0 lg:border-r border-primary/10">
|
||||||
|
{/* Ảnh tròn */}
|
||||||
|
<div className="relative mb-6 lg:mb-8">
|
||||||
|
{/* Ring gold trang trí */}
|
||||||
|
<div className="absolute inset-0 border-2 border-primary/30 rounded-full scale-110 pointer-events-none" />
|
||||||
|
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||||
|
<img
|
||||||
|
src={`${BASE}${speaker.photo}`}
|
||||||
|
alt={speaker.name}
|
||||||
|
className="w-36 h-36 lg:w-56 lg:h-56 rounded-full object-cover object-top border-4 border-surface"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Tên */}
|
||||||
|
<h4 className="font-[var(--font-display-lg)] text-xl lg:text-[26px] text-on-surface mb-2 leading-tight">
|
||||||
|
{speaker.name}
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
{/* Chức danh */}
|
||||||
|
<p className="font-[var(--font-label-caps)] text-xs lg:text-sm text-primary tracking-widest uppercase leading-relaxed">
|
||||||
|
{speaker.title}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* RIGHT — 70%: quote dàn hàng ngang */}
|
||||||
|
<div className="lg:w-[70%] relative flex items-center p-8 lg:p-16 xl:p-20">
|
||||||
|
{/* Dấu nháy mở trang trí */}
|
||||||
|
<div
|
||||||
|
className="absolute top-4 left-6 lg:top-8 lg:left-10 pointer-events-none select-none text-primary opacity-20 font-[var(--font-display-lg)] leading-none"
|
||||||
|
style={{ fontSize: "clamp(6rem, 12vw, 11rem)" }}
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
“
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="relative z-10 w-full">
|
||||||
|
{/* Quote */}
|
||||||
|
<blockquote className="font-[var(--font-display-lg)] not-italic text-on-surface leading-[1.4] text-[clamp(1.2rem,2vw+0.6rem,2.25rem)] mb-8 lg:mb-10">
|
||||||
|
“{speaker.quote}”
|
||||||
|
</blockquote>
|
||||||
|
|
||||||
|
{/* Gold divider */}
|
||||||
|
<div className="h-[2px] w-20 bg-primary rounded-full mb-5 lg:mb-6" />
|
||||||
|
|
||||||
|
{/* Attribution nhỏ */}
|
||||||
|
<p className="font-[var(--font-label-caps)] text-xs lg:text-sm text-on-surface-variant/50 uppercase tracking-widest">
|
||||||
|
{speaker.name} — {speaker.title}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -40,6 +40,18 @@
|
|||||||
{ "value": "50+", "label": "Tech Partners" }
|
{ "value": "50+", "label": "Tech Partners" }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"speakers": {
|
||||||
|
"eyebrow": "FEATURED SPEAKER",
|
||||||
|
"title": "Voices of Leadership",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"name": "Dr. Sureswaran Ramadass",
|
||||||
|
"title": "Chairman, APAC IPv6 Council",
|
||||||
|
"photo": "/assets/img/speakers/speakes.jpg",
|
||||||
|
"quote": "The transition to IPv6 is not just a technical upgrade — it is the fundamental infrastructure for the AI-driven economy of ASEAN."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"agenda": {
|
"agenda": {
|
||||||
"id": "agenda",
|
"id": "agenda",
|
||||||
"eyebrow": "SCHEDULE",
|
"eyebrow": "SCHEDULE",
|
||||||
@@ -65,6 +77,25 @@
|
|||||||
"description": "Closed-door discussion for regulators and industry leaders."
|
"description": "Closed-door discussion for regulators and industry leaders."
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"partners": {
|
||||||
|
"eyebrow": "Partners & Sponsors",
|
||||||
|
"title": "Trusted by Industry Leaders"
|
||||||
|
},
|
||||||
|
"gallery": {
|
||||||
|
"eyebrow": "Visual Journey",
|
||||||
|
"title": "Event Gallery",
|
||||||
|
"description": "Moments from our global journey toward IPv6 excellence.",
|
||||||
|
"items": [
|
||||||
|
{ "src": "/assets/img/gallery/ipv6_1.jpg", "alt": "IPv6 & AI Keynote Stage", "span": "wide" },
|
||||||
|
{ "src": "/assets/img/gallery/ipv6_2.png", "alt": "Networking Reception", "span": "normal" },
|
||||||
|
{ "src": "/assets/img/gallery/ipv6_3.png", "alt": "Tech Expo", "span": "normal" },
|
||||||
|
{ "src": "/assets/img/gallery/ipv6_4.png", "alt": "ASEAN Digital Future Stage", "span": "wide" },
|
||||||
|
{ "src": "/assets/img/gallery/ipv6_5.png", "alt": "Networking Breakfast", "span": "normal" },
|
||||||
|
{ "src": "/assets/img/gallery/ipv6_6.png", "alt": "Panel Discussion — IPV6", "span": "normal" },
|
||||||
|
{ "src": "/assets/img/gallery/ipv6_7.png", "alt": "IPv6 Summit 2026 Registration", "span": "normal" },
|
||||||
|
{ "src": "/assets/img/gallery/ipv6_8.jpg", "alt": "Network Visualization", "span": "full" }
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -40,6 +40,18 @@
|
|||||||
{ "value": "50+", "label": "Đối Tác Công Nghệ" }
|
{ "value": "50+", "label": "Đối Tác Công Nghệ" }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"speakers": {
|
||||||
|
"eyebrow": "DIỄN GIẢ NỔI BẬT",
|
||||||
|
"title": "Tiếng Nói Lãnh Đạo",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"name": "Dr. Sureswaran Ramadass",
|
||||||
|
"title": "Chủ tịch, Hội đồng IPv6 APAC",
|
||||||
|
"photo": "/assets/img/speakers/speakes.jpg",
|
||||||
|
"quote": "Chuyển đổi sang IPv6 không chỉ là nâng cấp kỹ thuật — đó là nền tảng hạ tầng thiết yếu cho nền kinh tế số được dẫn dắt bởi AI tại ASEAN."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"agenda": {
|
"agenda": {
|
||||||
"id": "agenda",
|
"id": "agenda",
|
||||||
"eyebrow": "LỊCH TRÌNH",
|
"eyebrow": "LỊCH TRÌNH",
|
||||||
@@ -65,5 +77,24 @@
|
|||||||
"description": "Thảo luận kín dành cho cơ quan quản lý và lãnh đạo ngành."
|
"description": "Thảo luận kín dành cho cơ quan quản lý và lãnh đạo ngành."
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"partners": {
|
||||||
|
"eyebrow": "Đối tác và Nhà tài trợ",
|
||||||
|
"title": "Được các nhà lãnh đạo trong ngành tin cậy"
|
||||||
|
},
|
||||||
|
"gallery": {
|
||||||
|
"eyebrow": "Hành Trình Hình Ảnh",
|
||||||
|
"title": "Thư Viện Ảnh",
|
||||||
|
"description": "Những khoảnh khắc từ hành trình hướng tới sự xuất sắc của IPv6.",
|
||||||
|
"items": [
|
||||||
|
{ "src": "/assets/img/gallery/ipv6_1.jpg", "alt": "Sân khấu Keynote IPv6 & AI", "span": "wide" },
|
||||||
|
{ "src": "/assets/img/gallery/ipv6_2.png", "alt": "Tiệc Networking", "span": "normal" },
|
||||||
|
{ "src": "/assets/img/gallery/ipv6_3.png", "alt": "Triển lãm Công nghệ", "span": "normal" },
|
||||||
|
{ "src": "/assets/img/gallery/ipv6_4.png", "alt": "Sân khấu Tương lai Số ASEAN", "span": "wide" },
|
||||||
|
{ "src": "/assets/img/gallery/ipv6_5.png", "alt": "Bữa sáng Networking", "span": "normal" },
|
||||||
|
{ "src": "/assets/img/gallery/ipv6_6.png", "alt": "Phiên thảo luận — IPV6", "span": "normal" },
|
||||||
|
{ "src": "/assets/img/gallery/ipv6_7.png", "alt": "Quầy đăng ký IPv6 Summit 2026", "span": "normal" },
|
||||||
|
{ "src": "/assets/img/gallery/ipv6_8.jpg", "alt": "Hình ảnh mạng lưới kết nối", "span": "full" }
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
|
After Width: | Height: | Size: 201 KiB |
|
After Width: | Height: | Size: 1.9 MiB |
|
After Width: | Height: | Size: 1.5 MiB |
|
After Width: | Height: | Size: 1.6 MiB |
|
After Width: | Height: | Size: 1.6 MiB |
|
After Width: | Height: | Size: 1.3 MiB |
|
After Width: | Height: | Size: 1.5 MiB |
|
After Width: | Height: | Size: 158 KiB |
|
After Width: | Height: | Size: 134 KiB |