nomadweb/frontend/src/components/WeekendIdeas.tsx
eric bdf0516197 Fix dating match gate and expand seed data for usable demos.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-04 06:18:25 -05:00

148 lines
6.3 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";
import ToolCityExits from "@/components/ToolCityExits";
interface Idea {
emoji: string;
title: string;
vibe: string;
cost: string;
}
const IDEAS: Record<string, Idea[]> = {
chiangmai: [
{ emoji: "⛰️", title: "徒步 Doi Suthep", vibe: "清晨清爽,下山喝咖啡续命", cost: "¥40–80" },
{ emoji: "🛁", title: "热水泉泡汤", vibe: "周末放松肩颈,适合写周报前", cost: "¥30–60" },
{ emoji: "🍜", title: "周日步行街夜市", vibe: "人多但氛围足,试小吃就好", cost: "¥50–100" },
{ emoji: "🧘", title: "乌布式瑜伽体验课", vibe: "本地工作室很多试听课", cost: "¥60–120" },
],
bali: [
{ emoji: "🏄", title: "库塔/Canggu 冲浪课", vibe: "初学友好,防晒务必做好", cost: "¥200–350" },
{ emoji: "🛕", title: "乌布神庙半日游", vibe: "避开正午,早晚光线最好", cost: "¥80–150" },
{ emoji: "🌾", title: "骑车穿梭梯田", vibe: "租摩托前先熟悉靠左行", cost: "¥100–180" },
{ emoji: "🌅", title: "乌鲁瓦图日落", vibe: "建议提前占位,注意崖边安全", cost: "¥50–90" },
],
lisbon: [
{ emoji: "🚋", title: "28 路电车+阿尔法玛", vibe: "早上去人少,适合拍照", cost: "¥20–40" },
{ emoji: "🏖️", title: "去 Cascais 吹海风", vibe: "火车 40 分钟,周末轻松局", cost: "¥60–120" },
{ emoji: "🎵", title: "Fado 小酒馆之夜", vibe: "预约座位,点一杯就够", cost: "¥150–250" },
{ emoji: "🏛️", title: "贝伦甜挞巡礼", vibe: "经典但值得,搭配公园散步", cost: "¥40–70" },
],
barcelona: [
{ emoji: "🏖️", title: "Barceloneta 海边慢跑", vibe: "清晨最舒服,避开午后暴晒", cost: "免费" },
{ emoji: "🎨", title: "公园桂尔半日", vibe: "提前买票,错峰体验更好", cost: "¥120–180" },
{ emoji: "🍷", title: "Gracia 小酒馆串店", vibe: "当地人周末去处", cost: "¥150–280" },
{ emoji: "⛰️", title: "Montjuïc 看城景", vibe: "日落时光线绝佳", cost: "¥30–60" },
],
mexico: [
{ emoji: "🌮", title: "街区 taco 巡礼", vibe: "选人气摊,循序渐进试辣", cost: "¥60–120" },
{ emoji: "🖼️", title: "人类学博物馆", vibe: "室内避暑+高质量展览", cost: "¥50–80" },
{ emoji: "🛶", title: "Xochimilco 彩色船", vibe: "周末热闹,建议结伴", cost: "¥150–250" },
{ emoji: "☕", title: "Roma/Condesa 咖啡漫步", vibe: "树荫街道适合边走边聊", cost: "¥40–90" },
],
tokyo: [
{ emoji: "♨️", title: "钱汤/温泉放松", vibe: "带走疲劳,注意礼仪", cost: "¥50–120" },
{ emoji: "🎌", title: "下北泽古着街", vibe: "周末市集氛围强", cost: "看心情" },
{ emoji: "🗻", title: "高尾山一日轻徒步", vibe: "电车直达,难度友好", cost: "¥80–150" },
{ emoji: "🍣", title: "筑地外围早餐", vibe: "早起有回报", cost: "¥100–200" },
],
};
interface Props {
destinations: Destination[];
}
export default function WeekendIdeas({ destinations }: Props) {
const [slug, setSlug] = useState(destinations[0]?.slug || "chiangmai");
const [energy, setEnergy] = useState<"chill" | "active" | "social">("chill");
const [picked, setPicked] = useState(0);
const list = useMemo(() => {
const all = IDEAS[slug] || IDEAS.chiangmai;
if (energy === "active") return [...all].sort((a, b) => b.title.length - a.title.length);
if (energy === "social") return [...all].reverse();
return all;
}, [slug, energy]);
const dest = destinations.find((d) => d.slug === slug);
const current = list[picked % list.length];
const shuffle = () => setPicked((p) => (p + 1) % list.length);
return (
<section className="section weekend-section" id="weekend">
<div className="container">
<div className="section-header reveal">
<span className="section-tag">🎉 WEEKEND</span>
<h2>周末活动灵感</h2>
<p>旅居不是只有工位,周末也要会玩会恢复</p>
</div>
<div className="weekend-card reveal">
<div className="weekend-controls">
<label>
<span>城市</span>
<select
value={slug}
onChange={(e) => {
setSlug(e.target.value);
setPicked(0);
}}
>
{destinations.map((d) => (
<option key={d.slug} value={d.slug}>{d.emoji} {d.name}</option>
))}
</select>
</label>
<div className="weekend-energy">
{([
["chill", "😌 轻松恢复"],
["active", "🏃 动起来"],
["social", "🍻 社交局"],
] as const).map(([k, label]) => (
<button
key={k}
type="button"
className={`weekend-chip${energy === k ? " active" : ""}`}
onClick={() => { setEnergy(k); setPicked(0); }}
>
{label}
</button>
))}
</div>
</div>
<div className="weekend-hero">
<span className="weekend-emoji">{current.emoji}</span>
<div>
<small>{dest?.emoji} {dest?.name} · 周末提案</small>
<h3>{current.title}</h3>
<p>{current.vibe}</p>
<em>预算约 {current.cost}</em>
</div>
</div>
<div className="weekend-actions">
<button type="button" className="btn btn-primary" onClick={shuffle}>换一个灵感 🎲</button>
<div className="weekend-list">
{list.map((idea, i) => (
<button
key={idea.title}
type="button"
className={`weekend-mini${i === picked % list.length ? " on" : ""}`}
onClick={() => setPicked(i)}
>
{idea.emoji} {idea.title}
</button>
))}
</div>
{dest && <ToolCityExits dest={dest} />}
</div>
</div>
</div>
</section>
);
}