@devrongx/games 0.4.19 → 0.4.21

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devrongx/games",
3
- "version": "0.4.19",
3
+ "version": "0.4.21",
4
4
  "description": "Game UI components for sports prediction markets",
5
5
  "license": "MIT",
6
6
  "main": "./src/index.ts",
@@ -129,7 +129,9 @@ export const PreMatchBetsPopup = ({ poolId, matchId: _matchId, match: matchProp
129
129
  const loaded: IUserBets = {};
130
130
  for (const bet of entryData.bets) {
131
131
  if (bet.parlay_id !== null) continue;
132
- const mIdx = config.markets.findIndex((m) => m.backendChallengeId === bet.challenge_id);
132
+ // pool.challenges[].id is the pool_challenge record ID (junction table), not the challenge's own ID.
133
+ // The bets response has both pool_challenge_id (= backendChallengeId) and challenge_id (different).
134
+ const mIdx = config.markets.findIndex((m) => m.backendChallengeId === bet.pool_challenge_id);
133
135
  if (mIdx < 0) continue;
134
136
  const oIdx = config.markets[mIdx].options.findIndex(
135
137
  (o) => (o.key ?? o.label.toLowerCase().replace(/\s+/g, "_")) === bet.selected_option,
@@ -287,8 +289,9 @@ export const PreMatchBetsPopup = ({ poolId, matchId: _matchId, match: matchProp
287
289
 
288
290
  await placeTDBets(poolId, betsToSubmit);
289
291
 
290
- // Snapshot submitted state for change detection
292
+ // Snapshot submitted state for change detection, and prevent restore effect from overwriting
291
293
  setSubmittedBets({ ...bets });
294
+ realBetsRestoredRef.current = true;
292
295
 
293
296
  // Refresh data
294
297
  await Promise.all([refetchEntry(), refetchLB(), refetchPool()]);
@@ -4,7 +4,7 @@
4
4
  import { useState, useEffect, useMemo, useCallback, useRef } from "react";
5
5
  import Image from "next/image";
6
6
  import { motion, AnimatePresence, useSpring, useTransform, useMotionValue } from "framer-motion";
7
- import { ChevronDown, Info, X, Play, Check, Loader2 } from "lucide-react";
7
+ import { ChevronDown, Info, X, Play, Pencil, Loader2 } from "lucide-react";
8
8
  import { IBetSummary, ILeaderboardEntry, IChallengeConfig, IUserBets, deriveParlayGroups, deriveMarketToParlay, calcDisplayReward, optionReward, calcParlayMultiplier } from "./config";
9
9
  import { OUTFIT, MARKET_ICONS, PointsIcon, SelectedCheck, AiInsightButton } from "./constants";
10
10
  import { useGamePopupStore } from "../../core/gamePopupStore";
@@ -529,19 +529,24 @@ export const PreMatchGame = ({ config, bets, onBetsChange, onOptionClick, onAmou
529
529
  <div className="flex items-stretch justify-between gap-3">
530
530
  {/* Left — two text lines stacked */}
531
531
  <div className="flex flex-col gap-2 flex-1 min-w-0">
532
- <motion.div
533
- className="flex items-baseline gap-1"
534
- initial={{ scale: 1.15, opacity: 0 }}
535
- animate={{ scale: 1, opacity: 1 }}
536
- transition={{ type: "spring", stiffness: 300, damping: 18, delay: 0.2 }}
537
- >
538
- <Image src="/iamgame_square_logo.jpg" alt="" width={totalEntry > 0 ? 14 : 20} height={totalEntry > 0 ? 14 : 20} className="rounded-[3px] transition-all duration-500" />
539
- <AnimatedNumber value={remainingBalance} className="text-white font-bold leading-none transition-all duration-500" style={{ ...OUTFIT, fontSize: totalEntry > 0 ? 16 : 24 }} />
540
- <span className="text-white font-bold leading-none transition-all duration-500" style={{ ...OUTFIT, fontSize: totalEntry > 0 ? 16 : 24 }}>/{config.startingBalance.toLocaleString()}</span>
541
- <span className="text-white font-medium leading-none transition-all duration-500" style={{ ...OUTFIT, fontSize: totalEntry > 0 ? 9 : 11 }}>
542
- points left to bet
543
- </span>
544
- </motion.div>
532
+ <AnimatePresence>
533
+ {remainingBalance > 0 && (
534
+ <motion.div
535
+ className="flex items-baseline gap-1"
536
+ initial={{ scale: 1.15, opacity: 0 }}
537
+ animate={{ scale: 1, opacity: 1 }}
538
+ exit={{ opacity: 0, height: 0 }}
539
+ transition={{ type: "spring", stiffness: 300, damping: 18, delay: 0.2 }}
540
+ >
541
+ <Image src="/iamgame_square_logo.jpg" alt="" width={totalEntry > 0 ? 14 : 20} height={totalEntry > 0 ? 14 : 20} className="rounded-[3px] transition-all duration-500" />
542
+ <AnimatedNumber value={remainingBalance} className="text-white font-bold leading-none transition-all duration-500" style={{ ...OUTFIT, fontSize: totalEntry > 0 ? 16 : 24 }} />
543
+ <span className="text-white font-bold leading-none transition-all duration-500" style={{ ...OUTFIT, fontSize: totalEntry > 0 ? 16 : 24 }}>/{config.startingBalance.toLocaleString()}</span>
544
+ <span className="text-white font-medium leading-none transition-all duration-500" style={{ ...OUTFIT, fontSize: totalEntry > 0 ? 9 : 11 }}>
545
+ points left to bet
546
+ </span>
547
+ </motion.div>
548
+ )}
549
+ </AnimatePresence>
545
550
  <AnimatePresence>
546
551
  {totalEntry > 0 && (
547
552
  <motion.div
@@ -609,18 +614,14 @@ export const PreMatchGame = ({ config, bets, onBetsChange, onOptionClick, onAmou
609
614
  {totalEntry > 0 && (() => {
610
615
  const spentPercent = totalEntry / config.startingBalance;
611
616
  const fillPercent = buttonState === "saved" ? 100 : 5 + spentPercent * 95;
612
- const fillColor = buttonState === "saved"
613
- ? "linear-gradient(135deg, #22c55e, #16a34a)"
614
- : "linear-gradient(135deg, #22E3E8, #9945FF, #f83cc5)";
615
- const bgColor = buttonState === "saved"
616
- ? "linear-gradient(135deg, rgba(34,197,94,0.2), rgba(22,163,74,0.2))"
617
- : "linear-gradient(135deg, rgba(34,227,232,0.2), rgba(153,69,255,0.2), rgba(248,60,197,0.2))";
617
+ const fillColor = "linear-gradient(135deg, #22E3E8, #9945FF, #f83cc5)";
618
+ const bgColor = "linear-gradient(135deg, rgba(34,227,232,0.2), rgba(153,69,255,0.2), rgba(248,60,197,0.2))";
618
619
 
619
620
  return (
620
621
  <motion.button
621
622
  onClick={onSubmit && buttonState !== "saved" ? onSubmit : undefined}
622
623
  disabled={submitting || buttonState === "saved"}
623
- className="relative w-[65px] rounded-xl overflow-hidden flex-shrink-0 flex items-center justify-center"
624
+ className="relative w-[65px] min-h-[54px] rounded-xl overflow-hidden flex-shrink-0 flex items-center justify-center py-3"
624
625
  style={{ background: bgColor, cursor: buttonState === "saved" ? "default" : "pointer" }}
625
626
  initial={{ opacity: 0, width: 0 }}
626
627
  animate={{ opacity: 1, width: 65 }}
@@ -646,7 +647,7 @@ export const PreMatchGame = ({ config, bets, onBetsChange, onOptionClick, onAmou
646
647
  {submitting
647
648
  ? <Loader2 size={22} strokeWidth={2.5} className="relative z-10 animate-spin text-white/70" />
648
649
  : buttonState === "saved"
649
- ? <Check size={24} strokeWidth={2.5} className="relative z-10 text-white" />
650
+ ? <Pencil size={20} strokeWidth={2} className="relative z-10 text-white/80" />
650
651
  : <Play size={26} fill="white" strokeWidth={0} className="relative z-10" />
651
652
  }
652
653
  </motion.button>