Unhandled Runtime Error
TypeError: Cannot read properties of undefined (reading 'fullTime')

Source
component\FootballDashboard.tsx (66:24) @ fullTime

  64 |       <div className="flex flex-col items-center w-2/12">
  65 |         <div className="text-sm font-bold">
> 66 |           {match.score.fullTime.home ?? '-'} - {match.score.fullTime.away ?? '-'}
     |                        ^
  67 |         </div>
  68 |         <div className="text-xs text-gray-500">
  69 |           {match.status === 'FINISHED' ? 'FT' : match.status === 'IN_PLAY' ? "LIVE" : ""}
Call Stack
Show collapsed frames

'use client'

import { useState, useEffect } from 'react'
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
import { ScrollArea } from "@/components/ui/scroll-area"
import { Button } from "@/components/ui/button"
import { RefreshCw, Play } from 'lucide-react'
import Image from 'next/image'
import LiveScore from './LiveScore'
import LeagueSelector from './LeagueSelector'
import NewsArticles from './NewsArticles'
import PlayerSearch from './PlayerSearch'
import TeamHistory from './TeamHistory'
import FantasySports from './FantasySports'
import VideoContent from './VideoContent'

const API_BASE_URL = 'http://localhost:5000'

export default function HomePage() {
  const [liveScores, setLiveScores] = useState<{ [key: string]: any[] }>({})
  const [articles, setArticles] = useState<any[]>([])
  const [videos, setVideos] = useState<any[]>([])
  const [isLoading, setIsLoading] = useState(true)
  const [error, setError] = useState<string | null>(null)

  useEffect(() => {
    const fetchData = async () => {
      setIsLoading(true)
      try {
        const [scoresRes, articlesRes, videosRes] = await Promise.all([
          fetch(`${API_BASE_URL}/api/scores`),
          fetch(`${API_BASE_URL}/api/articles`),
          fetch(`${API_BASE_URL}/api/videos/recent`)
        ])

        const scoresData = await scoresRes.json()
        const articlesData = await articlesRes.json()
        const videosData = await videosRes.json()

        setLiveScores(scoresData)
        setArticles(articlesData.slice(0, 5)) // Limit to 5 articles
        setVideos(videosData.response.slice(0, 3)) // Limit to 3 videos
        setError(null)
      } catch (error) {
        console.error("Fetching error:", error)
        setError("Failed to load data. Please try again later.")
      } finally {
        setIsLoading(false)
      }
    }

    fetchData()
    const intervalId = setInterval(fetchData, 60000) // Refresh every minute
    return () => clearInterval(intervalId)
  }, [])

  const MatchRow = ({ match }: { match: any }) => (
    <div className="flex items-center justify-between py-2 px-4 border-b last:border-b-0">
      <div className="flex items-center space-x-2 w-5/12">
        <Image src={match.homeTeam.crest} alt={match.homeTeam.name} width={24} height={24} className="rounded-full" />
        <span className="font-medium text-sm">{match.homeTeam.shortName}</span>
      </div>
      <div className="flex flex-col items-center w-2/12">
        <div className="text-sm font-bold">
          {match.score.fullTime.home ?? '-'} - {match.score.fullTime.away ?? '-'}
        </div>
        <div className="text-xs text-gray-500">
          {match.status === 'FINISHED' ? 'FT' : match.status === 'IN_PLAY' ? "LIVE" : ""}
        </div>
      </div>
      <div className="flex items-center justify-end space-x-2 w-5/12">
        <span className="font-medium text-sm">{match.awayTeam.shortName}</span>
        <Image src={match.awayTeam.crest} alt={match.awayTeam.name} width={24} height={24} className="rounded-full" />
      </div>
    </div>
  )

  return (
    <div className="container mx-auto px-4 py-8">
      <div className="flex justify-between items-center mb-8">
        <h1 className="text-4xl font-bold">FootballBuzz</h1>
      </div>

      <Tabs defaultValue="home">
        <TabsList className="mb-6">
          <TabsTrigger value="home">Home</TabsTrigger>
          <TabsTrigger value="liveScore">Live Score</TabsTrigger>
          <TabsTrigger value="leagues">Leagues</TabsTrigger>
          <TabsTrigger value="news">News</TabsTrigger>
          <TabsTrigger value="players">Players</TabsTrigger>
          <TabsTrigger value="teams">Teams</TabsTrigger>
          <TabsTrigger value="fantasy">Fantasy</TabsTrigger>
          <TabsTrigger value="videos">Videos</TabsTrigger>
        </TabsList>

        <TabsContent value="home">
          <div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
            {/* Live Scores Section */}
            <div className="lg:col-span-2">
              <Card className="mb-8">
                <CardHeader className="flex flex-row items-center justify-between pb-2">
                  <CardTitle className="text-2xl font-bold">Live Scores</CardTitle>
                  <Button variant="ghost" size="sm" onClick={() => window.location.reload()}>
                    <RefreshCw size={16} />
                  </Button>
                </CardHeader>
                <CardContent>
                  <ScrollArea className="h-[400px] pr-4">
                    {Object.entries(liveScores).map(([league, matches]) => (
                      <div key={league} className="mb-4">
                        <h3 className="font-semibold text-lg mb-2">{league}</h3>
                        {matches.map((match: any) => (
                          <MatchRow key={match.id} match={match} />
                        ))}
                      </div>
                    ))}
                  </ScrollArea>
                </CardContent>
              </Card>

              {/* News Articles Section */}
              <Card>
                <CardHeader>
                  <CardTitle className="text-2xl font-bold">Latest News</CardTitle>
                </CardHeader>
                <CardContent>
                  <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
                    {articles.map(article => (
                      <Card key={article.id} className="overflow-hidden">
                        <Image src={article.headingImage} alt={article.title} width={300} height={200} className="w-full h-40 object-cover" />
                        <CardContent className="p-4">
                          <h3 className="font-semibold text-lg mb-2">{article.title}</h3>
                          <p className="text-sm text-gray-600">{article.content.substring(0, 100)}...</p>
                        </CardContent>
                      </Card>
                    ))}
                  </div>
                </CardContent>
              </Card>
            </div>

            {/* Videos Section */}
            <div>
              <Card>
                <CardHeader>
                  <CardTitle className="text-2xl font-bold">Featured Videos</CardTitle>
                </CardHeader>
                <CardContent>
                  <div className="space-y-4">
                    {videos.map(video => (
                      <div key={video.id} className="relative">
                        <Image src={video.thumbnail} alt={video.title} width={300} height={200} className="w-full rounded-lg" />
                        <div className="absolute inset-0 flex items-center justify-center">
                          <Play className="h-12 w-12 text-white bg-black bg-opacity-50 rounded-full p-3" />
                        </div>
                        <h4 className="mt-2 font-semibold">{video.title}</h4>
                      </div>
                    ))}
                  </div>
                </CardContent>
              </Card>
            </div>
          </div>
        </TabsContent>

        <TabsContent value="liveScore">
          <LiveScore />
        </TabsContent>
        <TabsContent value="leagues">
          <LeagueSelector />
        </TabsContent>
        <TabsContent value="news">
          <NewsArticles />
        </TabsContent>
        <TabsContent value="players">
          <PlayerSearch />
        </TabsContent>
        <TabsContent value="teams">
          <TeamHistory />
        </TabsContent>
        <TabsContent value="fantasy">
          <FantasySports />
        </TabsContent>
        <TabsContent value="videos">
          <VideoContent />
        </TabsContent>
      </Tabs>
    </div>
  )
}
'use client'

import { useState, useEffect, useCallback, memo } from 'react'
import { Card, CardContent } from "@/components/ui/card"
import { Skeleton } from "@/components/ui/skeleton"
import { ScrollArea } from "@/components/ui/scroll-area"
import { Button } from "@/components/ui/button"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
import { motion } from "framer-motion"
import { RefreshCw, ChevronRight, ChevronDown } from 'lucide-react'
import Image from 'next/image'
import { FixedSizeList as List } from 'react-window'

// Define the height of each match row
const MATCH_ROW_HEIGHT = 80

const API_BASE_URL = 'http://localhost:5000'

interface Team {
  id: number
  name: string
  shortName: string
  tla: string
  crest: string
}

interface Score {
  winner: string | null
  duration: string
  fullTime: {
    home: number | null
    away: number | null
  }
  halfTime: {
    home: number | null
    away: number | null
  }
}

interface Match {
  id: number
  utcDate: string
  status: string
  league: string
  homeTeam: Team
  awayTeam: Team
  score: Score
  odds: string
  avgP: string
  avgG: string
  cG: number
  BTS: string
  FTS: string
  "c>2,5": string
}

interface LiveScores {
  [key: string]: Match[]
}

const leagueColors: { [key: string]: string } = {
  'UEFA Champions League': 'border-l-indigo-600',
  'Premier League': 'border-l-blue-600',
  'La Liga': 'border-l-red-600',
  'Serie A': 'border-l-green-600',
  'Ligue 1': 'border-l-yellow-600',
  'UEFA Europa League': 'border-l-orange-600',
  'Other': 'border-l-gray-400'
}

// Define the desired league order
const leagueOrder: string[] = [
  'UEFA Champions League',
  'Premier League',
  'La Liga',
  'Serie A',
  'Ligue 1',
  'UEFA Europa League',
  'Other'
]

export default function LiveScore() {
  const [liveScores, setLiveScores] = useState<LiveScores>({})
  const [error, setError] = useState<string | null>(null)
  const [isLoading, setIsLoading] = useState(true)
  const [activeTab, setActiveTab] = useState<'all' | 'live'>('all')
  
  // State to manage expanded leagues
  const [expandedLeagues, setExpandedLeagues] = useState<{ [key: string]: boolean }>({})

  const fetchLiveScores = async () => {
    setIsLoading(true)
    try {
      const response = await fetch(`${API_BASE_URL}/api/scores`)
      if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`)
      const data: LiveScores = await response.json()
      setLiveScores(data)
      setError(null)
    } catch (error) {
      console.error("Fetching error:", error)
      setError(error instanceof Error ? error.message : String(error))
    } finally {
      setIsLoading(false)
    }
  }

  useEffect(() => {
    fetchLiveScores()
    const intervalId = setInterval(fetchLiveScores, 60000) // Refresh every minute
    return () => clearInterval(intervalId)
  }, [])

  const toggleLeague = (league: string) => {
    setExpandedLeagues(prev => ({
      ...prev,
      [league]: !prev[league]
    }))
  }

  // Memoize MatchRow to prevent unnecessary re-renders
  const MatchRow = memo(({ match }: { match: Match }) => (
    <div className={`flex items-center justify-between py-4 px-6 border-l-4 ${leagueColors[match.league] || leagueColors['Other']} bg-white rounded-lg shadow-sm mb-2 hover:shadow-md transition-all duration-200`}>

      <div className="flex items-center space-x-4 w-5/12">
        <Image src={match.homeTeam.crest} alt={match.homeTeam.name} width={32} height={32} className="rounded-full" />
        <span className="font-medium text-sm md:text-base">{match.homeTeam.shortName}</span>
      </div>
      <div className="flex flex-col items-center w-2/12">
        <div className="text-lg font-bold">
          {match.score.fullTime.home ?? '-'} - {match.score.fullTime.away ?? '-'}
        </div>
        <div className={`text-xs ${match.status === 'IN_PLAY' ? 'text-green-600 font-semibold' : 'text-gray-500'}`}>
          {match.status === 'FINISHED' 
            ? 'FT' 
            : match.status === 'IN_PLAY'
              ? "LIVE"
              : new Date(match.utcDate).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
        </div>
      </div>
      <div className="flex items-center justify-end space-x-4 w-5/12">
        <span className="font-medium text-sm md:text-base">{match.awayTeam.shortName}</span>
        <Image src={match.awayTeam.crest} alt={match.awayTeam.name} width={32} height={32} className="rounded-full" />
      </div>
    </div>
  ))

  // Renderer for react-window
  const Row = useCallback(({ index, style, data }: { index: number, style: React.CSSProperties, data: Match[] }) => {
    const match = data[index]
    return (
      <div style={style}>
        <MatchRow match={match} />
      </div>
    )
  }, [MatchRow])

  const LeagueSection = ({ league, matches }: { league: string, matches: Match[] }) => {
    const isExpanded = expandedLeagues[league]
    const displayedMatches = isExpanded ? matches : matches.slice(0, 3)
    const matchCount = matches.length

    return (
      <div className="mb-8">
        <div 
          className="flex justify-between items-center cursor-pointer text-xl font-semibold mb-4 text-gray-800"
          onClick={() => toggleLeague(league)}
          aria-expanded={isExpanded}
        >
          <span>{league}</span>
          {isExpanded ? <ChevronDown size={20} /> : <ChevronRight size={20} />}
        </div>

        {!isExpanded ? (
          <List
            height={Math.min(displayedMatches.length, 3) * MATCH_ROW_HEIGHT}
            itemCount={displayedMatches.length}
            itemSize={MATCH_ROW_HEIGHT}
            width="100%"
            itemData={displayedMatches}
          >
            {Row}
          </List>
        ) : (
          <List
            height={Math.min(matches.length, 10) * MATCH_ROW_HEIGHT} // Limit max height
            itemCount={matches.length}
            itemSize={MATCH_ROW_HEIGHT}
            width="100%"
            itemData={matches}
          >
            {Row}
          </List>
        )}

        {!isExpanded && matchCount > 3 && (
          <div className="text-center mt-2 text-sm text-blue-500">
            +{matchCount - 3} more
          </div>
        )}
      </div>
    )
  }

  const filteredScores = activeTab === 'live'
    ? Object.fromEntries(
        Object.entries(liveScores).map(([league, matches]) => [
          league,
          matches.filter(match => match.status === 'IN_PLAY')
        ]).filter(([_, matches]) => matches.length > 0)
      )
    : liveScores

  // Sort leagues based on the defined order
  const sortedLeagues = leagueOrder.filter(league => Object.keys(filteredScores).includes(league))
    .concat(Object.keys(filteredScores).filter(league => !leagueOrder.includes(league)))

  return (
    <div className="max-w-4xl mx-auto px-4 py-8">
      <div className="flex justify-between items-center mb-8">
        <h2 className="text-3xl font-bold text-gray-800">Live Scores</h2>
        <Button
          variant="outline"
          size="icon"
          onClick={fetchLiveScores}
          className="rounded-full hover:bg-gray-100 transition-colors duration-300"
        >
          <RefreshCw size={20} />
        </Button>
      </div>

      <Tabs 
        value={activeTab} 
        onValueChange={(value) => setActiveTab(value as 'all' | 'live')} 
        className="w-full mb-6"
      >
        <TabsList className="grid w-full max-w-md mx-auto grid-cols-2">
          <TabsTrigger value="all">All Matches</TabsTrigger>
          <TabsTrigger value="live">Live Matches</TabsTrigger>
        </TabsList>
      </Tabs>

      <ScrollArea className="h-[calc(100vh-200px)] pr-4">
        {isLoading ? (
          Array(5).fill(0).map((_, index) => (
            <Skeleton key={index} className="h-20 w-full mb-4 rounded-lg" />
          ))
        ) : error ? (
          <Card className="bg-red-50 text-red-700 p-6 rounded-lg">
            <CardContent>{error}</CardContent>
          </Card>
        ) : (
          <div>
            {sortedLeagues.map(league => (
              <LeagueSection 
                key={league} 
                league={league} 
                matches={filteredScores[league]} 
              />
            ))}
            {sortedLeagues.length === 0 && (
              <motion.div
                initial={{ opacity: 0 }}
                animate={{ opacity: 1 }}
                exit={{ opacity: 0 }}
                className="text-center p-8 text-gray-600 bg-gray-50 rounded-lg"
              >
                No {activeTab === 'live' ? 'live ' : ''}matches at the moment.
              </motion.div>
            )}
          </div>
        )}
      </ScrollArea>
    </div>
  )
}
This is the error coming
// controllers/scoresController.js

const axios = require("axios");
const cache = require("../utils/cache");
const Score = require("../models/Score");
const logger = require("../utils/logger"); // Optional: Implement a logger for better logging

/**
 * Fetch live scores from the external API.
 * @returns {Promise<Array>} Array of score objects.
 */
const fetchScoresFromAPI = async () => {
  try {
    const apiUrl = "https://api.football-data.org/v4/matches";
    const response = await axios.get(apiUrl, {
      headers: {
        "X-Auth-Token": process.env.API_FOOTBALL_KEY,
      },
      params: {
        dateFrom: new Date(Date.now() - 24 * 60 * 60 * 1000)
          .toISOString()
          .split("T")[0],
        dateTo: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000)
          .toISOString()
          .split("T")[0],
      },
    });

    // Map competition IDs to league names
    const leagueIds = {
      2014: "La Liga",
      2021: "Premier League",
      2002: "Bundesliga",
      2019: "Serie A",
      2015: "Ligue 1",
      2001: "UEFA Champions League",
      2146: "UEFA Europa League",
      // Add more mappings as needed
    };

    // Transform and filter matches
    const filteredMatches = response.data.matches.map((match) => ({
      id: match.id,
      utcDate: match.utcDate,
      status: match.status,
      league: leagueIds[match.competition.id] || "Other",
      homeTeam: match.homeTeam,
      awayTeam: match.awayTeam,
      score: match.score,
      odds: Math.random().toFixed(2), // Placeholder for odds
      avgP: (Math.random() * 100).toFixed(2), // Placeholder for average probability
      avgG: (Math.random() * 5).toFixed(2), // Placeholder for average goals
      cG: Math.floor(Math.random() * 10), // Placeholder for custom metric
      BTS: Math.random() > 0.5 ? "Yes" : "No", // Both Teams to Score
      FTS: Math.random() > 0.5 ? "Yes" : "No", // First Team to Score
      cAG: Math.random() > 0.5 ? "Yes" : "No", // Custom metric
    }));

    return filteredMatches;
  } catch (error) {
    logger.error("Error fetching scores from API:", error.message);
    throw new Error("Failed to fetch scores from external API.");
  }
};

/**
 * Store scores in the MongoDB database.
 * @param {Array} scores - Array of score objects to store.
 */
const storeScoresInDB = async (scores) => {
  try {
    if (scores.length === 0) return;

    // Insert many with ordered:false to continue on duplicate errors
    await Score.insertMany(scores, { ordered: false });
    logger.info(`Inserted ${scores.length} scores into the database.`);
  } catch (err) {
    if (err.code === 11000) {
      logger.warn("Duplicate score entries detected. Skipping duplicates.");
    } else {
      logger.error("Error inserting scores into DB:", err.message);
      throw new Error("Failed to store scores in the database.");
    }
  }
};

/**
 * Fetch scores from the database.
 * @returns {Promise<Array>} Array of score documents.
 */
const getScoresFromDB = async () => {
  try {
    const scores = await Score.find({})
      .sort({ utcDate: -1 }) // Sort by latest date
      .limit(100) // Limit to latest 100 matches
      .lean(); // Returns plain JavaScript objects
    return scores;
  } catch (error) {
    logger.error("Error fetching scores from DB:", error.message);
    throw new Error("Failed to retrieve scores from the database.");
  }
};

/**
 * Group matches by league.
 * @param {Array} matches - Array of match objects.
 * @returns {Object} Grouped matches by league.
 */
const groupMatchesByLeague = (matches) => {
  return matches.reduce((acc, match) => {
    if (!acc[match.league]) {
      acc[match.league] = [];
    }
    acc[match.league].push(match);
    return acc;
  }, {});
};

/**
 * Controller to handle fetching and serving live scores.
 * Endpoint: GET /api/scores
 */
const scoresController = async (req, res) => {
  try {
    // Check if live scores are cached
    const cachedScores = cache.get("liveScores");
    if (cachedScores) {
      logger.info("Serving cached live scores.");
      return res.status(200).json(cachedScores);
    }

    // Fetch scores from the database
    let scores = await getScoresFromDB();

    if (scores.length === 0) {
      logger.info("No scores found in DB. Fetching from external API.");
      // Fetch scores from external API
      scores = await fetchScoresFromAPI();

      // Store fetched scores in the database
      await storeScoresInDB(scores);
    }

    // Group scores by league
    const groupedMatches = groupMatchesByLeague(scores);

    // Cache the grouped matches with TTL of 5 minutes (300 seconds)
    cache.set("liveScores", groupedMatches, 300);
    logger.info("Cached live scores.");

    // Serve the grouped matches
    res.status(200).json(groupedMatches);
  } catch (error) {
    logger.error("Error in scoresController:", error.message);
    res.status(500).json({
      message: "Unable to retrieve live scores. Please try again later.",
      error: error.message,
    });
  }
};

module.exports = scoresController;

Can you resolve it