"use client"; import { useEffect, useState } from "react"; import Image from "next/image"; import Link from "next/link"; import SessionCoverImage from "./SessionCoverImage"; import AddressLink from "./AddressLink"; import { getSessionsWithDates, ACTIVITY_TYPES, SESSION_TAGS, RECENT_JOINERS } from "@/config/home.config"; interface JoinStats { count: number; joiners: { nickname: string; avatar?: string; xiaohongshu_url?: string }[]; } const MOCK_JOINERS = RECENT_JOINERS.slice(0, 3); type SessionTag = keyof typeof SESSION_TAGS; interface SessionItem { id: string; type: "skill-exchange" | "side-hustle" | "loft" | "light-chat"; title: string; date: string; time: string; place: string; city: string; coverImage?: string; tags?: readonly SessionTag[]; } export default function SessionCard({ session, index, }: { session: SessionItem; index: number; }) { const [stats, setStats] = useState(null); useEffect(() => { fetch("/api/join/stats") .then((r) => r.json()) .then((d) => d?.stats?.[session.id] && setStats(d.stats[session.id])) .catch(() => {}); }, [session.id]); return (
{session.coverImage ? ( ) : (
{session.type === "skill-exchange" ? "🔄" : "☕"}
)}
{session.tags?.map((tag) => ( {SESSION_TAGS[tag]} ))} {index === 0 && ( 主推 )}
{ACTIVITY_TYPES[session.type]} · {session.city}

{session.title}

{session.date} {session.time} ·

报名这场 →
{(stats?.joiners?.length ? stats.joiners : MOCK_JOINERS).slice(0, 5).map((j, i) => { const nickname = "name" in j ? (j as { name: string }).name : (j as { nickname: string }).nickname; const avatar = "avatar" in j ? (j as { avatar: string }).avatar : (j as { avatar?: string }).avatar; const xhUrl = (j as { xiaohongshu_url?: string }).xiaohongshu_url; const showAvatar = avatar && avatar.length > 0; return ( { if (xhUrl) { e.preventDefault(); e.stopPropagation(); window.open(xhUrl, "_blank", "noopener,noreferrer"); } else { e.preventDefault(); } }} > {showAvatar ? ( ) : ( {nickname.slice(0, 1) || "?"} )} ); })}
{stats?.count ? `${stats.count} 人已报名` : "有人报名中"}
); }