"use client"; import { useEffect, useMemo, useState } from "react"; import type { Destination } from "@/lib/types"; import ToolCityExits from "@/components/ToolCityExits"; import { meetupCityHref } from "@/lib/meetupLinks"; import Link from "next/link"; interface Task { id: string; emoji: string; label: string; day: number; } const TASKS: Task[] = [ { id: "sim", emoji: "📱", label: "办本地 SIM / 激活 eSIM", day: 1 }, { id: "cash", emoji: "💵", label: "换少量现金 + 测试银行卡", day: 1 }, { id: "wifi", emoji: "📶", label: "测网速,确认可视频会议", day: 1 }, { id: "grocery", emoji: "🛒", label: "采购日用品与饮水", day: 1 }, { id: "maps", emoji: "🗺️", label: "标记医院 / 警局 / 超市", day: 2 }, { id: "cowork", emoji: "💻", label: "探访 1–2 个联合办公", day: 2 }, { id: "cafe", emoji: "☕", label: "找到常去的工作咖啡馆", day: 3 }, { id: "meetup", emoji: "🤝", label: "加本地游民群 / 报名 Meetup", day: 3 }, { id: "housing", emoji: "🏡", label: "确认长租或续住决策", day: 5 }, { id: "routine", emoji: "🕐", label: "固定作息与深度工作时段", day: 7 }, { id: "backup", emoji: "🔐", label: "备份证件照片到云盘", day: 2 }, { id: "insurance", emoji: "🏥", label: "确认保险客服与紧急电话", day: 1 }, ]; const STORAGE_PREFIX = "nomadro-arrival-"; interface Props { destinations: Destination[]; } export default function ArrivalChecklist({ destinations }: Props) { const [slug, setSlug] = useState(destinations[0]?.slug || "bali"); const [checked, setChecked] = useState>({}); const [ready, setReady] = useState(false); const dest = destinations.find((d) => d.slug === slug); useEffect(() => { try { const raw = localStorage.getItem(STORAGE_PREFIX + slug); setChecked(raw ? JSON.parse(raw) : {}); } catch { setChecked({}); } setReady(true); }, [slug]); useEffect(() => { if (!ready) return; localStorage.setItem(STORAGE_PREFIX + slug, JSON.stringify(checked)); }, [checked, slug, ready]); const done = Object.values(checked).filter(Boolean).length; const pct = Math.round((done / TASKS.length) * 100); const byDay = useMemo(() => { const map: Record = {}; TASKS.forEach((t) => { if (!map[t.day]) map[t.day] = []; map[t.day].push(t); }); return Object.entries(map).sort((a, b) => Number(a[0]) - Number(b[0])); }, []); const toggle = (id: string) => setChecked((p) => ({ ...p, [id]: !p[id] })); return (
🛬 ARRIVAL

落地第一周清单

从落地到进入工作节奏,按天勾选不慌乱

{destinations.map((d) => ( ))}

{dest?.emoji} {dest?.name} · 到站进度

{done}/{TASKS.length} 完成 · {pct}%

{byDay.map(([day, tasks]) => (

Day {day}

    {tasks.map((t) => (
  • ))}
))}
{dest && }
); }