diff --git a/src/features/trending/components/Invitation/CollectRewardsCard.tsx b/src/features/trending/components/Invitation/CollectRewardsCard.tsx index abb1c5de0..e33ea8b50 100644 --- a/src/features/trending/components/Invitation/CollectRewardsCard.tsx +++ b/src/features/trending/components/Invitation/CollectRewardsCard.tsx @@ -2,7 +2,7 @@ import React, { useState, useEffect, useCallback, useMemo, } from 'react'; import { Alert, AlertDescription } from '@/components/ui/alert'; -import { AlertCircle } from 'lucide-react'; +import { AlertCircle, Sparkles, TrendingUp } from 'lucide-react'; import { useAeSdk } from '@/hooks/useAeSdk'; import { getAffiliationTreasury } from '@/libs/affiliation'; import { Decimal } from '@/libs/decimal'; @@ -65,6 +65,45 @@ function normalizeAddressToAettosEntries(value: unknown): Array<[string, string] return []; } +const REWARD_PARTICLES = [ + { + id: 'p0', + left: '20%', + top: '30%', + delay: '0s', + }, + { + id: 'p1', + left: '35%', + top: '70%', + delay: '0.3s', + }, + { + id: 'p2', + left: '50%', + top: '30%', + delay: '0.6s', + }, + { + id: 'p3', + left: '65%', + top: '70%', + delay: '0.9s', + }, + { + id: 'p4', + left: '80%', + top: '30%', + delay: '1.2s', + }, + { + id: 'p5', + left: '95%', + top: '70%', + delay: '1.5s', + }, +] as const; + const CollectRewardsCard = () => { const { sdk, activeAccount } = useAeSdk(); @@ -259,45 +298,104 @@ const CollectRewardsCard = () => { } if (!thresholdReached) return 'Not eligible yet'; if (accumulatedRewardsAe.lte(Decimal.ZERO)) return 'No rewards yet'; - return 'Collect rewards'; + return ( + + + Collect rewards + + ); }, [collectingReward, thresholdReached, accumulatedRewardsAe]); + const collectRewardsShellClassName = [ + 'bg-[var(--glass-bg)] border border-[var(--glass-border)] backdrop-blur-[20px]', + 'rounded-[20px] p-6 md:p-8 lg:p-10 relative overflow-hidden min-h-0', + 'transition-all duration-300 hover:border-white/15', + 'hover:shadow-[0_8px_32px_rgba(0,0,0,0.3),0_2px_8px_rgba(0,0,0,0.2)] hover:-translate-y-1', + ].join(' '); + return ( -
+
+ {/* Animated background particles */} + {isEligibleForRewards && ( +
+ {REWARD_PARTICLES.map((p) => ( +
+ ))} +
+ )} + {/* Header */} -
+
- πŸ’° + {isEligibleForRewards ? ( + + ) : ( + 'πŸ’°' + )} +
+
+

+ Collect Your Rewards +

+

+ {isEligibleForRewards + ? "πŸŽ‰ You're eligible! Withdraw your rewards anytime." + : 'Invite friends and earn rewards when they buy tokens'} +

-

- Collect your rewards -

{/* Content */} -
+
{/* Description - Left Side */} -
-

- Rewards accumulate as your direct invitees participate in token sales. You can withdraw once - {' '} - - {MIN_INVITEES} +

+
+

+ Once + {' '} + + {MIN_INVITEES} + {' '} + direct invitees + {' '} - direct invitees - - {' '} - have each - spent at least - - {MIN_SPENT_AE} + have each spent at least {' '} - AE - - {' '} - (cumulative). -

-

+ + {MIN_SPENT_AE} + {' '} + AE + + {' '} + (cumulative) in token sales, you can withdraw accumulated rewards. + {' '} + Rewards build as your invitees participate. +

+
+
+
+ +

Rewards reflect on-chain activity from your invitees

+
+
+ βœ“ +

Withdraw after you meet the threshold and have a positive balance

+
+
+ βœ“ +

Track per-invitee progress toward the spending minimum

+
+
+

Note: eligibility and rewards depend on on-chain activity and are not guaranteed.

@@ -305,10 +403,19 @@ const CollectRewardsCard = () => { {/* Progress and Actions - Right Side */}
{/* Progress Section */} -
-
- Progress to rewards - +
+ {isEligibleForRewards && ( +
+ )} +
+ Progress to Rewards + {inviteesReachedCount} / {MIN_INVITEES} @@ -316,16 +423,35 @@ const CollectRewardsCard = () => { reached
-
+
+ > + {isEligibleForRewards && ( +
+ )} +
-
- {thresholdReached - ? 'πŸŽ‰ Eligible to withdraw' - : `${Math.max(0, MIN_INVITEES - inviteesReachedCount)} more invitees need to reach ${MIN_SPENT_AE} AE`} +
+ {thresholdReached ? ( + + + Eligible to withdraw + + ) : ( + `${Math.max(0, MIN_INVITEES - inviteesReachedCount)} more invitee${Math.max(0, MIN_INVITEES - inviteesReachedCount) === 1 ? '' : 's'} need to reach ${MIN_SPENT_AE} AE` + )}
{!thresholdReached && inviteesInProgressCount > 0 && (
@@ -372,15 +498,30 @@ const CollectRewardsCard = () => { {/* Rewards Display */}
-
- +
+ {isEligibleForRewards && ( +
+ )} + Available Rewards - +
+ +
{/* Error Message */} @@ -396,9 +537,10 @@ const CollectRewardsCard = () => { type="button" onClick={onCollectReward} disabled={collectingReward || !isEligibleForRewards} - className={`w-full p-4 md:p-5 lg:p-6 text-sm md:text-base font-bold uppercase tracking-wider break-words whitespace-normal min-h-12 rounded-xl transition-all duration-300 ${isEligibleForRewards - ? 'bg-gradient-to-r from-pink-500 to-purple-500 text-white shadow-lg shadow-pink-500/30 hover:-translate-y-0.5 hover:shadow-xl hover:shadow-pink-500/40' - : 'opacity-50 cursor-not-allowed bg-gray-600 transform-none' + className={`w-full p-5 md:p-6 lg:p-7 text-sm md:text-base font-bold uppercase tracking-wider break-words whitespace-normal min-h-14 rounded-xl transition-all duration-300 relative overflow-hidden ${ + isEligibleForRewards + ? "bg-gradient-to-r from-green-500 via-teal-500 to-blue-500 text-white shadow-lg shadow-green-500/40 hover:-translate-y-1 hover:shadow-xl hover:shadow-green-500/50 before:content-[''] before:absolute before:top-0 before:-left-full before:w-full before:h-full before:bg-gradient-to-r before:from-transparent before:via-white/20 before:to-transparent before:transition-all before:duration-500 hover:before:left-full" + : 'opacity-50 cursor-not-allowed bg-gray-600 transform-none' }`} > {collectButtonContent} diff --git a/src/features/trending/components/Invitation/EarningExplanation.tsx b/src/features/trending/components/Invitation/EarningExplanation.tsx new file mode 100644 index 000000000..f596d5b35 --- /dev/null +++ b/src/features/trending/components/Invitation/EarningExplanation.tsx @@ -0,0 +1,168 @@ +import { useState } from "react"; +import { TrendingUp, Users, Coins, ArrowRight } from "lucide-react"; +import { Decimal } from "@/libs/decimal"; +import LivePriceFormatter from "@/features/shared/components/LivePriceFormatter"; + +// Note: This percentage should be updated once confirmed from bctsl-contracts repo +// Currently using 0.5% as placeholder based on calculateBuyPriceWithAffiliationFee +const EARNING_PERCENTAGE = 0.5; // This will be updated with actual percentage + +interface EarningExplanationProps { + className?: string; +} + +export default function EarningExplanation({ className }: EarningExplanationProps) { + const [exampleAmount, setExampleAmount] = useState(100); + const [exampleInvitees, setExampleInvitees] = useState(10); + + // Calculate example earnings + const exampleEarnings = (exampleAmount * exampleInvitees * EARNING_PERCENTAGE) / 100; + + return ( +
+ {/* Animated background */} +
+ +
+ {/* Header */} +
+
+ πŸ’° +
+
+

+ How Much Can You Earn? +

+

+ Earn a percentage of every token purchase made by your invitees +

+
+
+ + {/* Earning Percentage Display */} +
+
+ + + Up to {EARNING_PERCENTAGE}% + +
+

+ of every token purchase made by your invitees +

+
+ + {/* Visual Flow */} +
+
+
+
+ +
+
+
Your Invitees
+
+ {exampleInvitees} friends +
+
+
+ +
+
+ +
+
+
They Buy Tokens
+ + each +
+
+ +
+
+ +
+
+
You Earn
+ +
+
+
+
+ + {/* Interactive Calculator */} +
+

Try It Yourself

+
+
+ + setExampleInvitees(Math.max(1, Math.min(100, Number(e.target.value) || 1)))} + className="w-full px-4 py-2 bg-white/5 border border-white/10 rounded-lg text-white text-sm sm:text-base focus:outline-none focus:border-pink-400 focus:ring-2 focus:ring-pink-400/20" + /> +
+
+ + setExampleAmount(Math.max(0.1, Math.min(10000, Number(e.target.value) || 0)))} + className="w-full px-4 py-2 bg-white/5 border border-white/10 rounded-lg text-white text-sm sm:text-base focus:outline-none focus:border-pink-400 focus:ring-2 focus:ring-pink-400/20" + /> +
+
+
+
Your Potential Earnings
+ +
+
+ + {/* Key Points */} +
+
+ βœ“ +

+ Earnings are automatic - no action needed once invitees buy tokens +

+
+
+ βœ“ +

+ Rewards accumulate over time from all your invitees' purchases +

+
+
+ βœ“ +

+ Withdraw anytime after 4+ invitees have purchased tokens +

+
+
+
+
+ ); +} + diff --git a/src/features/trending/components/Invitation/InviteAndEarnCard.tsx b/src/features/trending/components/Invitation/InviteAndEarnCard.tsx index d6f0fbc8a..74375f36c 100644 --- a/src/features/trending/components/Invitation/InviteAndEarnCard.tsx +++ b/src/features/trending/components/Invitation/InviteAndEarnCard.tsx @@ -1,4 +1,6 @@ -import { AlertCircle } from 'lucide-react'; +import { + AlertCircle, Share2, Twitter, MessageCircle, Copy, Check, +} from 'lucide-react'; import React, { useCallback, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useAccount } from '../../../../hooks/useAccount'; @@ -40,6 +42,7 @@ const InviteAndEarnCard = ({ const [invitationLinks, setInvitationLinks] = useState([]); const [linkHasBeenCopied, setLinkHasBeenCopied] = useState(false); const [closeBlockedPulse, setCloseBlockedPulse] = useState(false); + const [copiedLinkIndex, setCopiedLinkIndex] = useState(null); // Refs const amountInputRef = useRef(null); @@ -101,6 +104,7 @@ const InviteAndEarnCard = ({ setCopyInviteLinkDialog(false); setTimeout(() => { setLinkHasBeenCopied(false); + setCopiedLinkIndex(null); }, 500); }; @@ -124,32 +128,82 @@ const InviteAndEarnCard = ({ setCopyInviteLinkDialog(true); }, [linkHasBeenCopied, pulseCloseBlocked]); + const copyToClipboard = async (text: string, index: number) => { + try { + await navigator.clipboard.writeText(text); + setCopiedLinkIndex(index); + setTimeout(() => setCopiedLinkIndex(null), 2000); + } catch (err) { + console.error('Failed to copy:', err); + } + }; + + const shareToTwitter = (link: string) => { + const text = encodeURIComponent(`Join me on Superhero! Earn rewards by trading trend tokens. Use my invite link: ${link}`); + window.open(`https://twitter.com/intent/tweet?text=${text}`, '_blank'); + }; + + const shareToDiscord = (link: string) => { + copyToClipboard(link, -1); + }; + + const canUseNativeShare = typeof navigator !== 'undefined' && typeof navigator.share === 'function'; + + const shellClassName = [ + 'bg-[var(--glass-bg)] border border-[var(--glass-border)] backdrop-blur-[20px]', + 'rounded-[20px] p-6 md:p-8 lg:p-10 relative overflow-hidden min-h-0', + 'transition-all duration-300 hover:border-white/15', + 'hover:shadow-[0_8px_32px_rgba(0,0,0,0.3),0_2px_8px_rgba(0,0,0,0.2)] hover:-translate-y-1', + className || '', + ].filter(Boolean).join(' '); + return ( -
+
-
- 🎯 +
+ πŸš€ +
+
+

+ Generate Invite Links +

+

+ Create unique invite links and start earning rewards +

-

- Generate Invites -

{/* Description - Left Side */} -
-

- Create invite links by funding a one-time AE reward per invite. Each - link contains a secret code; when someone opens the link and claims - it, they receive the funded reward and the invitation is marked as - used. -

-

- You can generate multiple links at once and share them with friends - or your community. You can also revoke an invite before it’s claimed. -

-

- Important: save your links before closing the popup. The secret code - is only shown to you at creation time. +

+
+

+ Earn up to 0.5% + {' '} + of every token purchase made by your invitees. Fund a one-time AE reward per invite; + {' '} + when someone claims the link, they receive it and the invite is used. +

+
+
+
+ βœ“ +

Fund AE to create unique invite links

+
+
+ βœ“ +

Share links via social media or direct message

+
+
+ βœ“ +

+ Earn automatically when invitees buy tokens; you can revoke unclaimed invites +

+
+
+

+ Important: save your links before closing the popup. + {' '} + The secret is only shown at creation time.

@@ -282,13 +336,56 @@ const InviteAndEarnCard = ({ {/* Links */}
- {invitationLinks.map((link) => ( - + {invitationLinks.map((link, index) => ( +
+
+ + +
+
+ + + {canUseNativeShare && ( + + )} +
+
))}
diff --git a/src/features/trending/components/Invitation/StatsSection.tsx b/src/features/trending/components/Invitation/StatsSection.tsx new file mode 100644 index 000000000..d3eeb2705 --- /dev/null +++ b/src/features/trending/components/Invitation/StatsSection.tsx @@ -0,0 +1,95 @@ +import { Trophy, TrendingUp, Users } from "lucide-react"; + +interface StatsSectionProps { + className?: string; +} + +export default function StatsSection({ className }: StatsSectionProps) { + // Placeholder stats - these would be fetched from API in real implementation + const stats = { + totalRewardsEarned: 0, // Would be fetched from API + totalInvitees: 0, // Would be fetched from API + topEarner: null as { address: string; amount: number } | null, // Would be fetched from API + }; + + // Only show if we have data (for now, showing placeholder structure) + const showStats = false; // Set to true when API integration is ready + + if (!showStats) { + return null; + } + + return ( +
+ {/* Animated background */} +
+
+ +
+ {/* Header */} +
+ +
+

+ Community Stats +

+

+ See how the community is earning together +

+
+
+ + {/* Stats Grid */} +
+ {/* Total Rewards */} +
+
+
+ +
+
+ Total Rewards +
+
+
+ {/* Would display actual stats */} +
+
+ + {/* Total Invitees */} +
+
+
+ +
+
+ Total Invitees +
+
+
+ {/* Would display actual stats */} +
+
+ + {/* Top Earner */} + {stats.topEarner && ( +
+
+
+ +
+
+ Top Earner +
+
+
+ {/* Would display top earner address */} +
+
+ )} +
+
+
+ ); +} + diff --git a/src/features/trending/components/Invitation/StepGuide.tsx b/src/features/trending/components/Invitation/StepGuide.tsx new file mode 100644 index 000000000..17063791d --- /dev/null +++ b/src/features/trending/components/Invitation/StepGuide.tsx @@ -0,0 +1,146 @@ +import { useState } from "react"; +import { ChevronRight } from "lucide-react"; + +interface Step { + number: number; + title: string; + description: string; + icon: string; +} + +const steps: Step[] = [ + { + number: 1, + title: "Generate Invite Links", + description: "Stake AE tokens to create unique invite links. Each invite requires a small amount of AE.", + icon: "πŸ”—", + }, + { + number: 2, + title: "Share with Friends", + description: "Send your invite links to friends, community members, or anyone interested in trend tokens.", + icon: "πŸ“€", + }, + { + number: 3, + title: "They Buy Tokens", + description: "When your invitees purchase trend tokens, you automatically earn a percentage of their buy amount.", + icon: "πŸ’Ž", + }, + { + number: 4, + title: "Collect Rewards", + description: "Once 4+ invitees have purchased tokens, withdraw your accumulated rewards anytime.", + icon: "πŸ’°", + }, +]; + +interface StepGuideProps { + onDismiss?: () => void; +} + +export default function StepGuide({ onDismiss }: StepGuideProps) { + const [expandedStep, setExpandedStep] = useState(null); + + return ( +
+ {/* Animated background gradient */} +
+ + {/* Close button */} + {onDismiss && ( + + )} + +
+ {/* Header */} +
+
+ πŸš€ +
+
+

+ How It Works +

+

+ Follow these simple steps to start earning rewards +

+
+
+ + {/* Steps Grid */} +
+ {steps.map((step, index) => { + const isExpanded = expandedStep === step.number; + return ( +
setExpandedStep(isExpanded ? null : step.number)} + className={`group relative bg-white/5 hover:bg-white/10 border border-white/10 hover:border-white/15 rounded-xl sm:rounded-2xl p-5 sm:p-6 cursor-pointer transition-all duration-300 hover:-translate-y-1 hover:shadow-[0_8px_32px_rgba(0,0,0,0.3),0_2px_8px_rgba(0,0,0,0.2)] ${ + isExpanded ? "bg-white/10 border-white/15 shadow-[0_8px_32px_rgba(0,0,0,0.3),0_2px_8px_rgba(0,0,0,0.2)]" : "" + }`} + > + {/* Step Number Badge */} +
+
+
+ {step.number} +
+
+
+
+
+ {step.icon} +

+ {step.title} +

+
+
+
+ + {/* Description */} +
+

+ {step.description} +

+
+ + {/* Expand indicator for mobile */} +
+ +
+ + {/* Connecting line (desktop only) */} + {index < steps.length - 1 && ( +
+ )} +
+ ); + })} +
+ + {/* Call to Action */} +
+

+ Ready to start earning? Generate your first invite link below! +

+
+
+
+ ); +} + diff --git a/src/features/trending/components/Invitation/graphics/EarningFlow.tsx b/src/features/trending/components/Invitation/graphics/EarningFlow.tsx new file mode 100644 index 000000000..7b042ef22 --- /dev/null +++ b/src/features/trending/components/Invitation/graphics/EarningFlow.tsx @@ -0,0 +1,70 @@ +import { ArrowRight, Users, Coins, TrendingUp } from "lucide-react"; + +interface EarningFlowProps { + className?: string; +} + +export default function EarningFlow({ className }: EarningFlowProps) { + return ( +
+ {/* Flow visualization */} +
+ {/* Step 1: Invite */} +
+
+
+ +
+
+
+
+
You Invite
+
Friends
+
+
+ + {/* Arrow 1 */} + + + {/* Step 2: They Buy */} +
+
+
+ +
+
+
+
+
They Buy
+
Tokens
+
+
+ + {/* Arrow 2 */} + + + {/* Step 3: You Earn */} +
+
+
+ +
+
+
+
+
You Earn
+
+ Rewards +
+
+
+
+ + {/* Percentage badge */} +
+ Up to 0.5% Commission +
+
+ ); +} + diff --git a/src/features/trending/components/Invitation/graphics/NetworkVisualization.tsx b/src/features/trending/components/Invitation/graphics/NetworkVisualization.tsx new file mode 100644 index 000000000..0443011c0 --- /dev/null +++ b/src/features/trending/components/Invitation/graphics/NetworkVisualization.tsx @@ -0,0 +1,134 @@ +import { useState, useEffect } from "react"; + +interface NetworkVisualizationProps { + inviteCount?: number; + className?: string; +} + +export default function NetworkVisualization({ inviteCount = 0, className }: NetworkVisualizationProps) { + const [animatedNodes, setAnimatedNodes] = useState([]); + + useEffect(() => { + // Animate nodes appearing + const nodes = Math.min(inviteCount || 5, 12); + const interval = setInterval(() => { + setAnimatedNodes((prev) => { + if (prev.length < nodes) { + return [...prev, prev.length]; + } + return prev; + }); + }, 200); + + return () => clearInterval(interval); + }, [inviteCount]); + + const nodes = Math.min(inviteCount || 5, 12); + const centerX = 50; + const centerY = 50; + const radius = 35; + + return ( +
+
+ + {/* Center node (You) */} + + + + + You + + + {/* Network nodes */} + {Array.from({ length: nodes }).map((_, i) => { + const angle = (i * 2 * Math.PI) / nodes; + const x = centerX + radius * Math.cos(angle); + const y = centerY + radius * Math.sin(angle); + const isAnimated = animatedNodes.includes(i); + + return ( + + {/* Connection line */} + + {/* Node */} + + + + + ); + })} + + {/* Gradients */} + + + + + + + + + + + + + + + + + + {/* Stats overlay */} +
+
+
Your Network
+
+ {inviteCount || 0} Invitees +
+
+
+
+
+ ); +} + diff --git a/src/features/trending/components/Invitation/index.ts b/src/features/trending/components/Invitation/index.ts index 360b91dd8..1b7df57d7 100644 --- a/src/features/trending/components/Invitation/index.ts +++ b/src/features/trending/components/Invitation/index.ts @@ -2,3 +2,6 @@ export { default as InviteAndEarnCard } from './InviteAndEarnCard'; export { default as InvitationList } from './InvitationList'; export { default as CollectRewardsCard } from './CollectRewardsCard'; export { default as CollectInvitationLinkCard } from './CollectInvitationLinkCard'; +export { default as StepGuide } from './StepGuide'; +export { default as EarningExplanation } from './EarningExplanation'; +export { default as StatsSection } from './StatsSection'; diff --git a/src/views/Trendminer/Invite.tsx b/src/views/Trendminer/Invite.tsx index c916786d5..3a7abcabb 100644 --- a/src/views/Trendminer/Invite.tsx +++ b/src/views/Trendminer/Invite.tsx @@ -8,104 +8,139 @@ import { CollectRewardsCard, InvitationList, InviteAndEarnCard, + StepGuide, + EarningExplanation, + StatsSection, } from '../../features/trending/components/Invitation'; import Shell from '../../components/layout/Shell'; import { useAeSdk } from '../../hooks'; +import { useInvitations } from '../../features/trending/hooks/useInvitations'; +import NetworkVisualization from '../../features/trending/components/Invitation/graphics/NetworkVisualization'; +import EarningFlow from '../../features/trending/components/Invitation/graphics/EarningFlow'; export default function Invite() { const { activeAccount } = useAeSdk(); - const [showInfo, setShowInfo] = useState(() => { + const { invitations } = useInvitations(); + const [showStepGuide, setShowStepGuide] = useState(() => { try { - return localStorage.getItem('invite_info_dismissed') !== '1'; + if (localStorage.getItem('invite_step_guide_dismissed') === '1') return false; + if (localStorage.getItem('invite_info_dismissed') === '1') return false; + return true; } catch { return true; } }); + const handleDismissStepGuide = () => { + try { + localStorage.setItem('invite_step_guide_dismissed', '1'); + localStorage.setItem('invite_info_dismissed', '1'); + } catch {} + setShowStepGuide(false); + }; + + const inviteCount = invitations?.length || 0; + return ( -
- {/* Hero Section */} -
-

- - Invite & Earn - -
- Build your network, earn rewards +
+ {/* Hero Section - Redesigned */} +
+
+
+

+ + Invite & Earn + +

+
+ Earn up to + {' '} + + 0.5% + + {' '} + of Every Token Purchase
-

-
- {/* Info Card */} - {showInfo && ( -
- {/* Close button - absolute positioned on all screen sizes for better space usage */} - + Invite friends to join the platform. + {' '} + When they buy trend tokens, you automatically earn a percentage of their purchases. + {' '} + Simple, transparent, rewarding. +

-
-
- πŸ’‘ + {/* Quick Stats */} +
+
+
+ Up to 0.5% +
+
Commission Rate
-
-

- How it works -

-
-
-
- 1 -
-
- Generate invite links by funding a one-time AE reward per invite -
-
-
-
- 2 -
-
- Share links with friends and community -
-
-
-
- 3 -
-
- After 4 unique invitees buy tokens, you can withdraw accumulated rewards -
-
-
-
- 4 -
-
- Withdraw rewards anytime after eligibility -
-
+
+
+
+ 4+
+
Invitees Needed
+
+
+
+
+ ∞ +
+
Earning Potential
+
+ + {/* Step Guide */} + {showStepGuide && ( +
+ +
)} + + {/* Earning Explanation */} +
+ +
+ + {/* Earning Flow Visualization */} +
+ +
+ + {/* Network Visualization */} + {activeAccount && inviteCount > 0 && ( +
+
+

+ Your Invite Network +

+ +
+
+ )} + {/* Main Action Cards */} -
- {/* Generate Invites Card */} +
- {/* Rewards Card */}
+ + {/* Stats Section (when API is ready) */} +
+ +
+ {/* User Invitations */} {activeAccount && (