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 · a4553c19
This commit enhances the SportBet component by introducing a mobile collapsible bet slip with auto-open/-close logic based on bets count; it also improves display and user interaction in the bet slip and market card components. The changes generally improve UI/UX and provide better interactivity. However, the commit message lacks detail, and some areas could be optimized or better documented to reduce perceived risk.
Quality
?
75%
Security
?
80%
Business Value
?
70%
Maintainability
?
78%
Issues & Suggested Fixes
?
1AI detects the issue
2Shows file, line, or evidence
3Suggests the fix to apply
Lacks Descriptive Information
Where
commit message
Issue / Evidence
lacks descriptive information
Suggested Fix
update to clearly describe the nature and purpose of the changes to improve overall understanding and business value score
Useeffect Currently Only Depends On Bets.l...
Where
src/components/SportBet/SportBet.tsx:18
Issue / Evidence
useEffect currently only depends on bets.length
Suggested Fix
consider including bets array or memoizing to ensure correct updates and prevent potential stale closures, improving quality and reducing bug risk
Missing Test Coverage
Where
src/components/SportBet/SportBet.tsx:80
Issue / Evidence
updateStake called on input change converting input value to number
Suggested Fix
add validation to restrict invalid or negative inputs to improve quality and reduce bug risk
Batching Stake Updates In Foreach May Caus...
Where
src/components/SportBet/SportBet.tsx:110
Issue / Evidence
batching stake updates in forEach may cause performance issues
Suggested Fix
consider debouncing or optimizing updateStake calls for better performance and quality
The 'Getoverlaytext' Function Relies On Lo...
Where
src/components/sports/MarketCard.tsx:66
Issue / Evidence
the 'getOverlayText' function relies on loosely typed 'any' and string comparisons
Suggested Fix
improve type safety and handle possible undefined states to reduce bug risk
In 'Shouldshowoverlay', Make Sure Marketst...
Where
src/components/sports/MarketCard.tsx:69
Issue / Evidence
in 'shouldShowOverlay', make sure marketStatus is normalized before includes check to avoid bugs from casing or undefined, improving quality
Suggested Fix
Review and simplify this section.
Add Aria Labels And Roles To Interactive E...
Where
src/components/sportBet/SportBet.tsx line 79-84
Issue / Evidence
add aria-labels and roles to interactive elements like buttons and inputs to improve accessibility and quality
Suggested Fix
Review and simplify this section.
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;