import React, { useState, useRef, useEffect } from "react"; import { ExitIcon, PlayStopIcon, ResetIcon } from "../../icons/SimulationIcons"; import { useActiveTool } from "../../../store/store"; import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore, useResetButtonStore, } from "../../../store/usePlayButtonStore"; import { DailyProductionIcon, EndIcon, ExpandIcon, HourlySimulationIcon, MonthlyROI, SpeedIcon, StartIcon, } from "../../icons/ExportCommonIcons"; import { getAvatarColor } from "../../../modules/collaboration/functions/getAvatarColor"; const SimulationPlayer: React.FC = () => { const MAX_SPEED = 8; // Maximum speed const [expand, setExpand] = useState(true); const { speed, setSpeed } = useAnimationPlaySpeed(); const [playSimulation, setPlaySimulation] = useState(false); const { setIsPlaying } = usePlayButtonStore(); const sliderRef = useRef<HTMLDivElement>(null); const isDragging = useRef(false); const { setActiveTool } = useActiveTool(); const { isPaused, setIsPaused } = usePauseButtonStore(); const { isReset, setReset } = useResetButtonStore(); // Button functions const handleReset = () => { setReset(!isReset); setSpeed(1); }; const handlePlayStop = () => { setIsPaused(!isPaused); setPlaySimulation(!playSimulation); }; const handleExit = () => { setPlaySimulation(false); setIsPlaying(false); setActiveTool("cursor"); }; // Slider functions starts const handleSpeedChange = (event: React.ChangeEvent<HTMLInputElement>) => { setSpeed(parseFloat(event.target.value)); }; const calculateHandlePosition = () => { return ((speed - 0.5) / (MAX_SPEED - 0.5)) * 100; }; const handleMouseDown = () => { isDragging.current = true; document.addEventListener("mousemove", handleMouseMove); document.addEventListener("mouseup", handleMouseUp); }; const handleMouseMove = (e: MouseEvent) => { if (!isDragging.current || !sliderRef.current) return; const sliderRect = sliderRef.current.getBoundingClientRect(); const offsetX = e.clientX - sliderRect.left; const percentage = Math.min(Math.max(offsetX / sliderRect.width, 0), 1); const newValue = 0.5 + percentage * (50 - 0.5); setSpeed(parseFloat(newValue.toFixed(1))); }; const handleMouseUp = () => { isDragging.current = false; document.removeEventListener("mousemove", handleMouseMove); document.removeEventListener("mouseup", handleMouseUp); }; useEffect(() => { return () => { document.removeEventListener("mousemove", handleMouseMove); document.removeEventListener("mouseup", handleMouseUp); }; }, []); // Slider function ends // UI-Part const hourlySimulation = 25; const dailyProduction = 75; const monthlyROI = 50; const process = [ { name: "process 1", completed: 0 }, // 0% completed { name: "process 2", completed: 20 }, // 20% completed { name: "process 3", completed: 40 }, // 40% completed { name: "process 4", completed: 60 }, // 60% completed { name: "process 5", completed: 80 }, // 80% completed { name: "process 6", completed: 100 }, // 100% completed { name: "process 7", completed: 0 }, // 0% completed { name: "process 8", completed: 50 }, // 50% completed { name: "process 9", completed: 90 }, // 90% completed { name: "process 10", completed: 30 }, // 30% completed ]; // Move getRandomColor out of render const getRandomColor = () => { const letters = "0123456789ABCDEF"; let color = "#"; for (let i = 0; i < 6; i++) { color += letters[Math.floor(Math.random() * 16)]; } return color; }; // Store colors for each process item const [processColors, setProcessColors] = useState<string[]>([]); // Generate colors on mount or when process changes useEffect(() => { const generatedColors = process.map(() => getRandomColor()); setProcessColors(generatedColors); }, []); const intervals = [10, 20, 30, 40, 50, 60]; // in minutes const totalSegments = intervals.length; const progress = 20; // percent (example) const processPlayerRef = useRef<HTMLDivElement>(null); const processWrapperRef = useRef<HTMLDivElement>(null); const [playerPosition, setPlayerPosition] = useState(20); // initial position const handleProcessMouseDown = (e: React.MouseEvent) => { if (!processWrapperRef.current) return; const rect = processWrapperRef.current.getBoundingClientRect(); let x = e.clientX - rect.left; x = Math.max(0, Math.min(x, rect.width)); setPlayerPosition(x); const onMouseMove = (e: MouseEvent) => { if (!processWrapperRef.current) return; const newRect = processWrapperRef.current.getBoundingClientRect(); let newX = e.clientX - newRect.left; newX = Math.max(0, Math.min(newX, newRect.width)); setPlayerPosition(newX); const progressPercent = (newX / newRect.width) * 100; console.log(`Dragging at progress: ${progressPercent.toFixed(1)}%`); }; const onMouseUp = () => { document.removeEventListener("mousemove", onMouseMove); document.removeEventListener("mouseup", onMouseUp); }; document.addEventListener("mousemove", onMouseMove); document.addEventListener("mouseup", onMouseUp); }; return ( <div className="simulation-player-wrapper"> <div className={`simulation-player-container ${expand ? "open" : ""}`}> <div className="controls-container"> <div className="production-details"> {/* hourlySimulation */} <div className="hourly-wrapper production-wrapper"> <div className="header"> <div className="icon"> <HourlySimulationIcon /> </div> <div className="label">Hourly Simulation</div> </div> <div className="progress-wrapper"> <div className="progress" style={{ width: hourlySimulation }} ></div> </div> </div> {/* dailyProduction */} <div className="dailyProduction-wrapper production-wrapper"> <div className="header"> <div className="icon"> <DailyProductionIcon /> </div> <div className="label">Daily Production</div> </div> <div className="progress-wrapper"> <div className="progress" style={{ width: dailyProduction }} ></div> </div> </div> {/* monthlyROI */} <div className="monthlyROI-wrapper production-wrapper"> <div className="header"> <div className="icon"> <MonthlyROI /> </div> <div className="label">Monthly ROI</div> </div> <div className="progress-wrapper"> <div className="progress" style={{ width: monthlyROI }}></div> </div>{" "} </div> </div> <div className="controls-wrapper"> <button className="simulation-button-container" onClick={() => { handleReset(); }} > <ResetIcon /> Reset </button> <button className="simulation-button-container" onClick={() => { handlePlayStop(); }} > <PlayStopIcon /> {playSimulation ? "Play" : "Stop"} </button> <button className="simulation-button-container" onClick={() => { handleExit(); }} > <ExitIcon /> Exit </button> <button className="expand-icon-container" onClick={() => setExpand(!expand)} > <ExpandIcon isActive={!expand} /> </button> </div> </div> <div className="progresser-wrapper"> <div className="time-displayer"> <div className="start-time-wrappper"> <div className="icon"> <StartIcon /> </div> <div className="time-wrapper"> <div className="date">23 April ,25</div> <div className="time">04:41 PM</div> </div> </div> <div className="time-progresser"> <div className="timeline"> {intervals.map((label, index) => { const segmentProgress = (index / totalSegments) * 100; const isFilled = progress >= segmentProgress; return ( <React.Fragment key={index}> <div className="label-dot-wrapper"> <div className="label">{label} mins</div> <div className={`dot ${isFilled ? "filled" : ""}`} ></div> </div> {index < intervals.length - 1 && ( <div className={`line ${ progress >= ((index + 1) / totalSegments) * 100 ? "filled" : "" }`} ></div> )} </React.Fragment> ); })} </div> </div> <div className="end-time-wrappper"> <div className="time-wrapper"> <div className="time">00:10:20</div> </div> <div className="icon"> <EndIcon /> </div> </div> </div> <div className="speed-control-container"> <div className="min-value"> <div className="icon"> <SpeedIcon /> </div> Speed </div> <div className="slider-container" ref={sliderRef}> <div className="speed-label mix-value">0.5X</div> <div className="marker marker-10"></div> <div className="marker marker-20"></div> <div className="marker marker-30"></div> <div className="marker marker-40"></div> <div className="marker marker-50"></div> <div className="marker marker-60"></div> <div className="marker marker-70"></div> <div className="marker marker-80"></div> <div className="marker marker-90"></div> <div className="custom-slider"> <button className={`slider-handle ${isDragging ? "dragging" : ""}`} style={{ left: `${calculateHandlePosition()}%` }} onMouseDown={handleMouseDown} > {speed.toFixed(1)}x </button> <input type="range" min="0.5" max={MAX_SPEED} step="0.1" value={speed} onChange={handleSpeedChange} className="slider-input" /> </div> <div className="speed-label max-value">4x</div> </div> </div> </div> <div className="processDisplayer"> <div className="start-displayer timmer">00:00</div> <div className="end-displayer timmer">24:00</div> <div className="process-wrapper" style={{ padding: expand ? "0px" : "5px 35px" }} > <div className="process-container" ref={processWrapperRef} onMouseDown={handleProcessMouseDown} > {process.map((item, index) => ( <div key={index} className="process" style={{ width: `${item.completed}%`, backgroundColor: getAvatarColor(index), }} > <div className="process-player" ref={processPlayerRef} style={{ left: playerPosition, position: "absolute" }} ></div> </div> ))} </div> </div> </div> </div> </div> ); }; export default SimulationPlayer;