All files / web/src/components/practice PracticeLayout.tsx

0% Statements 0/92
0% Branches 0/1
0% Functions 0/1
0% Lines 0/92

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93                                                                                                                                                                                         
'use client'

import { useEffect, type ReactNode } from 'react'
import {
  SessionModeBannerProvider,
  useSessionModeBanner,
} from '@/contexts/SessionModeBannerContext'
import { useSessionMode } from '@/hooks/useSessionMode'
import { ProjectingBanner } from './ProjectingBanner'

// ============================================================================
// Types
// ============================================================================

interface PracticeLayoutProps {
  /** The student/player ID for fetching session mode */
  studentId: string
  /** Child content to render */
  children: ReactNode
  /** Callback when banner action is triggered (e.g., open StartPracticeModal) */
  onBannerAction?: () => void
}

// ============================================================================
// Inner Component (uses context)
// ============================================================================

interface PracticeLayoutInnerProps {
  children: ReactNode
  onBannerAction?: () => void
}

/**
 * Inner component that registers the action callback and renders the banner.
 * Needs to be inside the provider to access context.
 */
function PracticeLayoutInner({ children, onBannerAction }: PracticeLayoutInnerProps) {
  const { setOnAction } = useSessionModeBanner()

  // Register the action callback
  useEffect(() => {
    if (onBannerAction) {
      setOnAction(onBannerAction)
    }
  }, [onBannerAction, setOnAction])

  return (
    <>
      {/* The projecting banner renders via portal to body */}
      <ProjectingBanner />
      {children}
    </>
  )
}

// ============================================================================
// Main Component
// ============================================================================

/**
 * PracticeLayout - Wrapper component for all practice pages
 *
 * Provides:
 * - SessionModeBannerProvider for banner state
 * - ProjectingBanner for animated banner
 * - Session mode data fetching
 *
 * Usage:
 * ```tsx
 * <PracticeLayout
 *   studentId={params.studentId}
 *   onBannerAction={() => setShowModal(true)}
 * >
 *   <DashboardContent />
 * </PracticeLayout>
 * ```
 */
export function PracticeLayout({ studentId, children, onBannerAction }: PracticeLayoutProps) {
  // Fetch session mode
  const { data: sessionModeData, isLoading } = useSessionMode(studentId)

  return (
    <SessionModeBannerProvider
      sessionMode={sessionModeData?.sessionMode ?? null}
      isLoading={isLoading}
    >
      <PracticeLayoutInner onBannerAction={onBannerAction}>{children}</PracticeLayoutInner>
    </SessionModeBannerProvider>
  )
}

export default PracticeLayout