with complete info button

PHOTO EMBED

Fri Mar 21 2025 09:56:55 GMT+0000 (Coordinated Universal Time)

Saved by @Urvashi

import React, { useState, useEffect, useRef } from 'react';
import '../styles/slotmachine.scss';
import '../styles/tooltip.scss';
import Title from '../components/Title';
import Reel from '../components/Reel';
import { GameButton } from '../components/GameButton';
import Registration from './Registration';
import axiosInstance from '../utils/axiosInstance';
import { AxiosError } from 'axios';
import { GameConfig } from '../index';
import Model from '../components/Model';
import Tooltip from '../components/Tooltip';

interface SlotImage {
  id: number;
  image_path: string;
  section_number: number;
  prize_name?: string; // Optional field for prize name
}

const SlotMachine: React.FC<GameConfig> = ({
  gameTitle = '',
  titleColor = '',
  backgroundImage = '',
  reelBorder = '',
  buttonBackgroundColor = '',
  buttonTextColor = '',
}) => {
  const [reels, setReels] = useState<string[][]>([]);
  const [isSoundOn, setIsSoundOn] = useState(true);
  const [slotImages, setSlotImages] = useState<SlotImage[]>([]);
  const [error, setError] = useState<string | null>(null);
  const [isSpinning, setIsSpinning] = useState(false);
  const [completedReels, setCompletedReels] = useState(0);
  const [isRegistrationOpen, setIsRegistrationOpen] = useState(false);
  const [spinCombination, setSpinCombination] = useState<string | null>(null);
  const [spinResult, setSpinResult] = useState<'win' | 'loss' | null>(null);
  const [spinKey, setSpinKey] = useState(0);
  const [isInfoOpen, setIsInfoOpen] = useState(false);
  const [tooltips, setTooltips] = useState({
    sound: false,
    info: false,
  });

  const spinAudioRef = useRef<HTMLAudioElement | null>(null);
  const winAudioRef = useRef<HTMLAudioElement | null>(null);
  const loseAudioRef = useRef<HTMLAudioElement | null>(null);
  const baseSpinDuration = 2000;
  const delayBetweenStops = 600;
  const DEFAULT_SLOT_COUNT = 3;

  const API_BASE_URL = process.env.REACT_APP_API_URL;
  const SPIN_AUDIO_URL = `${API_BASE_URL}audio/wheel-spin.mp3`;
  const WIN_AUDIO_URL = `${API_BASE_URL}audio/winning_sound.mp3`;
  const LOSE_AUDIO_URL = `${API_BASE_URL}audio/losing_game.mp3`;

  useEffect(() => {
    const fetchImages = async () => {
      try {
        const response = await axiosInstance.get('/api/slot/images');
        if (response.data.status && response.data.data.images.length > 0) {
          setSlotImages(response.data.data.images);
        } else {
          throw new Error(response.data.message || 'Failed to fetch images');
        }
      } catch (error) {
        console.error('Error fetching slot images:', error);
        const axiosError = error as AxiosError;
        if (axiosError.message === 'Network Error' || !axiosError.response) {
          setError('Server not responding');
        } else {
          setError('Error fetching slot images');
        }
        setIsRegistrationOpen(true);
      }
    };
    fetchImages();
    setReels(Array.from({ length: DEFAULT_SLOT_COUNT }, () => []));
  }, []);

  useEffect(() => {
    if (!spinAudioRef.current) {
      spinAudioRef.current = new Audio(SPIN_AUDIO_URL);
      spinAudioRef.current.loop = true;
    }
  }, []);
  useEffect(() => {
    if (!winAudioRef.current) {
      winAudioRef.current = new Audio(WIN_AUDIO_URL);
      winAudioRef.current.loop = false;
    }
  }, []);
  useEffect(() => {
    if (!loseAudioRef.current) {
      loseAudioRef.current = new Audio(LOSE_AUDIO_URL);
      loseAudioRef.current.loop = false;
    }
  }, []);

  const handleSpin = () => {
    if (!isSpinning) {
      setIsRegistrationOpen(true);
      setSpinResult(null);
    }
  };

  const handleRegistrationSubmit = (
    username: string,
    phone: string,
    eligible: boolean,
    combination: string,
    result: string,
  ) => {
    if (eligible) {
      setSpinCombination(combination); // This will be used for game info
      setSpinResult(result as 'win' | 'loss');
      setIsSpinning(true);
      setCompletedReels(0);
      setIsRegistrationOpen(false);
      setSpinKey((prev) => prev + 1);
      if (isSoundOn && spinAudioRef.current) {
        spinAudioRef.current.currentTime = 0;
        spinAudioRef.current.play().catch((err) => console.error('Error playing spin audio:', err));
      }
    }
  };

  useEffect(() => {
    if (completedReels === reels.length && isSpinning) {
      setIsSpinning(false);
      setTimeout(() => {
        if (spinAudioRef.current && !spinAudioRef.current.paused) {
          spinAudioRef.current.pause();
          spinAudioRef.current.currentTime = 0;
        }
        if (isSoundOn) {
          if (spinResult === 'win' && winAudioRef.current) {
            winAudioRef.current.currentTime = 0;
            winAudioRef.current
              .play()
              .catch((err) => console.error('Error playing win audio:', err));
          } else if (spinResult === 'loss' && loseAudioRef.current) {
            loseAudioRef.current.currentTime = 0;
            loseAudioRef.current
              .play()
              .catch((err) => console.error('Error playing lose audio:', err));
          }
        }
        setIsRegistrationOpen(true);
      }, 3600);
    }
  }, [completedReels, reels.length, isSpinning, spinResult, isSoundOn]);

  const handleReelComplete = () => {
    setCompletedReels((prev) => prev + 1);
  };

  const toggleSound = () => {
    setIsSoundOn((prev) => {
      const newSoundState = !prev;
      if (!newSoundState) {
        if (spinAudioRef.current && !spinAudioRef.current.paused) {
          spinAudioRef.current.pause();
          spinAudioRef.current.currentTime = 0;
        }
        if (winAudioRef.current && !winAudioRef.current.paused) {
          winAudioRef.current.pause();
          winAudioRef.current.currentTime = 0;
        }
        if (loseAudioRef.current && !loseAudioRef.current.paused) {
          loseAudioRef.current.pause();
          loseAudioRef.current.currentTime = 0;
        }
      } else if (newSoundState && isSpinning && spinAudioRef.current) {
        spinAudioRef.current.currentTime = 0;
        spinAudioRef.current.play().catch((err) => console.error('Error playing spin audio:', err));
      }
      return newSoundState;
    });
  };

  const infomessage = () => {
    setIsInfoOpen(true);
  };

  const handleSoundMouseEnter = () => setTooltips((prev) => ({ ...prev, sound: true }));
  const handleSoundMouseLeave = () => setTooltips((prev) => ({ ...prev, sound: false }));
  const handleInfoMouseEnter = () => setTooltips((prev) => ({ ...prev, info: true }));
  const handleInfoMouseLeave = () => setTooltips((prev) => ({ ...prev, info: false }));

  const renderGameInfo = () => {
    // For now, use spinCombination or a fallback if API isn't ready
    const combination = spinCombination || '123'; // Fallback to '123' until API is integrated

    // Placeholder logic: assume combination maps to a single prize (e.g., first digit or full string as key)
    // Replace this with actual API mapping when available
    const prizeId = parseInt(combination[0]); // Temporary: using first digit as prize ID
    const slot = slotImages.find((img) => img.id === prizeId) || {
      id: prizeId,
      image_path: 'placeholder.png', // Placeholder until API data
      prize_name: `Prize for ${combination}`,
    };

    return (
      <div className="game-info-container">
        <h3 className="game-info-title">Combination Prize Preview</h3>
        <div className="game-info-item">
          <img src={slot.image_path} alt={`Prize for ${combination}`} className="game-info-image" />
          <p className="game-info-prize">{slot.prize_name || `Prize for ${combination}`}</p>
        </div>
      </div>
    );
  };

  return (
    <div className="main-slotmachine">
      <div className="slot-machine">
        <div
          id="framework-center"
          style={{
            backgroundImage: `url(${backgroundImage})`,
            backgroundSize: 'cover',
            backgroundPosition: 'center',
            backgroundRepeat: 'no-repeat',
          }}
        >
          <div className="game-title" style={{ color: titleColor }}>
            <Title gameTitle={gameTitle} titleColor={titleColor} />
          </div>
          <div className="control-buttons-container">
            <Tooltip
              text={isSoundOn ? 'Mute Sound' : 'Unmute Sound'}
              visible={tooltips.sound}
              position="bottom"
            >
              <GameButton
                variant="sound"
                isActive={isSoundOn}
                onClick={toggleSound}
                onMouseEnter={handleSoundMouseEnter}
                onMouseLeave={handleSoundMouseLeave}
              />
            </Tooltip>
            <Tooltip text="View Info" visible={tooltips.info} position="bottom">
              <GameButton
                variant="info"
                onClick={infomessage}
                onMouseEnter={handleInfoMouseEnter}
                onMouseLeave={handleInfoMouseLeave}
              />
            </Tooltip>
          </div>
          <div className="reels-container" style={{ borderColor: reelBorder }}>
            {reels.map((_, index) => {
              const targetId = spinCombination ? parseInt(spinCombination[index] || '0') : -1;
              return (
                <Reel
                  key={`${index}-${spinKey}`}
                  slotImages={slotImages}
                  isSpinning={isSpinning}
                  spinDuration={baseSpinDuration + index * delayBetweenStops}
                  onSpinComplete={handleReelComplete}
                  targetId={targetId}
                  reelBorder={reelBorder}
                />
              );
            })}
          </div>
          <div className="spin-container">
            <div className="spin-button-wrapper">
              <GameButton
                variant="spin"
                onClick={handleSpin}
                disabled={isSpinning}
                buttonBackgroundColor={buttonBackgroundColor}
                buttonTextColor={buttonTextColor}
                reelBorder={reelBorder}
              />
            </div>
          </div>
        </div>
        <Registration
          isOpen={isRegistrationOpen}
          setIsOpen={setIsRegistrationOpen}
          onSubmit={handleRegistrationSubmit}
          spinResult={isSpinning ? null : spinResult}
          error={error}
        />
        <Model
          isOpen={isInfoOpen}
          onClose={() => setIsInfoOpen(false)}
          title="Game Rules"
          content={
            <div>
              <p>Welcome to the Slot Machine Game!</p>
              {slotImages.length > 0 ? renderGameInfo() : <p>Loading prize preview...</p>}
            </div>
          }
          showCloseButton={true}
        />
      </div>
    </div>
  );
};

export default SlotMachine;
 
content_copyCOPY