Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ const customJestConfig = {
transform: {
'^.+\\.(js|jsx|ts|tsx)$': ['babel-jest', { presets: ['next/babel'] }],
},

// Transform node_modules that are ESM
transformIgnorePatterns: [
'node_modules/(?!(bson|mongoose|mongodb)/)',
],
};

module.exports = createJestConfig(customJestConfig);
2 changes: 1 addition & 1 deletion src/app/contexts/LoadingContext.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export function LoadingProvider({ children }) {
clearTimeoutRef.current = null;
}
}
}, [pathname]);
}, [pathname, isLoading]);

// Cleanup on unmount
useEffect(() => {
Expand Down
14 changes: 7 additions & 7 deletions src/app/hooks/useGames.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useState, useEffect } from 'react';
import { useState, useEffect, useCallback } from 'react';

/**
* Custom hook for fetching and managing games data
Expand All @@ -12,11 +12,7 @@ export function useGames({ sport = 'football', status = 'upcoming' } = {}) {
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);

useEffect(() => {
fetchGames();
}, [sport, status]);

const fetchGames = async () => {
const fetchGames = useCallback(async () => {
try {
setLoading(true);
setError(null);
Expand All @@ -39,7 +35,11 @@ export function useGames({ sport = 'football', status = 'upcoming' } = {}) {
} finally {
setLoading(false);
}
};
}, [sport, status]);

useEffect(() => {
fetchGames();
}, [fetchGames]);

return { games, loading, error, refetch: fetchGames };
}
2 changes: 1 addition & 1 deletion src/app/page.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export default function Home() {
if (isSignedIn) {
router.replace("/dashboard");
}
}, [isSignedIn, isLoaded]);
}, [isSignedIn, isLoaded, router]);

return (
<div className="min-h-screen bg-zinc-950 font-mono text-zinc-300">
Expand Down
4 changes: 2 additions & 2 deletions src/app/tasks/page.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use client';

import { useState, useEffect, useCallback } from 'react';
import { useState, useEffect, useCallback, useMemo } from 'react';
import useSWR from 'swr';
import { useUser } from '@clerk/nextjs';
import {
Expand Down Expand Up @@ -39,7 +39,7 @@ export default function TasksPage() {
revalidateOnFocus: false,
});

const tasks = data?.tasks || [];
const tasks = useMemo(() => data?.tasks || [], [data?.tasks]);

Copilot AI Dec 25, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The useMemo hook is used on line 42 but is not imported. Add useMemo to the React imports on line 3.

Copilot uses AI. Check for mistakes.
const summary = data?.summary || null;
const streak = data?.streak || null;
const loading = isLoading || !isLoaded;
Expand Down
8 changes: 4 additions & 4 deletions src/components/experimental/ui/MinimalBettingModal.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use client';

import { useState, useEffect, useMemo } from 'react';
import { useState, useEffect, useMemo, useCallback } from 'react';
import { useUserContext } from '@/app/contexts/UserContext';
import { useBetValidation } from '@/app/hooks';
import MinimalModal from './MinimalModal';
Expand Down Expand Up @@ -68,7 +68,7 @@ export default function MinimalBettingModal({ game, isOpen, onClose, onBetPlaced
};

// Handle bet placement
const handlePlaceBet = async () => {
const handlePlaceBet = useCallback(async () => {
try {
setLoading(true);
setError(null);
Expand Down Expand Up @@ -136,7 +136,7 @@ export default function MinimalBettingModal({ game, isOpen, onClose, onBetPlaced
} finally {
setLoading(false);
}
};
}, [betAmount, selectedTeam, userBiscuits, game, validate, refreshUser, onBetPlaced, onClose]);

// Keyboard shortcuts
useEffect(() => {
Expand Down Expand Up @@ -181,7 +181,7 @@ export default function MinimalBettingModal({ game, isOpen, onClose, onBetPlaced

document.addEventListener('keydown', handleKeyPress);
return () => document.removeEventListener('keydown', handleKeyPress);
}, [isOpen, betAmount, selectedTeam, loading, userBiscuits]);
}, [isOpen, betAmount, selectedTeam, loading, userBiscuits, handlePlaceBet]);

if (!game) return null;

Expand Down
30 changes: 9 additions & 21 deletions src/server/services/BettingService.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,27 +59,15 @@ class BettingService {
throw new Error(`Game is ${game.status} and not available for betting`);
}

// Use startTime first, fall back to gameDate (align with client-side useBetValidation)
const gameTimeValue = game.startTime || game.gameDate;
const gameDateTime = new Date(gameTimeValue);
// Validation: Check if game has started
// Use a small buffer (e.g., 5 seconds) to account for slight server time differences
const now = new Date();

console.log('[BettingService] Date comparison debug:', {
gameId,
startTime: game.startTime,
gameDate: game.gameDate,
usedValue: gameTimeValue,
gameDateParsed: gameDateTime.toISOString(),
now: now.toISOString(),
gameTimestamp: gameDateTime.getTime(),
nowTimestamp: now.getTime(),
isGameInPast: gameDateTime < now,
differenceMs: gameDateTime.getTime() - now.getTime(),
differenceHours: ((gameDateTime.getTime() - now.getTime()) / (1000 * 60 * 60)).toFixed(2),
});
const gameTime = new Date(game.gameDate || game.startTime); // Handle both field names
const BUFFER_MS = 5000;
const cutoffTime = new Date(now.getTime() - BUFFER_MS);

if (gameDateTime < now) {
throw new Error(`Betting is closed - game has already started (gameDate: ${gameDateTime.toISOString()}, now: ${now.toISOString()})`);
if (gameTime <= cutoffTime) {
throw new Error(`Betting is closed - game has already started (gameDate: ${gameTime.toISOString()}, now: ${now.toISOString()})`);
}

// 5. Calculate current odds BEFORE placing bet
Expand Down Expand Up @@ -368,5 +356,5 @@ class BettingService {
}
}

// Export singleton instance
export default new BettingService();
const bettingService = new BettingService();
export default bettingService;
8 changes: 4 additions & 4 deletions src/server/services/GameService.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Game from '../../models/Game';
import Game from '@server/models/Game';
import BettingService from './BettingService';
import { calculateOdds } from '../odds-calculator';
import { calculateOdds } from '@shared/utils/odds-calculator';

/**
* GameService
Expand Down Expand Up @@ -366,5 +366,5 @@ class GameService {
}
}

// Export singleton instance
export default new GameService();
const gameService = new GameService();
export default gameService;
4 changes: 2 additions & 2 deletions src/server/services/StatisticsService.js
Original file line number Diff line number Diff line change
Expand Up @@ -311,5 +311,5 @@ class StatisticsService {
}
}

// Export singleton instance
export default new StatisticsService();
const statisticsService = new StatisticsService();
export default statisticsService;
Loading