AI Review Center
Commit Review Workspace ?
Select a commit on the left to inspect quality, security, business value, findings, and suggested code changes.
Project AI Score
?
71%
Quality Avg
?
72%
Security Avg
?
74%
Reviews
?
123
Review Result ?
solutionbowl/website · aeb2edd0
The commit introduces a detailed UI component for a sports betting slip with state management for mobile open state, rendering bet slips, odds, and stake management. The added code handles bet showing and interaction well, providing clear UX on both mobile and desktop. However, the commit message is minimal and vague, reducing traceability. There is also no evident error handling for invalid input or edge cases in stake updates, which poses lower but existing bug risk. The security posture is neutral because input is numeric but not sanitized or validated beyond minimal constraints. The business value is moderate to high because this feature directly impacts user engagement with betting functionality.
Quality
?
85%
Security
?
50%
Business Value
?
75%
Maintainability
?
83%
Issues & Suggested Fixes
?
1AI detects the issue
2Shows file, line, or evidence
3Suggests the fix to apply
Vague And Non Descriptive
Where
commit message
Issue / Evidence
vague and non-descriptive
Suggested Fix
improve by adding a detailed description of what the change does and why
Missing Validation
Where
src/components/SportBet/SportBet.tsx:79
Issue / Evidence
missing validation or sanitization of numeric input
Suggested Fix
add validation to prevent negative or invalid stake amounts
Input Change Handler Does Not Debounce Or...
Where
src/components/SportBet/SportBet.tsx:110
Issue / Evidence
input change handler does not debounce or sanitize input
Suggested Fix
consider debouncing input changes and sanitizing user input to improve reliability
Disabled Condition Depends On Totalstake A...
Where
src/components/SportBet/SportBet.tsx:136
Issue / Evidence
disabled condition depends on totalStake and bets.length only
Suggested Fix
consider adding user feedback or tooltip explaining disabled status to improve UX
Getoverlaytext Returns Marketstatus Withou...
Where
src/components/sports/MarketCard.tsx:66
Issue / Evidence
getOverlayText returns marketStatus without normalization
Suggested Fix
add safer handling for undefined or unexpected values to reduce UI bugs
Shouldshowoverlay Relies On Hardcoded List...
Where
src/components/sports/MarketCard.tsx:71
Issue / Evidence
shouldShowOverlay relies on hardcoded list and inplay
Suggested Fix
consider externalizing overlay status config or adding unit tests for robustness
Code Change Preview · src/components/SportBet/SportBet.tsx
?
Removed / Before Commit
- diff --git a/src/components/SportBet/SportBet.tsx b/src/components/SportBet/SportBet.tsx - index 9be427e..f56f5db 100644 - import { Settings } from "lucide-react"; - import { useBetSlipStore } from "../../store/betSlipStore"; - import { useState } from "react"; - import BetSlipSettings from "./BetSlipSettings"; - import { useThemeColors } from "../../hooks/useThemeColors"; - - const colors = useThemeColors(); - const { bets, removeBet, updateStake, clearAll, getTotalStake, getTotalReturn } = useBetSlipStore(); - const [isSettingsOpen, setIsSettingsOpen] = useState(false); - - const totalStake = getTotalStake(); - const totalReturn = getTotalReturn(); - - return ( - <> - <aside
Added / After Commit
+ diff --git a/src/components/SportBet/SportBet.tsx b/src/components/SportBet/SportBet.tsx + index 9be427e..f56f5db 100644 + import { Settings } from "lucide-react"; + import { useBetSlipStore } from "../../store/betSlipStore"; + import { useState, useEffect } from "react"; + import BetSlipSettings from "./BetSlipSettings"; + import { useThemeColors } from "../../hooks/useThemeColors"; + + const colors = useThemeColors(); + const { bets, removeBet, updateStake, clearAll, getTotalStake, getTotalReturn } = useBetSlipStore(); + const [isSettingsOpen, setIsSettingsOpen] = useState(false); + const [mobileOpen, setMobileOpen] = useState(false); + + const totalStake = getTotalStake(); + const totalReturn = getTotalReturn(); + + // Auto-open when bet added, auto-close when all removed + useEffect(() => {
Removed / Before Commit
- diff --git a/src/components/sports/MarketCard.tsx b/src/components/sports/MarketCard.tsx - index 72e4cfb..d08beb4 100644 - eventName?: string; - } - - export default function MarketCard({ market, defaultOpen = false, eventName = "" }: MarketCardProps) { - - const colors = useThemeColors(); - const [isOpen, setIsOpen] = useState(defaultOpen); - const contentRef = useRef<HTMLDivElement>(null); - const [contentHeight, setContentHeight] = useState(0); - const { addBet } = useBetSlipStore(); - - // Track price changes for animation - now tracking individual prices - const [priceChanges, setPriceChanges] = useState<Record<string, { backPrice?: number; layPrice?: number }>>({}); - const prevPricesRef = useRef<Record<string, { backPrice?: number; layPrice?: number }>>({}); - - useLayoutEffect(() => {
Added / After Commit
+ diff --git a/src/components/sports/MarketCard.tsx b/src/components/sports/MarketCard.tsx + index 72e4cfb..d08beb4 100644 + eventName?: string; + } + + const OVERLAY_STATUSES = ['suspended', 'closed', 'ball running']; + + export default function MarketCard({ market, defaultOpen = false, eventName = "" }: MarketCardProps) { + const colors = useThemeColors(); + const [isOpen, setIsOpen] = useState(defaultOpen); + const contentRef = useRef<HTMLDivElement>(null); + const [contentHeight, setContentHeight] = useState(0); + const { addBet } = useBetSlipStore(); + const [priceChanges, setPriceChanges] = useState<Record<string, { backPrice?: number; layPrice?: number }>>({}); + const prevPricesRef = useRef<Record<string, { backPrice?: number; layPrice?: number }>>({}); + + useLayoutEffect(() => { + if (!contentRef.current) return;
Removed / Before Commit
- diff --git a/src/components/sports/MarketRunner.tsx b/src/components/sports/MarketRunner.tsx - index 16d1176..b61f1af 100644 - // import { useThemeColors } from "../../hooks/useThemeColors"; - // import type { MarketRunner as MarketRunnerType } from "../../types/matchDetails"; - // import OddsButton from "./OddsButton"; - - // interface MarketRunnerProps { - // runner: MarketRunnerType; - // onOddsClick?: (type: 'back' | 'lay', price: number) => void; - // } - - // export default function MarketRunner({ runner, onOddsClick }: MarketRunnerProps) { - // const colors = useThemeColors(); - // const isActive = runner.status === 'ACTIVE'; - - // return ( - // <div - // style={{
Added / After Commit
+ diff --git a/src/components/sports/MarketRunner.tsx b/src/components/sports/MarketRunner.tsx + index 16d1176..b61f1af 100644 + import { useThemeColors } from "../../hooks/useThemeColors"; + import type { MarketRunner as MarketRunnerType } from "../../types/matchDetails"; + import OddsButton from "./OddsButton"; + + interface MarketRunnerProps { + runner: MarketRunnerType; + marketStatus?: string; + onOddsClick?: (type: "back" | "lay", price: number) => void; + } + + export default function MarketRunner({ + runner, + marketStatus, + onOddsClick, + }: MarketRunnerProps) { + const colors = useThemeColors();
Removed / Before Commit
- diff --git a/src/components/sports/SportsMatchList.tsx b/src/components/sports/SportsMatchList.tsx - new file mode 100644 - index 0000000..b28ecb1
Added / After Commit
+ diff --git a/src/components/sports/SportsMatchList.tsx b/src/components/sports/SportsMatchList.tsx + new file mode 100644 + index 0000000..b28ecb1 + import { Link } from "react-router-dom"; + import { encryptEventId } from "../../utils/encryption"; + import { useThemeColors } from "../../hooks/useThemeColors"; + import type { CricketMatch } from "../../types/cricket"; + import { useEffect, useRef, useState } from "react"; + + interface SportsMatchListProps { + matches: CricketMatch[]; + showLive?: boolean; + } + + export default function SportsMatchList({ matches, showLive = false }: SportsMatchListProps) { + const colors = useThemeColors(); + + // Track price changes for flash animation
Removed / Before Commit
- diff --git a/src/components/sports/SportsTabNavigation.tsx b/src/components/sports/SportsTabNavigation.tsx - index 1ef8dce..c09396e 100644 - { id: 'leagues', label: 'Leagues', icon: <img src="/images/league.png" className="w-[16px]" alt="Leagues" /> } - ]; - - return ( - <div className="overflow-x-auto no-scrollbar"> - <div className="flex gap-2 mt-[20px] whitespace-nowrap"> - {tabs.map((tab) => ( - <button - key={tab.id} - onClick={() => onTabChange(tab.id)} - style={{ - backgroundColor: activeTab === tab.id ? colors.primary : colors.card, - color: activeTab === tab.id ? '#000000' : colors.textPrimary
Added / After Commit
+ diff --git a/src/components/sports/SportsTabNavigation.tsx b/src/components/sports/SportsTabNavigation.tsx + index 1ef8dce..c09396e 100644 + { id: 'leagues', label: 'Leagues', icon: <img src="/images/league.png" className="w-[16px]" alt="Leagues" /> } + ]; + + const handleTabClick = (tabId: string) => { + onTabChange(tabId); + + // Scroll to section + if (tabId === 'live') { + const liveSection = document.getElementById('live-section'); + if (liveSection) { + liveSection.scrollIntoView({ behavior: 'smooth', block: 'start' }); + } + } else if (tabId === 'upcoming') { + const upcomingSection = document.getElementById('upcoming-section'); + if (upcomingSection) { + upcomingSection.scrollIntoView({ behavior: 'smooth', block: 'start' });
Removed / Before Commit
- diff --git a/src/hooks/useListingOddsSocket.ts b/src/hooks/useListingOddsSocket.ts - new file mode 100644 - index 0000000..e5970ff - Listing Odds Socket Connected");
Added / After Commit
+ diff --git a/src/hooks/useListingOddsSocket.ts b/src/hooks/useListingOddsSocket.ts + new file mode 100644 + index 0000000..e5970ff + import { useEffect, useRef, useState } from "react"; + import { io, Socket } from "socket.io-client"; + + const SOCKET_URL = "https://api.digitalbowls.com"; + + export const useListingOddsSocket = ( + eventIds: string[], + onUpdate?: (data: any) => void + ) => { + const socketRef = useRef<Socket | null>(null); + const [isConnected, setIsConnected] = useState(false); + const [error, setError] = useState<string | null>(null); + const onUpdateRef = useRef(onUpdate); + + useEffect(() => {
Removed / Before Commit
- diff --git a/src/pages/Sports.tsx b/src/pages/Sports.tsx - index 3e3d2a5..a1fec80 100644 - import { useState } from "react"; - import { useParams } from "react-router-dom"; - import BannerCarousel from "../components/banner/BannerCarousel"; - import SportsTabNavigation from "../components/sports/SportsTabNavigation"; - import LoadingState from "../components/sports/LoadingState"; - import EmptyState from "../components/sports/EmptyState"; - import { useSportsMatches } from "../hooks/useSportsMatches"; - import { getSportId } from "../utils/sportsHelpers"; - import SportsMatchCard from "../components/sports/SportsMatchCard"; - - export default function Sports() { - const { sport } = useParams<{ sport: string }>(); - const [activeTab, setActiveTab] = useState("upcoming"); - - // Get sport ID from URL parameter - const sportId = getSportId(sport);
Added / After Commit
+ diff --git a/src/pages/Sports.tsx b/src/pages/Sports.tsx + index 3e3d2a5..a1fec80 100644 + import { useState, useEffect } from "react"; + import { useParams } from "react-router-dom"; + import BannerCarousel from "../components/banner/BannerCarousel"; + import SportsTabNavigation from "../components/sports/SportsTabNavigation"; + import LoadingState from "../components/sports/LoadingState"; + import EmptyState from "../components/sports/EmptyState"; + import { useSportsMatches } from "../hooks/useSportsMatches"; + import { useListingOddsSocket } from "../hooks/useListingOddsSocket"; + import { getSportId } from "../utils/sportsHelpers"; + import SportsMatchList from "../components/sports/SportsMatchList"; + import type { CricketMatch } from "../types/cricket"; + + export default function Sports() { + const { sport } = useParams<{ sport: string }>(); + const [activeTab, setActiveTab] = useState("live"); +
Removed / Before Commit
- diff --git a/src/pages/SportsDetailPage.tsx b/src/pages/SportsDetailPage.tsx - index 64b1d44..3fc42a3 100644 - - export default function SportsDetailPage() { - const { id } = useParams<{ id: string }>(); - const { clearAll } = useBetSlipStore(); - - const [markets, setMarkets] = useState<Market[]>([]); - const [loading, setLoading] = useState(true); - <div className="w-full max-w-[1400px] mx-auto px-[15px]"> - <div className="lg:flex gap-4 relative"> - {/* LEFT - Scrollable Markets */} - <div className="flex-1 pb-[130px] lg:pb-6"> - - {loading && <LoadingState message="Loading match details..." />} - - </div> - </div>
Added / After Commit
+ diff --git a/src/pages/SportsDetailPage.tsx b/src/pages/SportsDetailPage.tsx + index 64b1d44..3fc42a3 100644 + + export default function SportsDetailPage() { + const { id } = useParams<{ id: string }>(); + const { clearAll, bets } = useBetSlipStore(); + + const [markets, setMarkets] = useState<Market[]>([]); + const [loading, setLoading] = useState(true); + <div className="w-full max-w-[1400px] mx-auto px-[15px]"> + <div className="lg:flex gap-4 relative"> + {/* LEFT - Scrollable Markets */} + <div className={`flex-1 ${bets.length > 0 ? 'pb-[60px]' : 'pb-4'} lg:pb-6`}> + + {loading && <LoadingState message="Loading match details..." />} + + </div> + </div>
Removed / Before Commit
- diff --git a/src/types/cricket.ts b/src/types/cricket.ts - index 0adc563..e2c7791 100644 - marketTime: string; - match_status: string; - tournamentsName: string; - } - - export interface CricketMatchesResponse {
Added / After Commit
+ diff --git a/src/types/cricket.ts b/src/types/cricket.ts + index 0adc563..e2c7791 100644 + marketTime: string; + match_status: string; + tournamentsName: string; + listingOdds?: Array<{ + eventId: string; + exMarketId: string; + marketName: string; + inplay: boolean; + status: string; + runners: Array<{ + selectionId: number; + runnerName: string; + status: string; + back: { + price: number; + size: number;