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 · 6c2ec1b3
This commit adds a comprehensive Excel export feature for bets with multiple layouts accommodating different report styles. The code modularizes the export functionality and dynamically builds Excel sheets with relevant headers and rows based on layout. It also integrates linking between main bets and their legs. However, it lacks comments and type safety checks that could improve maintainability and reduce risks. No security-critical functionality was changed, but exporting potentially sensitive info (like IP addresses) requires careful consideration. The commit message is vague and lacks detail on changes.
Quality
?
85%
Security
?
50%
Business Value
?
80%
Maintainability
?
78%
Issues & Suggested Fixes
?
1AI detects the issue
2Shows file, line, or evidence
3Suggests the fix to apply
Poor Description
Where
commit message
Issue / Evidence
poor description
Suggested Fix
improve commit message to clearly describe the added features and changes
Lack Of Comments
Where
src/components/common/BetsExcelExportButton.tsx:94
Issue / Evidence
lack of comments
Suggested Fix
add inline comments explaining critical functions and data structures for maintainability
Missing Validation
Where
src/components/common/BetsExcelExportButton.tsx:104
Issue / Evidence
no validation on bets or leg data
Suggested Fix
add type checks or validation to avoid runtime errors
Cell Link Setting
Where
src/components/common/BetsExcelExportButton.tsx:126
Issue / Evidence
cell link setting
Suggested Fix
add check to ensure target row exists to avoid broken internal links in Excel
Sensitive Info Exposed
Where
src/components/common/BetsExcelExportButton.tsx:189
Issue / Evidence
sensitive info exposed
Suggested Fix
verify proper handling/permission for exporting user IP addresses
Duplicate Logic
Where
src/components/common/BetsExcelExportButton.tsx:247
Issue / Evidence
repeated similar code patterns in getMainRow and getLegRow
Suggested Fix
refactor to reduce duplication
Excel Sheet Creation
Where
src/components/common/BetsExcelExportButton.tsx:121
Issue / Evidence
Excel sheet creation
Suggested Fix
add error handling around XLSX library calls to gracefully handle failures
Code Change Preview · src/components/common/BetsExcelExportButton.tsx
?
Removed / Before Commit
- diff --git a/src/components/common/BetsExcelExportButton.tsx b/src/components/common/BetsExcelExportButton.tsx - index 38fb882..07aac68 100644 - filename: string; - rows: TRow[]; - getBet: (row: TRow) => BetsExcelBet; - onExport?: (rows: number) => void; - children?: ReactNode; - }; - filename, - rows, - getBet, - children = "Download XLSX", - disabled, - className, - setIsExporting(true); - const XLSX = await import("xlsx"); - - exportBetsExcel(XLSX, rows.map(getBet), filename);
Added / After Commit
+ diff --git a/src/components/common/BetsExcelExportButton.tsx b/src/components/common/BetsExcelExportButton.tsx + index 38fb882..07aac68 100644 + filename: string; + rows: TRow[]; + getBet: (row: TRow) => BetsExcelBet; + layout?: "default" | "bet-ticket" | "bet-list"; + onExport?: (rows: number) => void; + children?: ReactNode; + }; + filename, + rows, + getBet, + layout = "default", + children = "Download XLSX", + disabled, + className, + setIsExporting(true); + const XLSX = await import("xlsx");
Removed / Before Commit
- diff --git a/src/components/common/NetExposureExcelExportButton.tsx b/src/components/common/NetExposureExcelExportButton.tsx - new file mode 100644 - index 0000000..ad81f10
Added / After Commit
+ diff --git a/src/components/common/NetExposureExcelExportButton.tsx b/src/components/common/NetExposureExcelExportButton.tsx + new file mode 100644 + index 0000000..ad81f10 + import { useState } from "react"; + import { Download } from "lucide-react"; + import { toast } from "sonner"; + import { Button } from "@/components/ui/button"; + import { tenantApiClient } from "@/services/api/apiClient"; + + type ExposureMarket = { + marketId: string; + marketName: string; + totalStake: number; + worstBook: number; + bestBook: number; + }; + + type ExposureRace = {
Removed / Before Commit
- diff --git a/src/components/common/ProfitLossExcelExportButton.tsx b/src/components/common/ProfitLossExcelExportButton.tsx - new file mode 100644 - index 0000000..c5726d3
Added / After Commit
+ diff --git a/src/components/common/ProfitLossExcelExportButton.tsx b/src/components/common/ProfitLossExcelExportButton.tsx + new file mode 100644 + index 0000000..c5726d3 + import { useState } from "react"; + import { Download } from "lucide-react"; + import { toast } from "sonner"; + import { Button } from "@/components/ui/button"; + + type CellValue = string | number | boolean | null | undefined; + + export type ProfitLossExcelDetail = { + rowIndex: number; + sheetName: string; + loadBets: () => Promise<unknown>; + }; + + export default function ProfitLossExcelExportButton({ + filename,
Removed / Before Commit
- diff --git a/src/components/common/XlsxExportButton.tsx b/src/components/common/XlsxExportButton.tsx - new file mode 100644 - index 0000000..49f7eb5
Added / After Commit
+ diff --git a/src/components/common/XlsxExportButton.tsx b/src/components/common/XlsxExportButton.tsx + new file mode 100644 + index 0000000..49f7eb5 + import { useState, type ComponentProps, type ReactNode } from "react"; + import { Download } from "lucide-react"; + import { Button } from "@/components/ui/button"; + import type { CsvColumn } from "@/utils/csv/types"; + + export default function XlsxExportButton<TRow>({ + filename, + rows, + columns, + children = "Download XLSX", + disabled, + ...buttonProps + }: Omit<ComponentProps<typeof Button>, "onClick"> & { + filename: string; + rows: TRow[];
Removed / Before Commit
- diff --git a/src/routes/bet-list.tsx b/src/routes/bet-list.tsx - index 48696cb..f363642 100644 - filename={`bets-list-${formatApiDate(new Date())}.xlsx`} - rows={bets} - getBet={getBetExcelRow} - disabled={activeIsLoading || Boolean(activeError)} - className="ml-auto h-9 rounded-md bg-emerald-600 px-4 text-white hover:bg-emerald-700" - />
Added / After Commit
+ diff --git a/src/routes/bet-list.tsx b/src/routes/bet-list.tsx + index 48696cb..f363642 100644 + filename={`bets-list-${formatApiDate(new Date())}.xlsx`} + rows={bets} + getBet={getBetExcelRow} + layout="bet-list" + disabled={activeIsLoading || Boolean(activeError)} + className="ml-auto h-9 rounded-md bg-emerald-600 px-4 text-white hover:bg-emerald-700" + />
Removed / Before Commit
- diff --git a/src/routes/bet-ticket.tsx b/src/routes/bet-ticket.tsx - index 9c39012..2c7b934 100644 - filename={`bet-ticket-${new Date().toISOString().slice(0, 10)}.xlsx`} - rows={bets} - getBet={getBetTickerExcelRow} - disabled={isLoading} - className="h-9 rounded-lg bg-emerald-600 px-4 text-white hover:bg-emerald-700" - /> - <table className="w-full min-w-[780px] border-collapse text-left text-xs"> - <thead className="bg-background"> - <tr> - {["Leg", "Race", "Market", "Market ID", "Runner", "Selection", "Status"].map((h) => ( - <th key={h} className="whitespace-nowrap px-3 py-2 font-bold text-muted-foreground"> - {h} - </th>
Added / After Commit
+ diff --git a/src/routes/bet-ticket.tsx b/src/routes/bet-ticket.tsx + index 9c39012..2c7b934 100644 + filename={`bet-ticket-${new Date().toISOString().slice(0, 10)}.xlsx`} + rows={bets} + getBet={getBetTickerExcelRow} + layout="bet-ticket" + disabled={isLoading} + className="h-9 rounded-lg bg-emerald-600 px-4 text-white hover:bg-emerald-700" + /> + <table className="w-full min-w-[780px] border-collapse text-left text-xs"> + <thead className="bg-background"> + <tr> + {["Leg", "Race", "Market", "Market ID", "Runner", "Odds","Selection", "Status"].map((h) => ( + <th key={h} className="whitespace-nowrap px-3 py-2 font-bold text-muted-foreground"> + {h} + </th>
Removed / Before Commit
- diff --git a/src/routes/net-exposure.tsx b/src/routes/net-exposure.tsx - index 63d4d39..b09ed7b 100644 - import { internalServices } from "@/services/api/internalServices"; - import { setExposureParams } from "@/lib/nav-params"; - import { useRefreshToggle } from "@/hooks/useRefreshToggle"; - - export const Route = createFileRoute("/net-exposure")({ - component: NetExposurePage, - "Greyhound Racing", - ]; - - const { data: rawData, isLoading, error, refetch, isFetching } = useQuery({ - queryKey: ["net-exposure"], - queryFn: fetchNetExposure, - refetchOnMount: "always", - {/* Title + Refresh */} - <div className="flex flex-col gap-4 border-b border-border/60 p-5 lg:flex-row lg:items-center lg:justify-between"> - <h2 className="text-xl font-bold tracking-tight">Net Exposure</h2>
Added / After Commit
+ diff --git a/src/routes/net-exposure.tsx b/src/routes/net-exposure.tsx + index 63d4d39..b09ed7b 100644 + import { internalServices } from "@/services/api/internalServices"; + import { setExposureParams } from "@/lib/nav-params"; + import { useRefreshToggle } from "@/hooks/useRefreshToggle"; + import NetExposureExcelExportButton from "@/components/common/NetExposureExcelExportButton"; + + export const Route = createFileRoute("/net-exposure")({ + component: NetExposurePage, + "Greyhound Racing", + ]; + + const { + data: rawData, + isLoading, + error, + refetch, + isFetching,
Removed / Before Commit
- diff --git a/src/routes/pl-by-agent.tsx b/src/routes/pl-by-agent.tsx - index 940a3d9..1cc2e50 100644 - import { Card } from "@/components/ui/card"; - import { Button } from "@/components/ui/button"; - import DateRangePicker from "@/components/common/DateRangePicker"; - import CsvExportButton from "@/components/common/CsvExportButton"; - import { - Table, - TableBody, - agent: string; - market: string; - totals: ProfitLossTotals; - }; - - const defaultFilters = { - Race: row.race, - Agent: row.agent, - Market: row.market,
Added / After Commit
+ diff --git a/src/routes/pl-by-agent.tsx b/src/routes/pl-by-agent.tsx + index 940a3d9..1cc2e50 100644 + import { Card } from "@/components/ui/card"; + import { Button } from "@/components/ui/button"; + import DateRangePicker from "@/components/common/DateRangePicker"; + import ProfitLossExcelExportButton from "@/components/common/ProfitLossExcelExportButton"; + import { + Table, + TableBody, + agent: string; + market: string; + totals: ProfitLossTotals; + raceId: string; + agentId: string; + }; + + const defaultFilters = { + Race: row.race,
Removed / Before Commit
- diff --git a/src/routes/pl-by-market.tsx b/src/routes/pl-by-market.tsx - index 63f4500..d7b7ff2 100644 - import { Card } from "@/components/ui/card"; - import { Button } from "@/components/ui/button"; - import DateRangePicker from "@/components/common/DateRangePicker"; - import CsvExportButton from "@/components/common/CsvExportButton"; - import { - Table, - TableBody, - date: string; - race: string; - market: string; - totals: ProfitLossTotals; - }; - - - useReportSocket({ onProfitLoss: refreshFromSocket }); -
Added / After Commit
+ diff --git a/src/routes/pl-by-market.tsx b/src/routes/pl-by-market.tsx + index 63f4500..d7b7ff2 100644 + import { Card } from "@/components/ui/card"; + import { Button } from "@/components/ui/button"; + import DateRangePicker from "@/components/common/DateRangePicker"; + import ProfitLossExcelExportButton from "@/components/common/ProfitLossExcelExportButton"; + import { + Table, + TableBody, + date: string; + race: string; + market: string; + marketId: string; + totals: ProfitLossTotals; + }; + + + useReportSocket({ onProfitLoss: refreshFromSocket });
Removed / Before Commit
- diff --git a/src/routes/transfer.tsx b/src/routes/transfer.tsx - index dd93b4f..e7bd987 100644 - loginName: string; - action: TransferAction; - amount: string; - }; - - function StatCell({ - loginName: row.loginName, - action: getTransferAction(row) as TransferAction, - amount: getTransferAmount(row), - })), - ); - }; - }; - - const focusBulkTransferAmount = (rowKey: string) => { - bulkAmountInputRefs.current[rowKey]?.focus();
Added / After Commit
+ diff --git a/src/routes/transfer.tsx b/src/routes/transfer.tsx + index dd93b4f..e7bd987 100644 + loginName: string; + action: TransferAction; + amount: string; + isEditing: boolean; + }; + + function StatCell({ + loginName: row.loginName, + action: getTransferAction(row) as TransferAction, + amount: getTransferAmount(row), + isEditing: false, + })), + ); + }; + }; +
Removed / Before Commit
- diff --git a/src/routes/user-details.$userId.tsx b/src/routes/user-details.$userId.tsx - index 1985931..8dcabf0 100644 - import { format } from "date-fns"; - 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")({ - component: UserDetailsPage, - - return ( - <Card className="overflow-hidden rounded-2xl border-border/60 bg-card shadow-elegant"> - <div className="overflow-x-auto"> - <Table> - <TableHeader> -
Added / After Commit
+ diff --git a/src/routes/user-details.$userId.tsx b/src/routes/user-details.$userId.tsx + index 1985931..8dcabf0 100644 + import { format } from "date-fns"; + import { setExposureParams } from "@/lib/nav-params"; + import type { MultiBetLeg } from "@/services/api/betTypes"; + import BetsExcelExportButton from "@/components/common/BetsExcelExportButton"; + import XlsxExportButton from "@/components/common/XlsxExportButton"; + import type { CsvColumn } from "@/utils/csv/types"; + import NetExposureExcelExportButton from "@/components/common/NetExposureExcelExportButton"; + + export const Route = createFileRoute("/user-details/$userId")({ + component: UserDetailsPage, + + return ( + <Card className="overflow-hidden rounded-2xl border-border/60 bg-card shadow-elegant"> + <div className="flex justify-end border-b border-border/60 p-4"> + <XlsxExportButton + filename={`user-activity-${new Date().toISOString().slice(0, 10)}.xlsx`}
Removed / Before Commit
- diff --git a/src/routes/user-exposure-detail.tsx b/src/routes/user-exposure-detail.tsx - index 1e82173..7aa0a9d 100644 - return (Array.isArray(data) ? data : []) as OddsMarket[]; - } - - async function fetchHierarchy( - raceId: string, - marketId: string,
Added / After Commit
+ diff --git a/src/routes/user-exposure-detail.tsx b/src/routes/user-exposure-detail.tsx + index 1e82173..7aa0a9d 100644 + return (Array.isArray(data) ? data : []) as OddsMarket[]; + } + + + + async function fetchHierarchy( + raceId: string, + marketId: string,
Removed / Before Commit
- diff --git a/src/routes/user-listing.tsx b/src/routes/user-listing.tsx - index 2b8a7d1..8bf8c22 100644 - Pencil, - Plus, - RefreshCw, - Unlock, - } from "lucide-react"; - import { - DownlineUser, - UserBlockStatusResult, - } from "@/services/api/internalTypes"; - import CsvExportButton from "@/components/common/CsvExportButton"; - import type { CsvColumn } from "@/utils/csv/types"; - import { cn } from "@/lib/utils"; - - const statusFilterValues = ["ACTIVE", "INACTIVE", "CLOSED"] as const; - const [selectedDownlineTab, setSelectedDownlineTab] = useState<DownlineTab>("all"); - const [drillDownlineUserId, setDrillDownlineUserId] = useState<string | null>(null);
Added / After Commit
+ diff --git a/src/routes/user-listing.tsx b/src/routes/user-listing.tsx + index 2b8a7d1..8bf8c22 100644 + Pencil, + Plus, + RefreshCw, + Download, + Unlock, + } from "lucide-react"; + import { + DownlineUser, + UserBlockStatusResult, + } from "@/services/api/internalTypes"; + import type { CsvCellValue, CsvColumn } from "@/utils/csv/types"; + import { cn } from "@/lib/utils"; + + const statusFilterValues = ["ACTIVE", "INACTIVE", "CLOSED"] as const; + const [selectedDownlineTab, setSelectedDownlineTab] = useState<DownlineTab>("all"); + const [drillDownlineUserId, setDrillDownlineUserId] = useState<string | null>(null);
Removed / Before Commit
- diff --git a/src/services/api/internalServices.ts b/src/services/api/internalServices.ts - index 5a0fc1f..13a7a72 100644 - return unwrapData<DownlineUser[]>( - await authApiClient.get<unknown>( - `${authConfig.panelAuthEndpoints.downlineUsers}${queryString}`, - ), - ); - },
Added / After Commit
+ diff --git a/src/services/api/internalServices.ts b/src/services/api/internalServices.ts + index 5a0fc1f..13a7a72 100644 + return unwrapData<DownlineUser[]>( + await authApiClient.get<unknown>( + `${authConfig.panelAuthEndpoints.downlineUsers}${queryString}`, + { cache: "no-store" }, + ), + ); + },