All files / web/src/hooks useEntryPrompts.ts

100% Statements 113/113
78.94% Branches 15/19
100% Functions 6/6
100% Lines 113/113

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 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 1141x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 28x 28x 28x 28x 28x 28x 28x 28x 28x 28x 18x 18x 1x 1x 12x 12x 18x 28x 28x 28x 28x 28x 28x 28x 3x 3x 3x 3x 3x 1x 1x 1x 2x 3x 28x 2x 2x 2x 28x 28x 28x 28x 28x 3x 3x 3x 3x 3x 1x 1x 1x 2x 3x 28x 2x 2x 2x 28x 28x 28x 28x 10x 10x 28x 28x 28x 28x 28x 28x 28x 28x 28x 28x 28x 28x 28x 28x  
'use client'
 
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import { api } from '@/lib/queryClient'
import { entryPromptKeys } from '@/lib/queryKeys'
 
export interface EntryPrompt {
  id: string
  teacherId: string
  playerId: string
  classroomId: string
  expiresAt: string
  status: 'pending' | 'accepted' | 'declined' | 'expired'
  createdAt: string
  player: {
    id: string
    name: string
    emoji: string
  }
  classroom: {
    id: string
    name: string
  }
  teacher: {
    displayName: string
  }
}
 
/**
 * Hook for parents to manage entry prompts for their children
 *
 * - Fetches pending entry prompts
 * - Provides accept/decline mutations
 * - Real-time updates handled by useParentSocket via query invalidation
 */
export function useEntryPrompts(enabled = true) {
  const queryClient = useQueryClient()
 
  // Fetch pending prompts
  const {
    data: prompts = [],
    isLoading,
    error,
  } = useQuery({
    queryKey: entryPromptKeys.pending(),
    queryFn: async () => {
      const response = await api('entry-prompts')
      if (!response.ok) {
        throw new Error('Failed to fetch entry prompts')
      }
      const data = await response.json()
      return data.prompts as EntryPrompt[]
    },
    enabled,
    refetchInterval: 30000, // Refresh every 30s to catch expired prompts
  })
 
  // Accept mutation
  const acceptPrompt = useMutation({
    mutationFn: async (promptId: string) => {
      const response = await api(`entry-prompts/${promptId}/respond`, {
        method: 'POST',
        body: JSON.stringify({ action: 'accept' }),
      })
      if (!response.ok) {
        const data = await response.json()
        throw new Error(data.error || 'Failed to accept prompt')
      }
      return response.json()
    },
    onSuccess: () => {
      // Invalidate prompts query to refresh the list
      queryClient.invalidateQueries({ queryKey: entryPromptKeys.pending() })
    },
  })
 
  // Decline mutation
  const declinePrompt = useMutation({
    mutationFn: async (promptId: string) => {
      const response = await api(`entry-prompts/${promptId}/respond`, {
        method: 'POST',
        body: JSON.stringify({ action: 'decline' }),
      })
      if (!response.ok) {
        const data = await response.json()
        throw new Error(data.error || 'Failed to decline prompt')
      }
      return response.json()
    },
    onSuccess: () => {
      // Invalidate prompts query to refresh the list
      queryClient.invalidateQueries({ queryKey: entryPromptKeys.pending() })
    },
  })
 
  // Filter out expired prompts on the client side
  const activePrompts = prompts.filter((p) => {
    const expiresAt = new Date(p.expiresAt)
    return expiresAt > new Date() && p.status === 'pending'
  })
 
  return {
    prompts: activePrompts,
    isLoading,
    error,
    acceptPrompt: acceptPrompt.mutateAsync,
    declinePrompt: declinePrompt.mutateAsync,
    isAccepting: acceptPrompt.isPending,
    isDeclining: declinePrompt.isPending,
    acceptingPromptId: acceptPrompt.isPending ? acceptPrompt.variables : null,
    decliningPromptId: declinePrompt.isPending ? declinePrompt.variables : null,
  }
}