"use client"; import { useEffect, useMemo, useState } from "react"; import Link from "next/link"; import type { Destination } from "@/lib/types"; import { useI18n } from "@/lib/i18n"; import { useToast } from "@/lib/toast"; import { mergeDestinationsIntoTrip } from "@/lib/tripActions"; import { meetupCityHref } from "@/lib/meetupLinks"; const STORAGE_KEY = "nomadro-runway"; interface Props { destinations: Destination[]; } interface Saved { savings: number; monthly: number; income: number; slug: string; } export default function RunwayCalculator({ destinations }: Props) { const { t } = useI18n(); const { toast } = useToast(); const [savings, setSavings] = useState(80000); const [monthly, setMonthly] = useState(4500); const [income, setIncome] = useState(0); const [slug, setSlug] = useState(""); const [ready, setReady] = useState(false); const [pulse, setPulse] = useState(false); useEffect(() => { try { const raw = localStorage.getItem(STORAGE_KEY); if (raw) { const s = JSON.parse(raw) as Saved; setSavings(s.savings ?? 80000); setMonthly(s.monthly ?? 4500); setIncome(s.income ?? 0); setSlug(s.slug ?? ""); } } catch { /* ignore */ } setReady(true); }, []); useEffect(() => { if (!ready) return; localStorage.setItem(STORAGE_KEY, JSON.stringify({ savings, monthly, income, slug })); }, [savings, monthly, income, slug, ready]); const selected = destinations.find((d) => d.slug === slug); const netBurn = Math.max(monthly - income, 0); const months = useMemo(() => { if (netBurn <= 0) return income > 0 ? 999 : 0; return Math.floor((savings / netBurn) * 10) / 10; }, [savings, netBurn, income]); const pct = Math.min((months / 18) * 100, 100); const level = months >= 12 ? { label: "从容远航", color: "#4ECDC4", tip: "可以大胆规划长线旅居" } : months >= 6 ? { label: "稳健起步", color: "#FFE66D", tip: "适合 1–2 站深度停留" } : months >= 3 ? { label: "短途试水", color: "#F472B6", tip: "建议选低成本城市先验证" } : { label: "需要蓄力", color: "#FF6B6B", tip: "先攒启动金或提高远程收入" }; const applyDest = (s: string) => { const d = destinations.find((x) => x.slug === s); if (!d) return; setSlug(s); setMonthly(d.cost); setPulse(true); setTimeout(() => setPulse(false), 500); }; const addToPlan = () => { if (!selected) return; const stay = months >= 999 ? 6 : Math.max(1, Math.min(24, Math.round(months) || 1)); const { added } = mergeDestinationsIntoTrip([selected], stay); toast( added ? `${selected.emoji} ${selected.name} ${t.plan.addedToPlan}` : t.common.alreadyInPlan, added ? "success" : "info", { href: "/plan", label: t.strip.openPlan } ); }; const milestones = [ { m: 3, label: "试水期" }, { m: 6, label: "半程" }, { m: 12, label: "一年" }, { m: 18, label: "长线" }, ]; return (
🚀 RUNWAY

旅居跑道计算器

用存款与月开销,算出你还能自由飞多久

{months >= 999 ? "∞" : months} {months >= 999 ? "可持续" : "个月"}
{level.label}

{level.tip}

一键套用目的地月均成本
{destinations.slice(0, 6).map((d) => ( ))}
净月消耗 ¥{netBurn.toLocaleString()}
预计支撑 {months >= 999 ? "收入覆盖开销" : `${months} 个月`}
{milestones.map((m) => (
= m.m ? " on" : ""}`}> {m.label}
))}
{selected && (
{t.common.detail} {t.common.cityMeetups}
)}
); }