// component/PlayerModal.tsx
'use client';

import { useEffect, useState } from 'react';
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogClose } from "@/components/ui/dialog";
import { Button } from "@/components/ui/button";
import { Avatar, AvatarFallback } from "@/components/ui/avatar";
import { getPlayerDetails, getPlayerMatches } from '../utils/api';
import { Player, Match } from '../types';
import { useRouter } from 'next/navigation';
import { FaTwitter, FaFacebook, FaInstagram } from 'react-icons/fa';

interface PlayerModalProps {
playerId: string;
isOpen: boolean;
onClose: () => void;
}

const PlayerModal: React.FC = ({ playerId, isOpen, onClose }) => {
const [player, setPlayer] = useState<Player | null>(null);
const [matches, setMatches] = useState<Match[]>([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState('');
const router = useRouter();

useEffect(() => {
if (isOpen) {
setLoading(true);
setError('');
const fetchPlayerData = async () => {
try {
const fetchedPlayer = await getPlayerDetails(playerId);
setPlayer(fetchedPlayer);
const fetchedMatches = await getPlayerMatches(playerId, 5);
setMatches(fetchedMatches);
} catch (err: any) {
setError(err);
} finally {
setLoading(false);
}
};
fetchPlayerData();
}
}, [isOpen, playerId]);

const handleViewMatches = () => {
onClose(); // Close the modal
router.push(/players/${playerId}/matches);
};

if (!isOpen) return null;

return (

{player?.name}

{loading ? (
Loading...

) : error ? (
{error}

) : player ? (

{player.photo_url ? (
{player.name}
) : (
{player.name.charAt(0)}
)}

Position: {player.position || 'N/A'}

Nationality: {player.nationality || 'N/A'}

Team: {player.team || 'N/A'}

League: {player.league || 'N/A'}

Birthday: {player.birthday || 'N/A'}

Height: {player.height || 'N/A'}

Weight: {player.weight || 'N/A'}

Jersey Number: {player.jerseyNumber || 'N/A'}

Description: {player.description || 'No description available.'}

{player.social?.twitter && (

Twitter

)}
{player.social?.facebook && (

Facebook

)}
{player.social?.instagram && (

Instagram

)}

View Recent Matches

) : (
Player not found.

)}

);
};
export default PlayerModal;

// component/PlayerSearch.tsx
'use client';

import { useState } from 'react';
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { Avatar, AvatarFallback } from "@/components/ui/avatar";
import PlayerModal from './PlayerModal';
import { Player } from '../types';
import { searchPlayers } from '../utils/api';
import { toast } from 'react-toastify';

const PlayerSearch: React.FC = () => {
const [searchTerm, setSearchTerm] = useState('');
const [players, setPlayers] = useState<Player[]>([]);
const [loading, setLoading] = useState(false);
const [selectedPlayerId, setSelectedPlayerId] = useState<string | null>(null);
const [isModalOpen, setIsModalOpen] = useState(false);

const handleSearch = async () => {
if (!searchTerm.trim()) {
toast.error("Please enter a search term.");
setPlayers([]);
return;
}

setLoading(true);
setPlayers([]);

try {
const data = await searchPlayers(searchTerm);
setPlayers(data);
if (data.length === 0) {
toast.info("No players found. Try a different search.");
}
} catch (error: any) {
toast.error(error);
} finally {
setLoading(false);
}
};

const handlePlayerClick = (playerId: string) => {
setSelectedPlayerId(playerId);
setIsModalOpen(true);
};

const handleKeyPress = (e: React.KeyboardEvent) => {
if (e.key === 'Enter') {
handleSearch();
}
};

return (

Player Search

<Input
type="text"
placeholder="Search players..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
onKeyPress={handleKeyPress}
/>

{loading ? 'Searching...' : 'Search'}

{loading ? (
Searching...

) : (
players.map(player => (

handlePlayerClick(player.player_id)} >
{player.photo_url ? (
{player.name}
) : (

{player.name.charAt(0)}

)}

{player.name}

{player.position} - {player.team}

))
)}

{/* Player Modal */}
{selectedPlayerId && (
<PlayerModal
playerId={selectedPlayerId}
isOpen={isModalOpen}
onClose={() => setIsModalOpen(false)}
/>
)}

); };
export default PlayerSearch;

// component/Navbar.tsx
'use client';

import Link from 'next/link';
import { Button } from "@/components/ui/button";

const Navbar: React.FC = () => {
return (

FootballBuzz

{/* Add more navigation links if needed */}

);
};
export default Navbar;

// app/players/[id]/matches/page.tsx
'use client';

import { useEffect, useState } from 'react';
import { useRouter, useParams } from 'next/navigation';
import { getPlayerMatches, getPlayerDetails } from '../../../../utils/api';
import { Player, Match } from '../../../../types';
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import Navbar from '../../../../component/Navbar';
import Link from 'next/link';
import { toast } from 'react-toastify';
import { Avatar, AvatarFallback } from "@/components/ui/avatar";

const PlayerMatchesPage: React.FC = () => {
const router = useRouter();
const params = useParams();
const { id } = params;
const [player, setPlayer] = useState<Player | null>(null);
const [matches, setMatches] = useState<Match[]>([]);
const [loading, setLoading] = useState(true);

useEffect(() => {
if (!id) {
router.push('/');
return;
}

const fetchData = async () => {
try {
const fetchedPlayer = await getPlayerDetails(id);
setPlayer(fetchedPlayer);
const fetchedMatches = await getPlayerMatches(id, 5);
setMatches(fetchedMatches);
} catch (error: any) {
toast.error(error);
} finally {
setLoading(false);
}
};

fetchData();
}, [id, router]);

if (loading) return

Loading matches...

;
if (!player) return
Player not found.

;
return (
<>

Recent Matches of {player.name}

Back to Player Details
Home

{matches.length === 0 ? (
No recent matches found.

) : (

{matches.map(match => (

{match.homeTeam} vs {match.awayTeam}

{match.date} at {match.time}

Score: {match.homeScore !== null ? ${match.homeScore} - ${match.awayScore} : 'N/A'}

Status: {match.status}

League: {match.league}

Season: {match.season}

))}

)}

</>
);
};
export default PlayerMatchesPage;

// app/players/[id]/page.tsx
'use client';

import { useEffect, useState } from 'react';
import { useRouter, useParams } from 'next/navigation';
import PlayerModal from '../../../component/PlayerModal';
import Navbar from '../../../component/Navbar';
import { ToastContainer } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';

const PlayerPage: React.FC = () => {
const router = useRouter();
const params = useParams();
const { id } = params;
const [isModalOpen, setIsModalOpen] = useState(true);

useEffect(() => {
if (!id) {
router.push('/'); // Redirect to home if no player ID is found
}
}, [id, router]);

const handleClose = () => {
setIsModalOpen(false);
router.push('/'); // Redirect to home upon closing the modal
};

return (
<>

</>
);
};

export default PlayerPage;

// types/index.ts

export interface Player {
player_id: string;
name: string;
position: string;
nationality: string;
team: string;
league: string;
photo_url?: string;
description?: string;
birthday?: string;
height?: string;
weight?: string;
jerseyNumber?: string;
social?: {
twitter?: string;
facebook?: string;
instagram?: string;
};
}

export interface Match {
event_id: string;
league: string;
season: string;
date: string;
time: string;
homeTeam: string;
awayTeam: string;
homeScore: number | null;
awayScore: number | null;
status: string;
player_id: string;
fetchedAt: Date;
}

// utils/api.ts

import axios from 'axios';
import { Player, Match } from '../types';

// Ensure to set NEXT_PUBLIC_API_BASE_URL in your .env.local
const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL || 'http://localhost:5000/api';

/**

Search players by name.
@param query - Player name query.
@returns Promise resolving to an array of Player objects.
/
export const searchPlayers = async (query: string): Promise<Player[]> => {
try {
const response = await axios.get(${API_BASE_URL}/searchPlayers, {
params: { q: query },
});
return response.data.players;
} catch (error: any) {
console.error('Search Players Error:', error);
throw error.response?.data?.error || 'Error searching players';
}
};
/*

Get detailed information about a specific player by ID.
@param playerId - Unique identifier of the player.
@returns Promise resolving to a Player object.
/
export const getPlayerDetails = async (playerId: string): Promise => {
try {
const response = await axios.get(${API_BASE_URL}/playerDetails/${playerId});
return response.data.response;
} catch (error: any) {
console.error('Get Player Details Error:', error);
throw error.response?.data?.error || 'Error fetching player details';
}
};
/*

Get recent matches for a specific player.
@param playerId - Unique identifier of the player.
@param limit - Number of recent matches to retrieve.
@returns Promise resolving to an array of Match objects.
*/
export const getPlayerMatches = async (playerId: string, limit: number = 5): Promise<Match[]> => {
try {
const response = await axios.get(${API_BASE_URL}/playerMatches/${playerId}, {
params: { limit },
});
return response.data.matches;
} catch (error: any) {
console.error('Get Player Matches Error:', error);
throw error.response?.data?.error || 'Error fetching player matches';
}
};
// routes/playerRoutes.js

const express = require("express");
const router = express.Router();
const playerController = require("../controllers/playerSearchController");

// Search Players
router.get("/searchPlayers", playerController.searchPlayers);

// Get Player Details
router.get("/playerDetails/:id", playerController.getPlayerDetails);

// Get Player Matches
router.get("/playerMatches/:id", playerController.getPlayerMatches);

module.exports = router;

// controllers/playerSearchController.js

// controllers/playerSearchController.js

const axios = require("axios");
const Player = require("../models/Player");
const Match = require("../models/Match");

// Base URL for the V1 API provided by TheSportsDB
const baseURL = "https://www.thesportsdb.com/api/v1/json/3";

// Helper function to make API requests
const makeApiRequest = async (endpoint, params = {}) => {
try {
const response = await axios.get(${baseURL}/${endpoint}, { params });
console.log(API Response for ${endpoint} with params ${JSON.stringify(params)}:, response.data);
return response.data;
} catch (error) {
if (error.response) {
console.error(
API Error: ${error.response.status} - ${JSON.stringify( error.response.data )}
);
throw new Error(
API Error: ${error.response.data.error || "Unknown error"}
);
} else if (error.request) {
console.error("No response received from API");
throw new Error("No response received from API");
} else {
console.error("Error setting up the request:", error.message);
throw new Error("Error setting up the request");
}
}
};

/**

Search for players by name directly using the new V1 API.
Returns up to 5 matching players.
*/
exports.searchPlayers = async (req, res) => {
const query = req.query.q;
if (!query) {
return res.status(400).json({ error: "Search query 'q' is required" });
}

try {
// Search in the local database first
const localPlayers = await Player.find({
name: { $regex: query, $options: "i" },
}).limit(5);

if (localPlayers.length > 0) {
return res.json({ count: localPlayers.length, players: localPlayers });
}

// If not found locally, fetch from API
const playersResponse = await makeApiRequest("searchplayers.php", {
p: query,
});

// Validate response structure
if (
!playersResponse ||
!playersResponse.player ||
!Array.isArray(playersResponse.player)
) {
return res
.status(404)
.json({ error: "No players found matching the query" });
}

// Limit to 5 players
const limitedPlayers = playersResponse.player.slice(0, 3);

// Enrich player data with necessary fields
const enrichedPlayers = limitedPlayers.map((player) => ({
player_id: player.idPlayer,
name: player.strPlayer,
position: player.strPosition || "N/A",
nationality: player.strNationality || "N/A",
team: player.strTeam || "N/A",
league: player.strLeague || "N/A",
photo_url: player.strThumb || null,
description: player.strDescriptionEN || "No description available.",
birthday: player.dateBorn || "N/A",
height: player.strHeight || "N/A",
weight: player.strWeight || "N/A",
jerseyNumber: player.strNumber || "N/A",
social: {
twitter: player.strTwitter || null,
facebook: player.strFacebook || null,
instagram: player.strInstagram || null,
},
}));

// Save fetched players to the local database
await Player.insertMany(
enrichedPlayers,
{ ordered: false } // Continue on duplicate errors
).catch((err) => {
if (err.code === 11000) {
console.warn("Duplicate player entries detected.");
} else {
console.error("Error inserting players into DB:", err.message);
}
});

res.json({ count: enrichedPlayers.length, players: enrichedPlayers });
} catch (error) {
console.error("Error in searchPlayers:", error.message);
res.status(500).json({ error: error.message });
}
};

/**

Get detailed information about a specific player by ID using the new V1 API.
*/
exports.getPlayerDetails = async (req, res) => {
const playerId = req.params.id;
if (!playerId) {
return res.status(400).json({ error: "Player ID is required" });
}

try {
// Check local DB first
const localPlayer = await Player.findOne({ player_id: playerId });

if (localPlayer) {
console.log(Player ID "${playerId}" found in local DB.);
return res.json({ status: "success", response: localPlayer });
}

console.log(
Player ID "${playerId}" not found in local DB. Fetching from API.
);

// If not found locally, fetch from API
const playerDetailsResponse = await makeApiRequest("lookupplayer.php", {
id: playerId,
});

// Validate response structure
if (
!playerDetailsResponse ||
playerDetailsResponse.players === null ||
!Array.isArray(playerDetailsResponse.players) ||
playerDetailsResponse.players.length === 0
) {
console.warn(API returned no data for Player ID "${playerId}".);
return res.status(404).json({ error: "Player not found" });
}

const player = playerDetailsResponse.players[0];

// Structure the player details
const detailedPlayer = {
player_id: player.idPlayer,
name: player.strPlayer,
position: player.strPosition || "N/A",
nationality: player.strNationality || "N/A",
team: player.strTeam || "N/A",
league: player.strLeague || "N/A",
photo_url: player.strThumb || null,
description: player.strDescriptionEN || "No description available.",
birthday: player.dateBorn || "N/A",
height: player.strHeight || "N/A",
weight: player.strWeight || "N/A",
jerseyNumber: player.strNumber || "N/A",
social: {
twitter: player.strTwitter || null,
facebook: player.strFacebook || null,
instagram: player.strInstagram || null,
},
};

console.log(Fetched player details for Player ID "${playerId}" from API.);

// Save to local DB
try {
await Player.create(detailedPlayer);
console.log(Player ID "${playerId}" saved to local DB.);
} catch (err) {
if (err.code === 11000) {
console.warn(Player ID "${playerId}" already exists in the database.);
} else {
console.error("Error inserting player into DB:", err.message);
}
}

res.json({ status: "success", response: detailedPlayer });
} catch (error) {
console.error("Error in getPlayerDetails:", error.message);
if (
error.message.includes("404") ||
error.message.toLowerCase().includes("not found")
) {
res.status(404).json({ error: "Player not found" });
} else {
res.status(500).json({ error: error.message });
}
}
};

/**

Get recent matches/events for a specific player using the new V1 API.
This example assumes that you have stored matches in your local DB.
*/
exports.getPlayerMatches = async (req, res) => {
const playerId = req.params.id;
const limit = parseInt(req.query.limit) || 5;
if (!playerId) {
return res.status(400).json({ error: "Player ID is required" });
}

try {
// Check if the player exists in the database
const player = await Player.findOne({ player_id: playerId });
if (!player) {
console.log(
Player ID "${playerId}" not found in local DB. Fetching player details to proceed.
);
// Attempt to fetch player details from API
const playerDetailsResponse = await makeApiRequest("lookupplayer.php", {
id: playerId,
});

// Validate response structure
if (
!playerDetailsResponse ||
playerDetailsResponse.players === null ||
!Array.isArray(playerDetailsResponse.players) ||
playerDetailsResponse.players.length === 0
) {
console.warn(API returned no data for Player ID "${playerId}".);
return res.status(404).json({ error: "Player not found" });
}

const playerData = playerDetailsResponse.players[0];

// Structure the player details
const detailedPlayer = {
player_id: playerData.idPlayer,
name: playerData.strPlayer,
position: playerData.strPosition || "N/A",
nationality: playerData.strNationality || "N/A",
team: playerData.strTeam || "N/A",
league: playerData.strLeague || "N/A",
photo_url: playerData.strThumb || null,
description: playerData.strDescriptionEN || "No description available.",
birthday: playerData.dateBorn || "N/A",
height: playerData.strHeight || "N/A",
weight: playerData.strWeight || "N/A",
jerseyNumber: playerData.strNumber || "N/A",
social: {
twitter: playerData.strTwitter || null,
facebook: playerData.strFacebook || null,
instagram: playerData.strInstagram || null,
},
};

// Save to local DB
try {
await Player.create(detailedPlayer);
console.log(Player ID "${playerId}" saved to local DB.);
} catch (err) {
if (err.code === 11000) {
console.warn(
Player ID "${playerId}" already exists in the database.
);
} else {
console.error("Error inserting player into DB:", err.message);
}
}
} else {
console.log(Player ID "${playerId}" found in local DB.);
}

// Now that the player exists in the database, fetch matches
// Check local DB for matches
const localMatches = await Match.find({ player_id: playerId })
.sort({ date: -1 })
.limit(limit);

if (localMatches.length >= limit) {
console.log(
Found ${localMatches.length} matches in local DB for Player ID "${playerId}".
);
return res.json({
count: localMatches.length,
matches: localMatches,
});
}

console.log(
Found only ${localMatches.length} matches in local DB for Player ID "${playerId}". Fetching additional matches from API.
);

// Fetch player details to get the team ID
const playerDetailsResponse = await makeApiRequest("lookupplayer.php", {
id: playerId,
});

if (
!playerDetailsResponse ||
playerDetailsResponse.players === null ||
!Array.isArray(playerDetailsResponse.players) ||
playerDetailsResponse.players.length === 0
) {
throw new Error("Player not found");
}

const playerData = playerDetailsResponse.players[0];
const teamId = playerData.idTeam;

if (!teamId) {
throw new Error("Team ID not available for this player");
}

// Fetch recent events for the team
const eventsResponse = await makeApiRequest("eventslast.php", {
id: teamId,
});

if (
!eventsResponse ||
!eventsResponse.results ||
!Array.isArray(eventsResponse.results)
) {
throw new Error("No events found for the player's team");
}

// Structure the match details
const matches = eventsResponse.results
.slice(0, limit - localMatches.length)
.map((event) => ({
event_id: event.idEvent,
league: event.strLeague,
season: event.strSeason,
date: event.dateEvent,
time: event.strTime,
homeTeam: event.strHomeTeam,
awayTeam: event.strAwayTeam,
homeScore: event.intHomeScore,
awayScore: event.intAwayScore,
status: event.strStatus,
player_id: playerId,
fetchedAt: new Date(),
}));

console.log(
Fetched ${matches.length} new matches from API for Player ID "${playerId}".
);

// Save new matches to local DB
await Match.insertMany(
matches,
{ ordered: false } // Continue on duplicate errors
).catch((err) => {
if (err.code === 11000) {
console.warn("Duplicate match entries detected.");
} else {
console.error("Error inserting matches into DB:", err.message);
}
});

// Combine local and newly fetched matches
const combinedMatches = [...localMatches, ...matches].slice(0, limit);

console.log(
Returning ${combinedMatches.length} matches for Player ID "${playerId}".
);

res.json({ count: combinedMatches.length, matches: combinedMatches });
} catch (error) {
console.error("Error in getPlayerMatches:", error.message);
res.status(500).json({ error: error.message });
}
};

Now what is going on is that I am not able to search player

'use client'

import { useState } from 'react'
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
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'

// Mock API function
const fetchData = async (endpoint) => {
// Simulate API call
await new Promise(resolve => setTimeout(resolve, 500))
// Return mock data based on endpoint
switch (endpoint) {
case 'liveScore':
return { homeTeam: "Team A", awayTeam: "Team B", homeScore: 2, awayScore: 1 }
case 'leagues':
return ['Premier League', 'La Liga', 'Bundesliga', 'Serie A', 'Ligue 1', 'Champions League', 'Europa League']
case 'news':
return [
{ id: 1, title: "Breaking: Star Player Injured", content: "Team A's star player out for 3 weeks..." },
{ id: 2, title: "Transfer Rumors: Who's Moving Where?", content: "Latest transfer gossip from around the league..." },
]
case 'players':
return [
{ id: 1, name: "Lionel Messi", team: "Inter Miami", position: "Forward" },
{ id: 2, name: "Cristiano Ronaldo", team: "Al Nassr", position: "Forward" },
]
case 'teams':
return [
{ id: 1, name: "Manchester United", league: "Premier League", history: "Founded in 1878, 20 league titles..." },
{ id: 2, name: "Real Madrid", league: "La Liga", history: "Founded in 1902, 34 La Liga titles..." },
]
default:
return []
}
}

export default function FootballDashboard() {
const [activeTab, setActiveTab] = useState('liveScore')

return (

FootballBuzz

Live Score Leagues News Players Teams Fantasy Videos

<TabsContent value="liveScore">
  <LiveScore fetchData={fetchData} />
</TabsContent>
<TabsContent value="leagues">
  <LeagueSelector /> {/* Removed fetchData prop */}
</TabsContent>
<TabsContent value="news">
  <NewsArticles fetchData={fetchData} />
</TabsContent>
<TabsContent value="players">
  <PlayerSearch />
</TabsContent>
<TabsContent value="teams">
  <TeamHistory fetchData={fetchData} />
</TabsContent>
<TabsContent value="fantasy">
  <FantasySports />
</TabsContent>
<TabsContent value="videos">
  <VideoContent />
</TabsContent>
) } This is FootballDashboard.tsx
Now Problem is that on clicking photo+name it is showing me player but when screen opens there's no option of scrolling so I think problem is with frontend because backend is working fine photo is half cut one details screen
Can you please solve it make it scrollable and make it beutifull
A similar topic I found on stackoverflow
2

So basically when the user clicks on an image, I am displaying the details of the image as a modal/dialog box. But after the dialog box is closed, the screen scroll freezes. Things work on the screen, but I cant scroll down and access the other elements. Any idea how i can debug.

I feel its because somehow the modal is disabling my scroll bar.

Below is my dialog box code

import React from 'react'
import { Dialog, Transition } from '@headlessui/react'
import { Fragment } from 'react'
import {XIcon} from '@heroicons/react/solid'
import {useRecoilState} from 'recoil'
import {openState} from '../../../atoms/modalAtom'


const product = {
  name: "Women's Basic Tee",
  price: '$32',
  rating: 3.9,
  reviewCount: 512,
  href: '#',
  imageSrc: 'https://tailwindui.com/img/ecommerce-images/product-page-01-featured-product-shot.jpg',
  imageAlt: "Back of women's Basic Tee in black.",
  colors: [
    { name: 'Black', bgColor: 'bg-gray-900', selectedColor: 'ring-gray-900' },
    { name: 'Heather Grey', bgColor: 'bg-gray-400', selectedColor: 'ring-gray-400' },
  ],
  sizes: [
    { name: 'XXS', inStock: true },
    { name: 'XS', inStock: true },
    { name: 'S', inStock: true },
    { name: 'M', inStock: true },
    { name: 'L', inStock: true },
    { name: 'XL', inStock: true },
    { name: 'XXL', inStock: false },
  ],
}

export function DesignQuickView ({design}) {
  const [open, setOpen] = useRecoilState(openState)

  return (
    <Transition.Root show={open} as={Fragment}>
      <Dialog as="div" className="relative z-10" onClose={() => setOpen(false)}>
        <Transition.Child
          as={Fragment}
          enter="ease-out duration-300"
          enterFrom="opacity-0"
          enterTo="opacity-100"
          leave="ease-in duration-200"
          leaveFrom="opacity-100"
          leaveTo="opacity-0"
        >
          <div className="hidden fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity md:block" />
        </Transition.Child>

        <div className="fixed z-10 inset-0 overflow-y-auto">
          <div className="flex items-stretch md:items-center justify-center min-h-full text-center md:px-2 lg:px-4">
            {/* This element is to trick the browser into centering the modal contents. */}
            <span className="hidden md:inline-block md:align-middle md:h-screen" aria-hidden="true">
              &#8203;
            </span>
            <Transition.Child
              as={Fragment}
              enter="ease-out duration-300"
              enterFrom="opacity-0 translate-y-4 md:translate-y-0 md:scale-95"
              enterTo="opacity-100 translate-y-0 md:scale-100"
              leave="ease-in duration-200"
              leaveFrom="opacity-100 translate-y-0 md:scale-100"
              leaveTo="opacity-0 translate-y-4 md:translate-y-0 md:scale-95"
            >
              <Dialog.Panel className="flex text-base text-left transform transition w-full md:max-w-2xl md:px-4 md:my-8 lg:max-w-4xl">
                <div className="w-full relative flex items-center bg-white px-4 pt-14 pb-8 overflow-hidden shadow-2xl sm:px-6 sm:pt-8 md:p-6 lg:p-8">
                  <button
                    type="button"
                    className="absolute top-4 right-4 text-gray-400 hover:text-gray-500 sm:top-8 sm:right-6 md:top-6 md:right-6 lg:top-8 lg:right-8"
                    onClick={() => setOpen(false)}
                  >
                    <span className="sr-only">Close</span>
                    <XIcon className="h-6 w-6" aria-hidden="true" />
                  </button>

                  <div className="w-full grid grid-cols-1 gap-y-8 gap-x-6 items-start sm:grid-cols-12 lg:items-center lg:gap-x-8">
                    <div className="aspect-w-2 aspect-h-3 rounded-lg bg-gray-100 overflow-hidden sm:col-span-4 lg:col-span-5">
                      <img src={design.url} alt={design.name} className="object-center object-cover" />
                    </div>
                    <div className="sm:col-span-8 lg:col-span-7">
                      <h2 className="text-xl font-medium text-gray-900 sm:pr-12">Hoodie</h2>

                      <section aria-labelledby="information-heading" className="mt-1">
                        <h3 id="information-heading" className="sr-only">
                          Product information
                        </h3>

                        <p className="font-medium text-gray-900">{product.price}</p>

                      
                      </section>

                      <section aria-labelledby="options-heading" className="mt-8">
                        <h3 id="options-heading" className="sr-only">
                          Product options
                        </h3>

                        <form>
                          {/* Color picker */}
                          <div>
                            <h4 className="text-sm font-medium text-gray-900">Color</h4>

                            {/* <RadioGroup value={selectedColor} onChange={setSelectedColor} className="mt-2">
                              <RadioGroup.Label className="sr-only">Choose a color</RadioGroup.Label>
                              <div className="flex items-center space-x-3">
                                {product.colors.map((color) => (
                                  <RadioGroup.Option
                                    key={color.name}
                                    value={color}
                                    className={({ active, checked }) =>
                                      classNames(
                                        color.selectedColor,
                                        active && checked ? 'ring ring-offset-1' : '',
                                        !active && checked ? 'ring-2' : '',
                                        '-m-0.5 relative p-0.5 rounded-full flex items-center justify-center cursor-pointer focus:outline-none'
                                      )
                                    }
                                  >
                                    <RadioGroup.Label as="span" className="sr-only">
                                      {color.name}
                                    </RadioGroup.Label>
                                    <span
                                      aria-hidden="true"
                                      className={classNames(
                                        color.bgColor,
                                        'h-8 w-8 border border-black border-opacity-10 rounded-full'
                                      )}
                                    />
                                  </RadioGroup.Option>
                                ))}
                              </div>
                            </RadioGroup> */}
                          </div>

                          {/* Size picker */}
                          <div className="mt-8">
                            <div className="flex items-center justify-between">
                              <h4 className="text-sm font-medium text-gray-900">Size</h4>
                              <a href="#" className="text-sm font-medium text-indigo-600 hover:text-indigo-500">
                                Size guide
                              </a>
                            </div>

                            {/* <RadioGroup value={selectedSize} onChange={setSelectedSize} className="mt-2">
                              <RadioGroup.Label className="sr-only">Choose a size</RadioGroup.Label>
                              <div className="grid grid-cols-7 gap-2">
                                {product.sizes.map((size) => (
                                  <RadioGroup.Option
                                    key={size.name}
                                    value={size}
                                    className={({ active, checked }) =>
                                      classNames(
                                        size.inStock
                                          ? 'cursor-pointer focus:outline-none'
                                          : 'opacity-25 cursor-not-allowed',
                                        active ? 'ring-2 ring-offset-2 ring-indigo-500' : '',
                                        checked
                                          ? 'bg-indigo-600 border-transparent text-white hover:bg-indigo-700'
                                          : 'bg-white border-gray-200 text-gray-900 hover:bg-gray-50',
                                        'border rounded-md py-3 px-3 flex items-center justify-center text-sm font-medium uppercase sm:flex-1'
                                      )
                                    }
                                    disabled={!size.inStock}
                                  >
                                    <RadioGroup.Label as="span">{size.name}</RadioGroup.Label>
                                  </RadioGroup.Option>
                                ))}
                              </div>
                            </RadioGroup> */}
                          </div>

                        </form>
                      </section>
                    </div>
                  </div>
                </div>
              </Dialog.Panel>
            </Transition.Child>
          </div>
        </div>
      </Dialog>
    </Transition.Root>
  )
}

export default DesignQuickView

Here is how i am calling the modal/ dialog box

const Design = ({design}) => {

    const [open, setOpen] = useRecoilState(openState)

    
      return( 

      //Wrapping the image in a card
        <div>
                <div >
                    <img className= "inline-block h-14 w-14 rounded-md " 
                         src={design.url} 
                         alt="Custom design"
                         onClick={()=>setOpen(true)}
                         
                    />

                </div>
                <DesignQuickView design= {design}/>

        </div>
            )
}

export default Design
I think its the scrollbar. Any leads on how i can debug?

javascriptreactjsnext.jstailwind-csstailwind-ui
Share
Edit
Follow
asked Jul 10, 2022 at 21:40
rohit's user avatar
rohit
17922 silver badges1515 bronze badges
Add a comment
1 Answer
Sorted by:

Highest score (default)
2

I had the same issue where after closing the modal, scrolling stopped working on the main page.

Problem: I was rendering a list where each item had a button to open up the modal, and I had put the modal inside that list.

const [isOpen, setIsOpen] = useState(false);
const data = [
    // My data was an array of objects,
    // where each object contained position data
]

const closeModal = () => {
        setIsOpen(false);
    };

const openModal = () => {
    setIsOpen(true);
};

<div>
    {data.map((position) => (
        <button onClick={openModal}>Open Modal</button>
        <MyModal positionData={position} /> // <--------- Problem!
    ))}
</div>
Solution: Move the modal out of the list and create a state variable that gets updated in the openModal and closeModal functions:

const [selectedPosition, setSelectedPosition] = useState(null);
const [isOpen, setIsOpen] = useState(false);

const data = [
    // My data was an array of objects,
    // where each object contained position data
]

const closeModal = () => {
        setSelectedPosition(null);
        setIsOpen(false);
    };

const openModal = (position) => {
    setSelectedPosition(position);
    setIsOpen(true);
};

<div>
    {data.map((position) => (
        <button onClick={() => openModal(position)}>Open Modal</button>
    ))}
    <MyModal positionData={selectedPosition} /> // <-------- Solved!
</div>