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 · 7bf8a784
The commit adds a feature to detect if a user is already logged in another tab and prompt logout via a modal dialog. It imports and integrates single-tab check and logout utilities, and updates login handling to store token and notify via single tab utilities. The modal component is functional and styled properly. The commit includes bet placement changes but unrelated to single-tab logic. Quality is generally good with appropriate use of hooks and UI components. Some minor security concerns remain around localStorage token management. Business value is good as it prevents duplicate logins improving UX and session integrity. Bug risk is low as key flows have error handling though concurrency and race conditions in multi-tab scenarios could be further tested. Fake work risk is low due to clear purposeful changes.
Quality
?
85%
Security
?
70%
Business Value
?
80%
Maintainability
?
85%
Issues & Suggested Fixes
?
1AI detects the issue
2Shows file, line, or evidence
3Suggests the fix to apply
Using Window.location.replace('/') Causes...
Where
src/App.tsx:36
Issue / Evidence
Using window.location.replace('/') causes full page reload which is suboptimal
Suggested Fix
consider using router navigation methods to improve UX
Security Issue
Where
src/components/auth/LoginForm.tsx:99
Issue / Evidence
Storing authToken in localStorage can expose to XSS risks
Suggested Fix
consider HttpOnly cookies or secure storage
Missing Validation
Where
src/components/auth/LoginForm.tsx:97
Issue / Evidence
Calling setLogin with token should validate token format to avoid invalid states
Suggested Fix
Review and simplify this section.
Consider Localizing Or Externalizing Modal...
Where
src/components/common/SingleTabModal.tsx:28
Issue / Evidence
Consider localizing or externalizing modal texts for easier maintenance and i18n support
Suggested Fix
Review and simplify this section.
The Commit Message "Merged In Manya 1 (Pul...
Where
commit message
Issue / Evidence
The commit message "Merged in Manya-1 (pull request #44)" is vague, lacks description
Suggested Fix
use standardized descriptive messages explaining purpose and key changes
Code Change Preview · src/App.tsx
?
Removed / Before Commit
- diff --git a/src/App.tsx b/src/App.tsx - index 99ecb9d..b1baed8 100644 - import SportsDetailPage from "./pages/SportsDetailPage"; - import LiveCasinoPage from "./pages/LiveCasinoPage"; - import TransactionPage from "./pages/Transaction"; - - function App() { - const { isLoading, tenant, isSuspended } = useConfig(); - - if (isLoading) return <GlobalLoader />; - - } - - return ( - <BrowserRouter> - <Routes> - {/* Auth Routes */} - <Route path="/login" element={<Login />} />
Added / After Commit
+ diff --git a/src/App.tsx b/src/App.tsx + index 99ecb9d..b1baed8 100644 + import SportsDetailPage from "./pages/SportsDetailPage"; + import LiveCasinoPage from "./pages/LiveCasinoPage"; + import TransactionPage from "./pages/Transaction"; + import { checkSingleTab} from "./utils/singleTab"; + import { useEffect, useState } from "react"; + import SingleTabModal from "./components/common/SingleTabModal"; + + function App() { + const { isLoading, tenant, isSuspended } = useConfig(); + const [showSingleTabModal, setShowSingleTabModal] = useState(false); + + useEffect(() => { + const result = checkSingleTab(); + if (result.showToast) { + setShowSingleTabModal(true); + }
Removed / Before Commit
- diff --git a/src/components/auth/LoginForm.tsx b/src/components/auth/LoginForm.tsx - index 046394b..63bb259 100644 - import { useIsB2C } from "../../hooks"; - import { markLoginSocketForceLogoutPending } from "../../utils/loginSocketSession"; - import toast from 'react-hot-toast'; - - interface LoginFormProps { - onSwitch: () => void; - const [loading, setLoading] = useState(false); - - const handlePhoneChange = (e: React.ChangeEvent<HTMLInputElement>) => { - // Only allow digits - const value = e.target.value.replace(/\D/g, ""); - setPhone(value); - setOtpSent(false); - }); - - if (response.status) {
Added / After Commit
+ diff --git a/src/components/auth/LoginForm.tsx b/src/components/auth/LoginForm.tsx + index 046394b..63bb259 100644 + import { useIsB2C } from "../../hooks"; + import { markLoginSocketForceLogoutPending } from "../../utils/loginSocketSession"; + import toast from 'react-hot-toast'; + import { setLogin } from "../../utils/singleTab"; + + interface LoginFormProps { + onSwitch: () => void; + const [loading, setLoading] = useState(false); + + const handlePhoneChange = (e: React.ChangeEvent<HTMLInputElement>) => { + + const value = e.target.value.replace(/\D/g, ""); + setPhone(value); + setOtpSent(false); + }); +
Removed / Before Commit
- diff --git a/src/components/common/SingleTabModal.tsx b/src/components/common/SingleTabModal.tsx - new file mode 100644 - index 0000000..9e7b27c
Added / After Commit
+ diff --git a/src/components/common/SingleTabModal.tsx b/src/components/common/SingleTabModal.tsx + new file mode 100644 + index 0000000..9e7b27c + import { useThemeColors } from "../../hooks/useThemeColors"; + + interface SingleTabModalProps { + onConfirm: () => void; + } + + export default function SingleTabModal({ onConfirm }: SingleTabModalProps) { + const colors = useThemeColors(); + + return ( + <div + className="fixed inset-0 z-[9999] flex items-center justify-center" + style={{ backgroundColor: "rgba(0, 0, 0, 0.75)" }} + > + <div
Removed / Before Commit
- diff --git a/src/components/racing/MarketCard.tsx b/src/components/racing/MarketCard.tsx - index 9de3cf9..35a7346 100644 - {insuranceBetEnabled ? "Insurance Bet" : ""} - {insuranceBetEnabled && <i className="fa-regular fa-circle-question text-[11px]"></i>} - - <div className="absolute left-0 top-[22px] hidden group-hover:block z-50 w-[650px] bg-[#1f2937] text-white text-[11px] leading-[18px] p-3 rounded shadow-lg border border-gray-700 whitespace-pre-line"> - {`An “insurance bet” is a betting market in which the total bet amount placed by the customer is equally divided among all available Win and various Place markets available at the time the bet is placed for the selected horse. - The payout for the insurance bet shall be determined based on the final finishing position of the selected horse: - - If the horse wins, the applicable Win and Place market returns may apply. - - If the horse finishes in a qualifying place position, the payout shall be settled according to the relevant Place market terms. - - If the horse finishes outside the eligible positions, no payout shall be payable. - Accordingly, an insurance bet provides coverage across multiple available betting markets by distributing the stake equally at the time of placement, with settlement based on the horse’s finishing result.`} - </div> - </div> - - <div></div> - - <div className="text-center text-gray-400 text-[12px]">Back</div>
Added / After Commit
+ diff --git a/src/components/racing/MarketCard.tsx b/src/components/racing/MarketCard.tsx + index 9de3cf9..35a7346 100644 + {insuranceBetEnabled ? "Insurance Bet" : ""} + {insuranceBetEnabled && <i className="fa-regular fa-circle-question text-[11px]"></i>} + + {insuranceBetEnabled ? <div className="absolute left-0 top-[22px] hidden group-hover:block z-50 w-[650px] bg-[#1f2937] text-white text-[11px] leading-[18px] p-3 rounded shadow-lg border border-gray-700 whitespace-pre-line"> + {insuranceBetEnabled ?`An “insurance bet” is a betting market in which the total bet amount placed by the customer is equally divided among all available Win and various Place markets available at the time the bet is placed for the selected horse. + The payout for the insurance bet shall be determined based on the final finishing position of the selected horse: + - If the horse wins, the applicable Win and Place market returns may apply. + - If the horse finishes in a qualifying place position, the payout shall be settled according to the relevant Place market terms. + - If the horse finishes outside the eligible positions, no payout shall be payable. + Accordingly, an insurance bet provides coverage across multiple available betting markets by distributing the stake equally at the time of placement, with settlement based on the horse’s finishing result.`:" "} + </div>: ""} + </div> + <div></div> + + <div className="text-center text-gray-400 text-[12px]">Back</div>
Removed / Before Commit
- diff --git a/src/contexts/AuthContext.tsx b/src/contexts/AuthContext.tsx - index 250c2e5..c9c22e5 100644 - } from "react"; - import { queueSessionExpiredModal } from "../utils/authSessionExpired"; - import { dispatchLogoutEvent } from "../utils/authEvents"; - import { useLoginSocket } from "../hooks/useLoginSoket"; - import { authService } from "../services/authService"; - - - checkTokenSync(); - - // Also check on visibility change (when user switches tabs) - const handleVisibilityChange = () => { - if (!document.hidden) { - console.log('👁️ [TokenSync] Tab became visible, checking token sync'); - true; - - try {
Added / After Commit
+ diff --git a/src/contexts/AuthContext.tsx b/src/contexts/AuthContext.tsx + index 250c2e5..c9c22e5 100644 + } from "react"; + import { queueSessionExpiredModal } from "../utils/authSessionExpired"; + import { dispatchLogoutEvent } from "../utils/authEvents"; + import { logout as singleTabLogout } from "../utils/singleTab"; + import { useLoginSocket } from "../hooks/useLoginSoket"; + import { authService } from "../services/authService"; + + + checkTokenSync(); + + + const handleVisibilityChange = () => { + if (!document.hidden) { + console.log('👁️ [TokenSync] Tab became visible, checking token sync'); + true; +
Removed / Before Commit
- diff --git a/src/pages/MultiRaceDetail.tsx b/src/pages/MultiRaceDetail.tsx - index 53b882d..2732321 100644 - type MultiBetRace, - type PlaceMultiBetRequest, - } from "../services/racing" - import { useThemeColors } from "../hooks/useThemeColors" - import { formatRaceDateTime } from "../utils/dateFormat" - import { useAuth } from "../contexts/AuthContext" - ? slipSelections.reduce((total, selection) => total * (selection.runner.backOdd || 0), 1) - : 0 - - // Calculate potential payout with compound profit logic - const potentialPayout = useMemo(() => { - if (slipSelections.length === 0 || stakeAmount <= 0) { - return 0 - currentAmount = currentAmount + profit - } -
Added / After Commit
+ diff --git a/src/pages/MultiRaceDetail.tsx b/src/pages/MultiRaceDetail.tsx + index 53b882d..2732321 100644 + type MultiBetRace, + type PlaceMultiBetRequest, + } from "../services/racing" + import { betService } from "../services/betService" + import { useThemeColors } from "../hooks/useThemeColors" + import { formatRaceDateTime } from "../utils/dateFormat" + import { useAuth } from "../contexts/AuthContext" + ? slipSelections.reduce((total, selection) => total * (selection.runner.backOdd || 0), 1) + : 0 + + + const potentialPayout = useMemo(() => { + if (slipSelections.length === 0 || stakeAmount <= 0) { + return 0 + currentAmount = currentAmount + profit + }