"use client"; 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; }; type Props = { data: { id: string; eyebrow: string; title: string; description: string; items: AgendaItem[]; }; }; const CATEGORIES = [ "All Sessions", "Ceremonies", "Keynotes & Address", "Technical & Infrastructure", "Forums & Panels", "Breaks & Networking", ]; const renderFormattedText = (text: string) => { if (!text) return null; const lines = text.split("\n"); return lines.map((line, lineIndex) => { const parts = line.split(/(\*\*.*?\*\*|\*.*?\*)/g); const renderedLine = parts.map((part, partIndex) => { if (part.startsWith("**") && part.endsWith("**")) { return ( {part.slice(2, -2)} ); } if (part.startsWith("*") && part.endsWith("*")) { return ( {part.slice(1, -1)} ); } return part; }); return ( {renderedLine} ); }); }; 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([]); const [showOnlyBookmarks, setShowOnlyBookmarks] = useState(false); const [selectedModalItem, setSelectedModalItem] = useState(null); // 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 (
{avatarUrl ? ( // eslint-disable-next-line @next/next/no-img-element Speaker ) : ( )}
); }; return (
{/* Printable CSS style overlay for clean PDF / Print export */} {/* Header */}
{data.eyebrow}

{data.title}

{data.description}

{/* Main Container */}
{/* Printable Header (Visible only when printing) */}

APAC IPv6, AI & Cloud Summit 2026 - Agenda

Lotte Center, 54 Liễu Giai, Phường Giảng Võ, Hà Nội, Việt Nam

{/* Event Venue Banner with Direct Google Maps Button */}
Event Venue / Địa điểm tổ chức
Lotte Center, 54 Liễu Giai, Phường Giảng Võ, Hà Nội
📍 Google Maps Direct
{/* Control Toolbar */}
{/* Row 1: Search & My Schedule & Print & View Modes */}
{/* Search Input */}
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" />
{/* Right Action Controls */}
{/* My Schedule Filter Button */} {/* Print / Export PDF Button */} {/* Timezone Switcher */}
{(["ICT", "UTC", "Local"] as const).map((tz) => ( ))}
{/* View Mode Toggle */}
{/* Row 2: Category Filter Tags */}
{CATEGORIES.map((category) => ( ))}
{/* Results Counter Bar */}
Showing {filteredItems.length} of {(data.items || []).length} sessions {showOnlyBookmarks && " (My Schedule)"}
Event Date: Aug 4, 2026
{/* No Results Fallback */} {filteredItems.length === 0 && (

No matching sessions found.

)} {/* CARD VIEW: Wider Max Width + Reduced Gaps for Natural Text Flow */} {viewMode === "card" && filteredItems.length > 0 && (
{/* Central vertical line */}
{/* Timeline Items */}
{filteredItems.map((item, index) => { const isEven = index % 2 === 0; 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 (
{/* Timeline Dot (glowing on hover) */}
{/* Left/Right alternating grid layout */}
{isEven ? ( <> {/* Left: Card (Desktop) / Full content (Mobile) */}
{/* Top Bar inside Card: Category badge + Order + Star */}
{item.category}
{orderNum}
{/* Mobile-only time badge */}
{timeText}
{/* Session Title */}

{renderFormattedText(item.title)}

{/* Description & Speaker Info */}
{item.description && (
{renderFormattedText(item.description)}
)} {/* Speaker info with avatar */}
{hasSpeaker ? ( <> {renderAvatar(item.avatar)}
{item.speaker}
{item.speaker_title && (
{item.speaker_title}
)}
) : (
)}
{/* Location Badge */}
{item.location || "Main Stage"}
{/* Right: Time badge (Desktop-only) */}
{timeText} {timezoneMode !== "UTC" && item.time_utc && ( {item.time_utc} UTC )}
) : ( <> {/* Left: Time badge (Desktop-only) */}
{timeText} {timezoneMode !== "UTC" && item.time_utc && ( {item.time_utc} UTC )}
{/* Right: Card (Desktop) / Full content (Mobile) */}
{/* Top Bar inside Card: Category badge + Order + Star */}
{item.category}
{orderNum}
{/* Mobile-only time badge */}
{timeText}
{/* Session Title */}

{renderFormattedText(item.title)}

{/* Description & Speaker Info */}
{item.description && (
{renderFormattedText(item.description)}
)} {/* Speaker info with avatar */}
{hasSpeaker ? ( <> {renderAvatar(item.avatar)}
{item.speaker}
{item.speaker_title && (
{item.speaker_title}
)}
) : (
)}
{/* Location Badge */}
{item.location || "Main Stage"}
)}
); })}
)} {/* TABLE VIEW: Responsive Truncated Rows + Click Row to Open Detail Modal */} {viewMode === "table" && filteredItems.length > 0 && (
{filteredItems.map((item, idx) => { const isBookmarked = bookmarkedIds.includes(item.id); const isEven = idx % 2 === 0; const hasSpeaker = Boolean(item.speaker && item.speaker.trim() !== ""); return ( 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 */} {/* Category */} {/* Title & Description (Truncated for clean responsive table layout) */} {/* Speaker */} {/* Location */} {/* Actions: Save Star + Expand indicator */} ); })}
Time Category Session Title & Description Speaker / Presenter Location Action
{getTimeDisplay(item)}
{timezoneMode !== "UTC" && item.time_utc && (
{item.time_utc} UTC
)}
{item.category}

{item.title}

{item.description && (

{item.description}

)}
{hasSpeaker ? (
{renderAvatar(item.avatar, "w-7 h-7 lg:w-8 lg:h-8")}
{item.speaker}
{item.speaker_title && (
{item.speaker_title}
)}
) : ( )}
{item.location || "Main Stage"}
e.stopPropagation()}>
)}
{/* SESSION DETAIL MODAL (Opens on Row Click in Table View or Detail Click) */} {selectedModalItem && (
setSelectedModalItem(null)} >
e.stopPropagation()} > {/* Modal Header Bar */}
{selectedModalItem.category}
{/* Session Time & Location Badges */}
{getTimeDisplay(selectedModalItem)}
{selectedModalItem.time_utc && ( ({selectedModalItem.time_utc} UTC) )}
{selectedModalItem.location || "Main Stage"}
{/* Full Title (Untruncated) */}

{renderFormattedText(selectedModalItem.title)}

{/* Full Description (Untruncated) */} {selectedModalItem.description && (
Description
{renderFormattedText(selectedModalItem.description)}
)} {/* Speaker Information */}
Speaker / Presenter
{selectedModalItem.speaker && selectedModalItem.speaker.trim() !== "" ? (
{renderAvatar(selectedModalItem.avatar, "w-12 h-12")}
{selectedModalItem.speaker}
{selectedModalItem.speaker_title && (
{selectedModalItem.speaker_title}
)}
) : (
)}
{/* Modal Footer */}
)}
); }