nomadweb/frontend/src/components/SimGuide.tsx
eric 341fcdd15e Add insurance, expenses, SIM guide, about page; unify trip storage
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-29 00:09:10 -05:00

96 lines
3.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client";
import { useMemo, useState } from "react";
import type { Destination } from "@/lib/types";
interface SimPlan {
name: string;
emoji: string;
data: string;
price: string;
tip: string;
}
const SIMS: Record<string, SimPlan[]> = {
bali: [
{ name: "Telkomsel", emoji: "📶", data: "50GB / 30天", price: "约 ¥60", tip: "覆盖最好,机场可办" },
{ name: "XL / Axis", emoji: "📱", data: "40GB / 30天", price: "约 ¥40", tip: "性价比高,市区够用" },
{ name: "eSIM Airalo", emoji: "✈️", data: "按需充值", price: "约 $8 起", tip: "落地即用,免换卡" },
],
chiangmai: [
{ name: "AIS", emoji: "📶", data: "无限量套餐", price: "约 ¥80/月", tip: "网速稳,游民常用" },
{ name: "TrueMove", emoji: "📱", data: "大流量包", price: "约 ¥60/月", tip: "7-Eleven 随处可买" },
{ name: "eSIM Holafly", emoji: "✈️", data: "无限流量", price: "约 $19/周", tip: "适合短期跳岛" },
],
lisbon: [
{ name: "Vodafone PT", emoji: "📶", data: "20–50GB", price: "€15–25", tip: "覆盖全国,店内可办" },
{ name: "NOS / MEO", emoji: "📱", data: "Prepaid", price: "€10–20", tip: "预付费灵活" },
{ name: "eSIM Airalo", emoji: "✈️", data: "按需", price: "$5 起", tip: "抵达前先装好" },
],
barcelona: [
{ name: "Orange / Movistar", emoji: "📶", data: "20–50GB", price: "€10–20", tip: "申根区漫游友好" },
{ name: "Digi", emoji: "📱", data: "大流量便宜", price: "€5–15", tip: "预算优先之选" },
{ name: "eSIM", emoji: "✈️", data: "按需", price: "$6 起", tip: "免排队办卡" },
],
mexico: [
{ name: "Telcel", emoji: "📶", data: "Amigo 套餐", price: "$10–20", tip: "覆盖最广" },
{ name: "AT&T MX", emoji: "📱", data: "预付费", price: "$10–18", tip: "市中心信号好" },
{ name: "eSIM", emoji: "✈️", data: "按需", price: "$8 起", tip: "对接北美出差方便" },
],
tokyo: [
{ name: "IIJmio / SoftBank", emoji: "📶", data: "10–20GB", price: "¥2,000–4,000", tip: "便利店可买" },
{ name: "Sakura Mobile", emoji: "📱", data: "游民友好", price: "¥3,000+", tip: "支持英文客服" },
{ name: "eSIM Airalo", emoji: "✈️", data: "按需", price: "$6 起", tip: "机场落地即连" },
],
};
interface Props {
destinations: Destination[];
}
export default function SimGuide({ destinations }: Props) {
const [slug, setSlug] = useState(destinations[0]?.slug || "bali");
const plans = useMemo(() => SIMS[slug] || SIMS.bali, [slug]);
const dest = destinations.find((d) => d.slug === slug);
return (
<section className="section sim-section" id="sim">
<div className="container">
<div className="section-header reveal">
<span className="section-tag">📱 SIM / eSIM</span>
<h2>当地上网方案</h2>
<p>落地第一件事:连上网。实体卡与 eSIM 怎么选</p>
</div>
<div className="housing-cities reveal">
{destinations.map((d) => (
<button
key={d.slug}
className={`season-city-btn${slug === d.slug ? " active" : ""}`}
onClick={() => setSlug(d.slug)}
>
{d.emoji} {d.name}
</button>
))}
</div>
<h3 className="sim-city-title reveal">{dest?.emoji} {dest?.name} 推荐套餐</h3>
<div className="sim-grid">
{plans.map((p, i) => (
<div key={p.name} className="sim-card reveal" style={{ transitionDelay: `${i * 0.08}s` }}>
<span className="sim-emoji">{p.emoji}</span>
<h4>{p.name}</h4>
<div className="sim-meta">
<span>📦 {p.data}</span>
<span className="sim-price">{p.price}</span>
</div>
<p>{p.tip}</p>
</div>
))}
</div>
</div>
</section>
);
}