diff --git a/backend/app/routers/gamification.py b/backend/app/routers/gamification.py index 8a6c643..d7c5efb 100644 --- a/backend/app/routers/gamification.py +++ b/backend/app/routers/gamification.py @@ -34,7 +34,7 @@ class UserStatsResponse(BaseModel): tier_name: str house_fee: float xp_to_next_tier: int - tier_progress: float + tier_progress_percent: float total_wagered: float total_won: float net_profit: float @@ -418,7 +418,7 @@ async def get_my_stats( tier_name=TIER_CONFIG[stats.tier][2], house_fee=TIER_CONFIG[stats.tier][1], xp_to_next_tier=stats.xp_to_next_tier(), - tier_progress=stats.tier_progress_percent(), + tier_progress_percent=stats.tier_progress_percent(), total_wagered=stats.total_wagered, total_won=stats.total_won, net_profit=stats.net_profit, diff --git a/binance-more.png b/binance-more.png new file mode 100644 index 0000000..73535ae Binary files /dev/null and b/binance-more.png differ diff --git a/binance.png b/binance.png deleted file mode 100644 index 1fc4c61..0000000 Binary files a/binance.png and /dev/null differ diff --git a/coinex-header.png b/coinex-header.png new file mode 100644 index 0000000..be5665c Binary files /dev/null and b/coinex-header.png differ diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 2f38692..6551439 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -23,10 +23,17 @@ import { RewardsIndex, LeaderboardPage, AchievementsPage, - LootBoxesPage, + AirdropsPage, ActivityPage, } from './pages/rewards' +// New pages for More dropdown +import VIP from './pages/VIP' +import Affiliate from './pages/Affiliate' +import Referral from './pages/Referral' +import Launchpool from './pages/Launchpool' +import Megadrop from './pages/Megadrop' + const queryClient = new QueryClient({ defaultOptions: { queries: { @@ -69,12 +76,22 @@ function App() { } /> } /> } /> + + {/* Rewards Routes */} } /> } /> } /> - } /> + } /> + } /> } /> + {/* More Dropdown Routes */} + } /> + } /> + } /> + } /> + } /> + = { + common: { + color: 'text-gray-600', + bg: 'bg-gray-100', + border: 'border-gray-300', + icon: '📦', + }, + uncommon: { + color: 'text-green-600', + bg: 'bg-green-50', + border: 'border-green-300', + icon: '🎁', + }, + rare: { + color: 'text-blue-600', + bg: 'bg-blue-50', + border: 'border-blue-300', + icon: '💎', + }, + epic: { + color: 'text-purple-600', + bg: 'bg-purple-50', + border: 'border-purple-300', + icon: '👑', + }, + legendary: { + color: 'text-yellow-600', + bg: 'bg-yellow-50', + border: 'border-yellow-400', + icon: '🌟', + }, +} + +const REWARD_ICONS: Record = { + bonus_cash: '💰', + xp_boost: '⚡', + fee_reduction: '📉', + free_bet: '🎟️', + avatar_frame: '🖼️', + badge: '🏅', + nothing: '💨', +} + +export default function Airdrops() { + const queryClient = useQueryClient() + const [openingId, setOpeningId] = useState(null) + const [result, setResult] = useState(null) + const [showResult, setShowResult] = useState(false) + + const { data: airdrops = [], isLoading } = useQuery({ + queryKey: ['my-loot-boxes'], + queryFn: getMyLootBoxes, + retry: 1, + }) + + const openMutation = useMutation({ + mutationFn: openLootBox, + onSuccess: (data) => { + setResult(data) + setShowResult(true) + queryClient.invalidateQueries({ queryKey: ['my-loot-boxes'] }) + queryClient.invalidateQueries({ queryKey: ['my-stats'] }) + }, + onSettled: () => { + setOpeningId(null) + }, + }) + + const unclaimedAirdrops = airdrops.filter((box: LootBox) => !box.opened) + + const handleClaim = (box: LootBox) => { + setOpeningId(box.id) + setResult(null) + setShowResult(false) + openMutation.mutate(box.id) + } + + return ( +
+

+ 🪂 Airdrops + {unclaimedAirdrops.length > 0 && ( + + {unclaimedAirdrops.length} + + )} +

+ + {/* Result Modal */} + {showResult && result && ( +
+
+
{REWARD_ICONS[result.reward_type] || '🪂'}
+

+ {result.reward_type === 'nothing' ? 'Better luck next time!' : 'You received:'} +

+

{result.reward_value}

+

{result.message}

+ +
+
+ )} + + {isLoading ? ( +
+ {[...Array(3)].map((_, i) => ( +
+ ))} +
+ ) : unclaimedAirdrops.length === 0 ? ( +
+

🪂

+

No airdrops available

+

Earn them through achievements & VIP level ups!

+
+ ) : ( +
+ {unclaimedAirdrops.map((box: LootBox) => { + const config = RARITY_CONFIG[box.rarity] + const isOpening = openingId === box.id + + return ( + + ) + })} +
+ )} + + {/* Claimed History (collapsed) */} + {airdrops.filter((b: LootBox) => b.opened).length > 0 && ( +
+ + View claimed airdrops ({airdrops.filter((b: LootBox) => b.opened).length}) + +
+ {airdrops + .filter((b: LootBox) => b.opened) + .map((box: LootBox) => ( +
+ {RARITY_CONFIG[box.rarity].icon} + {box.rarity} + + {box.reward_value || 'Nothing'} +
+ ))} +
+
+ )} +
+ ) +} diff --git a/frontend/src/components/gamification/VIPProgress.tsx b/frontend/src/components/gamification/VIPProgress.tsx new file mode 100644 index 0000000..eab4424 --- /dev/null +++ b/frontend/src/components/gamification/VIPProgress.tsx @@ -0,0 +1,227 @@ +import { useQuery } from '@tanstack/react-query' +import { getMyStats, getTierInfo } from '../../api/gamification' +import type { TierInfo } from '../../types/gamification' + +// Map backend tier names to VIP display names +const VIP_NAMES: Record = { + 'Bronze I': 'VIP Bronze I', + 'Bronze II': 'VIP Bronze II', + 'Bronze III': 'VIP Bronze III', + 'Silver I': 'VIP Silver I', + 'Silver II': 'VIP Silver II', + 'Silver III': 'VIP Silver III', + 'Gold I': 'VIP Gold I', + 'Gold II': 'VIP Gold II', + 'Gold III': 'VIP Gold III', + 'Platinum': 'Institutional Platinum', + 'Diamond': 'Institutional Diamond', +} + +const VIP_ICONS: Record = { + 'Bronze I': '🥉', + 'Bronze II': '🥉', + 'Bronze III': '🥉', + 'Silver I': '🥈', + 'Silver II': '🥈', + 'Silver III': '🥈', + 'Gold I': '🥇', + 'Gold II': '🥇', + 'Gold III': '🥇', + 'Platinum': '💎', + 'Diamond': '👑', +} + +const VIP_COLORS: Record = { + 'Bronze I': { text: 'text-amber-700', bg: 'bg-amber-500', border: 'border-amber-400' }, + 'Bronze II': { text: 'text-amber-700', bg: 'bg-amber-500', border: 'border-amber-400' }, + 'Bronze III': { text: 'text-amber-700', bg: 'bg-amber-500', border: 'border-amber-400' }, + 'Silver I': { text: 'text-gray-600', bg: 'bg-gray-400', border: 'border-gray-400' }, + 'Silver II': { text: 'text-gray-600', bg: 'bg-gray-400', border: 'border-gray-400' }, + 'Silver III': { text: 'text-gray-600', bg: 'bg-gray-400', border: 'border-gray-400' }, + 'Gold I': { text: 'text-yellow-600', bg: 'bg-yellow-400', border: 'border-yellow-400' }, + 'Gold II': { text: 'text-yellow-600', bg: 'bg-yellow-400', border: 'border-yellow-400' }, + 'Gold III': { text: 'text-yellow-600', bg: 'bg-yellow-400', border: 'border-yellow-400' }, + 'Platinum': { text: 'text-cyan-600', bg: 'bg-cyan-400', border: 'border-cyan-400' }, + 'Diamond': { text: 'text-purple-600', bg: 'bg-purple-400', border: 'border-purple-400' }, +} + +interface VIPProgressProps { + compact?: boolean +} + +export default function VIPProgress({ compact = false }: VIPProgressProps) { + const { data: stats, isLoading: loadingStats } = useQuery({ + queryKey: ['my-stats'], + queryFn: getMyStats, + retry: 1, + }) + + const { data: tiers = [], isLoading: loadingTiers } = useQuery({ + queryKey: ['tier-info'], + queryFn: getTierInfo, + retry: 1, + }) + + const isLoading = loadingStats || loadingTiers + + if (isLoading) { + return ( +
+
+
+ ) + } + + if (!stats) { + return null + } + + const vipColors = VIP_COLORS[stats.tier_name] || VIP_COLORS['Bronze I'] + const vipIcon = VIP_ICONS[stats.tier_name] || '🏅' + const vipName = VIP_NAMES[stats.tier_name] || stats.tier_name + const isInstitutional = stats.tier >= 9 // Platinum and Diamond are institutional + + if (compact) { + return ( +
+
+
{vipIcon}
+
+
+ {vipName} + {stats.xp.toLocaleString()} XP +
+
+
+
+

+ {stats.tier < 10 + ? `${stats.xp_to_next_tier.toLocaleString()} XP to next VIP level` + : 'Max VIP level reached!'} +

+
+
+
+ ) + } + + return ( +
+

+ 👑 Your VIP Level +

+ + {/* Current VIP Level Display */} +
+
+
{vipIcon}
+
+

{vipName}

+

+ {isInstitutional ? ( + Institutional (Whale) + ) : ( + `VIP Level ${stats.tier} of 10` + )} +

+
+
+

{stats.house_fee}%

+

House Fee

+
+
+ + {/* XP Progress */} +
+
+ {stats.xp.toLocaleString()} XP + {stats.tier < 10 && ( + + {(stats.xp + stats.xp_to_next_tier).toLocaleString()} XP + + )} +
+
+
+
+

+ {stats.tier < 10 + ? `${stats.xp_to_next_tier.toLocaleString()} XP to next VIP level` + : '🎉 Max VIP level reached!'} +

+
+
+ + {/* Stats Summary */} +
+
+

= 0 ? 'text-green-600' : 'text-red-600'}`}> + ${stats.net_profit >= 0 ? '+' : ''}{stats.net_profit.toLocaleString()} +

+

Net Profit

+
+
+

${stats.total_wagered.toLocaleString()}

+

Total Wagered

+
+
+ + {/* VIP Level Roadmap */} +
+ + + View all VIP levels & benefits + +
+ {tiers.map((tier: TierInfo) => { + const isCurrentTier = tier.tier === stats.tier + const isUnlocked = tier.tier <= stats.tier + const colors = VIP_COLORS[tier.name] || VIP_COLORS['Bronze I'] + const displayName = VIP_NAMES[tier.name] || tier.name + const tierIsInstitutional = tier.tier >= 9 + + return ( +
+ {VIP_ICONS[tier.name] || '🏅'} +
+ + {displayName} + + {tierIsInstitutional && ( + (Whale) + )} + + {tier.min_xp.toLocaleString()} XP + +
+ + {tier.house_fee}% + + {isCurrentTier && ( + + YOU + + )} +
+ ) + })} +
+
+
+ ) +} diff --git a/frontend/src/components/gamification/index.ts b/frontend/src/components/gamification/index.ts index 571e98b..7ea5165 100644 --- a/frontend/src/components/gamification/index.ts +++ b/frontend/src/components/gamification/index.ts @@ -1,6 +1,8 @@ export { default as Leaderboard } from './Leaderboard' export { default as WhaleTracker } from './WhaleTracker' export { default as Achievements } from './Achievements' -export { default as LootBoxes } from './LootBoxes' +export { default as Airdrops } from './Airdrops' +export { default as LootBoxes } from './LootBoxes' // Keep for backward compatibility export { default as ActivityFeed } from './ActivityFeed' -export { default as TierProgress } from './TierProgress' +export { default as VIPProgress } from './VIPProgress' +export { default as TierProgress } from './TierProgress' // Keep for backward compatibility diff --git a/frontend/src/components/layout/Header.tsx b/frontend/src/components/layout/Header.tsx index 5e6de55..748da68 100644 --- a/frontend/src/components/layout/Header.tsx +++ b/frontend/src/components/layout/Header.tsx @@ -1,19 +1,59 @@ import { useState, useRef, useEffect } from 'react' import { Link } from 'react-router-dom' import { useAuthStore } from '@/store' -import { Wallet, LogOut, User, ChevronDown, Trophy, Medal, Gift, Activity, LayoutDashboard } from 'lucide-react' +import { + Wallet, + LogOut, + User, + ChevronDown, + Gift, + Crown, + Users, + Rocket, + Zap, + Share2, + Settings, + Receipt +} from 'lucide-react' import { useWeb3Wallet } from '@/blockchain/hooks/useWeb3Wallet' import { Button } from '@/components/common/Button' -const REWARDS_DROPDOWN = [ - { path: '/rewards', label: 'Overview', icon: LayoutDashboard }, - { path: '/rewards/leaderboard', label: 'Leaderboard', icon: Trophy }, - { path: '/rewards/achievements', label: 'Achievements', icon: Medal }, - { path: '/rewards/loot-boxes', label: 'Loot Boxes', icon: Gift }, - { path: '/rewards/activity', label: 'Activity', icon: Activity }, +// More dropdown menu items +const MORE_DROPDOWN = [ + { + path: '/vip', + label: 'VIP & Institutional', + description: 'Your trusted digital betting platform for VIPs and institutions', + icon: Crown + }, + { + path: '/affiliate', + label: 'Affiliate', + description: 'Earn up to 50% commission per bet from referrals', + icon: Users + }, + { + path: '/referral', + label: 'Referral', + description: 'Invite friends to earn either a commission rebate or a one-time reward', + icon: Share2 + }, + { + path: '/launchpool', + label: 'Launchpool', + description: 'Discover and gain access to new bets', + icon: Rocket + }, + { + path: '/megadrop', + label: 'Megadrop', + description: 'Lock your bets and complete quests for boosted airdrop rewards', + icon: Zap + }, ] -function RewardsDropdown() { +// Reusable dropdown hook +function useDropdown() { const [isOpen, setIsOpen] = useState(false) const dropdownRef = useRef(null) @@ -27,29 +67,38 @@ function RewardsDropdown() { return () => document.removeEventListener('mousedown', handleClickOutside) }, []) + return { isOpen, setIsOpen, dropdownRef } +} + +function MoreDropdown() { + const { isOpen, setIsOpen, dropdownRef } = useDropdown() + return (
{isOpen && ( -
- {REWARDS_DROPDOWN.map((item) => { +
+ {MORE_DROPDOWN.map((item) => { const Icon = item.icon return ( setIsOpen(false)} - className="flex items-center gap-2 px-4 py-2 text-gray-700 hover:bg-gray-50 hover:text-primary transition-colors" + className="flex items-start gap-3 px-4 py-3 hover:bg-gray-50 transition-colors" > - - {item.label} + +
+

{item.label}

+

{item.description}

+
) })} @@ -59,103 +108,167 @@ function RewardsDropdown() { ) } -export const Header = () => { +function ProfileDropdown() { const { user, logout } = useAuthStore() const { walletAddress, isConnected, connectWallet, disconnectWallet } = useWeb3Wallet() + const { isOpen, setIsOpen, dropdownRef } = useDropdown() + + if (!user) return null + + return ( +
+ + + {isOpen && ( +
+ {/* User info header */} +
+

{user.display_name || user.username}

+

{user.email}

+
+ + {/* Menu items */} +
+ setIsOpen(false)} + className="flex items-center gap-3 px-4 py-2 text-gray-700 hover:bg-gray-50 hover:text-primary transition-colors" + > + + Profile Settings + + setIsOpen(false)} + className="flex items-center gap-3 px-4 py-2 text-gray-700 hover:bg-gray-50 hover:text-primary transition-colors" + > + + My Bets + + setIsOpen(false)} + className="flex items-center gap-3 px-4 py-2 text-gray-700 hover:bg-gray-50 hover:text-primary transition-colors" + > + + Wallet + +
+ + {/* Web3 Wallet section */} +
+ {isConnected ? ( + + ) : ( + + )} +
+ + {/* Logout */} +
+ +
+
+ )} +
+ ) +} + +export const Header = () => { + const { user } = useAuthStore() return (
-
+
+ {/* Logo */}

H2H

- {user ? ( - // Logged in navigation - - ) : ( - // Non-logged in navigation - - )} + )} +
diff --git a/frontend/src/components/layout/RewardsLayout.tsx b/frontend/src/components/layout/RewardsLayout.tsx index f0c634a..ac7937a 100644 --- a/frontend/src/components/layout/RewardsLayout.tsx +++ b/frontend/src/components/layout/RewardsLayout.tsx @@ -13,7 +13,7 @@ const NAV_ITEMS = [ { path: '/rewards', label: 'Overview', icon: LayoutDashboard, exact: true }, { path: '/rewards/leaderboard', label: 'Leaderboard', icon: Trophy }, { path: '/rewards/achievements', label: 'Achievements', icon: Medal }, - { path: '/rewards/loot-boxes', label: 'Loot Boxes', icon: Gift }, + { path: '/rewards/airdrops', label: 'Airdrops', icon: Gift }, { path: '/rewards/activity', label: 'Activity', icon: Activity }, ] diff --git a/frontend/src/pages/Affiliate.tsx b/frontend/src/pages/Affiliate.tsx new file mode 100644 index 0000000..f6cb555 --- /dev/null +++ b/frontend/src/pages/Affiliate.tsx @@ -0,0 +1,95 @@ +import { Header } from '@/components/layout/Header' +import { Users, DollarSign, TrendingUp, BarChart3, Wallet } from 'lucide-react' + +const AFFILIATE_FEATURES = [ + { + icon: DollarSign, + title: 'Up to 50% Commission', + description: 'Earn up to 50% commission on every bet your referrals place' + }, + { + icon: TrendingUp, + title: 'Lifetime Earnings', + description: 'Continue earning from your referrals for as long as they bet' + }, + { + icon: BarChart3, + title: 'Real-Time Dashboard', + description: 'Track your earnings, clicks, and conversions in real-time' + }, + { + icon: Wallet, + title: 'Weekly Payouts', + description: 'Receive your commissions every week, no minimum threshold' + } +] + +export default function Affiliate() { + return ( +
+
+ + {/* Hero Section */} +
+
+
+
+ + Affiliate Program +
+

+ Earn Up to 50% Commission Per Bet +

+

+ Join our affiliate program and start earning passive income by promoting H2H to your audience. +

+ +
+
+
+ + {/* Features */} +
+
+ {AFFILIATE_FEATURES.map((feature, index) => { + const Icon = feature.icon + return ( +
+
+ +
+

{feature.title}

+

{feature.description}

+
+ ) + })} +
+
+ + {/* Commission Tiers */} +
+
+

Commission Structure

+
+
+
+ 0-10 referrals/month + 30% Commission +
+
+ 11-50 referrals/month + 40% Commission +
+
+ 50+ referrals/month + 50% Commission +
+
+
+
+
+
+ ) +} diff --git a/frontend/src/pages/Launchpool.tsx b/frontend/src/pages/Launchpool.tsx new file mode 100644 index 0000000..d52a548 --- /dev/null +++ b/frontend/src/pages/Launchpool.tsx @@ -0,0 +1,123 @@ +import { Header } from '@/components/layout/Header' +import { Rocket, Clock, Trophy, ArrowRight } from 'lucide-react' + +const UPCOMING_POOLS = [ + { + name: 'UFC 300 Special', + description: 'Exclusive early access to UFC 300 betting markets', + starts: 'Jan 15, 2026', + reward: '2x XP Boost', + status: 'upcoming' + }, + { + name: 'Super Bowl LXIII', + description: 'Premium props and side bets for the big game', + starts: 'Feb 1, 2026', + reward: 'VIP Airdrop', + status: 'upcoming' + }, + { + name: 'March Madness Early Bird', + description: 'Get first access to tournament brackets', + starts: 'Mar 1, 2026', + reward: 'Exclusive Badge', + status: 'upcoming' + } +] + +export default function Launchpool() { + return ( +
+
+ + {/* Hero Section */} +
+
+
+
+ + Launchpool +
+

+ Discover and Gain Early Access to New Bets +

+

+ Be the first to access exclusive betting markets before they go live. Place early bets to earn rewards and priority access. +

+
+
+
+ + {/* How It Works */} +
+

How Launchpool Works

+
+
+
+ 1 +
+

Browse Pools

+

Explore upcoming betting events and markets

+
+
+
+ 2 +
+

Reserve Your Spot

+

Commit funds to secure early access to the pool

+
+
+
+ 3 +
+

Earn Rewards

+

Get XP boosts, badges, and exclusive perks

+
+
+
+ 4 +
+

Get Early Access

+

Place bets before the general public

+
+
+
+ + {/* Upcoming Pools */} +
+
+

Upcoming Pools

+
+ {UPCOMING_POOLS.map((pool, index) => ( +
+
+
+ +
+
+

{pool.name}

+

{pool.description}

+
+ + + Starts {pool.starts} + + + + {pool.reward} + +
+
+
+ +
+ ))} +
+
+
+
+ ) +} diff --git a/frontend/src/pages/Megadrop.tsx b/frontend/src/pages/Megadrop.tsx new file mode 100644 index 0000000..3cb7481 --- /dev/null +++ b/frontend/src/pages/Megadrop.tsx @@ -0,0 +1,185 @@ +import { Header } from '@/components/layout/Header' +import { Zap, Lock, CheckCircle, Gift, Star } from 'lucide-react' +import { useAuthStore } from '@/store' + +const QUESTS = [ + { + title: 'Place Your First Bet', + description: 'Make any bet of $10 or more', + reward: '100 Megadrop Points', + completed: false + }, + { + title: 'Win 3 Bets in a Row', + description: 'Build a winning streak', + reward: '500 Megadrop Points', + completed: false + }, + { + title: 'Refer a Friend', + description: 'Invite someone who places a bet', + reward: '250 Megadrop Points', + completed: false + }, + { + title: 'Reach VIP Bronze', + description: 'Earn 1,000 XP to unlock Bronze tier', + reward: '1,000 Megadrop Points', + completed: false + }, + { + title: 'Complete Daily Check-in', + description: 'Log in 7 days in a row', + reward: '300 Megadrop Points', + completed: false + } +] + +export default function Megadrop() { + const { user } = useAuthStore() + + return ( +
+
+ + {/* Hero Section */} +
+
+
+
+ + Megadrop +
+

+ Lock Your Bets, Complete Quests, Earn Boosted Airdrops +

+

+ Participate in Megadrop events to earn exclusive rewards. Lock your stakes, complete quests, and climb the leaderboard for massive airdrop bonuses. +

+
+
+
+ + {/* Stats Bar */} +
+
+
+
+

$500K

+

Total Airdrop Pool

+
+
+

12,345

+

Participants

+
+
+

5 Days

+

Time Remaining

+
+
+

{user ? '0' : '-'}

+

Your Points

+
+
+
+
+ + {/* Lock Section */} +
+
+ {/* Lock Bets */} +
+
+
+ +
+
+

Lock Your Stake

+

Earn points based on locked amount

+
+
+ +
+
+ Lock Amount + $0.00 +
+
+ Lock Period + 30 Days +
+
+ Daily Points + 0 pts/day +
+
+ + +
+ + {/* Your Rewards */} +
+
+
+ +
+
+

Your Rewards

+

Estimated airdrop based on points

+
+
+ +
+

$0.00

+

Estimated Airdrop Value

+
+ +
+
+ Your Points + 0 +
+
+ Your Rank + {user ? '#-' : 'Sign in'} +
+
+
+
+
+ + {/* Quests */} +
+
+

Complete Quests for Bonus Points

+
+ {QUESTS.map((quest, index) => ( +
+
+
+ {quest.completed ? ( + + ) : ( + + )} +
+
+

{quest.title}

+

{quest.description}

+
+
+
+ {quest.reward} +
+
+ ))} +
+
+
+
+ ) +} diff --git a/frontend/src/pages/Referral.tsx b/frontend/src/pages/Referral.tsx new file mode 100644 index 0000000..b64a435 --- /dev/null +++ b/frontend/src/pages/Referral.tsx @@ -0,0 +1,113 @@ +import { Header } from '@/components/layout/Header' +import { Share2, Copy, Check } from 'lucide-react' +import { useState } from 'react' +import { useAuthStore } from '@/store' + +export default function Referral() { + const { user } = useAuthStore() + const [copied, setCopied] = useState(false) + const referralLink = user ? `https://h2h.bet/ref/${user.username}` : 'https://h2h.bet/ref/YOURCODE' + + const handleCopy = () => { + navigator.clipboard.writeText(referralLink) + setCopied(true) + setTimeout(() => setCopied(false), 2000) + } + + return ( +
+
+ + {/* Hero Section */} +
+
+
+
+ + Referral Program +
+

+ Invite Friends, Earn Rewards +

+

+ Share your referral link and earn either a commission rebate or a one-time reward for each friend who joins. +

+
+
+
+ + {/* Referral Link Box */} +
+
+

Your Referral Link

+
+ + +
+ {!user && ( +

+ Sign in to get your personalized referral link +

+ )} +
+
+ + {/* Reward Options */} +
+

Choose Your Reward Type

+
+
+
💰
+

Commission Rebate

+

+ Earn 20% of your friend's betting fees forever. The more they bet, the more you earn. +

+
20% Lifetime
+
+
+
🎁
+

One-Time Reward

+

+ Get an instant $25 bonus when your friend makes their first bet of $50 or more. +

+
$25 Instant
+
+
+
+ + {/* Stats */} + {user && ( +
+
+

Your Referral Stats

+
+
+

0

+

Friends Invited

+
+
+

$0

+

Total Earned

+
+
+

0

+

Pending Rewards

+
+
+
+
+ )} +
+ ) +} diff --git a/frontend/src/pages/VIP.tsx b/frontend/src/pages/VIP.tsx new file mode 100644 index 0000000..b95ff74 --- /dev/null +++ b/frontend/src/pages/VIP.tsx @@ -0,0 +1,118 @@ +import { Header } from '@/components/layout/Header' +import { Crown, Shield, Zap, Users, TrendingUp, Award } from 'lucide-react' + +const VIP_BENEFITS = [ + { + icon: Crown, + title: 'Exclusive VIP Status', + description: 'Join an elite group of high-volume bettors with premium benefits' + }, + { + icon: Shield, + title: 'Lower Fees', + description: 'Enjoy significantly reduced house fees on all your bets' + }, + { + icon: Zap, + title: 'Priority Support', + description: '24/7 dedicated account manager and priority customer support' + }, + { + icon: Users, + title: 'Institutional Access', + description: 'API access and custom solutions for institutional traders' + }, + { + icon: TrendingUp, + title: 'Higher Limits', + description: 'Increased betting limits and exclusive high-stakes events' + }, + { + icon: Award, + title: 'Exclusive Rewards', + description: 'Access to VIP-only airdrops, events, and promotional offers' + } +] + +export default function VIP() { + return ( +
+
+ + {/* Hero Section */} +
+
+
+
+ + VIP & Institutional Program +
+

+ Your Trusted Digital Betting Platform for VIPs and Institutions +

+

+ Experience premium benefits, lower fees, and exclusive access designed for high-volume traders and institutional clients. +

+
+ + +
+
+
+
+ + {/* Benefits Grid */} +
+

VIP Benefits

+
+ {VIP_BENEFITS.map((benefit, index) => { + const Icon = benefit.icon + return ( +
+
+ +
+

{benefit.title}

+

{benefit.description}

+
+ ) + })} +
+
+ + {/* VIP Tiers */} +
+
+

VIP Levels

+
+
+
🥉
+

VIP Bronze

+

Starting tier for active traders

+

8% Fees

+
+
+
+ Popular +
+
🥇
+

VIP Gold

+

For high-volume traders

+

5% Fees

+
+
+
💎
+

Institutional

+

For whales & institutions

+

Custom Rates

+
+
+
+
+
+ ) +} diff --git a/frontend/src/pages/rewards/AirdropsPage.tsx b/frontend/src/pages/rewards/AirdropsPage.tsx new file mode 100644 index 0000000..6d2e452 --- /dev/null +++ b/frontend/src/pages/rewards/AirdropsPage.tsx @@ -0,0 +1,75 @@ +import { useAuthStore } from '@/store' +import { Link } from 'react-router-dom' +import { RewardsLayout } from '@/components/layout/RewardsLayout' +import { Airdrops, VIPProgress } from '@/components/gamification' + +export default function AirdropsPage() { + const { isAuthenticated } = useAuthStore() + + return ( + + {isAuthenticated ? ( +
+
+ + + {/* How to earn airdrops */} +
+

How to Earn Airdrops

+
+
+ ⬆️ +
+

Level Up VIP

+

Reach a new VIP level to earn an airdrop

+
+
+
+ 🎖️ +
+

Achievements

+

Unlock achievements for bonus airdrops

+
+
+
+ 📅 +
+

Daily Rewards

+

Log in daily to earn streak rewards

+
+
+
+ 🔥 +
+

Win Streaks

+

Build winning streaks for rare airdrops

+
+
+
+
+
+
+ +
+
+ ) : ( +
+ 🔒 +

Sign In Required

+

+ Sign in to view and claim your airdrops. +

+ + Sign In + +
+ )} +
+ ) +} diff --git a/frontend/src/pages/rewards/RewardsIndex.tsx b/frontend/src/pages/rewards/RewardsIndex.tsx index 4a4fbc3..94c87ce 100644 --- a/frontend/src/pages/rewards/RewardsIndex.tsx +++ b/frontend/src/pages/rewards/RewardsIndex.tsx @@ -1,13 +1,13 @@ import { Link } from 'react-router-dom' import { useAuthStore } from '@/store' import { RewardsLayout } from '@/components/layout/RewardsLayout' -import { Leaderboard, WhaleTracker, TierProgress } from '@/components/gamification' +import { Leaderboard, WhaleTracker, VIPProgress } from '@/components/gamification' import { Trophy, Medal, Gift, Activity, ArrowRight } from 'lucide-react' const QUICK_LINKS = [ { path: '/rewards/leaderboard', label: 'Leaderboard', icon: Trophy, description: 'See top players and rankings' }, { path: '/rewards/achievements', label: 'Achievements', icon: Medal, description: 'Track your progress and badges' }, - { path: '/rewards/loot-boxes', label: 'Loot Boxes', icon: Gift, description: 'Open rewards and claim prizes' }, + { path: '/rewards/airdrops', label: 'Airdrops', icon: Gift, description: 'Claim rewards and prizes' }, { path: '/rewards/activity', label: 'Activity', icon: Activity, description: 'View recent wins and bets' }, ] @@ -47,7 +47,7 @@ export default function RewardsIndex() { {/* Left Column - Personal Stats (if authenticated) */} {isAuthenticated ? (
- +
) : (
@@ -76,28 +76,28 @@ export default function RewardsIndex() {
- {/* Tier Benefits Info */} + {/* VIP Benefits Info */}

- 💡 How Tiers Work + 💡 How VIP Levels Work

🎯 Earn XP

- Place bets, win, and complete achievements to earn XP and level up your tier. + Place bets, win, and complete achievements to earn XP and level up your VIP status.

📉 Lower Fees

- Higher tiers mean lower house fees! Start at 10% and work your way down to 5%. + Higher VIP levels mean lower house fees! Start at 10% and work your way down to 5%.

-

🎁 Unlock Rewards

+

🪂 Unlock Rewards

- Earn loot boxes, achievement badges, and exclusive perks as you climb the ranks. + Earn airdrops, achievement badges, and exclusive perks as you climb the VIP ranks.

diff --git a/frontend/src/pages/rewards/index.ts b/frontend/src/pages/rewards/index.ts index 352d15c..2f9ae5f 100644 --- a/frontend/src/pages/rewards/index.ts +++ b/frontend/src/pages/rewards/index.ts @@ -1,5 +1,5 @@ export { default as RewardsIndex } from './RewardsIndex' export { default as LeaderboardPage } from './LeaderboardPage' export { default as AchievementsPage } from './AchievementsPage' -export { default as LootBoxesPage } from './LootBoxesPage' +export { default as AirdropsPage } from './AirdropsPage' export { default as ActivityPage } from './ActivityPage' diff --git a/frontend/test-results/home-with-events.png b/frontend/test-results/home-with-events.png deleted file mode 100644 index 6caa3db..0000000 Binary files a/frontend/test-results/home-with-events.png and /dev/null differ diff --git a/frontend/test-results/rewards-dropdown.png b/frontend/test-results/rewards-dropdown.png deleted file mode 100644 index cf942b2..0000000 Binary files a/frontend/test-results/rewards-dropdown.png and /dev/null differ diff --git a/frontend/tests/screenshots/after-login.png b/frontend/tests/screenshots/after-login.png index 9042efb..28cdf36 100644 Binary files a/frontend/tests/screenshots/after-login.png and b/frontend/tests/screenshots/after-login.png differ diff --git a/frontend/tests/screenshots/events-list.png b/frontend/tests/screenshots/events-list.png index 0647cc6..c2a5b6f 100644 Binary files a/frontend/tests/screenshots/events-list.png and b/frontend/tests/screenshots/events-list.png differ diff --git a/frontend/tests/screenshots/homepage.png b/frontend/tests/screenshots/homepage.png index 420b35e..482a9e4 100644 Binary files a/frontend/tests/screenshots/homepage.png and b/frontend/tests/screenshots/homepage.png differ