diff --git a/frontend/src/components/Footer.tsx b/frontend/src/components/Footer.tsx
index cb4aa0c..3fe3b91 100644
--- a/frontend/src/components/Footer.tsx
+++ b/frontend/src/components/Footer.tsx
@@ -44,6 +44,7 @@ export default function Footer() {
{t.footer.explore}
{t.footer.destinations}
+
{t.nav.memberMap}
{t.footer.nextStop}
{!isHome &&
{t.footer.plan}}
{!isHome &&
{t.footer.compare}}
diff --git a/frontend/src/components/GigsClient.tsx b/frontend/src/components/GigsClient.tsx
index 23becea..d7a1dc2 100644
--- a/frontend/src/components/GigsClient.tsx
+++ b/frontend/src/components/GigsClient.tsx
@@ -69,8 +69,8 @@ export default function GigsClient({ gigs }: { gigs: GigItem[] }) {
return next;
});
toast(t.gigs.applyOk, "success", {
- href: "/digital",
- label: t.nav.digital,
+ href: "/notifications",
+ label: t.nav.notifications,
});
} catch {
toast(t.gigs.applyFail, "error");
diff --git a/frontend/src/components/Navbar.tsx b/frontend/src/components/Navbar.tsx
index aa48839..95f1097 100644
--- a/frontend/src/components/Navbar.tsx
+++ b/frontend/src/components/Navbar.tsx
@@ -49,6 +49,7 @@ export default function Navbar() {
const explore: NavItem[] = [
{ href: "/#destinations", label: t.nav.destinations, section: "destinations" },
{ href: "/#map", label: t.nav.map, section: "map" },
+ { href: "/map", label: t.nav.memberMap, section: "member-map" },
{ href: "/next-stop", label: t.nav.nextStop, section: "next-stop" },
...(!isHome
? [
@@ -147,6 +148,7 @@ export default function Navbar() {
if (l.href === "/book") return pathname.startsWith("/book");
if (l.href === "/digital") return pathname.startsWith("/digital");
if (l.href === "/dating") return pathname.startsWith("/dating");
+ if (l.href === "/map") return pathname === "/map";
if (l.href === "/meetups") return pathname.startsWith("/meetups");
if (l.href === "/community") return pathname.startsWith("/community");
if (l.href === "/gigs") return pathname.startsWith("/gigs");
diff --git a/frontend/src/components/VisaWizard.tsx b/frontend/src/components/VisaWizard.tsx
index b413ab3..39828da 100644
--- a/frontend/src/components/VisaWizard.tsx
+++ b/frontend/src/components/VisaWizard.tsx
@@ -1,51 +1,100 @@
"use client";
import { useMemo, useState } from "react";
+import Link from "next/link";
import type { Visa } from "@/lib/types";
+import { useI18n } from "@/lib/i18n";
interface Props {
visas: Visa[];
}
-const INCOME_OPTIONS = [
- { key: "low", label: "💰 低于 ¥8,000/月", max: 8000 },
- { key: "mid", label: "💼 ¥8,000 – ¥20,000/月", max: 20000 },
- { key: "high", label: "🚀 ¥20,000 – ¥50,000/月", max: 50000 },
- { key: "premium", label: "👑 高于 ¥50,000/月", max: Infinity },
-];
+const INCOME_KEYS = [
+ { key: "low", max: 8000 },
+ { key: "mid", max: 20000 },
+ { key: "high", max: 50000 },
+ { key: "premium", max: Infinity },
+] as const;
-const DURATION_OPTIONS = [
- { key: "short", label: "⏱️ 1-3 个月", months: 3 },
- { key: "medium", label: "📅 3-12 个月", months: 12 },
- { key: "long", label: "🏠 1 年以上", months: 24 },
-];
+const DURATION_KEYS = [
+ { key: "short", months: 3 },
+ { key: "medium", months: 12 },
+ { key: "long", months: 24 },
+] as const;
-const REGION_OPTIONS = [
- { key: "any", label: "🌏 不限" },
- { key: "europe", label: "🏰 欧洲" },
- { key: "sea", label: "🌴 东南亚" },
- { key: "latam", label: "🌮 拉美" },
-];
+const REGION_KEYS = ["any", "europe", "sea", "latam"] as const;
const VISA_INCOME: Record
= {
- "1": 6000, "2": 17000, "3": 2000, "4": 47000, "5": 18000, "6": 28000,
+ "1": 6000,
+ "2": 17000,
+ "3": 2000,
+ "4": 47000,
+ "5": 18000,
+ "6": 28000,
+ "7": 2000,
+ "8": 35000,
+ "9": 5000,
+ "10": 30000,
+ "11": 25000,
+ "12": 15000,
};
const VISA_REGION: Record = {
- "1": "europe", "2": "europe", "3": "sea", "4": "sea", "5": "latam", "6": "europe",
+ "1": "europe",
+ "2": "europe",
+ "3": "sea",
+ "4": "sea",
+ "5": "latam",
+ "6": "europe",
+ "7": "europe",
+ "8": "any",
+ "9": "latam",
+ "10": "sea",
+ "11": "europe",
+ "12": "sea",
+};
+
+const VISA_DEST: Record = {
+ "1": "lisbon",
+ "2": "barcelona",
+ "3": "bali",
+ "4": "chiangmai",
+ "5": "mexico",
+ "7": "tbilisi",
+ "8": "dubai",
+ "9": "medellin",
+ "10": "seoul",
+ "11": "berlin",
+ "12": "chiangmai",
};
export default function VisaWizard({ visas }: Props) {
+ const { t } = useI18n();
+ const w = t.visaWizard;
const [step, setStep] = useState(0);
const [income, setIncome] = useState("");
const [duration, setDuration] = useState("");
const [region, setRegion] = useState("any");
const [showResults, setShowResults] = useState(false);
+ const incomeOptions = INCOME_KEYS.map((o) => ({
+ ...o,
+ label: w[`income_${o.key}` as keyof typeof w] as string,
+ }));
+ const durationOptions = DURATION_KEYS.map((o) => ({
+ ...o,
+ label: w[`duration_${o.key}` as keyof typeof w] as string,
+ }));
+ const regionOptions = REGION_KEYS.map((key) => ({
+ key,
+ label: w[`region_${key}` as keyof typeof w] as string,
+ }));
+ const stepLabels = [w.stepIncome, w.stepDuration, w.stepRegion];
+
const results = useMemo(() => {
if (!income || !duration) return [];
- const incomeOpt = INCOME_OPTIONS.find((o) => o.key === income)!;
- const durOpt = DURATION_OPTIONS.find((o) => o.key === duration)!;
+ const incomeOpt = INCOME_KEYS.find((o) => o.key === income)!;
+ const durOpt = DURATION_KEYS.find((o) => o.key === duration)!;
return visas
.map((v) => {
@@ -91,16 +140,19 @@ export default function VisaWizard({ visas }: Props) {
return (
-
🧙 VISA WIZARD
-
签证智能匹配
-
回答 3 个问题,找到最适合你的远程工作签证
+
{w.tag}
+
{w.title}
+
{w.subtitle}
{!showResults ? (
- {["月收入", "停留时长", "目的地"].map((label, i) => (
-
+ {stepLabels.map((label, i) => (
+
{i < step ? "✓" : i + 1}
{label}
@@ -109,8 +161,13 @@ export default function VisaWizard({ visas }: Props) {
{step === 0 && (
- {INCOME_OPTIONS.map((o) => (
-