forked from UKSOURCE/ipv6
feat: update summit title to Cloud, venue to Lotte Center Hanoi and sync 21 agenda sessions
This commit is contained in:
@@ -1,10 +1,17 @@
|
||||
"use client";
|
||||
|
||||
type AgendaItem = {
|
||||
order: string;
|
||||
time: string;
|
||||
import { useState, useEffect, useMemo } from "react";
|
||||
|
||||
export type AgendaItem = {
|
||||
id: number;
|
||||
time_ict: string;
|
||||
time_utc: string;
|
||||
category: string;
|
||||
title: string;
|
||||
description: string;
|
||||
speaker: string;
|
||||
speaker_title: string;
|
||||
location: string;
|
||||
avatar?: string;
|
||||
};
|
||||
|
||||
@@ -18,15 +25,20 @@ type Props = {
|
||||
};
|
||||
};
|
||||
|
||||
const CATEGORIES = [
|
||||
"All Sessions",
|
||||
"Ceremonies",
|
||||
"Keynotes & Address",
|
||||
"Technical & Infrastructure",
|
||||
"Forums & Panels",
|
||||
"Breaks & Networking",
|
||||
];
|
||||
|
||||
const renderFormattedText = (text: string) => {
|
||||
if (!text) return "";
|
||||
|
||||
if (!text) return null;
|
||||
const lines = text.split("\n");
|
||||
|
||||
return lines.map((line, lineIndex) => {
|
||||
// Split the line by markdown bold (**bold**) and italic (*italic*)
|
||||
const parts = line.split(/(\*\*.*?\*\*|\*.*?\*)/g);
|
||||
|
||||
const renderedLine = parts.map((part, partIndex) => {
|
||||
if (part.startsWith("**") && part.endsWith("**")) {
|
||||
return (
|
||||
@@ -55,30 +67,197 @@ const renderFormattedText = (text: string) => {
|
||||
|
||||
export default function AgendaSection({ data }: Props) {
|
||||
const base = process.env.NEXT_PUBLIC_BASE_PATH || "";
|
||||
const [searchQuery, setSearchQuery] = useState("");
|
||||
const [selectedCategory, setSelectedCategory] = useState("All Sessions");
|
||||
const [viewMode, setViewMode] = useState<"table" | "card">("card");
|
||||
const [timezoneMode, setTimezoneMode] = useState<"ICT" | "UTC" | "Local">("ICT");
|
||||
const [bookmarkedIds, setBookmarkedIds] = useState<number[]>([]);
|
||||
const [showOnlyBookmarks, setShowOnlyBookmarks] = useState(false);
|
||||
const [selectedModalItem, setSelectedModalItem] = useState<AgendaItem | null>(null);
|
||||
|
||||
const renderAvatar = (avatarUrl: string, sizeClass: string) => (
|
||||
<div className={`${sizeClass} rounded-full overflow-hidden border border-primary/20 bg-white/5 flex items-center justify-center backdrop-blur-sm shadow-inner shrink-0`}>
|
||||
{avatarUrl.includes("placeholder") ? (
|
||||
<svg className="w-1/2 h-1/2 text-primary/60" fill="none" stroke="currentColor" viewBox="0 0 24 24" strokeWidth={1.5}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M15.75 6a3.75 3.75 0 1 1-7.5 0 3.75 3.75 0 0 1 7.5 0ZM4.501 20.118a7.5 7.5 0 0 1 14.998 0A17.933 17.933 0 0 1 12 21.75c-2.676 0-5.216-.584-7.499-1.632Z" />
|
||||
</svg>
|
||||
) : (
|
||||
// Load bookmarks from localStorage
|
||||
useEffect(() => {
|
||||
try {
|
||||
const saved = localStorage.getItem("ipv6_agenda_bookmarks");
|
||||
if (saved) {
|
||||
setBookmarkedIds(JSON.parse(saved));
|
||||
}
|
||||
} catch {
|
||||
// Ignore localStorage errors
|
||||
}
|
||||
}, []);
|
||||
|
||||
// Keyboard shortcut to close modal
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (e: KeyboardEvent) => {
|
||||
if (e.key === "Escape") {
|
||||
setSelectedModalItem(null);
|
||||
}
|
||||
};
|
||||
window.addEventListener("keydown", handleKeyDown);
|
||||
return () => window.removeEventListener("keydown", handleKeyDown);
|
||||
}, []);
|
||||
|
||||
// Save bookmarks to localStorage
|
||||
const toggleBookmark = (id: number) => {
|
||||
setBookmarkedIds((prev) => {
|
||||
const next = prev.includes(id) ? prev.filter((item) => item !== id) : [...prev, id];
|
||||
try {
|
||||
localStorage.setItem("ipv6_agenda_bookmarks", JSON.stringify(next));
|
||||
} catch {
|
||||
// Ignore localStorage errors
|
||||
}
|
||||
return next;
|
||||
});
|
||||
};
|
||||
|
||||
// Convert time display based on selected timezone mode
|
||||
const getTimeDisplay = (item: AgendaItem) => {
|
||||
if (timezoneMode === "UTC") {
|
||||
return item.time_utc ? `${item.time_utc} UTC` : item.time_ict;
|
||||
}
|
||||
if (timezoneMode === "ICT") {
|
||||
return item.time_ict ? `${item.time_ict} ICT` : item.time_utc;
|
||||
}
|
||||
// Local Time mode
|
||||
try {
|
||||
const timeStr = item.time_utc;
|
||||
if (!timeStr) return item.time_ict;
|
||||
const parts = timeStr.split(" - ");
|
||||
if (parts.length < 1) return item.time_ict;
|
||||
|
||||
const parseUtcHourMin = (s: string) => {
|
||||
const m = s.trim().match(/(\d+)[\.:](\d+)\s*(am|pm)/i);
|
||||
if (!m) return null;
|
||||
let h = parseInt(m[1], 10);
|
||||
const min = parseInt(m[2], 10);
|
||||
const period = m[3].toLowerCase();
|
||||
if (period === "pm" && h < 12) h += 12;
|
||||
if (period === "am" && h === 12) h = 0;
|
||||
const d = new Date();
|
||||
d.setUTCHours(h, min, 0, 0);
|
||||
return d.toLocaleTimeString([], { hour: "numeric", minute: "2-digit", hour12: true });
|
||||
};
|
||||
|
||||
const startLocal = parseUtcHourMin(parts[0]);
|
||||
const endLocal = parts[1] ? parseUtcHourMin(parts[1]) : null;
|
||||
|
||||
if (startLocal && endLocal) return `${startLocal} - ${endLocal} (Local)`;
|
||||
if (startLocal) return `${startLocal} (Local)`;
|
||||
return item.time_ict;
|
||||
} catch {
|
||||
return item.time_ict;
|
||||
}
|
||||
};
|
||||
|
||||
// Filter items
|
||||
const filteredItems = useMemo(() => {
|
||||
return (data.items || []).filter((item) => {
|
||||
// Category filter
|
||||
if (selectedCategory !== "All Sessions" && item.category !== selectedCategory) {
|
||||
const categoryMatch =
|
||||
selectedCategory === "Breaks & Networking" && (item.category?.includes("Breaks") || item.category?.includes("Nghỉ"));
|
||||
const ceremonyMatch =
|
||||
selectedCategory === "Ceremonies" && (item.category?.includes("Ceremony") || item.category?.includes("Lễ"));
|
||||
const keynoteMatch =
|
||||
selectedCategory === "Keynotes & Address" && (item.category?.includes("Keynote") || item.category?.includes("Bài phát biểu"));
|
||||
const techMatch =
|
||||
selectedCategory === "Technical & Infrastructure" && (item.category?.includes("Technical") || item.category?.includes("Kỹ thuật"));
|
||||
const forumMatch =
|
||||
selectedCategory === "Forums & Panels" && (item.category?.includes("Forum") || item.category?.includes("Tọa đàm"));
|
||||
|
||||
if (!categoryMatch && !ceremonyMatch && !keynoteMatch && !techMatch && !forumMatch) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Bookmark filter
|
||||
if (showOnlyBookmarks && !bookmarkedIds.includes(item.id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Search query
|
||||
if (searchQuery.trim()) {
|
||||
const q = searchQuery.toLowerCase();
|
||||
const matchTitle = item.title?.toLowerCase().includes(q);
|
||||
const matchDesc = item.description?.toLowerCase().includes(q);
|
||||
const matchSpeaker = item.speaker?.toLowerCase().includes(q) || item.speaker_title?.toLowerCase().includes(q);
|
||||
const matchCategory = item.category?.toLowerCase().includes(q);
|
||||
if (!matchTitle && !matchDesc && !matchSpeaker && !matchCategory) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}, [data.items, selectedCategory, showOnlyBookmarks, bookmarkedIds, searchQuery]);
|
||||
|
||||
const handlePrint = () => {
|
||||
window.print();
|
||||
};
|
||||
|
||||
const renderAvatar = (avatarUrl?: string, sizeClass: string = "w-9 h-9 lg:w-10 lg:h-10") => {
|
||||
return (
|
||||
<div className={`${sizeClass} rounded-full overflow-hidden border border-primary/30 bg-primary/10 flex items-center justify-center backdrop-blur-sm shadow-sm shrink-0`}>
|
||||
{avatarUrl ? (
|
||||
// eslint-disable-next-line @next/next/no-img-element
|
||||
<img
|
||||
src={`${base}${avatarUrl}`}
|
||||
src={`${base}${avatarUrl.startsWith("/") ? avatarUrl : `/${avatarUrl}`}`}
|
||||
alt="Speaker"
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
) : (
|
||||
<svg className="w-1/2 h-1/2 text-primary" fill="none" stroke="currentColor" viewBox="0 0 24 24" strokeWidth={1.5}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M15.75 6a3.75 3.75 0 1 1-7.5 0 3.75 3.75 0 0 1 7.5 0ZM4.501 20.118a7.5 7.5 0 0 1 14.998 0A17.933 17.933 0 0 1 12 21.75c-2.676 0-5.216-.584-7.499-1.632Z" />
|
||||
</svg>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<section className="py-16 lg:py-24 max-w-hd mx-auto px-[var(--spacing-gutter)]" id={data.id}>
|
||||
{/* Section Header */}
|
||||
<div className="flex flex-col lg:flex-row justify-between lg:items-end mb-16 lg:mb-28 gap-8">
|
||||
<section className="py-12 lg:py-20 max-w-hd mx-auto px-[var(--spacing-gutter)]" id={data.id}>
|
||||
{/* Printable CSS style overlay for clean PDF / Print export */}
|
||||
<style jsx global>{`
|
||||
@media print {
|
||||
body * {
|
||||
visibility: hidden;
|
||||
}
|
||||
#printable-agenda-area,
|
||||
#printable-agenda-area * {
|
||||
visibility: visible;
|
||||
}
|
||||
#printable-agenda-area {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
color: #000 !important;
|
||||
background: #fff !important;
|
||||
}
|
||||
.no-print {
|
||||
display: none !important;
|
||||
}
|
||||
.glass-panel {
|
||||
background: #fff !important;
|
||||
border: 1px solid #ddd !important;
|
||||
color: #000 !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
.text-primary {
|
||||
color: #1a56db !important;
|
||||
}
|
||||
.text-on-surface,
|
||||
.text-on-surface-variant {
|
||||
color: #111 !important;
|
||||
}
|
||||
}
|
||||
`}</style>
|
||||
|
||||
{/* Header */}
|
||||
<div className="flex flex-col lg:flex-row justify-between lg:items-end mb-8 gap-4 no-print">
|
||||
<div className="max-w-3xl">
|
||||
<span className="font-[var(--font-label-caps)] text-xs lg:text-sm text-primary block mb-4 lg:mb-6 tracking-[0.25em] uppercase">
|
||||
<span className="font-[var(--font-label-caps)] text-xs lg:text-sm text-primary block mb-2 tracking-[0.25em] uppercase">
|
||||
{data.eyebrow}
|
||||
</span>
|
||||
<h2 className="font-[var(--font-display-lg)] text-on-surface uppercase tracking-tight text-[clamp(1.75rem,3vw+1rem,3.5rem)]">
|
||||
@@ -90,9 +269,192 @@ export default function AgendaSection({ data }: Props) {
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Timeline Container */}
|
||||
<div className="relative w-full max-w-5xl mx-auto pl-10 lg:pl-0">
|
||||
{/* Main Container */}
|
||||
<div id="printable-agenda-area" className="w-full">
|
||||
{/* Printable Header (Visible only when printing) */}
|
||||
<div className="hidden print:block mb-8 border-b pb-4">
|
||||
<h1 className="text-2xl font-bold text-gray-900">APAC IPv6, AI & Cloud Summit 2026 - Agenda</h1>
|
||||
<p className="text-sm text-gray-600">Lotte Center, 54 Liễu Giai, Phường Giảng Võ, Hà Nội, Việt Nam</p>
|
||||
</div>
|
||||
|
||||
{/* Event Venue Banner with Direct Google Maps Button */}
|
||||
<div className="glass-panel p-4 rounded-2xl mb-6 border border-primary/30 bg-surface/50 flex flex-col sm:flex-row items-start sm:items-center justify-between gap-3 no-print">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-10 h-10 rounded-full bg-primary/10 border border-primary/30 flex items-center justify-center text-primary shrink-0">
|
||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" strokeWidth={2}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M15 10.5a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" />
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M19.5 10.5c0 7.142-7.5 11.25-7.5 11.25S4.5 17.642 4.5 10.5a7.5 7.5 0 1 1 15 0Z" />
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-[11px] uppercase tracking-wider text-primary font-bold">Event Venue / Địa điểm tổ chức</div>
|
||||
<div className="text-sm font-semibold text-on-surface">Lotte Center, 54 Liễu Giai, Phường Giảng Võ, Hà Nội</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a
|
||||
href="https://maps.app.goo.gl/DXwWCY4BKAV6BZbc8"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="px-4 py-2 rounded-full gold-gradient-bg text-on-primary font-bold text-xs uppercase tracking-wider hover:shadow-[0_0_15px_rgba(197,160,89,0.4)] transition-all flex items-center gap-1.5 shrink-0"
|
||||
>
|
||||
<span>📍 Google Maps Direct</span>
|
||||
<svg className="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24" strokeWidth={2}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M13.5 6H5.25A2.25 2.25 0 0 0 3 8.25v10.5A2.25 2.25 0 0 0 5.25 21h10.5A2.25 2.25 0 0 0 18 18.75V10.5m-10.5 6L21 3m0 0h-5.25M21 3v5.25" />
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{/* Control Toolbar */}
|
||||
<div className="glass-panel p-3.5 lg:p-5 rounded-3xl mb-6 space-y-3 no-print border border-primary/20 bg-surface/40 backdrop-blur-md">
|
||||
{/* Row 1: Search & My Schedule & Print & View Modes */}
|
||||
<div className="flex flex-col md:flex-row gap-3 justify-between items-center">
|
||||
{/* Search Input */}
|
||||
<div className="relative w-full md:w-80">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search sessions, speaker..."
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
className="w-full pl-9 pr-3 py-2 rounded-full bg-background/80 border border-primary/20 text-on-surface text-xs lg:text-sm focus:outline-none focus:border-primary transition-colors placeholder:text-on-surface-variant/50"
|
||||
/>
|
||||
<svg
|
||||
className="w-4 h-4 text-primary absolute left-3 top-1/2 -translate-y-1/2 pointer-events-none"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
strokeWidth={2}
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
{/* Right Action Controls */}
|
||||
<div className="flex flex-wrap items-center gap-2.5 w-full md:w-auto justify-end">
|
||||
{/* My Schedule Filter Button */}
|
||||
<button
|
||||
onClick={() => setShowOnlyBookmarks(!showOnlyBookmarks)}
|
||||
className={`px-3.5 py-1.5 rounded-full text-xs font-semibold uppercase tracking-wider transition-all duration-300 flex items-center gap-1.5 border ${
|
||||
showOnlyBookmarks
|
||||
? "bg-primary text-background border-primary shadow-[0_0_15px_rgba(197,160,89,0.4)]"
|
||||
: "bg-surface/60 text-on-surface border-primary/30 hover:border-primary"
|
||||
}`}
|
||||
>
|
||||
<span>★ My Schedule ({bookmarkedIds.length})</span>
|
||||
</button>
|
||||
|
||||
{/* Print / Export PDF Button */}
|
||||
<button
|
||||
onClick={handlePrint}
|
||||
className="px-3.5 py-1.5 rounded-full text-xs font-semibold uppercase tracking-wider border border-primary/30 bg-surface/60 text-primary hover:bg-primary/10 transition-colors flex items-center gap-1.5"
|
||||
title="Print or Save as PDF"
|
||||
>
|
||||
<svg className="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24" strokeWidth={2}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M6.72 13.829c-.24.03-.48.062-.72.096m.72-.096a42.415 42.415 0 0 1 10.56 0m-10.56 0L6.34 18m10.94-4.171c.24.03.48.062.72.096m-.72-.096L17.66 18m0 0 .229 2.523a1.125 1.125 0 0 1-1.12 1.227H7.231c-.6 0-1.1-.47-1.12-1.07L6.34 18m11.318 0h1.091A2.25 2.25 0 0 0 21 15.75V9.456c0-1.081-.768-2.015-1.837-2.175a48.055 48.055 0 0 0-1.913-.247M6.34 18H5.25A2.25 2.25 0 0 1 3 15.75V9.456c0-1.081.768-2.015 1.837-2.175a48.041 48.041 0 0 1 1.913-.247m10.5 0a48.536 48.536 0 0 0-10.5 0m10.5 0V3.375c0-.621-.504-1.125-1.125-1.125h-8.25c-.621 0-1.125.504-1.125 1.125v3.656" />
|
||||
</svg>
|
||||
<span>Print / PDF</span>
|
||||
</button>
|
||||
|
||||
{/* Timezone Switcher */}
|
||||
<div className="flex items-center bg-background/80 rounded-full border border-primary/20 p-1 text-xs">
|
||||
{(["ICT", "UTC", "Local"] as const).map((tz) => (
|
||||
<button
|
||||
key={tz}
|
||||
onClick={() => setTimezoneMode(tz)}
|
||||
className={`px-2 py-0.5 rounded-full transition-colors ${
|
||||
timezoneMode === tz ? "bg-primary text-background font-bold" : "text-on-surface-variant/70 hover:text-on-surface"
|
||||
}`}
|
||||
>
|
||||
{tz}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* View Mode Toggle */}
|
||||
<div className="flex items-center bg-background/80 rounded-full border border-primary/20 p-1 text-xs">
|
||||
<button
|
||||
onClick={() => setViewMode("card")}
|
||||
className={`px-2.5 py-0.5 rounded-full transition-colors flex items-center gap-1 ${
|
||||
viewMode === "card" ? "bg-primary text-background font-bold" : "text-on-surface-variant/70 hover:text-on-surface"
|
||||
}`}
|
||||
>
|
||||
<svg className="w-3.5 h-3.5" fill="currentColor" viewBox="0 0 24 24">
|
||||
<path d="M4 4h7v16H4V4zm9 0h7v7h-7V4zm0 9h7v7h-7v-7z" />
|
||||
</svg>
|
||||
<span>Timeline</span>
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setViewMode("table")}
|
||||
className={`px-2.5 py-0.5 rounded-full transition-colors flex items-center gap-1 ${
|
||||
viewMode === "table" ? "bg-primary text-background font-bold" : "text-on-surface-variant/70 hover:text-on-surface"
|
||||
}`}
|
||||
>
|
||||
<svg className="w-3.5 h-3.5" fill="currentColor" viewBox="0 0 24 24">
|
||||
<path d="M4 4h16v16H4V4zm2 4v4h12V8H6zm0 6v4h12v-4H6z" />
|
||||
</svg>
|
||||
<span>Table</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Row 2: Category Filter Tags */}
|
||||
<div className="flex items-center gap-1.5 overflow-x-auto pb-1 scrollbar-none border-t border-primary/10 pt-3">
|
||||
{CATEGORIES.map((category) => (
|
||||
<button
|
||||
key={category}
|
||||
onClick={() => setSelectedCategory(category)}
|
||||
className={`px-3 py-1 rounded-full text-xs whitespace-nowrap transition-all duration-300 font-medium ${
|
||||
selectedCategory === category
|
||||
? "bg-primary/20 text-primary border border-primary/50 font-semibold"
|
||||
: "bg-surface/40 text-on-surface-variant/80 border border-transparent hover:border-primary/20 hover:text-on-surface"
|
||||
}`}
|
||||
>
|
||||
{category}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Results Counter Bar */}
|
||||
<div className="flex justify-between items-center mb-6 text-xs text-on-surface-variant/70 px-2 no-print">
|
||||
<div>
|
||||
Showing <span className="text-primary font-bold">{filteredItems.length}</span> of {(data.items || []).length} sessions
|
||||
{showOnlyBookmarks && " (My Schedule)"}
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="relative flex h-2.5 w-2.5">
|
||||
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-emerald-400 opacity-75"></span>
|
||||
<span className="relative inline-flex rounded-full h-2.5 w-2.5 bg-emerald-500"></span>
|
||||
</span>
|
||||
<span className="text-emerald-400 font-medium uppercase tracking-wider text-[11px]">Event Date: Aug 4, 2026</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* No Results Fallback */}
|
||||
{filteredItems.length === 0 && (
|
||||
<div className="text-center py-12 glass-panel rounded-3xl">
|
||||
<p className="text-on-surface-variant/60 text-base mb-2">No matching sessions found.</p>
|
||||
<button
|
||||
onClick={() => {
|
||||
setSearchQuery("");
|
||||
setSelectedCategory("All Sessions");
|
||||
setShowOnlyBookmarks(false);
|
||||
}}
|
||||
className="text-primary hover:underline text-xs font-semibold"
|
||||
>
|
||||
Reset all filters
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* CARD VIEW: Wider Max Width + Reduced Gaps for Natural Text Flow */}
|
||||
{viewMode === "card" && filteredItems.length > 0 && (
|
||||
<div className="relative w-full max-w-6xl mx-auto pl-8 lg:pl-0">
|
||||
{/* Central vertical line */}
|
||||
<div
|
||||
className="absolute left-4 lg:left-1/2 top-4 bottom-4 w-[2px] bg-gradient-to-b from-primary via-primary/45 to-primary/5 -translate-x-1/2 pointer-events-none"
|
||||
@@ -100,116 +462,444 @@ export default function AgendaSection({ data }: Props) {
|
||||
/>
|
||||
|
||||
{/* Timeline Items */}
|
||||
<div className="space-y-12 lg:space-y-16">
|
||||
{data.items.map((item, index) => {
|
||||
<div className="space-y-6 lg:space-y-8">
|
||||
{filteredItems.map((item, index) => {
|
||||
const isEven = index % 2 === 0;
|
||||
return (
|
||||
<div key={item.order} className="relative w-full group">
|
||||
const isBookmarked = bookmarkedIds.includes(item.id);
|
||||
const orderNum = String(index + 1).padStart(2, "0");
|
||||
const timeText = getTimeDisplay(item);
|
||||
const hasSpeaker = Boolean(item.speaker && item.speaker.trim() !== "");
|
||||
|
||||
return (
|
||||
<div key={item.id || index} className="relative w-full group">
|
||||
{/* Timeline Dot (glowing on hover) */}
|
||||
<div
|
||||
className="absolute left-[-24px] lg:left-1/2 top-6 lg:top-8 -translate-x-1/2 w-4 h-4 rounded-full bg-background border-2 border-primary z-20 group-hover:scale-125 group-hover:bg-primary group-hover:shadow-[0_0_10px_#c5a059] transition-all duration-300 status-led"
|
||||
/>
|
||||
<div className="absolute left-[-24px] lg:left-1/2 top-5 lg:top-6 -translate-x-1/2 w-4 h-4 rounded-full bg-background border-2 border-primary z-20 group-hover:scale-125 group-hover:bg-primary group-hover:shadow-[0_0_10px_#c5a059] transition-all duration-300 status-led" />
|
||||
|
||||
{/* Left/Right alternating grid layout */}
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4 lg:gap-16 w-full">
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-3 lg:gap-8 w-full">
|
||||
{isEven ? (
|
||||
<>
|
||||
{/* Left: Card (Desktop) / Full content (Mobile) */}
|
||||
<div className="order-1 lg:order-1">
|
||||
<div className="glass-panel p-6 lg:p-8 rounded-3xl relative overflow-hidden hover:border-primary/50 transition-colors duration-300">
|
||||
<span className="absolute top-6 right-6 text-xs lg:text-sm font-[var(--font-display-lg)] text-primary/30 group-hover:text-primary transition-colors duration-300">
|
||||
{item.order}
|
||||
<div className="glass-panel p-4 lg:p-6 rounded-3xl relative overflow-hidden hover:border-primary/50 transition-all duration-300">
|
||||
{/* Top Bar inside Card: Category badge + Order + Star */}
|
||||
<div className="flex justify-between items-center mb-3 gap-2">
|
||||
<span className="px-2.5 py-0.5 rounded-full text-[11px] font-medium bg-primary/10 text-primary border border-primary/20">
|
||||
{item.category}
|
||||
</span>
|
||||
<div className="flex items-center gap-2.5">
|
||||
<span className="text-xs font-[var(--font-display-lg)] text-primary/40 group-hover:text-primary transition-colors duration-300">
|
||||
{orderNum}
|
||||
</span>
|
||||
<button
|
||||
onClick={() => toggleBookmark(item.id)}
|
||||
className="text-lg no-print hover:scale-110 transition-transform leading-none"
|
||||
title={isBookmarked ? "Remove from My Schedule" : "Add to My Schedule"}
|
||||
>
|
||||
<span className={isBookmarked ? "text-amber-400" : "text-on-surface-variant/30 hover:text-amber-400"}>
|
||||
{isBookmarked ? "★" : "☆"}
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Mobile-only time badge */}
|
||||
<div className="lg:hidden mb-3">
|
||||
<span className="font-[var(--font-display-lg)] text-base text-primary">
|
||||
{item.time}
|
||||
<div className="lg:hidden mb-2">
|
||||
<span className="font-[var(--font-display-lg)] text-sm text-primary">
|
||||
{timeText}
|
||||
</span>
|
||||
</div>
|
||||
<h3 className="font-[var(--font-display-lg)] text-base lg:text-lg text-on-surface mb-3 leading-snug text-balance pr-8">
|
||||
|
||||
{/* Session Title */}
|
||||
<h3 className="font-[var(--font-display-lg)] text-base lg:text-lg text-on-surface mb-2 leading-snug lg:text-right">
|
||||
{renderFormattedText(item.title)}
|
||||
</h3>
|
||||
{!item.description && item.avatar && (
|
||||
<div className="flex lg:justify-end justify-start mb-3">
|
||||
{renderAvatar(item.avatar, "w-10 h-10 lg:w-12 lg:h-12")}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Description & Speaker Info */}
|
||||
<div className="text-xs lg:text-sm text-on-surface-variant/75 leading-relaxed border-t border-primary/10 pt-2.5 mt-2.5 flex flex-col gap-2.5">
|
||||
{item.description && (
|
||||
<div className="text-xs lg:text-sm text-on-surface-variant/75 leading-relaxed border-t border-primary/10 pt-3 mt-3 flex flex-row lg:flex-row-reverse items-center gap-4">
|
||||
{item.avatar && (
|
||||
<div className="shrink-0">
|
||||
{renderAvatar(item.avatar, "w-10 h-10 lg:w-12 lg:h-12")}
|
||||
</div>
|
||||
)}
|
||||
<div className="flex-1 lg:text-right">
|
||||
<div className="lg:text-right">
|
||||
{renderFormattedText(item.description)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Speaker info with avatar */}
|
||||
<div className="flex flex-row lg:flex-row-reverse items-center gap-2.5 border-t border-primary/5 pt-2">
|
||||
{hasSpeaker ? (
|
||||
<>
|
||||
{renderAvatar(item.avatar)}
|
||||
<div className="lg:text-right">
|
||||
<div className="font-semibold text-on-surface text-xs lg:text-sm">
|
||||
{item.speaker}
|
||||
</div>
|
||||
{item.speaker_title && (
|
||||
<div className="text-[11px] text-on-surface-variant/60">
|
||||
{item.speaker_title}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<div className="text-on-surface-variant/40 font-bold lg:text-right w-full text-base">
|
||||
—
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Location Badge */}
|
||||
<div className="flex items-center lg:justify-end gap-1 text-[11px] text-on-surface-variant/60">
|
||||
<svg className="w-3 h-3 text-primary shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z" />
|
||||
</svg>
|
||||
<span>{item.location || "Main Stage"}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right: Time badge (Desktop-only) */}
|
||||
<div className="order-2 lg:order-2 hidden lg:flex items-center justify-start pl-4">
|
||||
<span className="font-[var(--font-display-lg)] text-xl lg:text-[22px] text-primary group-hover:text-on-surface transition-colors duration-300">
|
||||
{item.time}
|
||||
<div className="order-2 lg:order-2 hidden lg:flex flex-col items-start justify-center pl-2">
|
||||
<span className="font-[var(--font-display-lg)] text-lg lg:text-xl text-primary group-hover:text-on-surface transition-colors duration-300">
|
||||
{timeText}
|
||||
</span>
|
||||
{timezoneMode !== "UTC" && item.time_utc && (
|
||||
<span className="text-xs text-on-surface-variant/50">
|
||||
{item.time_utc} UTC
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{/* Left: Time badge (Desktop-only) */}
|
||||
<div className="order-2 lg:order-1 hidden lg:flex items-center justify-end pr-4 text-right">
|
||||
<span className="font-[var(--font-display-lg)] text-xl lg:text-[22px] text-primary group-hover:text-on-surface transition-colors duration-300">
|
||||
{item.time}
|
||||
<div className="order-2 lg:order-1 hidden lg:flex flex-col items-end justify-center pr-2 text-right">
|
||||
<span className="font-[var(--font-display-lg)] text-lg lg:text-xl text-primary group-hover:text-on-surface transition-colors duration-300">
|
||||
{timeText}
|
||||
</span>
|
||||
{timezoneMode !== "UTC" && item.time_utc && (
|
||||
<span className="text-xs text-on-surface-variant/50">
|
||||
{item.time_utc} UTC
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Right: Card (Desktop) / Full content (Mobile) */}
|
||||
<div className="order-1 lg:order-2">
|
||||
<div className="glass-panel p-6 lg:p-8 rounded-3xl relative overflow-hidden hover:border-primary/50 transition-colors duration-300">
|
||||
<span className="absolute top-6 right-6 text-xs lg:text-sm font-[var(--font-display-lg)] text-primary/30 group-hover:text-primary transition-colors duration-300">
|
||||
{item.order}
|
||||
<div className="glass-panel p-4 lg:p-6 rounded-3xl relative overflow-hidden hover:border-primary/50 transition-all duration-300">
|
||||
{/* Top Bar inside Card: Category badge + Order + Star */}
|
||||
<div className="flex justify-between items-center mb-3 gap-2">
|
||||
<span className="px-2.5 py-0.5 rounded-full text-[11px] font-medium bg-primary/10 text-primary border border-primary/20">
|
||||
{item.category}
|
||||
</span>
|
||||
<div className="flex items-center gap-2.5">
|
||||
<span className="text-xs font-[var(--font-display-lg)] text-primary/40 group-hover:text-primary transition-colors duration-300">
|
||||
{orderNum}
|
||||
</span>
|
||||
<button
|
||||
onClick={() => toggleBookmark(item.id)}
|
||||
className="text-lg no-print hover:scale-110 transition-transform leading-none"
|
||||
title={isBookmarked ? "Remove from My Schedule" : "Add to My Schedule"}
|
||||
>
|
||||
<span className={isBookmarked ? "text-amber-400" : "text-on-surface-variant/30 hover:text-amber-400"}>
|
||||
{isBookmarked ? "★" : "☆"}
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Mobile-only time badge */}
|
||||
<div className="lg:hidden mb-3">
|
||||
<span className="font-[var(--font-display-lg)] text-base text-primary">
|
||||
{item.time}
|
||||
<div className="lg:hidden mb-2">
|
||||
<span className="font-[var(--font-display-lg)] text-sm text-primary">
|
||||
{timeText}
|
||||
</span>
|
||||
</div>
|
||||
<h3 className="font-[var(--font-display-lg)] text-base lg:text-lg text-on-surface mb-3 leading-snug text-balance pr-8">
|
||||
|
||||
{/* Session Title */}
|
||||
<h3 className="font-[var(--font-display-lg)] text-base lg:text-lg text-on-surface mb-2 leading-snug">
|
||||
{renderFormattedText(item.title)}
|
||||
</h3>
|
||||
{!item.description && item.avatar && (
|
||||
<div className="flex justify-start mb-3">
|
||||
{renderAvatar(item.avatar, "w-10 h-10 lg:w-12 lg:h-12")}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Description & Speaker Info */}
|
||||
<div className="text-xs lg:text-sm text-on-surface-variant/75 leading-relaxed border-t border-primary/10 pt-2.5 mt-2.5 flex flex-col gap-2.5">
|
||||
{item.description && (
|
||||
<div className="text-xs lg:text-sm text-on-surface-variant/75 leading-relaxed border-t border-primary/10 pt-3 mt-3 flex flex-row items-center gap-4">
|
||||
{item.avatar && (
|
||||
<div className="shrink-0">
|
||||
{renderAvatar(item.avatar, "w-10 h-10 lg:w-12 lg:h-12")}
|
||||
<div>{renderFormattedText(item.description)}</div>
|
||||
)}
|
||||
|
||||
{/* Speaker info with avatar */}
|
||||
<div className="flex flex-row items-center gap-2.5 border-t border-primary/5 pt-2">
|
||||
{hasSpeaker ? (
|
||||
<>
|
||||
{renderAvatar(item.avatar)}
|
||||
<div>
|
||||
<div className="font-semibold text-on-surface text-xs lg:text-sm">
|
||||
{item.speaker}
|
||||
</div>
|
||||
{item.speaker_title && (
|
||||
<div className="text-[11px] text-on-surface-variant/60">
|
||||
{item.speaker_title}
|
||||
</div>
|
||||
)}
|
||||
<div className="flex-1">
|
||||
{renderFormattedText(item.description)}
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<div className="text-on-surface-variant/40 font-bold text-base">
|
||||
—
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Location Badge */}
|
||||
<div className="flex items-center gap-1 text-[11px] text-on-surface-variant/60">
|
||||
<svg className="w-3 h-3 text-primary shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z" />
|
||||
</svg>
|
||||
<span>{item.location || "Main Stage"}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* TABLE VIEW: Responsive Truncated Rows + Click Row to Open Detail Modal */}
|
||||
{viewMode === "table" && filteredItems.length > 0 && (
|
||||
<div className="glass-panel rounded-3xl overflow-hidden border border-primary/20 shadow-2xl">
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full text-left text-xs lg:text-sm text-on-surface border-collapse">
|
||||
<thead className="bg-primary/10 uppercase tracking-wider text-[11px] font-bold text-primary border-b border-primary/20">
|
||||
<tr>
|
||||
<th className="py-3 px-3 lg:px-4 w-36 shrink-0">Time</th>
|
||||
<th className="py-3 px-3 w-32 shrink-0">Category</th>
|
||||
<th className="py-3 px-4 min-w-[240px]">Session Title & Description</th>
|
||||
<th className="py-3 px-3 lg:px-4 w-48 shrink-0">Speaker / Presenter</th>
|
||||
<th className="py-3 px-3 w-28 shrink-0">Location</th>
|
||||
<th className="py-3 px-2 w-16 text-center no-print shrink-0">Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-primary/10">
|
||||
{filteredItems.map((item, idx) => {
|
||||
const isBookmarked = bookmarkedIds.includes(item.id);
|
||||
const isEven = idx % 2 === 0;
|
||||
const hasSpeaker = Boolean(item.speaker && item.speaker.trim() !== "");
|
||||
|
||||
return (
|
||||
<tr
|
||||
key={item.id || idx}
|
||||
onClick={() => setSelectedModalItem(item)}
|
||||
className={`hover:bg-primary/10 transition-colors group cursor-pointer ${
|
||||
isEven ? "bg-surface/20" : "bg-transparent"
|
||||
}`}
|
||||
title="Click to view full session details"
|
||||
>
|
||||
{/* Time */}
|
||||
<td className="py-3 px-3 lg:px-4 align-top">
|
||||
<div className="font-bold text-primary whitespace-nowrap text-xs lg:text-sm">
|
||||
{getTimeDisplay(item)}
|
||||
</div>
|
||||
{timezoneMode !== "UTC" && item.time_utc && (
|
||||
<div className="text-[11px] text-on-surface-variant/50 mt-0.5 whitespace-nowrap">
|
||||
{item.time_utc} UTC
|
||||
</div>
|
||||
)}
|
||||
</td>
|
||||
|
||||
{/* Category */}
|
||||
<td className="py-3 px-3 align-top">
|
||||
<span className="inline-block px-2.5 py-0.5 rounded-full text-[11px] font-medium bg-primary/10 text-primary border border-primary/20 whitespace-nowrap">
|
||||
{item.category}
|
||||
</span>
|
||||
</td>
|
||||
|
||||
{/* Title & Description (Truncated for clean responsive table layout) */}
|
||||
<td className="py-3 px-4 align-top max-w-md">
|
||||
<h4 className="font-semibold text-on-surface text-xs lg:text-sm mb-1 leading-snug line-clamp-2 group-hover:text-primary transition-colors">
|
||||
{item.title}
|
||||
</h4>
|
||||
{item.description && (
|
||||
<p className="text-[11px] lg:text-xs text-on-surface-variant/75 leading-relaxed line-clamp-1">
|
||||
{item.description}
|
||||
</p>
|
||||
)}
|
||||
</td>
|
||||
|
||||
{/* Speaker */}
|
||||
<td className="py-3 px-3 lg:px-4 align-top">
|
||||
{hasSpeaker ? (
|
||||
<div className="flex items-center gap-2">
|
||||
{renderAvatar(item.avatar, "w-7 h-7 lg:w-8 lg:h-8")}
|
||||
<div className="min-w-0">
|
||||
<div className="font-semibold text-on-surface text-xs truncate">
|
||||
{item.speaker}
|
||||
</div>
|
||||
{item.speaker_title && (
|
||||
<div className="text-[10px] text-on-surface-variant/60 truncate">
|
||||
{item.speaker_title}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<span className="text-on-surface-variant/40 font-bold text-base">—</span>
|
||||
)}
|
||||
</td>
|
||||
|
||||
{/* Location */}
|
||||
<td className="py-3 px-3 align-top">
|
||||
<div className="flex items-center gap-1 text-xs text-on-surface-variant/80 whitespace-nowrap">
|
||||
<svg className="w-3.5 h-3.5 text-primary shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z" />
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 11a3 3 0 11-6 0 3 3 0 016 0z" />
|
||||
</svg>
|
||||
<span>{item.location || "Main Stage"}</span>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
{/* Actions: Save Star + Expand indicator */}
|
||||
<td className="py-3 px-2 align-top text-center no-print" onClick={(e) => e.stopPropagation()}>
|
||||
<div className="flex items-center justify-center gap-1">
|
||||
<button
|
||||
onClick={() => toggleBookmark(item.id)}
|
||||
className="p-1 rounded-full hover:bg-primary/20 transition-all text-base"
|
||||
title={isBookmarked ? "Remove from My Schedule" : "Add to My Schedule"}
|
||||
>
|
||||
<span className={isBookmarked ? "text-amber-400" : "text-on-surface-variant/30 hover:text-amber-400"}>
|
||||
{isBookmarked ? "★" : "☆"}
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setSelectedModalItem(item)}
|
||||
className="p-1 rounded-full text-on-surface-variant/40 hover:text-primary transition-colors"
|
||||
title="Expand details"
|
||||
>
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" strokeWidth={2}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M3.75 3.75v4.5m0-4.5h4.5m-4.5 0L9 9M20.25 3.75h-4.5m4.5 0v4.5m0-4.5L15 9m5.25 11.25v-4.5m0 4.5h-4.5m4.5 0L15 15m-11.25 5.25h4.5m-4.5 0v-4.5m0 4.5L9 15" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* SESSION DETAIL MODAL (Opens on Row Click in Table View or Detail Click) */}
|
||||
{selectedModalItem && (
|
||||
<div
|
||||
className="fixed inset-0 z-50 bg-black/75 backdrop-blur-md flex items-center justify-center p-4 no-print animate-in fade-in duration-200"
|
||||
onClick={() => setSelectedModalItem(null)}
|
||||
>
|
||||
<div
|
||||
className="glass-panel p-6 lg:p-8 rounded-3xl max-w-2xl w-full relative max-h-[90vh] overflow-y-auto border border-primary/30 shadow-2xl bg-surface/95 text-on-surface"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
{/* Modal Header Bar */}
|
||||
<div className="flex items-center justify-between gap-4 mb-4 pb-4 border-b border-primary/20">
|
||||
<span className="px-3 py-1 rounded-full text-xs font-semibold bg-primary/20 text-primary border border-primary/40">
|
||||
{selectedModalItem.category}
|
||||
</span>
|
||||
<div className="flex items-center gap-3">
|
||||
<button
|
||||
onClick={() => toggleBookmark(selectedModalItem.id)}
|
||||
className="px-3 py-1 rounded-full text-xs font-semibold uppercase tracking-wider border border-primary/40 bg-surface/60 text-primary hover:bg-primary/20 transition-colors flex items-center gap-1.5"
|
||||
>
|
||||
<span className={bookmarkedIds.includes(selectedModalItem.id) ? "text-amber-400" : "text-on-surface-variant/40"}>
|
||||
{bookmarkedIds.includes(selectedModalItem.id) ? "★ Saved" : "☆ Save to Schedule"}
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setSelectedModalItem(null)}
|
||||
className="p-1.5 rounded-full hover:bg-primary/20 text-on-surface-variant/70 hover:text-on-surface transition-colors"
|
||||
title="Close Modal (Esc)"
|
||||
>
|
||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" strokeWidth={2}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Session Time & Location Badges */}
|
||||
<div className="flex flex-wrap items-center gap-3 text-xs text-on-surface-variant/80 mb-4">
|
||||
<div className="flex items-center gap-1.5 bg-primary/10 px-3 py-1 rounded-full text-primary font-bold">
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" strokeWidth={2}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M12 6v6h4.5m4.5 0a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
|
||||
</svg>
|
||||
<span>{getTimeDisplay(selectedModalItem)}</span>
|
||||
</div>
|
||||
|
||||
{selectedModalItem.time_utc && (
|
||||
<span className="text-on-surface-variant/60 text-[11px]">
|
||||
({selectedModalItem.time_utc} UTC)
|
||||
</span>
|
||||
)}
|
||||
|
||||
<div className="flex items-center gap-1 text-on-surface-variant/80 bg-surface/80 px-3 py-1 rounded-full border border-primary/20">
|
||||
<svg className="w-3.5 h-3.5 text-primary" fill="none" stroke="currentColor" viewBox="0 0 24 24" strokeWidth={2}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z" />
|
||||
</svg>
|
||||
<span>{selectedModalItem.location || "Main Stage"}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Full Title (Untruncated) */}
|
||||
<h2 className="font-[var(--font-display-lg)] text-xl lg:text-2xl text-on-surface font-bold mb-4 leading-snug">
|
||||
{renderFormattedText(selectedModalItem.title)}
|
||||
</h2>
|
||||
|
||||
{/* Full Description (Untruncated) */}
|
||||
{selectedModalItem.description && (
|
||||
<div className="text-sm text-on-surface-variant/85 leading-relaxed bg-surface/40 p-4 rounded-2xl border border-primary/10 mb-6">
|
||||
<div className="text-xs uppercase font-bold text-primary mb-1 tracking-wider">Description</div>
|
||||
{renderFormattedText(selectedModalItem.description)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Speaker Information */}
|
||||
<div className="border-t border-primary/20 pt-4">
|
||||
<div className="text-xs uppercase font-bold text-primary mb-2 tracking-wider">Speaker / Presenter</div>
|
||||
{selectedModalItem.speaker && selectedModalItem.speaker.trim() !== "" ? (
|
||||
<div className="flex items-center gap-4 bg-primary/5 p-3.5 rounded-2xl border border-primary/15">
|
||||
{renderAvatar(selectedModalItem.avatar, "w-12 h-12")}
|
||||
<div>
|
||||
<div className="font-bold text-on-surface text-base">
|
||||
{selectedModalItem.speaker}
|
||||
</div>
|
||||
{selectedModalItem.speaker_title && (
|
||||
<div className="text-xs text-on-surface-variant/70 mt-0.5">
|
||||
{selectedModalItem.speaker_title}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="text-on-surface-variant/40 font-bold text-lg">—</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Modal Footer */}
|
||||
<div className="mt-6 pt-4 border-t border-primary/10 flex justify-end">
|
||||
<button
|
||||
onClick={() => setSelectedModalItem(null)}
|
||||
className="px-6 py-2.5 rounded-full gold-gradient-bg text-on-primary font-bold text-xs uppercase tracking-wider hover:shadow-lg transition-all"
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
+168
-21
@@ -153,142 +153,289 @@
|
||||
"description": "A strategic overview of the technical and strategic sessions.",
|
||||
"items": [
|
||||
{
|
||||
"id": 1,
|
||||
"order": "01",
|
||||
"time": "09:00 - 09:15",
|
||||
"time_ict": "09:00 - 09:15",
|
||||
"time_utc": "02:00 - 02:15",
|
||||
"category": "Breaks & Networking",
|
||||
"title": "Registration & Networking",
|
||||
"description": ""
|
||||
"description": "",
|
||||
"speaker": "",
|
||||
"speaker_title": "",
|
||||
"location": "Main Stage"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"order": "02",
|
||||
"time": "09:15 - 09:30",
|
||||
"time_ict": "09:15 - 09:30",
|
||||
"time_utc": "02:15 - 02:30",
|
||||
"category": "Ceremonies",
|
||||
"title": "Arrival of VIPs",
|
||||
"description": ""
|
||||
"description": "",
|
||||
"speaker": "",
|
||||
"speaker_title": "",
|
||||
"location": "Main Stage"
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"order": "03",
|
||||
"time": "09:30 - 09:50",
|
||||
"time_ict": "09:30 - 09:50",
|
||||
"time_utc": "02:30 - 02:50",
|
||||
"category": "Keynotes & Speeches",
|
||||
"title": "Welcome Address & Keynote Address: **National Sovereign Cloud in the Intelligent Era: Shaping Independent and Resilient Digital Infrastructure**",
|
||||
"description": "Speaker: **Prof Emeritus Dr Sureswaran Ramadass**\n*Chairman, APAC IPv6 Council*",
|
||||
"description": "Welcome address and keynote on National Sovereign Cloud in the Intelligent Era.",
|
||||
"speaker": "Prof Emeritus Dr Sureswaran Ramadass",
|
||||
"speaker_title": "Chairman, APAC IPv6 Council",
|
||||
"location": "Main Stage",
|
||||
"avatar": "/assets/img/speakers/speaker.png"
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"order": "04",
|
||||
"time": "09:50 - 10:00",
|
||||
"time_ict": "09:50 - 10:00",
|
||||
"time_utc": "02:50 - 03:00",
|
||||
"category": "Keynotes & Speeches",
|
||||
"title": "Keynote Address: **Viet Nam’s IPv6 Journey: Bridging the Gaps, Accelerating IPv6-Only, and Driving Robust and Intelligent Network Evolution**",
|
||||
"description": "Speaker: **Mr. Nguyen Truong Giang**\n*Acting Director General, VNNIC*",
|
||||
"description": "Keynote on Viet Nam's IPv6 journey and accelerating IPv6-only transition.",
|
||||
"speaker": "Mr. Nguyen Truong Giang",
|
||||
"speaker_title": "Acting Director General, VNNIC",
|
||||
"location": "Main Stage",
|
||||
"avatar": "/assets/img/speakers/placeholder.png"
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"order": "05",
|
||||
"time": "10:00 - 10:10",
|
||||
"time_ict": "10:00 - 10:10",
|
||||
"time_utc": "03:00 - 03:10",
|
||||
"category": "Ceremonies",
|
||||
"title": "Launch Ceremony **\"IPv6-First & IPv6 Enhanced National Implementation Strategy Guideline\"**",
|
||||
"description": ""
|
||||
"description": "",
|
||||
"speaker": "",
|
||||
"speaker_title": "",
|
||||
"location": "Main Stage"
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"order": "06",
|
||||
"time": "10:10 - 10:30",
|
||||
"time_ict": "10:10 - 10:30",
|
||||
"time_utc": "03:10 - 03:30",
|
||||
"category": "Keynotes & Speeches",
|
||||
"title": "Keynote Address: **AI WAN: Empowering Carriers into the New Era of Token Monetization**",
|
||||
"description": "Speaker: **Mr. Jun Cai**\n*Vice President, Data Communication Product Line, Huawei, China*",
|
||||
"description": "Keynote address on AI WAN empowering carriers in the new era of token monetization.",
|
||||
"speaker": "Mr. Jun Cai",
|
||||
"speaker_title": "Vice President, Data Communication Product Line, Huawei, China",
|
||||
"location": "Main Stage",
|
||||
"avatar": "/assets/img/speakers/placeholder.png"
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"order": "07",
|
||||
"time": "10:30 - 11:00",
|
||||
"time_ict": "10:30 - 11:00",
|
||||
"time_utc": "03:30 - 04:00",
|
||||
"category": "Breaks & Networking",
|
||||
"title": "Morning Coffee Break / Press Conference + Photos",
|
||||
"description": ""
|
||||
"description": "Morning refreshment break, press conference and group photo session.",
|
||||
"speaker": "",
|
||||
"speaker_title": "",
|
||||
"location": "Main Stage"
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"order": "08",
|
||||
"time": "11:00 - 11:20",
|
||||
"time_ict": "11:00 - 11:20",
|
||||
"time_utc": "04:00 - 04:20",
|
||||
"category": "Technical & Infrastructure",
|
||||
"title": "Carrier Perspectives: **IPv6-Driven Robust Infrastructure Fueling Enterprise Cloud and Computing Networks**",
|
||||
"description": "Speaker: **Speaker from Vietnam ISP / Data Centre Operator**",
|
||||
"description": "Carrier presentation analyzing how IPv6-driven robust infrastructure fuels enterprise cloud and computing networks.",
|
||||
"speaker": "Speaker from Vietnam ISP / Data Centre Operator",
|
||||
"speaker_title": "Technical Representative",
|
||||
"location": "Main Stage",
|
||||
"avatar": "/assets/img/speakers/placeholder.png"
|
||||
},
|
||||
{
|
||||
"id": 9,
|
||||
"order": "09",
|
||||
"time": "11:20 - 11:40",
|
||||
"time_ict": "11:20 - 11:40",
|
||||
"time_utc": "04:20 - 04:40",
|
||||
"category": "Technical & Infrastructure",
|
||||
"title": "Speaker from Vietnam ISP / Network Operator",
|
||||
"description": "Speaker: **Speaker from Vietnam ISP / Network Operator**",
|
||||
"description": "Operational perspectives and network deployment insights from Vietnam ISP / Network Operator.",
|
||||
"speaker": "Speaker from Vietnam ISP / Network Operator",
|
||||
"speaker_title": "Network Operator Representative",
|
||||
"location": "Main Stage",
|
||||
"avatar": "/assets/img/speakers/placeholder.png"
|
||||
},
|
||||
{
|
||||
"id": 10,
|
||||
"order": "10",
|
||||
"time": "11:40 - 12:00",
|
||||
"time_ict": "11:40 - 12:00",
|
||||
"time_utc": "04:40 - 05:00",
|
||||
"category": "Technical & Infrastructure",
|
||||
"title": "Building AI-Ready Network Infrastructure",
|
||||
"description": "Speaker: **Mr. Bayu Hanantasena**\n*Strategic Advisor to CEO, Indosat, Indonesia*",
|
||||
"description": "Case studies and structural designs for building AI-ready network infrastructure.",
|
||||
"speaker": "Mr. Bayu Hanantasena",
|
||||
"speaker_title": "Strategic Advisor to CEO, Indosat, Indonesia",
|
||||
"location": "Main Stage",
|
||||
"avatar": "/assets/img/speakers/placeholder.png"
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"order": "11",
|
||||
"time": "12:00 - 12:20",
|
||||
"time_ict": "12:00 - 12:20",
|
||||
"time_utc": "05:00 - 05:20",
|
||||
"category": "Technical & Infrastructure",
|
||||
"title": "ASEAN Digital Transformation Agenda: **AI and ASEAN Digital Economy Framework Agreement (ASEAN DEFA)**",
|
||||
"description": "Speaker: **Mr. Sivaram Superamanian**\n*Assistant Director, Digital Economy Division, The ASEAN Secretariat*",
|
||||
"description": "Strategic sharing on policy frameworks, digital economy, and ASEAN DEFA alignment.",
|
||||
"speaker": "Mr. Sivaram Superamanian",
|
||||
"speaker_title": "Assistant Director, Digital Economy Division, The ASEAN Secretariat",
|
||||
"location": "Main Stage",
|
||||
"avatar": "/assets/img/speakers/placeholder.png"
|
||||
},
|
||||
{
|
||||
"id": 12,
|
||||
"order": "12",
|
||||
"time": "12:20 - 12:40",
|
||||
"time_ict": "12:20 - 12:40",
|
||||
"time_utc": "05:20 - 05:40",
|
||||
"category": "Technical & Infrastructure",
|
||||
"title": "Pioneering the AI Era: **Building Secure and Smart Digital Ecosystems with IPv6 and AI**",
|
||||
"description": "Speaker: **Dr. Navaneethan C Arjuman**\n*Chair of IPv6 and 5G Working Group, APAN; Global Coordinator, IPv6 Forum Academy*",
|
||||
"description": "Technical presentation on building secure and smart digital ecosystems with IPv6 and AI.",
|
||||
"speaker": "Dr. Navaneethan C Arjuman",
|
||||
"speaker_title": "Chair of IPv6 and 5G Working Group, APAN; Global Coordinator, IPv6 Forum Academy",
|
||||
"location": "Main Stage",
|
||||
"avatar": "/assets/img/speakers/placeholder.png"
|
||||
},
|
||||
{
|
||||
"id": 13,
|
||||
"order": "13",
|
||||
"time": "12:40 - 13:30",
|
||||
"time_ict": "12:40 - 13:30",
|
||||
"time_utc": "05:40 - 06:30",
|
||||
"category": "Breaks & Networking",
|
||||
"title": "Lunch",
|
||||
"description": ""
|
||||
"description": "Lunch and networking recess for all registered delegates and speakers.",
|
||||
"speaker": "",
|
||||
"speaker_title": "",
|
||||
"location": "Main Stage"
|
||||
},
|
||||
{
|
||||
"id": 14,
|
||||
"order": "14",
|
||||
"time": "13:30 - 13:40",
|
||||
"time_ict": "13:30 - 13:40",
|
||||
"time_utc": "06:30 - 06:40",
|
||||
"category": "Keynotes & Speeches",
|
||||
"title": "Keynote Address: **Navigating the Global Digital Landscape: European-American Practices and Strategic Insights for Viet Nam’s in Intelligent Era**",
|
||||
"description": "Speaker: **Nguyen Chien Thang**\n*Director General, Institute for European and Americas Studies, VASS*",
|
||||
"description": "Keynote address on global digital landscape, European-American practices, and strategic insights for Vietnam.",
|
||||
"speaker": "Nguyen Chien Thang",
|
||||
"speaker_title": "Director General, Institute for European and Americas Studies, VASS",
|
||||
"location": "Main Stage",
|
||||
"avatar": "/assets/img/speakers/placeholder.png"
|
||||
},
|
||||
{
|
||||
"id": 15,
|
||||
"order": "15",
|
||||
"time": "13:40 - 14:30",
|
||||
"time_ict": "13:40 - 14:30",
|
||||
"time_utc": "06:40 - 07:30",
|
||||
"category": "Forums & Panels",
|
||||
"title": "Forum: **AI Regulation & Data Sovereignty**",
|
||||
"description": "Moderator: **Moderator from Council**\nPanellists: **Panellists from Gov Officials or ISPs of Indonesia, Malaysia, Vietnam**"
|
||||
"description": "A high-level panel discussion on policy frameworks governing data sovereignty and AI regulation.",
|
||||
"speaker": "Moderator from Council / Panellists from Gov Officials or ISPs",
|
||||
"speaker_title": "Moderator & Panellists",
|
||||
"location": "Main Stage"
|
||||
},
|
||||
{
|
||||
"id": 16,
|
||||
"order": "16",
|
||||
"time": "14:30 - 14:50",
|
||||
"time_ict": "14:30 - 14:50",
|
||||
"time_utc": "07:30 - 07:50",
|
||||
"category": "Technical & Infrastructure",
|
||||
"title": "On-chain Economy in the AI-Native Era: **Integrating IPv6-Enhanced and Digital Asset Ecosystems in Data Centres**",
|
||||
"description": "Speaker: **Tran Quy (SIMS)**",
|
||||
"description": "Presentation on integrating IPv6-Enhanced and digital asset ecosystems in data centres in the AI-native era.",
|
||||
"speaker": "Trần Quý (SIMS)",
|
||||
"speaker_title": "Representative (SIMS)",
|
||||
"location": "Main Stage",
|
||||
"avatar": "/assets/img/speakers/placeholder.png"
|
||||
},
|
||||
{
|
||||
"id": 17,
|
||||
"order": "17",
|
||||
"time": "14:50 - 15:15",
|
||||
"time_ict": "14:50 - 15:15",
|
||||
"time_utc": "07:50 - 08:15",
|
||||
"category": "Technical & Infrastructure",
|
||||
"title": "AI & Cloud (IPv6) - Developments in China",
|
||||
"description": "Speaker: **Mr. Liu Shuai**\n*Vice President, BII, China*",
|
||||
"description": "A technical overview of global IPv6 adoption patterns and AI & Cloud developments in China.",
|
||||
"speaker": "Mr. Liu Shuai",
|
||||
"speaker_title": "Vice President, BII, China",
|
||||
"location": "Main Stage",
|
||||
"avatar": "/assets/img/speakers/placeholder.png"
|
||||
},
|
||||
{
|
||||
"id": 18,
|
||||
"order": "18",
|
||||
"time": "15:15 - 15:30",
|
||||
"time_ict": "15:15 - 15:30",
|
||||
"time_utc": "08:15 - 08:30",
|
||||
"category": "Breaks & Networking",
|
||||
"title": "Afternoon Coffee Break",
|
||||
"description": ""
|
||||
"description": "Afternoon tea and coffee, networking with cloud partners.",
|
||||
"speaker": "",
|
||||
"speaker_title": "",
|
||||
"location": "Main Stage"
|
||||
},
|
||||
{
|
||||
"id": 19,
|
||||
"order": "19",
|
||||
"time": "15:30 - 16:00",
|
||||
"time_ict": "15:30 - 16:00",
|
||||
"time_utc": "08:30 - 09:00",
|
||||
"category": "Technical & Infrastructure",
|
||||
"title": "IEC standards between Powermeter and distributed energy resource",
|
||||
"description": "Speaker: **Dr. Masaki Umejima**\n*Convener of Energy Resource Aggregation Business ERAB, IEC System Committee; Project Professor, Keio University Global Research Institute KGRI, Japan*",
|
||||
"description": "Technical presentation on IEC standards between powermeters and distributed energy resources.",
|
||||
"speaker": "Dr. Masaki Umejima",
|
||||
"speaker_title": "Convener of Energy Resource Aggregation Business ERAB, IEC System Committee; Project Professor, Keio University KGRI",
|
||||
"location": "Main Stage",
|
||||
"avatar": "/assets/img/speakers/placeholder.png"
|
||||
},
|
||||
{
|
||||
"id": 20,
|
||||
"order": "20",
|
||||
"time": "16:00 - 17:30",
|
||||
"time_ict": "16:00 - 17:30",
|
||||
"time_utc": "09:00 - 10:30",
|
||||
"category": "Technical & Infrastructure",
|
||||
"title": "Technical sharing & demo (TBD by Local partner - SIMS)",
|
||||
"description": ""
|
||||
"description": "Hands-on showcase and technical integration demo led by local partner SIMS.",
|
||||
"speaker": "Local Partner (SIMS)",
|
||||
"speaker_title": "Technical Specialists",
|
||||
"location": "Main Stage"
|
||||
},
|
||||
{
|
||||
"id": 21,
|
||||
"order": "21",
|
||||
"time": "17:30 - 18:00",
|
||||
"time_ict": "17:30 - 18:00",
|
||||
"time_utc": "10:30 - 11:00",
|
||||
"category": "Ceremonies",
|
||||
"title": "Conference Adjourns",
|
||||
"description": ""
|
||||
"description": "Final wrap-up and event closing remarks.",
|
||||
"speaker": "",
|
||||
"speaker_title": "",
|
||||
"location": "Main Stage"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
+303
-21
@@ -136,144 +136,426 @@
|
||||
"eyebrow": "LỊCH TRÌNH",
|
||||
"title": "Chương Trình Sự Kiện",
|
||||
"description": "Tổng quan chiến lược về các phiên kỹ thuật và chiến lược.",
|
||||
"badgeText": "27 Tháng 8/2026 | Lotte Center Hà Nội, Việt Nam",
|
||||
"title": "Hội nghị Thượng đỉnh IPv6 Nâng cao cho AI & Cloud 2026",
|
||||
"subtitle": "Khám phá tiềm năng Hạ tầng thế hệ mới. Chung sức định hình kỷ nguyên kết nối số tại ASEAN cùng doanh nghiệp hàng đầu quốc tế.",
|
||||
"primaryCta": { "label": "Đăng Ký", "href": "/registration" },
|
||||
"secondaryCta": { "label": "Xem Lịch Trình", "href": "#agenda" }
|
||||
},
|
||||
"vision": {
|
||||
"eyebrow": "TẦM NHÌN & MỤC TIÊU",
|
||||
"title": "Tương Lai Số của ASEAN",
|
||||
"paragraphs": [
|
||||
"Việt Nam và khu vực ASEAN đang đứng trước bước ngoặt quan trọng trong chuyển đổi số. Việc chuyển đổi sang IPv6 là nền tảng hạ tầng thiết yếu cho sự phát triển AI và mở rộng quy mô trung tâm dữ liệu.",
|
||||
"IPv6 là xương sống cho điện toán đám mây có thể mở rộng, kết nối bảo mật và mạng sẵn sàng cho AI trên toàn khu vực."
|
||||
],
|
||||
"cards": [
|
||||
{
|
||||
"icon": "hub",
|
||||
"title": "Kết Nối",
|
||||
"description": "Giao lưu cùng lãnh đạo cấp C, nhà hoạch định chính sách và các chuyên gia kỹ thuật hàng đầu trong hệ sinh thái."
|
||||
},
|
||||
{
|
||||
"icon": "insights",
|
||||
"title": "Công Nghệ Tiên Phong",
|
||||
"description": "Phân tích chuyên sâu về khối lượng công việc AI, hạ tầng siêu quy mô và vận hành IPv6 thế hệ mới."
|
||||
},
|
||||
{
|
||||
"icon": "gavel",
|
||||
"title": "Chính Sách",
|
||||
"description": "Diễn đàn về tiêu chuẩn hạ tầng quốc gia, quản trị và hợp tác xuyên biên giới."
|
||||
}
|
||||
]
|
||||
},
|
||||
"stats": {
|
||||
"id": "stats",
|
||||
"items": [
|
||||
{ "value": "130+", "label": "Diễn Giả Quốc Tế" },
|
||||
{ "value": "14", "label": "Quốc Gia ASEAN" },
|
||||
{ "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": "Prof. Sureswaran Ramadass",
|
||||
"title": "Chủ tịch, Hội đồng IPv6 APAC",
|
||||
"photo": "/assets/img/speakers/speaker.png",
|
||||
"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."
|
||||
}
|
||||
]
|
||||
},
|
||||
"keyInvitees": {
|
||||
"id": "key-invitees",
|
||||
"eyebrow": "KHÁCH MỜI VIP",
|
||||
"title": "Khách Mời VIP / Key",
|
||||
"items": [
|
||||
{
|
||||
"no": 1,
|
||||
"name": "Prof Emeritus Dr. Sureswaran Ramadass",
|
||||
"designation": "Chairman",
|
||||
"organization": "APAC IPv6 Council",
|
||||
"category": "NPO",
|
||||
"country": "APAC"
|
||||
},
|
||||
{
|
||||
"no": 2,
|
||||
"name": "Dr Muhammad Asyraf Mohammad Naim",
|
||||
"designation": "Director for Training & Consultancy",
|
||||
"organization": "IPv6 Forum Malaysia",
|
||||
"category": "Forum",
|
||||
"country": "Malaysia"
|
||||
},
|
||||
{
|
||||
"no": 3,
|
||||
"name": "Ms. Vallikkannu Nagappan",
|
||||
"designation": "Chief Secretary",
|
||||
"organization": "APAC IPv6 Council",
|
||||
"category": "NPO",
|
||||
"country": "APAC"
|
||||
},
|
||||
{
|
||||
"no": 4,
|
||||
"name": "Mr. Hong Thang / Mr. Nguyen Truong Giang",
|
||||
"designation": "Executive",
|
||||
"organization": "VNNIC",
|
||||
"category": "Government",
|
||||
"country": "Vietnam"
|
||||
},
|
||||
{
|
||||
"no": 5,
|
||||
"name": "Mr. Bayu Hanantasena",
|
||||
"designation": "Strategic Advisor to CEO",
|
||||
"organization": "Indosat",
|
||||
"category": "ISP",
|
||||
"country": "Indonesia"
|
||||
},
|
||||
{
|
||||
"no": 6,
|
||||
"name": "Saysomvang Souvannavong",
|
||||
"designation": "Deputy Director",
|
||||
"organization": "Laos National Internet Centre",
|
||||
"category": "Government",
|
||||
"country": "Laos"
|
||||
},
|
||||
{
|
||||
"no": 7,
|
||||
"name": "Dr. Will Liu",
|
||||
"designation": "Head of Europe Datacom Standard and Industry Development",
|
||||
"organization": "Huawei",
|
||||
"category": "ISP",
|
||||
"country": "China"
|
||||
},
|
||||
{
|
||||
"no": 8,
|
||||
"name": "Dr. Gopinath Rao",
|
||||
"designation": "Chair of AI Standards Task Force",
|
||||
"organization": "MTFSB",
|
||||
"category": "Government",
|
||||
"country": "Malaysia"
|
||||
},
|
||||
{
|
||||
"no": 9,
|
||||
"name": "Dr. Navaneethan C Arjuman",
|
||||
"designation": "Global Coordinator",
|
||||
"organization": "IPv6 Forum Education Certification Logo Programme",
|
||||
"category": "NPO",
|
||||
"country": "Global"
|
||||
}
|
||||
]
|
||||
},
|
||||
"agenda": {
|
||||
"id": "agenda",
|
||||
"eyebrow": "LỊCH TRÌNH",
|
||||
"title": "Chương Trình Sự Kiện",
|
||||
"description": "Tổng quan chiến lược về các phiên kỹ thuật và chiến lược.",
|
||||
"items": [
|
||||
{
|
||||
"id": 1,
|
||||
"order": "01",
|
||||
"time": "09:00 - 09:15",
|
||||
"time_ict": "09:00 - 09:15",
|
||||
"time_utc": "02:00 - 02:15",
|
||||
"category": "Breaks & Networking",
|
||||
"title": "Đăng ký & Kết nối",
|
||||
"description": ""
|
||||
"description": "Thời gian chào đón đại biểu và hoàn tất thủ tục đăng ký tham dự.",
|
||||
"speaker": "",
|
||||
"speaker_title": "",
|
||||
"location": "Sân khấu chính"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"order": "02",
|
||||
"time": "09:15 - 09:30",
|
||||
"time_ict": "09:15 - 09:30",
|
||||
"time_utc": "02:15 - 02:30",
|
||||
"category": "Ceremonies",
|
||||
"title": "Đón tiếp đại biểu VIP",
|
||||
"description": ""
|
||||
"description": "Nghi thức đón tiếp các quan chức chính phủ và đại biểu VIP.",
|
||||
"speaker": "",
|
||||
"speaker_title": "",
|
||||
"location": "Sân khấu chính"
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"order": "03",
|
||||
"time": "09:30 - 09:50",
|
||||
"time_ict": "09:30 - 09:50",
|
||||
"time_utc": "02:30 - 02:50",
|
||||
"category": "Keynotes & Speeches",
|
||||
"title": "Phát biểu chào mừng & Bài phát biểu Keynote: **Điện toán Đám mây Chủ quyền Quốc gia trong Kỷ nguyên Thông minh: Định hình Hạ tầng Số Độc lập và Tự cường**",
|
||||
"description": "Diễn giả: **Prof Emeritus Dr Sureswaran Ramadass**\n*Chủ tịch, Hội đồng APAC IPv6*",
|
||||
"description": "Phát biểu khai mạc và bài trình bày về chiến lược điện toán đám mây chủ quyền.",
|
||||
"speaker": "Prof Emeritus Dr Sureswaran Ramadass",
|
||||
"speaker_title": "Chủ tịch, Hội đồng APAC IPv6",
|
||||
"location": "Sân khấu chính",
|
||||
"avatar": "/assets/img/speakers/speaker.png"
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"order": "04",
|
||||
"time": "09:50 - 10:00",
|
||||
"time_ict": "09:50 - 10:00",
|
||||
"time_utc": "02:50 - 03:00",
|
||||
"category": "Keynotes & Speeches",
|
||||
"title": "Bài phát biểu Keynote: **Hành trình IPv6 của Việt Nam: Thu hẹp Khoảng cách, Thúc đẩy IPv6-Only và Phát triển Mạng lưới Thông minh, Vững chắc**",
|
||||
"description": "Diễn giả: **Ông Nguyễn Trường Giang**\n*Quyền Tổng Cục trưởng, VNNIC*",
|
||||
"description": "Báo cáo chiến lược chuyển đổi IPv6 quốc gia và định hướng hạ tầng sẵn sàng cho AI.",
|
||||
"speaker": "Ông Nguyễn Trường Giang",
|
||||
"speaker_title": "Quyền Tổng Cục trưởng, VNNIC",
|
||||
"location": "Sân khấu chính",
|
||||
"avatar": "/assets/img/speakers/placeholder.png"
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"order": "05",
|
||||
"time": "10:00 - 10:10",
|
||||
"time_ict": "10:00 - 10:10",
|
||||
"time_utc": "03:00 - 03:10",
|
||||
"category": "Ceremonies",
|
||||
"title": "Lễ công bố **\"Hướng dẫn Chiến lược Triển khai Quốc gia IPv6-First & IPv6 Nâng cao\"**",
|
||||
"description": ""
|
||||
"description": "Nghi thức công bố khung hướng dẫn triển khai quốc gia IPv6-First & IPv6 Enhanced.",
|
||||
"speaker": "",
|
||||
"speaker_title": "",
|
||||
"location": "Sân khấu chính"
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"order": "06",
|
||||
"time": "10:10 - 10:30",
|
||||
"time_ict": "10:10 - 10:30",
|
||||
"time_utc": "03:10 - 03:30",
|
||||
"category": "Keynotes & Speeches",
|
||||
"title": "Bài phát biểu Keynote: **AI WAN: Trao quyền cho các Nhà mạng trong Kỷ nguyên Mới về Hóa tài sản Token**",
|
||||
"description": "Diễn giả: **Ông Jun Cai**\n*Phó Chủ tịch, Dòng Sản phẩm Truyền thông Dữ liệu, Huawei, Trung Quốc*",
|
||||
"description": "Phát biểu kỹ thuật về công nghệ AI WAN và mô hình thương mại hóa cho nhà mạng.",
|
||||
"speaker": "Ông Jun Cai",
|
||||
"speaker_title": "Phó Chủ tịch, Dòng Sản phẩm Truyền thông Dữ liệu, Huawei, Trung Quốc",
|
||||
"location": "Sân khấu chính",
|
||||
"avatar": "/assets/img/speakers/placeholder.png"
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"order": "07",
|
||||
"time": "10:30 - 11:00",
|
||||
"time_ict": "10:30 - 11:00",
|
||||
"time_utc": "03:30 - 04:00",
|
||||
"category": "Breaks & Networking",
|
||||
"title": "Nghỉ giải lao sáng / Họp báo + Chụp ảnh lưu niệm",
|
||||
"description": ""
|
||||
"description": "Nghỉ dùng trà sáng, phiên họp báo và chụp ảnh lưu niệm dành cho đại biểu.",
|
||||
"speaker": "",
|
||||
"speaker_title": "",
|
||||
"location": "Sân khấu chính"
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"order": "08",
|
||||
"time": "11:00 - 11:20",
|
||||
"time_ict": "11:00 - 11:20",
|
||||
"time_utc": "04:00 - 04:20",
|
||||
"category": "Technical & Infrastructure",
|
||||
"title": "Góc nhìn Nhà mạng: **Hạ tầng Vững chắc dựa trên IPv6 Thúc đẩy Mạng Điện toán & Đám mây Doanh nghiệp**",
|
||||
"description": "Diễn giả: **Diễn giả từ ISP / Đơn vị Vận hành Trung tâm Dữ liệu Việt Nam**",
|
||||
"description": "Báo cáo phân tích hạ tầng mạng viễn thông hỗ trợ điện toán đám mây doanh nghiệp.",
|
||||
"speaker": "Diễn giả từ ISP / Đơn vị Vận hành Trung tâm Dữ liệu Việt Nam",
|
||||
"speaker_title": "Đại diện Kỹ thuật",
|
||||
"location": "Sân khấu chính",
|
||||
"avatar": "/assets/img/speakers/placeholder.png"
|
||||
},
|
||||
{
|
||||
"id": 9,
|
||||
"order": "09",
|
||||
"time": "11:20 - 11:40",
|
||||
"time_ict": "11:20 - 11:40",
|
||||
"time_utc": "04:20 - 04:40",
|
||||
"category": "Technical & Infrastructure",
|
||||
"title": "Diễn giả từ ISP / Nhà mạng Việt Nam",
|
||||
"description": "Diễn giả: **Diễn giả từ ISP / Nhà mạng Việt Nam**",
|
||||
"description": "Chia sẻ kinh nghiệm thực tiễn triển khai hạ tầng từ các nhà mạng nội địa.",
|
||||
"speaker": "Diễn giả từ ISP / Nhà mạng Việt Nam",
|
||||
"speaker_title": "Đại diện Nhà mạng Việt Nam",
|
||||
"location": "Sân khấu chính",
|
||||
"avatar": "/assets/img/speakers/placeholder.png"
|
||||
},
|
||||
{
|
||||
"id": 10,
|
||||
"order": "10",
|
||||
"time": "11:40 - 12:00",
|
||||
"time_ict": "11:40 - 12:00",
|
||||
"time_utc": "04:40 - 05:00",
|
||||
"category": "Technical & Infrastructure",
|
||||
"title": "Xây dựng Hạ tầng Mạng Sẵn sàng cho AI",
|
||||
"description": "Diễn giả: **Ông Bayu Hanantasena**\n*Cố vấn Chiến lược CEO, Indosat, Indonesia*",
|
||||
"description": "Nghiên cứu điển hình và kiến trúc thiết kế hạ tầng mạng tối ưu cho khối lượng công việc AI.",
|
||||
"speaker": "Ông Bayu Hanantasena",
|
||||
"speaker_title": "Cố vấn Chiến lược CEO, Indosat, Indonesia",
|
||||
"location": "Sân khấu chính",
|
||||
"avatar": "/assets/img/speakers/placeholder.png"
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"order": "11",
|
||||
"time": "12:00 - 12:20",
|
||||
"time_ict": "12:00 - 12:20",
|
||||
"time_utc": "05:00 - 05:20",
|
||||
"category": "Technical & Infrastructure",
|
||||
"title": "Chương trình Chuyển đổi Số ASEAN: **AI và Hiệp định Khung Kinh tế Số ASEAN (ASEAN DEFA)**",
|
||||
"description": "Diễn giả: **Ông Sivaram Superamanian**\n*Phó Giám đốc, Bộ phận Kinh tế Số, Ban Thư ký ASEAN*",
|
||||
"description": "Phát biểu chiến lược về định hướng kinh tế số và liên kết hạ tầng khu vực ASEAN.",
|
||||
"speaker": "Ông Sivaram Superamanian",
|
||||
"speaker_title": "Phó Giám đốc, Bộ phận Kinh tế Số, Ban Thư ký ASEAN",
|
||||
"location": "Sân khấu chính",
|
||||
"avatar": "/assets/img/speakers/placeholder.png"
|
||||
},
|
||||
{
|
||||
"id": 12,
|
||||
"order": "12",
|
||||
"time": "12:20 - 12:40",
|
||||
"time_ict": "12:20 - 12:40",
|
||||
"time_utc": "05:20 - 05:40",
|
||||
"category": "Technical & Infrastructure",
|
||||
"title": "Tiên phong Kỷ nguyên AI: **Xây dựng Hệ sinh thái Số An toàn & Thông minh với IPv6 và AI**",
|
||||
"description": "Diễn giả: **Dr. Navaneethan C Arjuman**\n*Chủ tịch Nhóm làm việc IPv6 & 5G, APAN; Tổng Điều phối viên Toàn cầu, Học viện IPv6 Forum*",
|
||||
"description": "Giải pháp bảo mật và xây dựng hệ sinh thái số thông minh tích hợp AI và IPv6.",
|
||||
"speaker": "Dr. Navaneethan C Arjuman",
|
||||
"speaker_title": "Chủ tịch Nhóm làm việc IPv6 & 5G, APAN; Tổng Điều phối viên Toàn cầu, Học viện IPv6 Forum",
|
||||
"location": "Sân khấu chính",
|
||||
"avatar": "/assets/img/speakers/placeholder.png"
|
||||
},
|
||||
{
|
||||
"id": 13,
|
||||
"order": "13",
|
||||
"time": "12:40 - 13:30",
|
||||
"time_ict": "12:40 - 13:30",
|
||||
"time_utc": "05:40 - 06:30",
|
||||
"category": "Breaks & Networking",
|
||||
"title": "Bữa trưa",
|
||||
"description": ""
|
||||
"description": "Dùng bữa trưa và giao lưu kết nối dành cho các đại biểu và diễn giả.",
|
||||
"speaker": "",
|
||||
"speaker_title": "",
|
||||
"location": "Sân khấu chính"
|
||||
},
|
||||
{
|
||||
"id": 14,
|
||||
"order": "14",
|
||||
"time": "13:30 - 13:40",
|
||||
"time_ict": "13:30 - 13:40",
|
||||
"time_utc": "06:30 - 06:40",
|
||||
"category": "Keynotes & Speeches",
|
||||
"title": "Bài phát biểu Keynote: **Định hướng Tiên phong trong Cảnh quan Số Toàn cầu: Thực tiễn Âu - Mỹ & Góc nhìn Chiến lược cho Việt Nam trong Kỷ nguyên Thông minh**",
|
||||
"description": "Diễn giả: **Nguyễn Chiến Thắng**\n*Viện trưởng, Viện Nghiên cứu Châu Âu và Châu Mỹ, VASS*",
|
||||
"description": "Bài trình bày kinh nghiệm thực tiễn Âu - Mỹ và góc nhìn chiến lược cho chuyển đổi số Việt Nam.",
|
||||
"speaker": "Nguyễn Chiến Thắng",
|
||||
"speaker_title": "Viện trưởng, Viện Nghiên cứu Châu Âu và Châu Mỹ, VASS",
|
||||
"location": "Sân khấu chính",
|
||||
"avatar": "/assets/img/speakers/placeholder.png"
|
||||
},
|
||||
{
|
||||
"id": 15,
|
||||
"order": "15",
|
||||
"time": "13:40 - 14:30",
|
||||
"time_ict": "13:40 - 14:30",
|
||||
"time_utc": "06:40 - 07:30",
|
||||
"category": "Forums & Panels",
|
||||
"title": "Tọa đàm: **Quản lý AI & Chủ quyền Dữ liệu**",
|
||||
"description": "Điều phối viên: **Điều phối viên từ Hội đồng**\nDiễn giả: **Quan chức Chính phủ hoặc ISP của Indonesia, Malaysia, Việt Nam**"
|
||||
"description": "Phiên thảo luận bàn tròn về quy định quản trị AI và bảo vệ chủ quyền dữ liệu.",
|
||||
"speaker": "Điều phối viên & Diễn giả Tọa đàm",
|
||||
"speaker_title": "Đại diện Cơ quan Quản lý & Nhà mạng",
|
||||
"location": "Sân khấu chính"
|
||||
},
|
||||
{
|
||||
"id": 16,
|
||||
"order": "16",
|
||||
"time": "14:30 - 14:50",
|
||||
"time_ict": "14:30 - 14:50",
|
||||
"time_utc": "07:30 - 07:50",
|
||||
"category": "Technical & Infrastructure",
|
||||
"title": "Nền kinh tế On-chain trong Kỷ nguyên AI-Native: **Tích hợp IPv6 Nâng cao và Hệ sinh thái Tài sản Số tại các Trung tâm Dữ liệu**",
|
||||
"description": "Diễn giả: **Trần Quý (SIMS)**",
|
||||
"description": "Bài phát biểu về việc ứng dụng IPv6 nâng cao kết hợp tài sản số trong trung tâm dữ liệu.",
|
||||
"speaker": "Trần Quý (SIMS)",
|
||||
"speaker_title": "Đại diện Đối tác SIMS",
|
||||
"location": "Sân khấu chính",
|
||||
"avatar": "/assets/img/speakers/placeholder.png"
|
||||
},
|
||||
{
|
||||
"id": 17,
|
||||
"order": "17",
|
||||
"time": "14:50 - 15:15",
|
||||
"time_ict": "14:50 - 15:15",
|
||||
"time_utc": "07:50 - 08:15",
|
||||
"category": "Technical & Infrastructure",
|
||||
"title": "AI & Cloud (IPv6) - Các bước Phát triển tại Trung Quốc",
|
||||
"description": "Diễn giả: **Ông Liu Shuai**\n*Phó Chủ tịch, BII, Trung Quốc*",
|
||||
"description": "Tổng quan xu hướng phát triển và triển khai IPv6 tích hợp AI & Đám mây tại Trung Quốc.",
|
||||
"speaker": "Ông Liu Shuai",
|
||||
"speaker_title": "Phó Chủ tịch, BII, Trung Quốc",
|
||||
"location": "Sân khấu chính",
|
||||
"avatar": "/assets/img/speakers/placeholder.png"
|
||||
},
|
||||
{
|
||||
"id": 18,
|
||||
"order": "18",
|
||||
"time": "15:15 - 15:30",
|
||||
"time_ict": "15:15 - 15:30",
|
||||
"time_utc": "08:15 - 08:30",
|
||||
"category": "Breaks & Networking",
|
||||
"title": "Nghỉ giải lao chiều",
|
||||
"description": ""
|
||||
"description": "Nghỉ dùng trà chiều và thăm quan khu vực triển lãm.",
|
||||
"speaker": "",
|
||||
"speaker_title": "",
|
||||
"location": "Sân khấu chính"
|
||||
},
|
||||
{
|
||||
"id": 19,
|
||||
"order": "19",
|
||||
"time": "15:30 - 16:00",
|
||||
"time_ict": "15:30 - 16:00",
|
||||
"time_utc": "08:30 - 09:00",
|
||||
"category": "Technical & Infrastructure",
|
||||
"title": "Tiêu chuẩn IEC giữa Đồng hồ Đo điện và Tài nguyên Năng lượng Phân tán",
|
||||
"description": "Diễn giả: **Dr. Masaki Umejima**\n*Trưởng ban Doanh nghiệp Tổng hợp Tài nguyên Năng lượng ERAB, Ủy ban Hệ thống IEC; Giáo sư Dự án, Viện Nghiên cứu Toàn cầu Đại học Keio KGRI, Nhật Bản*",
|
||||
"description": "Báo cáo kỹ thuật về áp dụng tiêu chuẩn IEC trong đo lường năng lượng phân tán.",
|
||||
"speaker": "Dr. Masaki Umejima",
|
||||
"speaker_title": "Trưởng ban Doanh nghiệp Tổng hợp Tài nguyên Năng lượng ERAB, IEC System Committee; Giáo sư Dự án, Viện KGRI Đại học Keio",
|
||||
"location": "Sân khấu chính",
|
||||
"avatar": "/assets/img/speakers/placeholder.png"
|
||||
},
|
||||
{
|
||||
"id": 20,
|
||||
"order": "20",
|
||||
"time": "16:00 - 17:30",
|
||||
"time_ict": "16:00 - 17:30",
|
||||
"time_utc": "09:00 - 10:30",
|
||||
"category": "Technical & Infrastructure",
|
||||
"title": "Chia sẻ Kỹ thuật & Demo (Nội dung do đối tác địa phương quyết định - SIMS)",
|
||||
"description": ""
|
||||
"description": "Phiên trình diễn trực tiếp và thử nghiệm giải pháp kỹ thuật do SIMS trì.",
|
||||
"speaker": "Đối tác Địa phương (SIMS)",
|
||||
"speaker_title": "Đội ngũ Chuyên gia Kỹ thuật",
|
||||
"location": "Sân khấu chính"
|
||||
},
|
||||
{
|
||||
"id": 21,
|
||||
"order": "21",
|
||||
"time": "17:30 - 18:00",
|
||||
"time_ict": "17:30 - 18:00",
|
||||
"time_utc": "10:30 - 11:00",
|
||||
"category": "Ceremonies",
|
||||
"title": "Bế mạc hội thảo",
|
||||
"description": ""
|
||||
"description": "Tổng kết hội thảo, tri ân đơn vị đồng hành và phát biểu bế mạc.",
|
||||
"speaker": "",
|
||||
"speaker_title": "",
|
||||
"location": "Sân khấu chính"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user