import React, { useState, useEffect, useRef } from 'react'; import { Plane, Train, Utensils, Camera, MapPin, Hotel, Coffee, Sun, CloudRain, Navigation, X, Ticket, Info, Map as MapIcon, ChevronRight } from 'lucide-react'; // --- 模擬資料結構 (13天行程範例) --- // 實際應用中,這裡可以替換為從 Firebase 或 JSON 檔案讀取的資料 const TRIP_DATA = Array.from({ length: 13 }, (_, i) => { const dayNum = i + 1; const isFlightDay = dayNum === 1 || dayNum === 13; // 產生每天的假資料 return { id: dayNum, date: `2025-10-${10 + i}`, location: dayNum === 1 ? "台北 -> 東京" : dayNum === 13 ? "東京 -> 台北" : "東京, 日本", weather: dayNum % 3 === 0 ? "rainy" : "sunny", temp: dayNum % 3 === 0 ? "18°C" : "24°C", events: [ { time: "09:00", type: isFlightDay ? "flight" : "breakfast", title: isFlightDay ? (dayNum === 1 ? "出發:桃園機場 (TPE)" : "返程:成田機場 (NRT)") : "飯店早餐", location: isFlightDay ? "Terminal 2" : "Hotel Metropolitan", desc: isFlightDay ? "航班 BR198 | 座位 42A" : "享受豐盛的日式自助餐", coords: "Tokyo", // 用於 Google Maps 搜尋 detail: isFlightDay ? { type: "ticket", label: "電子機票", info: "訂位代號: 6XQ29Z\n登機門: C4\n登機時間: 08:30" } : null }, { time: "11:30", type: "sightseeing", title: isFlightDay ? "抵達東京" : `探索景點 - Day ${dayNum}`, location: isFlightDay ? "Narita Airport" : "淺草寺 (Senso-ji)", desc: "雷門拍照、仲見世通逛街吃人形燒。", coords: "Senso-ji Tokyo", detail: { type: "info", label: "景點介紹", info: "東京都內最古老的寺院。參拜前記得先在手水舍洗手。" } }, { time: "13:00", type: "food", title: "午餐時間", location: "今半 壽喜燒", desc: "預約編號: #9921,百年老店頂級黑毛和牛。", coords: "Asakusa Imahan", detail: { type: "info", label: "預約資訊", info: "已預付訂金 5000日圓。\n請出示 Email 確認信。" } }, { time: "15:30", type: "transport", title: "移動", location: "東京地鐵", desc: "搭乘銀座線前往澀谷 (G19 -> G01)", coords: "Shibuya Station", }, { time: "19:00", type: "hotel", title: "飯店 Check-in", location: "Shibuya Stream Excel Hotel", desc: "絕佳視野,直通地鐵站。", coords: "Shibuya Stream Excel Hotel Tokyu", detail: { type: "hotel", label: "訂房憑證", info: "訂單編號: 12345678\n房型: 標準雙人房\n含早餐: 是" } } ] }; }); // --- 元件:圖示選擇器 --- const getIcon = (type) => { switch (type) { case 'flight': return ; case 'train': case 'transport': return ; case 'food': case 'breakfast': return ; case 'sightseeing': return ; case 'hotel': return ; case 'coffee': return ; default: return ; } }; // --- 元件:天氣 Widget (Hero Section) --- const WeatherWidget = ({ weather, temp, location, date }) => { return (
{/* 背景裝飾 */}

{date}

{location}

{weather === 'sunny' ? ( ) : ( )} {temp}
{/* 搜尋欄位模擬 (視覺用) */}

今日旅程進度

Day {location.includes("->") ? "Movement" : "Exploring"}

13 Days Trip
); }; // --- 元件:詳細資訊 Modal --- const DetailModal = ({ isOpen, onClose, data }) => { if (!isOpen || !data) return null; return (
{/* 背景遮罩 */}
{/* 彈窗內容 */}
{data.type === 'ticket' ? : }

{data.label}

              {data.info}
            
{data.type === 'ticket' && ( )}
); }; // --- 主應用程式 --- export default function App() { const [currentDay, setCurrentDay] = useState(1); const [activeModal, setActiveModal] = useState(null); // 儲存當前打開的 modal 資料 const scrollRef = useRef(null); // 取得當前日期的資料 const currentData = TRIP_DATA.find(d => d.id === currentDay); // 開啟 Google Maps const openMap = (query) => { const encodedQuery = encodeURIComponent(query); window.open(`https://www.google.com/maps/search/?api=1&query=${encodedQuery}`, '_blank'); }; // 自動捲動到底部導航的選中項目 useEffect(() => { if (scrollRef.current) { const selectedBtn = scrollRef.current.children[currentDay - 1]; if (selectedBtn) { selectedBtn.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'center' }); } } }, [currentDay]); return (
{/* 1. Hero Section */} {/* 2. Timeline Content */}
{currentData.events.map((event, index) => { const isLast = index === currentData.events.length - 1; return (
{/* 左側:時間軸與線條 */}
{event.time}
{getIcon(event.type)}
{/* 連接線 */} {!isLast && (
)}
{/* 右側:活動卡片 */}

{event.title}

{event.location}

{event.desc}

{/* 關鍵字按鈕 (如果有 detail) */} {event.detail && ( )}
); })} {/* End of Day Marker */}
End of Day {currentDay}
{/* 3. Bottom Navigation (Sticky) */}
{/* Action Bar */}
Day {currentDay} / 13
{/* Scrollable Days */}
{TRIP_DATA.map((day) => ( ))}
{/* Modal Render */} setActiveModal(null)} data={activeModal} /> {/* Custom Styles for hidden scrollbar */}
); }