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
?
72%
Quality Avg
?
77%
Security Avg
?
64%
Reviews
?
91
Review Result ?
solutionbowl/super-admin · 239001ab
This commit introduces a new XLSX export feature for bets with well-structured types, dynamic import of the xlsx library, and UI integration. It improves business value by enabling users to export data easily. The code is mostly clean and uses types, but some small improvements in error handling and filename sanitization could enhance robustness and security.
Quality
?
85%
Security
?
70%
Business Value
?
90%
Maintainability
?
85%
Issues & Suggested Fixes
?
1AI detects the issue
2Shows file, line, or evidence
3Suggests the fix to apply
Lack Of Error Handling On Excel File Creat...
Where
src/components/common/BetsExcelExportButton.tsx:93
Issue / Evidence
Lack of error handling on Excel file creation can cause silent failures
Suggested Fix
add try-catch around workbook and sheet creation
Missing Validation
Where
src/components/common/BetsExcelExportButton.tsx:178
Issue / Evidence
No validation or sanitization of filename before writing file
Suggested Fix
sanitize filename to prevent path traversal or injection
Ensurexlsxfilename Only Checks Lower Case
Where
src/components/common/BetsExcelExportButton.tsx:182
Issue / Evidence
ensureXlsxFilename only checks lower case
Suggested Fix
normalize input before check for robustness
Tocelltext Converting Undefined/Null To Em...
Where
src/components/common/BetsExcelExportButton.tsx:186
Issue / Evidence
toCellText converting undefined/null to empty string may obscure missing data
Suggested Fix
consider stricter handling or explicit null representation
Vague And Non Descriptive
Where
commit message
Issue / Evidence
vague and non-descriptive
Suggested Fix
improve commit message to describe the feature and impact clearly for better maintainability and traceability
Code Change Preview · package.json
?
Removed / Before Commit
- diff --git a/package.json b/package.json - index 4aa3e60..f7a01c0 100644 - "input-otp": "^1.4.2", - "lucide-react": "^0.575.0", - "motion": "^12.38.0", - "react": "^19.2.0", - "react-day-picker": "^9.14.0", - "react-dom": "^19.2.0", - "tw-animate-css": "^1.3.4", - "vaul": "^1.1.2", - "vite-tsconfig-paths": "^6.0.2", - "zod": "^3.24.2" - }, - "devDependencies": {
Added / After Commit
+ diff --git a/package.json b/package.json + index 4aa3e60..f7a01c0 100644 + "input-otp": "^1.4.2", + "lucide-react": "^0.575.0", + "motion": "^12.38.0", + "number-to-words": "^1.2.4", + "react": "^19.2.0", + "react-day-picker": "^9.14.0", + "react-dom": "^19.2.0", + "tw-animate-css": "^1.3.4", + "vaul": "^1.1.2", + "vite-tsconfig-paths": "^6.0.2", + "xlsx": "^0.18.5", + "zod": "^3.24.2" + }, + "devDependencies": {
Removed / Before Commit
- diff --git a/src/components/common/BetsExcelExportButton.tsx b/src/components/common/BetsExcelExportButton.tsx - new file mode 100644 - index 0000000..38fb882
Added / After Commit
+ diff --git a/src/components/common/BetsExcelExportButton.tsx b/src/components/common/BetsExcelExportButton.tsx + new file mode 100644 + index 0000000..38fb882 + import { useState, type ComponentProps, type ReactNode } from "react"; + import { Download } from "lucide-react"; + import { toast } from "sonner"; + import { Button } from "@/components/ui/button"; + + type CellValue = string | number | boolean | null | undefined; + type XlsxModule = typeof import("xlsx"); + + export type BetsExcelLeg = { + raceId?: CellValue; + market?: CellValue; + marketId?: CellValue; + runnerId?: CellValue; + selectionNo?: CellValue; + runnerName?: CellValue;
Removed / Before Commit
- diff --git a/src/routes/bet-list.tsx b/src/routes/bet-list.tsx - index c6d13a2..9f3be0b 100644 - import type { DateRange } from "react-day-picker"; - import { Button } from "@/components/ui/button"; - import { Input } from "@/components/ui/input"; - import CsvExportButton from "@/components/common/CsvExportButton"; - import { - Select, - SelectContent, - } from "@/components/ui/select"; - import { internalServices } from "@/services/api/internalServices"; - import type { Bet, MultiBetLeg } from "@/services/api/betTypes"; - import type { CsvColumn } from "@/utils/csv/types"; - import { cn } from "@/lib/utils"; - - export const Route = createFileRoute("/bet-list")({ - const activeIsLoading = isAgentMode ? agentBetsQuery.isLoading : isLoading; - const activeIsFetching = isAgentMode ? agentBetsQuery.isFetching : isFetching;
Added / After Commit
+ diff --git a/src/routes/bet-list.tsx b/src/routes/bet-list.tsx + index c6d13a2..9f3be0b 100644 + import type { DateRange } from "react-day-picker"; + import { Button } from "@/components/ui/button"; + import { Input } from "@/components/ui/input"; + import BetsExcelExportButton from "@/components/common/BetsExcelExportButton"; + import { + Select, + SelectContent, + } from "@/components/ui/select"; + import { internalServices } from "@/services/api/internalServices"; + import type { Bet, MultiBetLeg } from "@/services/api/betTypes"; + import { cn } from "@/lib/utils"; + + export const Route = createFileRoute("/bet-list")({ + const activeIsLoading = isAgentMode ? agentBetsQuery.isLoading : isLoading; + const activeIsFetching = isAgentMode ? agentBetsQuery.isFetching : isFetching; + const activeError = isAgentMode ? agentBetsQuery.error : error;
Removed / Before Commit
- diff --git a/src/routes/bet-ticket.tsx b/src/routes/bet-ticket.tsx - index cacc2ea..8bee2a3 100644 - import { cn } from "@/lib/utils"; - import { format } from "date-fns"; - import { useAutoRefresh } from "@/hooks/useAutoRefresh"; - import CsvExportButton from "@/components/common/CsvExportButton"; - import type { CsvColumn } from "@/utils/csv/types"; - - export const Route = createFileRoute("/bet-ticket")({ - component: BetTicketPage, - const total = data?.total ?? 0; - const totalItems = data?.total ?? 0; - const totalPages = data?.totalPages ?? 1; - const csvColumns = useMemo( - () => - [ - { header: "Legs", accessor: (bet) => bet.totalLegs ?? bet.legs?.length ?? "" }, - { header: "User", accessor: (bet) => bet.username || "-" },
Added / After Commit
+ diff --git a/src/routes/bet-ticket.tsx b/src/routes/bet-ticket.tsx + index cacc2ea..8bee2a3 100644 + import { cn } from "@/lib/utils"; + import { format } from "date-fns"; + import { useAutoRefresh } from "@/hooks/useAutoRefresh"; + import BetsExcelExportButton from "@/components/common/BetsExcelExportButton"; + + export const Route = createFileRoute("/bet-ticket")({ + component: BetTicketPage, + const total = data?.total ?? 0; + const totalItems = data?.total ?? 0; + const totalPages = data?.totalPages ?? 1; + + const [currentPage, setCurrentPage] = useState(1); + // const PAGE_SIZE = 20; + <RefreshCw className={cn("mr-2 h-4 w-4", isFetching && "animate-spin")} /> + Refresh {countdown}s + </Button>
Removed / Before Commit
- diff --git a/src/routes/transfer.tsx b/src/routes/transfer.tsx - index 1c5baa2..d1e0c96 100644 - import { createFileRoute } from "@tanstack/react-router"; - import { useEffect, useMemo, useState, type ReactNode } from "react"; - import { useMutation, useQuery } from "@tanstack/react-query"; - import { motion } from "motion/react"; - import { toast } from "sonner"; - - type TransferTabValue = "all" | CreatableRole; - type TransferTab = { value: TransferTabValue; label: string }; - - function StatCell({ - label, - const [selectedRows, setSelectedRows] = useState<string[]>([]); - const [transferTarget, setTransferTarget] = useState<TransferRow | null>(null); - const [transferAmount, setTransferAmount] = useState(""); - const pageSize = 20; -
Added / After Commit
+ diff --git a/src/routes/transfer.tsx b/src/routes/transfer.tsx + index 1c5baa2..d1e0c96 100644 + import { createFileRoute } from "@tanstack/react-router"; + import { useEffect, useMemo, useRef, useState, type ReactNode } from "react"; + import { useMutation, useQuery } from "@tanstack/react-query"; + import { motion } from "motion/react"; + import { toast } from "sonner"; + + type TransferTabValue = "all" | CreatableRole; + type TransferTab = { value: TransferTabValue; label: string }; + type BulkTransferRow = { + key: string; + childUserId: string; + loginName: string; + action: TransferAction; + amount: string; + }; +
Removed / Before Commit
- diff --git a/src/routes/user-details.$userId.tsx b/src/routes/user-details.$userId.tsx - index 8e90c94..f659b5c 100644 - import { setExposureParams } from "@/lib/nav-params"; - import type { MultiBetLeg } from "@/services/api/betTypes"; - import CsvExportButton from "@/components/common/CsvExportButton"; - import type { CsvColumn } from "@/utils/csv/types"; - - export const Route = createFileRoute("/user-details/$userId")({ - () => bets.slice((currentPage - 1) * pageSize, currentPage * pageSize), - [bets, currentPage], - ); - const csvColumns = useMemo( - () => - [ - { header: "Legs", accessor: (bet) => getUserBetTotalLegs(bet) }, - { header: "Bet ID", accessor: (bet) => bet.bet_id ?? "" }, - { header: "Market", accessor: (bet) => bet.market_name ?? "" }, - { header: "Selection", accessor: (bet) => bet.selection ?? "" },
Added / After Commit
+ diff --git a/src/routes/user-details.$userId.tsx b/src/routes/user-details.$userId.tsx + index 8e90c94..f659b5c 100644 + import { setExposureParams } from "@/lib/nav-params"; + import type { MultiBetLeg } from "@/services/api/betTypes"; + import CsvExportButton from "@/components/common/CsvExportButton"; + import BetsExcelExportButton from "@/components/common/BetsExcelExportButton"; + import type { CsvColumn } from "@/utils/csv/types"; + + export const Route = createFileRoute("/user-details/$userId")({ + () => bets.slice((currentPage - 1) * pageSize, currentPage * pageSize), + [bets, currentPage], + ); + return ( + <Card className="overflow-hidden rounded-2xl border-border/60 bg-card shadow-elegant"> + {/* Filters */} + {f} + </Button> + ))}
Removed / Before Commit
- diff --git a/src/routes/user-exposure-bet.tsx b/src/routes/user-exposure-bet.tsx - new file mode 100644 - index 0000000..87ea24c
Added / After Commit
+ diff --git a/src/routes/user-exposure-bet.tsx b/src/routes/user-exposure-bet.tsx + new file mode 100644 + index 0000000..87ea24c + import { createFileRoute } from '@tanstack/react-router' + + export const Route = createFileRoute('/user-exposure-bet')({ + component: RouteComponent, + }) + + function RouteComponent() { + return <div>Hello "/user-exposure-bet"!</div> + }
Removed / Before Commit
- diff --git a/src/routes/user-listing.tsx b/src/routes/user-listing.tsx - index 472568d..e3e0b1d 100644 - const [editCreditRefUser, setEditCreditRefUser] = useState<DownlineUser | null>(null); - const [newCreditRefValue, setNewCreditRefValue] = useState(""); - const [creditRefError, setCreditRefError] = useState(""); - - useEffect(() => { - setPageInput(String(currentPage)); - <div className="flex flex-wrap items-center divide-x divide-black/10 "> - <StatCell - label="Credit Limit" - value={<div style={{ marginTop: "20px" }}>{fmt(creditSummary.creditLimit)} </div>} - /> - <StatCell - label="Distributed to downline" - </span> - )} - </div>
Added / After Commit
+ diff --git a/src/routes/user-listing.tsx b/src/routes/user-listing.tsx + index 472568d..e3e0b1d 100644 + const [editCreditRefUser, setEditCreditRefUser] = useState<DownlineUser | null>(null); + const [newCreditRefValue, setNewCreditRefValue] = useState(""); + const [creditRefError, setCreditRefError] = useState(""); + const createCreditLimitWords = formatRupeesInWords(formData.creditLimit); + + useEffect(() => { + setPageInput(String(currentPage)); + <div className="flex flex-wrap items-center divide-x divide-black/10 "> + <StatCell + label="Credit Limit" + value={ + <div className="mt-5"> + <div>{fmt(creditSummary.creditLimit)}</div> + + <div className="mt-1 text-xs font-semibold text-green-600"> + {creditSummary.creditLimit
Removed / Before Commit
- diff --git a/src/services/api/internalServices.ts b/src/services/api/internalServices.ts - index 9d5d297..5a0fc1f 100644 - CreatePanelUserRequest, - CreditTransferRequest, - CreditTransferResult, - TransferSubmitRequest, - TransferListResponse, - DownlineUser, - const root = asRecord(payload); - return asRecord(root.data ?? payload) as TransferListResponse; - }, - async submitTransfer(payload: TransferSubmitRequest) { - return authApiClient.post<unknown>(authConfig.panelAuthEndpoints.transferList, payload); - }, - async balance() {
Added / After Commit
+ diff --git a/src/services/api/internalServices.ts b/src/services/api/internalServices.ts + index 9d5d297..5a0fc1f 100644 + CreatePanelUserRequest, + CreditTransferRequest, + CreditTransferResult, + BulkTransferSubmitRequest, + TransferSubmitRequest, + TransferListResponse, + DownlineUser, + const root = asRecord(payload); + return asRecord(root.data ?? payload) as TransferListResponse; + }, + async submitTransfer(payload: TransferSubmitRequest | BulkTransferSubmitRequest) { + return authApiClient.post<unknown>(authConfig.panelAuthEndpoints.transferList, payload); + }, + async balance() {
Removed / Before Commit
- diff --git a/src/services/api/internalTypes.ts b/src/services/api/internalTypes.ts - index f9031fc..93e3c52 100644 - action: TransferAction; - } - - export interface TransferListProfile { - id?: string; - username?: string; - id?: string; - username?: string; - role?: string; - isBetBlocked: boolean, - isTransferBlocked: boolean, - }; - credit?: { - creditLimit?: number; - redirect_link?: string; - publish_time?: string;
Added / After Commit
+ diff --git a/src/services/api/internalTypes.ts b/src/services/api/internalTypes.ts + index f9031fc..93e3c52 100644 + action: TransferAction; + } + + export interface BulkTransferSubmitRequest { + data: Array<{ + childUserId: string; + action: TransferAction; + amount: number; + }>; + } + + export interface TransferListProfile { + id?: string; + username?: string; + id?: string; + username?: string;