diff --git a/README.md b/README.md
index d1e837b..515d00d 100644
--- a/README.md
+++ b/README.md
@@ -111,6 +111,10 @@ docker compose up -d
| 📶 网速评级 | 远程办公 WiFi 评级与排行 |
| 🎯 启动金目标 | 存款进度追踪,预估出发时间 |
| ✨ 今日金句 | 每日游民灵感语录 |
+| 😴 时差估算 | 时差跨度与恢复期建议 |
+| 🏡 住宿指南 | Villa / Coliving / 公寓方案对比 |
+| 🗣️ 生存短语 | 多语言常用语一键复制 |
+| 💬 反馈入口 | 浮动反馈表单 |
| 📝 博客 | 标签筛选 + 阅读进度 + 相关推荐 + 分享 |
| ❓ FAQ | 搜索 + 展开/收起全部 |
diff --git a/frontend/src/app/globals.css b/frontend/src/app/globals.css
index 7b3b242..cef70fc 100644
--- a/frontend/src/app/globals.css
+++ b/frontend/src/app/globals.css
@@ -6377,3 +6377,234 @@ img { max-width: 100%; display: block; }
grid-template-columns: 1fr;
}
}
+
+/* ===== Jet Lag ===== */
+.jetlag-card {
+ display: grid;
+ grid-template-columns: 1fr 1.1fr;
+ gap: 28px;
+ padding: 28px;
+ background: var(--bg-card);
+ border: var(--border-glass);
+ border-radius: var(--radius-xl);
+ backdrop-filter: blur(20px);
+}
+
+.jetlag-form { display: flex; flex-direction: column; gap: 16px; }
+
+.jetlag-result {
+ padding: 24px;
+ background: rgba(167, 139, 250, 0.06);
+ border: 1px solid rgba(167, 139, 250, 0.15);
+ border-radius: var(--radius-lg);
+}
+
+.jetlag-diff {
+ text-align: center;
+ margin-bottom: 20px;
+}
+
+.jetlag-emoji { font-size: 2.5rem; display: block; margin-bottom: 8px; }
+.jetlag-diff strong { font-size: 1.8rem; display: block; }
+.jetlag-diff small { color: var(--text-secondary); font-size: 0.85rem; }
+
+.jetlag-recovery {
+ text-align: center;
+ margin-bottom: 16px;
+ padding-bottom: 16px;
+ border-bottom: 1px solid rgba(255,255,255,0.06);
+}
+
+.jetlag-recovery span {
+ display: block;
+ font-size: 0.8rem;
+ color: var(--text-secondary);
+ margin-bottom: 4px;
+}
+
+.jetlag-recovery strong { font-size: 1.5rem; }
+
+.jetlag-note {
+ margin-top: 8px;
+ font-size: 0.8rem;
+ color: var(--accent-2);
+}
+
+.jetlag-tips {
+ list-style: none;
+ padding: 0;
+ margin: 0;
+}
+
+.jetlag-tips li {
+ font-size: 0.9rem;
+ color: var(--text-secondary);
+ padding: 6px 0;
+ line-height: 1.45;
+}
+
+/* ===== Housing ===== */
+.housing-cities {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 8px;
+ justify-content: center;
+ margin-bottom: 24px;
+}
+
+.housing-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 20px;
+ flex-wrap: wrap;
+ gap: 12px;
+}
+
+.housing-header h3 { font-size: 1.2rem; }
+
+.housing-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
+ gap: 18px;
+}
+
+.housing-card {
+ padding: 22px;
+ background: var(--bg-card);
+ border: var(--border-glass);
+ border-radius: var(--radius-lg);
+ backdrop-filter: blur(16px);
+ transition: transform 0.3s;
+}
+
+.housing-card:hover { transform: translateY(-4px); }
+
+.housing-card-top {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 10px;
+}
+
+.housing-emoji { font-size: 1.8rem; }
+.housing-price { font-size: 0.85rem; color: var(--accent-2); font-weight: 600; }
+.housing-card h4 { font-size: 1.05rem; margin-bottom: 6px; }
+.housing-best { font-size: 0.85rem; color: var(--text-secondary); margin-bottom: 12px; }
+
+.housing-pros { display: flex; flex-wrap: wrap; gap: 6px; }
+.housing-pro {
+ font-size: 0.75rem;
+ padding: 4px 10px;
+ background: rgba(255,255,255,0.05);
+ border-radius: 20px;
+ color: var(--text-secondary);
+}
+
+/* ===== Phrasebook ===== */
+.phrase-card {
+ padding: 28px;
+ background: var(--bg-card);
+ border: var(--border-glass);
+ border-radius: var(--radius-xl);
+ backdrop-filter: blur(20px);
+}
+
+.phrase-lang {
+ text-align: center;
+ font-weight: 600;
+ margin-bottom: 20px;
+ color: var(--accent-3);
+}
+
+.phrase-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
+ gap: 12px;
+}
+
+.phrase-item {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 4px;
+ padding: 14px 16px;
+ background: rgba(255,255,255,0.03);
+ border: 1px solid rgba(255,255,255,0.08);
+ border-radius: var(--radius-md);
+ color: var(--text-primary);
+ cursor: pointer;
+ text-align: left;
+ transition: all 0.25s;
+}
+
+.phrase-item:hover {
+ border-color: rgba(78, 205, 196, 0.4);
+ background: rgba(78, 205, 196, 0.08);
+ transform: translateY(-2px);
+}
+
+.phrase-item strong { font-size: 1rem; }
+.phrase-pinyin { font-size: 0.75rem; color: var(--accent-3); }
+.phrase-zh { font-size: 0.85rem; color: var(--text-secondary); }
+
+/* ===== Feedback Widget ===== */
+.feedback-fab {
+ position: fixed;
+ bottom: 100px;
+ right: 24px;
+ z-index: 8600;
+ width: 48px;
+ height: 48px;
+ border-radius: 50%;
+ border: var(--border-glass);
+ background: var(--bg-card);
+ backdrop-filter: blur(16px);
+ font-size: 1.3rem;
+ cursor: pointer;
+ box-shadow: var(--shadow-card);
+ transition: transform 0.25s;
+}
+
+.feedback-fab:hover { transform: scale(1.08); }
+
+.feedback-modal { max-width: 420px; }
+
+.feedback-body {
+ padding: 8px 4px 4px;
+ text-align: center;
+}
+
+.feedback-body h2 { margin: 12px 0 8px; font-size: 1.4rem; }
+.feedback-body > p { color: var(--text-secondary); font-size: 0.9rem; margin-bottom: 20px; }
+
+.feedback-types {
+ display: flex;
+ gap: 8px;
+ justify-content: center;
+ margin-bottom: 14px;
+ flex-wrap: wrap;
+}
+
+.feedback-textarea {
+ width: 100%;
+ padding: 14px;
+ margin-bottom: 14px;
+ background: rgba(255,255,255,0.04);
+ border: var(--border-glass);
+ border-radius: var(--radius-md);
+ color: var(--text-primary);
+ font-size: 0.9rem;
+ resize: vertical;
+ font-family: inherit;
+}
+
+.feedback-textarea:focus {
+ outline: none;
+ border-color: rgba(78, 205, 196, 0.5);
+}
+
+@media (max-width: 768px) {
+ .jetlag-card { grid-template-columns: 1fr; }
+ .feedback-fab { bottom: 160px; right: 16px; }
+}
diff --git a/frontend/src/components/FeedbackWidget.tsx b/frontend/src/components/FeedbackWidget.tsx
new file mode 100644
index 0000000..4a54a62
--- /dev/null
+++ b/frontend/src/components/FeedbackWidget.tsx
@@ -0,0 +1,88 @@
+"use client";
+
+import { useState } from "react";
+import { useToast } from "@/lib/toast";
+
+const TYPES = [
+ { key: "idea", label: "💡 想法" },
+ { key: "bug", label: "🐛 问题" },
+ { key: "love", label: "❤️ 喜欢" },
+];
+
+export default function FeedbackWidget() {
+ const { toast } = useToast();
+ const [open, setOpen] = useState(false);
+ const [type, setType] = useState("idea");
+ const [msg, setMsg] = useState("");
+ const [sending, setSending] = useState(false);
+
+ const submit = async (e: React.FormEvent) => {
+ e.preventDefault();
+ if (!msg.trim()) return;
+ setSending(true);
+ // Persist locally for demo; can wire to API later
+ try {
+ const key = "nomadro-feedback";
+ const prev = JSON.parse(localStorage.getItem(key) || "[]");
+ prev.push({ type, msg: msg.trim(), at: new Date().toISOString() });
+ localStorage.setItem(key, JSON.stringify(prev.slice(-50)));
+ toast("感谢反馈!我们会认真看的 🙏");
+ setMsg("");
+ setOpen(false);
+ } finally {
+ setSending(false);
+ }
+ };
+
+ return (
+ <>
+
+
+ {open && (
+
e.target === e.currentTarget && setOpen(false)}>
+
+
+
+
💬 FEEDBACK
+
给 nomadro 提建议
+
功能想法、体验问题,或只是想说喜欢
+
+
+
+
+ )}
+ >
+ );
+}
diff --git a/frontend/src/components/Footer.tsx b/frontend/src/components/Footer.tsx
index 36fee6c..ce467cb 100644
--- a/frontend/src/components/Footer.tsx
+++ b/frontend/src/components/Footer.tsx
@@ -42,17 +42,18 @@ export default function Footer() {
目的地
命运转盘
网速评级
+ 住宿指南
+ 生存短语
签证指南
- 活动日历
博客
工具
行程规划
+
时差估算
启动金目标
机票估算
费用计算器
-
时区看板
API 文档
diff --git a/frontend/src/components/Hero.tsx b/frontend/src/components/Hero.tsx
index b1cae96..ed639f5 100644
--- a/frontend/src/components/Hero.tsx
+++ b/frontend/src/components/Hero.tsx
@@ -69,7 +69,7 @@ export default function Hero({ stats }: Props) {
- 🚀 全球 {stats.total_nomads} 数字游民正在路上
+ nomadro · 全球 {stats.total_nomads} 数字游民正在路上
用一行代码
环游世界 🌏
diff --git a/frontend/src/components/HomeClient.tsx b/frontend/src/components/HomeClient.tsx
index 662a53d..275e78e 100644
--- a/frontend/src/components/HomeClient.tsx
+++ b/frontend/src/components/HomeClient.tsx
@@ -40,6 +40,10 @@ import NomadEvents from "./NomadEvents";
import WifiWorkScore from "./WifiWorkScore";
import SavingsGoal from "./SavingsGoal";
import DailyQuote from "./DailyQuote";
+import JetLagEstimator from "./JetLagEstimator";
+import HousingGuide from "./HousingGuide";
+import Phrasebook from "./Phrasebook";
+import FeedbackWidget from "./FeedbackWidget";
import KeyboardShortcuts from "./KeyboardShortcuts";
import BackToTop from "./BackToTop";
@@ -112,6 +116,7 @@ export default function HomeClient(props: Props) {
+
@@ -124,8 +129,10 @@ export default function HomeClient(props: Props) {
+
+
@@ -139,6 +146,7 @@ export default function HomeClient(props: Props) {
setMatcherOpen(true)} />
+
{compareList.length > 0 && (
diff --git a/frontend/src/components/HousingGuide.tsx b/frontend/src/components/HousingGuide.tsx
new file mode 100644
index 0000000..0580146
--- /dev/null
+++ b/frontend/src/components/HousingGuide.tsx
@@ -0,0 +1,101 @@
+"use client";
+
+import { useMemo, useState } from "react";
+import Link from "next/link";
+import type { Destination } from "@/lib/types";
+
+interface HousingOption {
+ type: string;
+ emoji: string;
+ priceRange: string;
+ pros: string[];
+ bestFor: string;
+}
+
+const HOUSING: Record
= {
+ bali: [
+ { type: "Villa / 别墅合租", emoji: "🏡", priceRange: "¥2,500–5,000/月", pros: ["私密", "泳池", "安静"], bestFor: "中长期旅居" },
+ { type: "Coliving", emoji: "🏠", priceRange: "¥3,000–6,000/月", pros: ["社区", "含网", "活动多"], bestFor: "社交型游民" },
+ { type: "酒店式公寓", emoji: "🏨", priceRange: "¥4,000–8,000/月", pros: ["灵活", "干净", "服务"], bestFor: "短期过渡" },
+ ],
+ chiangmai: [
+ { type: "服务式公寓", emoji: "🏢", priceRange: "¥1,800–3,500/月", pros: ["性价比", "市中心", "含家具"], bestFor: "预算友好" },
+ { type: "Condo", emoji: "🏙️", priceRange: "¥2,200–4,000/月", pros: ["泳池", "健身房", "安全"], bestFor: "稳定办公" },
+ { type: "民宿长住", emoji: "🛏️", priceRange: "¥1,500–2,800/月", pros: ["本地感", "便宜", "灵活"], bestFor: "探路期" },
+ ],
+ lisbon: [
+ { type: "Coliving", emoji: "🏠", priceRange: "€600–1,200/月", pros: ["社区", "家具齐全", "签证友好区"], bestFor: "新到欧洲" },
+ { type: "整租公寓", emoji: "🔑", priceRange: "€900–1,600/月", pros: ["私密", "可做饭", "长期"], bestFor: "D7 定居" },
+ { type: "房间合租", emoji: "🚪", priceRange: "€450–750/月", pros: ["省钱", "社交", "灵活"], bestFor: "预算优先" },
+ ],
+ barcelona: [
+ { type: "房间合租", emoji: "🚪", priceRange: "€500–850/月", pros: ["市中心", "社交", "灵活"], bestFor: "短期探索" },
+ { type: "整租公寓", emoji: "🔑", priceRange: "€1,100–1,800/月", pros: ["私密", "舒适", "长期"], bestFor: "Nomad Visa" },
+ { type: "Coliving", emoji: "🏠", priceRange: "€700–1,300/月", pros: ["活动", "家具", "国际社区"], bestFor: "社交办公" },
+ ],
+ mexico: [
+ { type: "Airbnb 长住", emoji: "🛏️", priceRange: "$500–1,000/月", pros: ["灵活", "家具", "厨房"], bestFor: "探路 1–2 月" },
+ { type: "Coliving", emoji: "🏠", priceRange: "$600–1,200/月", pros: ["社区", "活动", "时区友好"], bestFor: "对接北美" },
+ { type: "本地公寓", emoji: "🔑", priceRange: "$400–800/月", pros: ["便宜", "本地感", "长期"], bestFor: "沉浸生活" },
+ ],
+ tokyo: [
+ { type: "月租公寓", emoji: "🏢", priceRange: "¥8,000–15,000/月", pros: ["干净", "交通便利", "安全"], bestFor: "高效都市" },
+ { type: "胶囊 / 旅舍长住", emoji: "🛌", priceRange: "¥4,000–7,000/月", pros: ["极简", "便宜", "灵活"], bestFor: "短期出差" },
+ { type: "Share House", emoji: "🏠", priceRange: "¥5,500–9,000/月", pros: ["社交", "含家具", "省押金"], bestFor: "初到日本" },
+ ],
+};
+
+interface Props {
+ destinations: Destination[];
+}
+
+export default function HousingGuide({ destinations }: Props) {
+ const [slug, setSlug] = useState(destinations[0]?.slug || "bali");
+ const options = useMemo(() => HOUSING[slug] || HOUSING.bali, [slug]);
+ const dest = destinations.find((d) => d.slug === slug);
+
+ return (
+
+
+
+
🏡 HOUSING
+
住宿选择指南
+
按城市对比 Villa、Coliving、公寓等方案,快速找到落脚点
+
+
+
+ {destinations.map((d) => (
+
+ ))}
+
+
+
+
{dest?.emoji} {dest?.name} · 住宿方案
+ 城市详情 →
+
+
+
+ {options.map((o, i) => (
+
+
+ {o.emoji}
+ {o.priceRange}
+
+
{o.type}
+
适合:{o.bestFor}
+
+ {o.pros.map((p) => {p})}
+
+
+ ))}
+
+
+
+ );
+}
diff --git a/frontend/src/components/JetLagEstimator.tsx b/frontend/src/components/JetLagEstimator.tsx
new file mode 100644
index 0000000..4e1f0a6
--- /dev/null
+++ b/frontend/src/components/JetLagEstimator.tsx
@@ -0,0 +1,111 @@
+"use client";
+
+import { useMemo, useState } from "react";
+import type { Destination } from "@/lib/types";
+
+const TZ_OFFSET: Record = {
+ bali: 8, lisbon: 0, chiangmai: 7, mexico: -6, barcelona: 1, tokyo: 9,
+};
+
+const HOME_OFFSETS = [
+ { key: "cn", label: "🇨🇳 中国 (UTC+8)", offset: 8 },
+ { key: "us_east", label: "🇺🇸 美东 (UTC-5)", offset: -5 },
+ { key: "us_west", label: "🇺🇸 美西 (UTC-8)", offset: -8 },
+ { key: "uk", label: "🇬🇧 英国 (UTC+0)", offset: 0 },
+ { key: "eu", label: "🇪🇺 中欧 (UTC+1)", offset: 1 },
+];
+
+function tipsForDiff(absDiff: number): string[] {
+ if (absDiff <= 2) return ["时差很小,几乎无感", "抵达当天可正常作息", "保持常规睡眠即可"];
+ if (absDiff <= 5) return ["建议提前 2–3 天微调作息", "抵达后当天晒太阳帮助校准", "避免落地立刻开长会"];
+ if (absDiff <= 8) return ["东飞更伤,预留 2–3 天恢复", "第一晚尽量睡满 7 小时", "褪黑素/镁片可辅助(遵医嘱)"];
+ return ["跨半球长途,预留 4–5 天缓冲", "分段飞行比直达更易恢复", "前两天安排轻量工作即可"];
+}
+
+interface Props {
+ destinations: Destination[];
+}
+
+export default function JetLagEstimator({ destinations }: Props) {
+ const [home, setHome] = useState("cn");
+ const [slug, setSlug] = useState(destinations[0]?.slug || "bali");
+ const [direction, setDirection] = useState<"east" | "west">("east");
+
+ const result = useMemo(() => {
+ const homeOff = HOME_OFFSETS.find((h) => h.key === home)?.offset ?? 8;
+ const destOff = TZ_OFFSET[slug] ?? 8;
+ let diff = destOff - homeOff;
+ if (diff > 12) diff -= 24;
+ if (diff < -12) diff += 24;
+ const abs = Math.abs(diff);
+ const dir = diff >= 0 ? "east" : "west";
+ const recovery = Math.max(1, Math.ceil(abs / 2));
+ return { diff, abs, dir, recovery, tips: tipsForDiff(abs) };
+ }, [home, slug]);
+
+ const dest = destinations.find((d) => d.slug === slug);
+
+ return (
+
+
+
+
😴 JET LAG
+
时差恢复估算
+
算出时差跨度,提前安排恢复期与工作节奏
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {dest?.emoji || "🌍"}
+ {result.abs === 0 ? "无时差" : `${result.abs} 小时`}
+
+ {result.diff === 0
+ ? "与当前时区相同"
+ : result.diff > 0
+ ? `目的地快 ${result.abs} 小时`
+ : `目的地慢 ${result.abs} 小时`}
+
+
+
+
建议恢复期
+
{result.recovery} 天
+ {(direction === "east" || result.dir === "east") && result.abs > 3 && (
+
向东飞行通常更难适应,可多留一天缓冲
+ )}
+
+
+ {result.tips.map((t) => - 💡 {t}
)}
+
+
+
+
+
+ );
+}
diff --git a/frontend/src/components/Phrasebook.tsx b/frontend/src/components/Phrasebook.tsx
new file mode 100644
index 0000000..f62f408
--- /dev/null
+++ b/frontend/src/components/Phrasebook.tsx
@@ -0,0 +1,135 @@
+"use client";
+
+import { useMemo, useState } from "react";
+import { useToast } from "@/lib/toast";
+import type { Destination } from "@/lib/types";
+
+interface Phrase {
+ local: string;
+ pinyin?: string;
+ zh: string;
+}
+
+const PHRASES: Record = {
+ bali: {
+ lang: "印尼语", flag: "🇮🇩",
+ items: [
+ { local: "Halo", zh: "你好" },
+ { local: "Terima kasih", zh: "谢谢" },
+ { local: "Berapa harganya?", zh: "这个多少钱?" },
+ { local: "WiFi-nya bagus?", zh: "网速好吗?" },
+ { local: "Saya mau sewa sebulan", zh: "我想租一个月" },
+ { local: "Di mana toilet?", zh: "洗手间在哪?" },
+ ],
+ },
+ chiangmai: {
+ lang: "泰语", flag: "🇹🇭",
+ items: [
+ { local: "สวัสดี", pinyin: "Sawatdee", zh: "你好" },
+ { local: "ขอบคุณ", pinyin: "Khop khun", zh: "谢谢" },
+ { local: "เท่าไหร่", pinyin: "Tao rai", zh: "多少钱?" },
+ { local: "WiFi แรงไหม", pinyin: "WiFi raeng mai", zh: "网快吗?" },
+ { local: "อร่อยมาก", pinyin: "Aroi mak", zh: "很好吃" },
+ { local: "ไม่เผ็ด", pinyin: "Mai phet", zh: "不要辣" },
+ ],
+ },
+ lisbon: {
+ lang: "葡萄牙语", flag: "🇵🇹",
+ items: [
+ { local: "Olá", zh: "你好" },
+ { local: "Obrigado / Obrigada", zh: "谢谢(男/女)" },
+ { local: "Quanto custa?", zh: "多少钱?" },
+ { local: "O WiFi é bom?", zh: "网好吗?" },
+ { local: "Uma cerveja, por favor", zh: "请来一杯啤酒" },
+ { local: "Onde fica o coworking?", zh: "联合办公在哪?" },
+ ],
+ },
+ barcelona: {
+ lang: "西班牙语", flag: "🇪🇸",
+ items: [
+ { local: "Hola", zh: "你好" },
+ { local: "Gracias", zh: "谢谢" },
+ { local: "¿Cuánto cuesta?", zh: "多少钱?" },
+ { local: "¿Hay buen WiFi?", zh: "有好网吗?" },
+ { local: "La cuenta, por favor", zh: "买单" },
+ { local: "¿Dónde está el metro?", zh: "地铁在哪?" },
+ ],
+ },
+ mexico: {
+ lang: "西班牙语", flag: "🇲🇽",
+ items: [
+ { local: "Qué onda", zh: "嘿(口语)" },
+ { local: "Gracias", zh: "谢谢" },
+ { local: "¿Cuánto cuesta?", zh: "多少钱?" },
+ { local: "Sin chile, por favor", zh: "不要辣" },
+ { local: "¿Tienen WiFi?", zh: "有 WiFi 吗?" },
+ { local: "La cuenta, por favor", zh: "买单" },
+ ],
+ },
+ tokyo: {
+ lang: "日语", flag: "🇯🇵",
+ items: [
+ { local: "こんにちは", pinyin: "Konnichiwa", zh: "你好" },
+ { local: "ありがとう", pinyin: "Arigatou", zh: "谢谢" },
+ { local: "いくらですか", pinyin: "Ikura desu ka", zh: "多少钱?" },
+ { local: "Wi-Fiはありますか", pinyin: "Wi-Fi wa arimasu ka", zh: "有 WiFi 吗?" },
+ { local: "おいしい", pinyin: "Oishii", zh: "好吃" },
+ { local: "すみません", pinyin: "Sumimasen", zh: "不好意思 / 借过" },
+ ],
+ },
+};
+
+interface Props {
+ destinations: Destination[];
+}
+
+export default function Phrasebook({ destinations }: Props) {
+ const { toast } = useToast();
+ const [slug, setSlug] = useState(destinations[0]?.slug || "bali");
+ const book = useMemo(() => PHRASES[slug] || PHRASES.bali, [slug]);
+ const dest = destinations.find((d) => d.slug === slug);
+
+ const copy = async (text: string) => {
+ await navigator.clipboard.writeText(text);
+ toast(`已复制:${text}`);
+ };
+
+ return (
+
+
+
+
🗣️ PHRASES
+
游民生存短语本
+
点一下复制常用语,落地就能开口
+
+
+
+ {destinations.map((d) => (
+
+ ))}
+
+
+
+
+ {book.flag} {dest?.name} · {book.lang}
+
+
+ {book.items.map((p) => (
+
+ ))}
+
+
+
+
+ );
+}
diff --git a/frontend/src/components/SectionNav.tsx b/frontend/src/components/SectionNav.tsx
index ea8d030..765a1b5 100644
--- a/frontend/src/components/SectionNav.tsx
+++ b/frontend/src/components/SectionNav.tsx
@@ -7,6 +7,7 @@ const SECTIONS = [
{ id: "destinations", emoji: "🌍", label: "目的地" },
{ id: "spin", emoji: "🎲", label: "转盘" },
{ id: "timezone", emoji: "🕐", label: "时区" },
+ { id: "jetlag", emoji: "😴", label: "时差" },
{ id: "trip", emoji: "🗓️", label: "行程" },
{ id: "calculator", emoji: "🧮", label: "计算器" },
{ id: "cost-compare", emoji: "💰", label: "省钱" },
@@ -16,8 +17,10 @@ const SECTIONS = [
{ id: "lifestyle", emoji: "💻", label: "生活" },
{ id: "visa", emoji: "📋", label: "签证" },
{ id: "coworking", emoji: "💻", label: "办公" },
+ { id: "housing", emoji: "🏡", label: "住宿" },
{ id: "season", emoji: "🌤️", label: "季节" },
{ id: "packing", emoji: "🧳", label: "行李" },
+ { id: "phrases", emoji: "🗣️", label: "短语" },
{ id: "events", emoji: "📅", label: "活动" },
{ id: "blog", emoji: "📝", label: "博客" },
];