Files
2026-03-09 05:07:59 -05:00

110 lines
4.5 KiB
TypeScript

"use client";
import { useState, useMemo } from "react";
import { useLocale, useTranslation } from "@/i18n/navigation";
import { useTheme } from "@/app/context/ThemeContext";
import { MembersMap } from "@/app/lib/charts";
import { MAP_CITIES } from "@/app/data/map-cities";
export default function MembersMapPage() {
const { t } = useTranslation("membersMap");
const locale = useLocale();
const { theme } = useTheme();
const [hoveredCity, setHoveredCity] = useState<typeof MAP_CITIES[0] | null>(null);
const totalMembers = useMemo(
() => MAP_CITIES.reduce((sum, c) => sum + c.nomadsNow, 0),
[]
);
const sortedCities = useMemo(
() => [...MAP_CITIES].sort((a, b) => b.nomadsNow - a.nomadsNow),
[]
);
return (
<div className="min-h-screen bg-[#fafafa] dark:bg-gray-950">
<main className="max-w-[1200px] mx-auto px-4 sm:px-6 py-8 sm:py-12">
<header className="mb-8">
<h1 className="text-2xl sm:text-3xl font-bold text-gray-900 dark:text-gray-100 flex items-center gap-2">
<span className="w-1 h-8 bg-[#ff4d4f] rounded-full" />
{t("title")}
</h1>
<p className="text-gray-500 dark:text-gray-400 mt-1 text-sm sm:text-base">
{t("subtitle")}
</p>
</header>
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6 lg:gap-8">
<div className="lg:col-span-2 bg-white dark:bg-gray-900 rounded-2xl shadow-sm border border-gray-100 dark:border-gray-800 overflow-hidden p-4 sm:p-6">
<div className="rounded-xl overflow-hidden bg-gray-50 dark:bg-gray-800/50">
<MembersMap
data={MAP_CITIES}
locale={locale}
dark={theme === "dark"}
className="min-h-[400px]"
onCityHover={setHoveredCity}
/>
</div>
{hoveredCity && (
<div className="mt-4 p-3 bg-gray-50 dark:bg-gray-800 rounded-lg">
<p className="text-sm font-medium text-gray-900 dark:text-gray-100">
{locale === "zh" ? hoveredCity.name : hoveredCity.nameEn} {hoveredCity.emoji}
</p>
<p className="text-xs text-gray-500 dark:text-gray-400 mt-0.5">
{hoveredCity.nomadsNow} {t("nomadsInCity")}
</p>
</div>
)}
</div>
<div className="space-y-6">
<div className="bg-white dark:bg-gray-900 rounded-2xl shadow-sm border border-gray-100 dark:border-gray-800 p-6">
<h3 className="text-sm font-bold text-gray-400 dark:text-gray-500 uppercase tracking-wider mb-2">
{t("totalMembers")}
</h3>
<p className="text-3xl font-bold text-gray-900 dark:text-gray-100">
{totalMembers.toLocaleString()}+
</p>
</div>
<div className="bg-white dark:bg-gray-900 rounded-2xl shadow-sm border border-gray-100 dark:border-gray-800 p-6">
<h3 className="text-sm font-bold text-gray-400 dark:text-gray-500 uppercase tracking-wider mb-4">
{t("cities")}
</h3>
<ul className="space-y-2 max-h-[280px] overflow-y-auto">
{sortedCities.map((city) => (
<li
key={city.id}
className={`flex items-center justify-between py-2 px-3 rounded-lg cursor-pointer transition-colors ${
hoveredCity?.id === city.id
? "bg-[#ff4d4f]/10 text-[#ff4d4f]"
: "hover:bg-gray-50 dark:hover:bg-gray-800"
}`}
onMouseEnter={() => setHoveredCity(city)}
onMouseLeave={() => setHoveredCity(null)}
>
<span className="flex items-center gap-2">
<span>{city.emoji}</span>
<span className="font-medium text-gray-900 dark:text-gray-100">
{locale === "zh" ? city.name : city.nameEn}
</span>
</span>
<span className="text-sm text-gray-500 dark:text-gray-400">
{city.nomadsNow}
</span>
</li>
))}
</ul>
</div>
</div>
</div>
<p className="text-xs text-gray-400 dark:text-gray-500 mt-6 text-center">
{t("demoHint")}
</p>
</main>
</div>
);
}