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