diff --git a/apps/web/src/app/api/players/with-skill-data/route.ts b/apps/web/src/app/api/players/with-skill-data/route.ts index 75565ffc..b00cdfab 100644 --- a/apps/web/src/app/api/players/with-skill-data/route.ts +++ b/apps/web/src/app/api/players/with-skill-data/route.ts @@ -8,8 +8,8 @@ import { getPlayersWithSkillData } from '@/lib/curriculum/server' */ export async function GET() { try { - const players = await getPlayersWithSkillData() - return NextResponse.json({ players }) + const result = await getPlayersWithSkillData() + return NextResponse.json({ players: result.players }) } catch (error) { console.error('Failed to fetch players with skill data:', error) return NextResponse.json({ error: 'Failed to fetch players' }, { status: 500 }) diff --git a/apps/web/src/app/practice/page.tsx b/apps/web/src/app/practice/page.tsx index fe94ab93..08724164 100644 --- a/apps/web/src/app/practice/page.tsx +++ b/apps/web/src/app/practice/page.tsx @@ -1,25 +1,6 @@ -import { eq } from 'drizzle-orm' -import { db, schema } from '@/db' import { getPlayersWithSkillData } from '@/lib/curriculum/server' -import { getViewerId } from '@/lib/viewer' import { PracticeClient } from './PracticeClient' -/** - * Get or create user record for a viewerId (guestId) - */ -async function getOrCreateUser(viewerId: string) { - let user = await db.query.users.findFirst({ - where: eq(schema.users.guestId, viewerId), - }) - - if (!user) { - const [newUser] = await db.insert(schema.users).values({ guestId: viewerId }).returning() - user = newUser - } - - return user -} - /** * Practice page - Server Component * @@ -30,13 +11,8 @@ async function getOrCreateUser(viewerId: string) { */ export default async function PracticePage() { // Fetch players with skill data directly on server - no HTTP round-trip - const players = await getPlayersWithSkillData() + // Returns players, viewerId, and userId in a single call to avoid redundant lookups + const { players, viewerId, userId } = await getPlayersWithSkillData() - // Get viewer ID for session observation - const viewerId = await getViewerId() - - // Get database user ID for parent socket notifications - const user = await getOrCreateUser(viewerId) - - return + return } diff --git a/apps/web/src/lib/curriculum/server.ts b/apps/web/src/lib/curriculum/server.ts index a473f858..eef161cb 100644 --- a/apps/web/src/lib/curriculum/server.ts +++ b/apps/web/src/lib/curriculum/server.ts @@ -88,6 +88,15 @@ export async function getPlayersForViewer(): Promise { return players } +/** + * Result from getPlayersWithSkillData including context for page rendering + */ +export interface PlayersWithSkillDataResult { + players: StudentWithSkillData[] + viewerId: string + userId: string +} + /** * Get all players for the current viewer with enhanced skill data. * @@ -97,11 +106,13 @@ export async function getPlayersForViewer(): Promise { * - skillCategory: Computed highest-level skill category * - intervention: Intervention data if student needs attention * + * Also returns viewerId and userId to avoid redundant calls in page components. + * * Performance: Uses batched queries to avoid N+1 query patterns. * - Single query for all skill mastery records across all players - * - Single query for session history across all players needing intervention + * - Returns viewerId/userId to avoid redundant getViewerId() calls */ -export async function getPlayersWithSkillData(): Promise { +export async function getPlayersWithSkillData(): Promise { const viewerId = await getViewerId() // Get or create user record @@ -114,6 +125,8 @@ export async function getPlayersWithSkillData(): Promise user = newUser } + const userId = user.id + // Get player IDs linked via parent_child table const linkedPlayerIds = await db.query.parentChild.findMany({ where: eq(parentChild.parentUserId, user.id), @@ -135,7 +148,7 @@ export async function getPlayersWithSkillData(): Promise } if (players.length === 0) { - return [] + return { players: [], viewerId, userId } } // OPTIMIZATION: Batch query all skill mastery records for all players at once @@ -186,7 +199,7 @@ export async function getPlayersWithSkillData(): Promise // Intervention badges are helpful but not critical for initial render. // They can be computed lazily on the client if needed. // This avoids N additional database queries for session history. - return playersWithBasicSkills + return { players: playersWithBasicSkills, viewerId, userId } } // Re-export the individual functions for granular prefetching