From 09fc7c5017f476909ac6f1451ca2c3c09798bfd3 Mon Sep 17 00:00:00 2001 From: nafsonig Date: Fri, 19 Jun 2026 14:51:51 +0000 Subject: [PATCH] added the component --- .../waitlist/components/ReferralProgram.tsx | 197 ++++++++++++++++++ 1 file changed, 197 insertions(+) create mode 100644 src/app/(landing)/waitlist/components/ReferralProgram.tsx diff --git a/src/app/(landing)/waitlist/components/ReferralProgram.tsx b/src/app/(landing)/waitlist/components/ReferralProgram.tsx new file mode 100644 index 0000000..c8d4d1b --- /dev/null +++ b/src/app/(landing)/waitlist/components/ReferralProgram.tsx @@ -0,0 +1,197 @@ +'use client'; + +import React, { useEffect, useMemo, useState } from 'react'; +import { useAppDispatch, useAppSelector } from '@/store/hooks'; +import ReferralLink from '@/components/referral/ReferralLink'; +import QRCodeDisplay from '@/components/QRCodeDisplay'; +import { fetchDashboard, trackShare } from '@/store/referralSlice'; + +type LeaderboardItem = { + displayName: string; + referrals: number; + rank: number; +}; + +function maskName(name: string) { + if (!name) return 'Anonymous'; + // show first char and a short suffix, keep anonymous feel + return `${name.charAt(0)}***#${(Math.abs(hashCode(name)) % 9000) + 1000}`; +} + +function hashCode(str: string) { + let h = 0; + for (let i = 0; i < str.length; i++) h = (h << 5) - h + str.charCodeAt(i); + return h | 0; +} + +export default function ReferralProgram({ userId, isPremium }: { userId: string; isPremium?: boolean }) { + const dispatch = useAppDispatch(); + const { data } = useAppSelector((s) => s.referral); + const [leaderboard, setLeaderboard] = useState([]); + const [streamError, setStreamError] = useState(null); + const [notifiedMilestones, setNotifiedMilestones] = useState>({}); + + useEffect(() => { + if (!userId) return; + dispatch(fetchDashboard(userId)); + + let mounted = true; + + // fetch leaderboard snapshot + fetch('/api/referrals/leaderboard') + .then((r) => r.ok ? r.json() : Promise.reject('Failed')) + .then((json) => { + if (!mounted) return; + setLeaderboard(json.leaderboard || []); + }) + .catch(() => { + // ignore — leaderboard optional + }); + + // try real-time updates via EventSource + let es: EventSource | null = null; + try { + es = new EventSource('/api/referrals/leaderboard/stream'); + es.onmessage = (ev) => { + try { + const parsed = JSON.parse(ev.data); + if (Array.isArray(parsed)) setLeaderboard(parsed as LeaderboardItem[]); + } catch (err) { + // ignore + } + }; + es.onerror = () => setStreamError('Leaderboard realtime connection failed'); + } catch (err) { + // EventSource not available or not supported + } + + return () => { + mounted = false; + if (es) es.close(); + }; + }, [dispatch, userId]); + + // computed values + const referralUrl = useMemo(() => { + if (!data?.referralCode) return null; + if (typeof window === 'undefined') return `/?ref=${data.referralCode}`; + return `${window.location.origin}/?ref=${data.referralCode}`; + }, [data?.referralCode]); + + const advancementPerReferral = isPremium ? 2 : 1; + const totalAdvancement = (data?.successfulReferrals || 0) * advancementPerReferral; + + // send milestone notification request when hit 5 referrals (free premium month) + useEffect(() => { + if (!userId || !data) return; + const count = data.successfulReferrals || 0; + const milestones = [5]; + milestones.forEach((m) => { + if (count >= m && !notifiedMilestones[m]) { + // request backend to send email/notification for milestone + fetch(`/api/users/${userId}/notify-milestone`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ milestone: m, referrals: count }), + }).catch(() => {}); + setNotifiedMilestones((p) => ({ ...p, [m]: true })); + } + }); + }, [data, userId, notifiedMilestones]); + + const handleShare = async (channel: string) => { + if (!data?.referralCode) return; + dispatch(trackShare({ userId, referralCode: data.referralCode, shareChannel: channel as any } as any)); + }; + + if (!data) return null; + + return ( +
+

Invite friends — move up the waitlist

+ +
+
+ {data.referralCode && } + +
+

Referrals: {data.successfulReferrals}

+

Waitlist advancement: {totalAdvancement} positions

+

Per-referral credit: {advancementPerReferral}

+

Free premium month at 5 referrals.

+
+ + {/* sharing buttons */} +
+

Share

+
+ + + + + + + + + +
+
+
+ +
+
+

Leaderboard

+ {streamError &&

{streamError}

} +
    + {leaderboard.slice(0, 10).map((it) => ( +
  1. +
    + #{it.rank} + {maskName(it.displayName)} +
    +
    {it.referrals}
    +
  2. + ))} +
+ +
+ Top referrers receive exclusive rewards. Your progress updates in near real-time. +
+
+ +
+

Exclusive milestones

+
    +
  • Move up 1 position per referral (2 if Premium).
  • +
  • Free Premium month at 5 successful referrals.
  • +
  • Top referrers get early access and exclusive badges.
  • +
+
+
+
+ + {/* QR preview */} + {referralUrl && ( +
+ handleShare('qr')} /> +
Scan to join using your referral link.
+
+ )} +
+ ); +}