diff --git a/backend/src/routes/dashboard.js b/backend/src/routes/dashboard.js index a65e92e..a907b9a 100644 --- a/backend/src/routes/dashboard.js +++ b/backend/src/routes/dashboard.js @@ -7,6 +7,7 @@ const express = require('express'); const router = express.Router(); const { authenticate } = require('../middleware/auth'); const { PrismaClient } = require('@prisma/client'); +const { getEventsOnlineCounts } = require('../socket'); const prisma = new PrismaClient(); @@ -38,6 +39,15 @@ router.get('/', authenticate, async (req, res, next) => { }, }); + // Get online counts for all events (safely handle if socket not initialized) + let onlineCounts = {}; + try { + const eventIds = eventParticipants.map(ep => ep.event.id); + onlineCounts = getEventsOnlineCounts(eventIds); + } catch (_) { + // Socket may not be initialized (e.g., during tests) + } + // Get user's heats for each event const activeEvents = await Promise.all( eventParticipants.map(async (ep) => { @@ -72,6 +82,7 @@ router.get('/', authenticate, async (req, res, next) => { startDate: ep.event.startDate, endDate: ep.event.endDate, participantsCount: ep.event.participantsCount, + onlineCount: onlineCounts[ep.event.id] || 0, myHeats: heats.map((h) => ({ id: h.id, competitionType: h.competitionType, diff --git a/backend/src/socket/index.js b/backend/src/socket/index.js index 6c418f7..308e034 100644 --- a/backend/src/socket/index.js +++ b/backend/src/socket/index.js @@ -461,4 +461,21 @@ function initializeSocket(httpServer) { return io; } -module.exports = { initializeSocket, getIO }; +// Get count of online users for a specific event +function getEventOnlineCount(eventId) { + if (!activeUsers.has(eventId)) { + return 0; + } + return activeUsers.get(eventId).size; +} + +// Get online counts for multiple events +function getEventsOnlineCounts(eventIds) { + const counts = {}; + for (const eventId of eventIds) { + counts[eventId] = getEventOnlineCount(eventId); + } + return counts; +} + +module.exports = { initializeSocket, getIO, getEventOnlineCount, getEventsOnlineCounts }; diff --git a/frontend/src/pages/DashboardPage.jsx b/frontend/src/pages/DashboardPage.jsx index 687ea9a..58bce6c 100644 --- a/frontend/src/pages/DashboardPage.jsx +++ b/frontend/src/pages/DashboardPage.jsx @@ -326,6 +326,12 @@ const EventCard = ({ event }) => {
{event.participantsCount} participants + {event.onlineCount > 0 && ( + + + {event.onlineCount} online + + )}
diff --git a/frontend/src/pages/__tests__/DashboardPage.test.jsx b/frontend/src/pages/__tests__/DashboardPage.test.jsx index 30f260f..135d5f5 100644 --- a/frontend/src/pages/__tests__/DashboardPage.test.jsx +++ b/frontend/src/pages/__tests__/DashboardPage.test.jsx @@ -128,6 +128,59 @@ describe('DashboardPage', () => { }); }); + it('should display online count when users are online', async () => { + dashboardAPI.getData.mockResolvedValue({ + activeEvents: [ + { + id: 1, + slug: 'test-event', + name: 'Test Event', + location: 'Warsaw', + startDate: '2025-12-01', + endDate: '2025-12-03', + participantsCount: 50, + onlineCount: 5, + myHeats: [], + }, + ], + activeMatches: [], + matchRequests: { incoming: [], outgoing: [] }, + }); + + renderWithRouter(); + + await waitFor(() => { + expect(screen.getByText('5 online')).toBeInTheDocument(); + }); + }); + + it('should not display online count when zero', async () => { + dashboardAPI.getData.mockResolvedValue({ + activeEvents: [ + { + id: 1, + slug: 'test-event', + name: 'Test Event', + location: 'Warsaw', + startDate: '2025-12-01', + endDate: '2025-12-03', + participantsCount: 50, + onlineCount: 0, + myHeats: [], + }, + ], + activeMatches: [], + matchRequests: { incoming: [], outgoing: [] }, + }); + + renderWithRouter(); + + await waitFor(() => { + expect(screen.getByText('Test Event')).toBeInTheDocument(); + }); + expect(screen.queryByText(/online/)).not.toBeInTheDocument(); + }); + it('should navigate to event chat when clicking Enter Chat', async () => { dashboardAPI.getData.mockResolvedValue({ activeEvents: [