From cc05691e9a4fa9b7ece96808c804514f63dfdaab Mon Sep 17 00:00:00 2001 From: eric Date: Fri, 4 Sep 2026 06:51:44 -0500 Subject: [PATCH] Route meetup RSVP to live/calendar and add Connect stats on profile. Co-authored-by: Cursor --- backend/app/routers/api.py | 28 +++++++++++++--- frontend/src/app/changelog/page.tsx | 8 +++++ frontend/src/app/globals.css | 28 ++++++++++++++++ frontend/src/app/profile/page.tsx | 39 ++++++++++++++++++++++- frontend/src/components/MeetupsClient.tsx | 5 +-- frontend/src/lib/i18n/dictionaries.ts | 12 +++++++ 6 files changed, 113 insertions(+), 7 deletions(-) diff --git a/backend/app/routers/api.py b/backend/app/routers/api.py index 7582804..ea13582 100644 --- a/backend/app/routers/api.py +++ b/backend/app/routers/api.py @@ -275,15 +275,35 @@ async def rsvp_meetup(body: MeetupRsvpRequest, authorization: str | None = Heade raise HTTPException(400, "活动已满员") meetup = community_store.get_meetup(body.meetup_id) or {} + title = meetup.get("title") or "活动" + mode = meetup.get("mode") or "" organizer_id = meetup.get("organizer_id") or "" - if organizer_id and organizer_id != user_id: - from app.services.notify import notify_user + from app.services.notify import notify_user + if organizer_id and organizer_id != user_id: notify_user( organizer_id, "新的活动报名", - f"{user.get('name', '游民')} 报名了「{meetup.get('title', '活动')}」", - f"/meetups", + f"{user.get('name', '游民')} 报名了「{title}」", + "/meetups", + "meetup", + ) + + # Confirm to attendee with next-step deep link (live room or profile calendar) + if mode in ("online", "hybrid"): + notify_user( + user_id, + "报名成功", + f"你已报名「{title}」,活动开始时可进入直播间", + f"/meetups/{body.meetup_id}/live", + "meetup", + ) + else: + notify_user( + user_id, + "报名成功", + f"你已报名「{title}」,可在个人中心导出日历", + "/profile", "meetup", ) return {"success": True, "message": res["message"], "rsvp_count": res["rsvp_count"]} diff --git a/frontend/src/app/changelog/page.tsx b/frontend/src/app/changelog/page.tsx index 4184f50..d40cfa1 100644 --- a/frontend/src/app/changelog/page.tsx +++ b/frontend/src/app/changelog/page.tsx @@ -8,6 +8,14 @@ export const metadata: Metadata = { }; const LOGS = [ + { + date: "2026-09-04", + tag: "活动报名 · Connect 数据", + items: [ + "报名成功 toast:线上/混合直达直播间,线下引导导出日历;报名确认写入通知箱", + "个人中心新增 Connect 数据条(喜欢我的 / 互相喜欢 / 私信 / 已报名),可点进对应页面", + ], + }, { date: "2026-09-04", tag: "匹配资料 · 私信头", diff --git a/frontend/src/app/globals.css b/frontend/src/app/globals.css index 9b52b0c..8110f25 100644 --- a/frontend/src/app/globals.css +++ b/frontend/src/app/globals.css @@ -2767,6 +2767,22 @@ img { max-width: 100%; display: block; } margin-bottom: 48px; } +.profile-connect-block { + margin-bottom: 48px; +} + +.profile-connect-title { + font-size: 0.95rem; + font-weight: 600; + margin: 0 0 12px; + color: var(--text-muted); + letter-spacing: 0.02em; +} + +.profile-connect-stats { + margin-bottom: 0; +} + .profile-stat-card { background: var(--bg-card); border: var(--border-glass); @@ -2776,6 +2792,18 @@ img { max-width: 100%; display: block; } backdrop-filter: blur(20px); } +a.profile-stat-link { + display: block; + color: inherit; + text-decoration: none; + transition: border-color 0.2s ease, transform 0.2s ease; +} + +a.profile-stat-link:hover { + border-color: var(--accent, #e8a87c); + transform: translateY(-2px); +} + .profile-stat-card strong { display: block; font-size: 1.75rem; diff --git a/frontend/src/app/profile/page.tsx b/frontend/src/app/profile/page.tsx index ff03657..3dd040f 100644 --- a/frontend/src/app/profile/page.tsx +++ b/frontend/src/app/profile/page.tsx @@ -60,6 +60,7 @@ export default function ProfilePage() { const [stats, setStats] = useState(null); const [favoriteDests, setFavoriteDests] = useState([]); const [rsvps, setRsvps] = useState([]); + const [connect, setConnect] = useState({ likes: 0, matches: 0, chats: 0 }); const [trip, setTrip] = useState([]); const [meta, setMeta] = useState(null); const [loading, setLoading] = useState(true); @@ -109,11 +110,21 @@ export default function ProfilePage() { const idSet = new Set(mine.ids || []); return all.filter((m) => idSet.has(m.id)).slice(0, 6); }), - ]).then(([s, f, v, myMeetups]) => { + Promise.all([ + api.getMatchLikesReceived(token).catch(() => []), + api.getMutualMatches(token).catch(() => []), + api.getConversations(token).catch(() => []), + ]).then(([likes, matches, chats]) => ({ + likes: likes.length, + matches: matches.length, + chats: chats.length, + })), + ]).then(([s, f, v, myMeetups, connectStats]) => { setStats(s); setFavoriteDests(f); setVip(Boolean(v.vip)); setRsvps(myMeetups); + setConnect(connectStats); }).catch(() => {}).finally(() => setLoading(false)); refreshPlan(); @@ -220,6 +231,32 @@ export default function ProfilePage() { )} +
+

{t.profile.connectStrip}

+
+ + 💕 + {connect.likes} + {t.profile.statLikes} + + + ✨ + {connect.matches} + {t.profile.statMatches} + + + ✉️ + {connect.chats} + {t.profile.statChats} + + + 🎉 + {rsvps.length} + {t.profile.statRsvps} + +
+
+
diff --git a/frontend/src/components/MeetupsClient.tsx b/frontend/src/components/MeetupsClient.tsx index 2e8a0e5..108481f 100644 --- a/frontend/src/components/MeetupsClient.tsx +++ b/frontend/src/components/MeetupsClient.tsx @@ -128,9 +128,10 @@ export default function MeetupsClient({ meetups }: Props) { try { const res = await api.rsvpMeetup(meetup.id, undefined, token); setRsvpIds(new Set(rsvpIds).add(meetup.id)); + const live = meetup.mode === "online" || meetup.mode === "hybrid"; toast(res.message || t.meetups.rsvpOk, "success", { - href: "/dating", - label: t.meetups.goDating, + href: live ? meetupEventHref(meetup) : "/profile", + label: live ? t.meetups.liveBtn : t.meetups.rsvpCalCta, }); void loadPeers(meetup.id); } catch { diff --git a/frontend/src/lib/i18n/dictionaries.ts b/frontend/src/lib/i18n/dictionaries.ts index 923cbd9..aaf3237 100644 --- a/frontend/src/lib/i18n/dictionaries.ts +++ b/frontend/src/lib/i18n/dictionaries.ts @@ -685,6 +685,7 @@ export const zh = { tripEvents: "行程城市活动", meetPeers: "同城可能认识的人", goDating: "去匹配", + rsvpCalCta: "导出日历", viewProfile: "查看资料", }, community: { @@ -1074,6 +1075,11 @@ export const zh = { statCities: "计划城市", statReady: "出发就绪", statBadges: "成就徽章", + connectStrip: "Connect 动态", + statLikes: "喜欢我的", + statMatches: "互相喜欢", + statChats: "私信会话", + statRsvps: "已报名活动", openPlan: "打开计划中心", tripEmpty: "还没有规划行程", goPlan: "去规划 →", @@ -1953,6 +1959,7 @@ export const en: { [K in keyof typeof zh]: typeof zh[K] extends string ? string tripEvents: "Events near your trip", meetPeers: "People you may meet nearby", goDating: "Go match", + rsvpCalCta: "Export calendar", viewProfile: "View profile", }, community: { @@ -2294,6 +2301,11 @@ export const en: { [K in keyof typeof zh]: typeof zh[K] extends string ? string statCities: "Plan cities", statReady: "Readiness", statBadges: "Badges", + connectStrip: "Connect pulse", + statLikes: "Liked you", + statMatches: "Matches", + statChats: "Chats", + statRsvps: "RSVPs", openPlan: "Open plan hub", tripEmpty: "No trip planned yet", goPlan: "Start planning →",