@devrongx/games 0.3.4 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,211 @@
1
+ // @devrongx/games — games/prematch-bets/PreMatchResults.tsx
2
+ // Final results view: rank, payout, bet breakdown, stats. Pool status = COMPLETE.
3
+ "use client";
4
+
5
+ import { useMemo } from "react";
6
+ import { Trophy, CheckCircle2, ArrowRight, TrendingUp } from "lucide-react";
7
+ import { IChallengeConfig } from "./config";
8
+ import { LeaderboardRow } from "./LeaderboardRow";
9
+ import { OUTFIT, MARKET_ICONS } from "./constants";
10
+ import type { ITDPoolDetail, ITDMyEntryResponse, ITDLeaderboardEntry } from "../../pools/types";
11
+
12
+ // USDC icon inline
13
+ const UsdcIcon = ({ size = 12 }: { size?: number }) => (
14
+ <svg width={size} height={size} viewBox="0 0 32 32" fill="none">
15
+ <circle cx="16" cy="16" r="16" fill="#2775CA" />
16
+ <path d="M20.5 18.2c0-2.1-1.3-2.8-3.8-3.1-1.8-.3-2.2-.7-2.2-1.5s.7-1.3 1.8-1.3c1 0 1.6.4 1.9 1.1.1.1.2.2.3.2h.7c.2 0 .3-.1.3-.3-.3-1-1-1.8-2.1-2v-1.2c0-.2-.1-.3-.3-.3h-.6c-.2 0-.3.1-.3.3v1.2c-1.5.2-2.5 1.2-2.5 2.4 0 2 1.2 2.7 3.7 3 1.7.3 2.3.8 2.3 1.6 0 1-.8 1.6-2 1.6-1.5 0-2-.6-2.2-1.4 0-.1-.2-.2-.3-.2h-.7c-.2 0-.3.1-.3.3.3 1.2 1 2 2.5 2.3v1.2c0 .2.1.3.3.3h.6c.2 0 .3-.1.3-.3v-1.2c1.6-.2 2.6-1.3 2.6-2.6z" fill="#fff" />
17
+ </svg>
18
+ );
19
+
20
+ interface PreMatchResultsProps {
21
+ config: IChallengeConfig;
22
+ poolId: number;
23
+ pool?: ITDPoolDetail;
24
+ entryData: ITDMyEntryResponse | null;
25
+ rankings: ITDLeaderboardEntry[];
26
+ onViewFullLeaderboard: () => void;
27
+ }
28
+
29
+ export function PreMatchResults({
30
+ config,
31
+ pool,
32
+ entryData,
33
+ rankings,
34
+ onViewFullLeaderboard,
35
+ }: PreMatchResultsProps) {
36
+ const entry = entryData?.entry;
37
+ const myBets = entryData?.bets ?? [];
38
+ const hasPartnerPayout = pool?.currency?.partner_payout ?? false;
39
+
40
+ const myRanking = rankings.find((r) => r.entry_id === entry?.id);
41
+ const totalEntrants = rankings.length;
42
+
43
+ const stats = useMemo(() => {
44
+ const individualBets = myBets.filter((b) => b.parlay_id === null);
45
+ const won = individualBets.filter((b) => b.status === "won").length;
46
+ const lost = individualBets.filter((b) => b.status === "lost").length;
47
+ const totalSpent = individualBets.reduce((s, b) => s + b.coin_amount, 0);
48
+ const totalEarned = entry?.final_coins ?? 0;
49
+ return {
50
+ betsPlaced: individualBets.length,
51
+ totalChallenges: pool?.challenge_count ?? config.markets.length,
52
+ won,
53
+ lost,
54
+ winRate: individualBets.length > 0 ? Math.round((won / individualBets.length) * 100) : 0,
55
+ totalSpent,
56
+ totalEarned,
57
+ roi: totalSpent > 0 ? Math.round(((totalEarned - config.startingBalance) / totalSpent) * 100) : 0,
58
+ };
59
+ }, [myBets, entry, pool, config]);
60
+
61
+ const miniRankings = useMemo(() => {
62
+ const top = rankings.slice(0, 3);
63
+ if (!myRanking || (myRanking.rank ?? 0) <= 3) return top;
64
+ return [...top, myRanking];
65
+ }, [rankings, myRanking]);
66
+
67
+ return (
68
+ <div className="w-full flex flex-col gap-4 px-4 pb-8">
69
+ {/* Header */}
70
+ <div className="flex items-center gap-2 pt-5">
71
+ <Trophy size={18} style={{ color: "#f59e0b" }} />
72
+ <span className="text-[18px] font-bold text-white" style={OUTFIT}>Results</span>
73
+ <span className="text-[12px] text-white/40" style={OUTFIT}>— {config.matchTitle}</span>
74
+ </div>
75
+
76
+ {/* Rank card */}
77
+ {myRanking && (
78
+ <div
79
+ className="relative overflow-hidden rounded-2xl px-4 py-5 flex flex-col items-center gap-2"
80
+ style={{ background: "linear-gradient(135deg, rgba(34,227,232,0.08), rgba(153,69,255,0.08))", border: "1px solid rgba(34,227,232,0.15)" }}
81
+ >
82
+ <p className="text-[11px] text-white/50 uppercase tracking-widest font-semibold" style={OUTFIT}>YOUR FINAL RANK</p>
83
+ <div className="flex items-baseline gap-1">
84
+ <span className="text-[52px] font-bold leading-none" style={{ ...OUTFIT, color: "#22E3E8" }}>#{myRanking.rank}</span>
85
+ <span className="text-[14px] text-white/40" style={OUTFIT}>of {totalEntrants.toLocaleString()}</span>
86
+ </div>
87
+ <p className="text-[13px] font-semibold text-white" style={OUTFIT}>
88
+ Final Score: {(myRanking.final_coins ?? myRanking.current_coins).toLocaleString()} pts
89
+ </p>
90
+ {hasPartnerPayout && myRanking.payout_amount !== null && myRanking.payout_amount !== undefined && (
91
+ <div
92
+ className="flex items-center gap-1.5 px-4 py-2 rounded-xl mt-1"
93
+ style={{ background: "rgba(39,117,202,0.2)", border: "1px solid rgba(39,117,202,0.35)" }}
94
+ >
95
+ <UsdcIcon size={14} />
96
+ <span className="text-[14px] font-bold text-white" style={OUTFIT}>PAYOUT: ${myRanking.payout_amount.toFixed(2)} USDC</span>
97
+ </div>
98
+ )}
99
+ {!hasPartnerPayout && (
100
+ <p className="text-[10px] text-white/30" style={OUTFIT}>
101
+ Earned {(myRanking.final_coins ?? myRanking.current_coins).toLocaleString()} {pool?.currency?.symbol ?? "pts"}
102
+ </p>
103
+ )}
104
+ </div>
105
+ )}
106
+
107
+ <div className="h-px" style={{ background: "rgba(255,255,255,0.06)" }} />
108
+
109
+ {/* Bet breakdown */}
110
+ {myBets.length > 0 && (
111
+ <div>
112
+ <p className="text-[10px] font-semibold uppercase tracking-wider text-white/50 mb-2" style={OUTFIT}>
113
+ YOUR BETS — {stats.won} Won, {stats.lost} Lost
114
+ </p>
115
+ <div className="flex flex-col gap-1">
116
+ {myBets.filter((b) => b.parlay_id === null).map((bet) => {
117
+ const market = config.markets.find((m) => m.backendChallengeId === bet.challenge_id);
118
+ const Icon = MARKET_ICONS[market?.icon ?? "coin"];
119
+ const won = bet.status === "won";
120
+ const lost = bet.status === "lost";
121
+
122
+ return (
123
+ <div
124
+ key={bet.id}
125
+ className="flex items-start gap-2 px-3 py-2 rounded-lg"
126
+ style={{
127
+ background: won ? "rgba(34,197,94,0.05)" : lost ? "rgba(239,68,68,0.05)" : "rgba(255,255,255,0.03)",
128
+ border: `1px solid ${won ? "rgba(34,197,94,0.15)" : lost ? "rgba(239,68,68,0.15)" : "rgba(255,255,255,0.05)"}`,
129
+ }}
130
+ >
131
+ {won
132
+ ? <CheckCircle2 size={11} style={{ color: "#22c55e", flexShrink: 0, marginTop: 1 }} />
133
+ : lost
134
+ ? <span className="text-[10px] flex-shrink-0 mt-0.5" style={{ color: "#ef4444" }}>✗</span>
135
+ : Icon && <Icon size={11} style={{ color: market?.accent ?? "#22E3E8", flexShrink: 0, marginTop: 1 }} />
136
+ }
137
+ <div className="flex-1 min-w-0">
138
+ <p className="text-[9px] text-white/60 font-medium leading-tight truncate" style={OUTFIT}>
139
+ {market?.question ?? `Challenge #${bet.challenge_id}`}
140
+ </p>
141
+ <p className="text-[9px] mt-0.5 font-semibold" style={{ ...OUTFIT, color: won ? "#22c55e" : lost ? "#ef4444" : "rgba(255,255,255,0.5)" }}>
142
+ {market?.options.find((o) => o.label.toLowerCase().replace(/\s+/g, "_") === bet.selected_option)?.label ?? bet.selected_option}
143
+ {" @ "}{bet.odds_at_bet}x — {bet.coin_amount} pts
144
+ {won && ` → +${Math.round(bet.actual_return ?? bet.potential_return)} pts`}
145
+ {lost && " → 0 pts"}
146
+ </p>
147
+ </div>
148
+ </div>
149
+ );
150
+ })}
151
+ </div>
152
+ </div>
153
+ )}
154
+
155
+ <div className="h-px" style={{ background: "rgba(255,255,255,0.06)" }} />
156
+
157
+ {/* Stats */}
158
+ <div>
159
+ <div className="flex items-center gap-1.5 mb-2">
160
+ <TrendingUp size={11} style={{ color: "#22E3E8" }} />
161
+ <p className="text-[10px] font-semibold uppercase tracking-wider text-white/50" style={OUTFIT}>STATS</p>
162
+ </div>
163
+ <div className="grid grid-cols-2 gap-x-4 gap-y-1">
164
+ {[
165
+ ["Bets placed", `${stats.betsPlaced}/${stats.totalChallenges}`],
166
+ ["Win rate", `${stats.winRate}%`],
167
+ ["Points spent", stats.totalSpent.toLocaleString()],
168
+ ["Final score", stats.totalEarned.toLocaleString()],
169
+ ].map(([label, value]) => (
170
+ <div key={label} className="flex items-center justify-between py-0.5">
171
+ <span className="text-[10px] text-white/40" style={OUTFIT}>{label}</span>
172
+ <span className="text-[10px] font-semibold text-white" style={OUTFIT}>{value}</span>
173
+ </div>
174
+ ))}
175
+ </div>
176
+ </div>
177
+
178
+ <div className="h-px" style={{ background: "rgba(255,255,255,0.06)" }} />
179
+
180
+ {/* Final leaderboard */}
181
+ {miniRankings.length > 0 && (
182
+ <div>
183
+ <p className="text-[10px] font-semibold uppercase tracking-wider text-white/50 mb-2" style={OUTFIT}>FINAL LEADERBOARD</p>
184
+ <div className="flex flex-col">
185
+ {miniRankings.map((r) => (
186
+ <LeaderboardRow
187
+ key={r.entry_id}
188
+ entry={{
189
+ wallet: r.partner_ext_id ?? `User #${r.user_id}`,
190
+ pts: r.final_coins ?? r.current_coins,
191
+ payout: 0,
192
+ isYou: r.entry_id === entry?.id,
193
+ rank: r.rank,
194
+ }}
195
+ rank={r.rank}
196
+ isLast={false}
197
+ />
198
+ ))}
199
+ </div>
200
+ <button
201
+ onClick={onViewFullLeaderboard}
202
+ className="mt-2 flex items-center justify-center gap-1 w-full text-[9px] font-semibold"
203
+ style={{ ...OUTFIT, color: "rgba(34,227,232,0.6)" }}
204
+ >
205
+ See Full Leaderboard <ArrowRight size={10} />
206
+ </button>
207
+ </div>
208
+ )}
209
+ </div>
210
+ );
211
+ }
@@ -0,0 +1,183 @@
1
+ // @devrongx/games — games/prematch-bets/PreMatchSubmitted.tsx
2
+ // Shows after bets are submitted, while the pool is still OPEN (match hasn't started).
3
+ "use client";
4
+
5
+ import { useState, useEffect } from "react";
6
+ import { CheckCircle2, Clock, ArrowRight, Edit2 } from "lucide-react";
7
+ import { IChallengeConfig } from "./config";
8
+ import { LeaderboardRow } from "./LeaderboardRow";
9
+ import { OUTFIT, MARKET_ICONS } from "./constants";
10
+ import type { ITDMyEntryResponse, ITDLeaderboardEntry } from "../../pools/types";
11
+
12
+ // Countdown hook
13
+ function useCountdown(targetIso: string): string {
14
+ const [text, setText] = useState("");
15
+ useEffect(() => {
16
+ if (!targetIso) return;
17
+ const tick = () => {
18
+ const diff = new Date(targetIso).getTime() - Date.now();
19
+ if (diff <= 0) { setText("LIVE NOW"); return; }
20
+ const d = Math.floor(diff / 86400000);
21
+ const h = Math.floor((diff % 86400000) / 3600000);
22
+ const m = Math.floor((diff % 3600000) / 60000);
23
+ const s = Math.floor((diff % 60000) / 1000);
24
+ if (d > 0) setText(`${d}d ${h}h ${m}m`);
25
+ else if (h > 0) setText(`${h}h ${m}m ${s}s`);
26
+ else setText(`${m}m ${s}s`);
27
+ };
28
+ tick();
29
+ const id = setInterval(tick, 1000);
30
+ return () => clearInterval(id);
31
+ }, [targetIso]);
32
+ return text;
33
+ }
34
+
35
+ interface PreMatchSubmittedProps {
36
+ config: IChallengeConfig;
37
+ poolId: number;
38
+ entryData: ITDMyEntryResponse | null;
39
+ rankings: ITDLeaderboardEntry[];
40
+ onAdjust: () => void;
41
+ onViewLeaderboard: () => void;
42
+ }
43
+
44
+ export function PreMatchSubmitted({
45
+ config,
46
+ entryData,
47
+ rankings,
48
+ onAdjust,
49
+ onViewLeaderboard,
50
+ }: PreMatchSubmittedProps) {
51
+ const countdown = useCountdown(config.matchStartTime);
52
+ const entry = entryData?.entry;
53
+ const bets = entryData?.bets ?? [];
54
+
55
+ // Only show individual (non-parlay) bets
56
+ const individualBets = bets.filter((b) => b.parlay_id === null);
57
+
58
+ const totalSpent = individualBets.reduce((s, b) => s + b.coin_amount, 0);
59
+ const totalPotential = individualBets.reduce((s, b) => s + b.potential_return, 0);
60
+
61
+ // Mini leaderboard (top 3)
62
+ const miniRankings = rankings.slice(0, 5);
63
+
64
+ return (
65
+ <div className="w-full flex flex-col gap-4 px-4 pb-8">
66
+ {/* Success header */}
67
+ <div className="flex flex-col items-center gap-2 pt-6 pb-4">
68
+ <div className="flex items-center justify-center w-12 h-12 rounded-full" style={{ background: "rgba(34,197,94,0.15)" }}>
69
+ <CheckCircle2 size={24} style={{ color: "#22c55e" }} />
70
+ </div>
71
+ <p className="text-[18px] font-bold text-white text-center" style={OUTFIT}>Bets Submitted!</p>
72
+ {countdown && (
73
+ <div className="flex items-center gap-1.5 px-3 py-1 rounded-full" style={{ background: "rgba(255,255,255,0.06)" }}>
74
+ <Clock size={11} style={{ color: "rgba(255,255,255,0.5)" }} />
75
+ <span className="text-[11px] text-white/60" style={OUTFIT}>Match starts in <span className="text-white font-bold">{countdown}</span></span>
76
+ </div>
77
+ )}
78
+ </div>
79
+
80
+ <div className="h-px" style={{ background: "rgba(255,255,255,0.06)" }} />
81
+
82
+ {/* Your bets */}
83
+ {individualBets.length > 0 && (
84
+ <div>
85
+ <p className="text-[10px] font-semibold uppercase tracking-wider text-white/50 mb-2" style={OUTFIT}>
86
+ YOUR BETS ({individualBets.length} markets)
87
+ </p>
88
+ <div className="flex flex-col gap-1.5">
89
+ {individualBets.map((bet, i) => {
90
+ const market = config.markets.find((m) => m.backendChallengeId === bet.challenge_id);
91
+ const Icon = MARKET_ICONS[market?.icon ?? "coin"];
92
+ return (
93
+ <div
94
+ key={bet.id}
95
+ className="flex items-center gap-2 px-3 py-2 rounded-lg"
96
+ style={{ background: "rgba(255,255,255,0.04)", border: "1px solid rgba(255,255,255,0.06)" }}
97
+ >
98
+ {Icon && <Icon size={11} style={{ color: market?.accent ?? "#22E3E8", flexShrink: 0 }} />}
99
+ <span className="flex-1 text-[10px] text-white/70 font-medium truncate" style={OUTFIT}>
100
+ {market?.question ?? `Challenge #${bet.challenge_id}`}
101
+ </span>
102
+ <span className="text-[9px] font-bold" style={{ ...OUTFIT, color: "#22E3E8" }}>
103
+ {bet.odds_at_bet}x
104
+ </span>
105
+ <span className="text-[9px] text-white/50" style={OUTFIT}>—</span>
106
+ <span className="text-[9px] font-semibold text-white" style={OUTFIT}>{bet.coin_amount} pts</span>
107
+ </div>
108
+ );
109
+ })}
110
+ </div>
111
+ </div>
112
+ )}
113
+
114
+ <div className="h-px" style={{ background: "rgba(255,255,255,0.06)" }} />
115
+
116
+ {/* Summary */}
117
+ {entry && (
118
+ <div className="flex flex-col gap-1">
119
+ <p className="text-[10px] font-semibold uppercase tracking-wider text-white/50 mb-1" style={OUTFIT}>Summary</p>
120
+ <div className="flex items-center justify-between">
121
+ <span className="text-[11px] text-white/50" style={OUTFIT}>Points spent</span>
122
+ <span className="text-[11px] font-semibold text-white" style={OUTFIT}>{totalSpent} / {config.startingBalance.toLocaleString()}</span>
123
+ </div>
124
+ <div className="flex items-center justify-between">
125
+ <span className="text-[11px] text-white/50" style={OUTFIT}>Points remaining</span>
126
+ <span className="text-[11px] font-semibold text-white" style={OUTFIT}>{entry.current_coins.toLocaleString()}</span>
127
+ </div>
128
+ <div className="flex items-center justify-between">
129
+ <span className="text-[11px] text-white/50" style={OUTFIT}>Max potential</span>
130
+ <span className="text-[11px] font-bold" style={{ ...OUTFIT, color: "#22E3E8" }}>{Math.round(totalPotential + entry.current_coins).toLocaleString()}</span>
131
+ </div>
132
+ </div>
133
+ )}
134
+
135
+ {/* Adjust bets button */}
136
+ <button
137
+ onClick={onAdjust}
138
+ className="flex items-center justify-center gap-1.5 w-full py-2.5 rounded-xl"
139
+ style={{ background: "rgba(255,255,255,0.06)", border: "1px solid rgba(255,255,255,0.1)" }}
140
+ >
141
+ <Edit2 size={12} style={{ color: "rgba(255,255,255,0.5)" }} />
142
+ <span className="text-[11px] font-semibold text-white/60" style={OUTFIT}>Adjust Bets</span>
143
+ </button>
144
+
145
+ <div className="h-px" style={{ background: "rgba(255,255,255,0.06)" }} />
146
+
147
+ {/* Mini leaderboard */}
148
+ {miniRankings.length > 0 && (
149
+ <div>
150
+ <p className="text-[10px] font-semibold uppercase tracking-wider text-white/50 mb-2" style={OUTFIT}>Leaderboard</p>
151
+ <div className="flex items-center gap-2 px-3 mb-1">
152
+ <span className="w-[28px] text-right text-[8px] text-white/30 uppercase tracking-wide" style={OUTFIT}>#</span>
153
+ <span className="flex-1 text-[8px] text-white/30 uppercase tracking-wide" style={OUTFIT}>Player</span>
154
+ <span className="w-[52px] text-right text-[8px] text-white/30 uppercase tracking-wide" style={OUTFIT}>Pts</span>
155
+ </div>
156
+ <div className="flex flex-col">
157
+ {miniRankings.map((r) => (
158
+ <LeaderboardRow
159
+ key={r.entry_id}
160
+ entry={{
161
+ wallet: r.partner_ext_id ?? `User #${r.user_id}`,
162
+ pts: r.current_coins,
163
+ payout: 0,
164
+ isYou: false,
165
+ rank: r.rank,
166
+ }}
167
+ rank={r.rank}
168
+ isLast={false}
169
+ />
170
+ ))}
171
+ </div>
172
+ <button
173
+ onClick={onViewLeaderboard}
174
+ className="mt-2 flex items-center justify-center gap-1 w-full text-[9px] font-semibold"
175
+ style={{ ...OUTFIT, color: "rgba(34,227,232,0.6)" }}
176
+ >
177
+ See Full Leaderboard <ArrowRight size={10} />
178
+ </button>
179
+ </div>
180
+ )}
181
+ </div>
182
+ );
183
+ }
@@ -20,6 +20,7 @@ export interface IChallengeMarket {
20
20
  icon: string;
21
21
  accent: string;
22
22
  category: string; // "toss" | "match" | "csk_innings" | "rcb_innings" | "match_level"
23
+ backendChallengeId?: number; // Maps to challenges.id in the TD DB — required for API bet placement
23
24
  options: IChallengeOption[];
24
25
  userBet?: IBetEntry; // Present when API returns a logged-in user's saved bet for this market
25
26
  }
@@ -2,7 +2,7 @@
2
2
  // Shared constants and micro-components for IPL betting UI.
3
3
 
4
4
  import Image from "next/image";
5
- import { Coins, Trophy, Swords, Square, Zap, Target, BarChart3, Star } from "lucide-react";
5
+ import { Coins, Trophy, Swords, Square, Zap, Target, BarChart3, Star, ArrowUpRight } from "lucide-react";
6
6
 
7
7
  /** Font style applied to all betting UI text */
8
8
  export const OUTFIT = { fontFamily: "Outfit, sans-serif" } as const;
@@ -31,3 +31,41 @@ export const SelectedCheck = ({ size = 10 }: { size?: number }) => (
31
31
  <path d="M5 8l2 2 4-4" stroke="#9945FF" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
32
32
  </svg>
33
33
  );
34
+
35
+ /** Build iplgpt.com URL with question + options pre-filled */
36
+ export const buildIplGptUrl = (question: string, options: string[]) => {
37
+ const params = new URLSearchParams({ q: question, opts: options.join(",") });
38
+ return `https://www.iplgpt.com/?${params.toString()}`;
39
+ };
40
+
41
+ /**
42
+ * AI Insights button — solid cyan, floating rounded pill, Sparkles icon only.
43
+ * Redirects to iplgpt.com with question + options pre-filled in the query string.
44
+ */
45
+ export const AiInsightButton = ({
46
+ question,
47
+ options,
48
+ onClick,
49
+ }: {
50
+ question: string;
51
+ options: string[];
52
+ onClick?: (e: React.MouseEvent) => void;
53
+ }) => (
54
+ <a
55
+ href={buildIplGptUrl(question, options)}
56
+ target="_blank"
57
+ rel="noopener noreferrer"
58
+ onClick={(e) => { e.stopPropagation(); onClick?.(e); }}
59
+ className="inline-flex items-center gap-[3px] select-none"
60
+ style={{
61
+ borderBottom: "1px dotted rgba(34,227,232,0.9)",
62
+ paddingBottom: 2,
63
+ textDecoration: "none",
64
+ }}
65
+ >
66
+ <span className="text-[11px] font-semibold" style={{ ...OUTFIT, color: "#22E3E8", letterSpacing: "0.02em" }}>
67
+ Expert Insight
68
+ </span>
69
+ <ArrowUpRight size={12} style={{ color: "#22E3E8", flexShrink: 0 }} />
70
+ </a>
71
+ );
package/src/index.ts CHANGED
@@ -12,3 +12,19 @@ export type { IChallengeConfig, IChallengeMarket, IChallengeOption, IChallengeTe
12
12
  // Matches — TD match calendar, data fetching, and types
13
13
  export { configureTDClient, fetchTDMatches, useTDMatches, MatchCalendar } from "./matches";
14
14
  export type { ITDClientConfig, ITDMatch, ITDTeam, ITDPlayer, ITDVenue, ITDTournament, ITDStage, ITDMatchesResponse, ITDMatchesParams, UseTDMatchesResult } from "./matches";
15
+
16
+ // Pools — real API data layer
17
+ export {
18
+ useTDPools, useTDPool, useTDPoolEntry, useTDLeaderboard,
19
+ joinTDPool, placeTDBets,
20
+ fetchTDPools, fetchTDPoolDetail, fetchTDLeaderboard, fetchTDMyEntry,
21
+ buildPMBConfig,
22
+ } from "./pools";
23
+ export type {
24
+ ITDPool, ITDPoolDetail, ITDChallenge, ITDChallengeOption, ITDCurrency,
25
+ ITDPMBConfig, ITDPoolContentConfig, ITDPayoutTier,
26
+ ITDLeaderboardEntry, ITDLeaderboardResponse,
27
+ ITDPoolEntryRecord, ITDPoolBet, ITDPoolParlay, ITDMyEntryResponse,
28
+ ITDBetInput,
29
+ UseTDPoolsResult, UseTDPoolResult, UseTDPoolEntryResult, UseTDLeaderboardResult, UseTDLeaderboardOptions,
30
+ } from "./pools";