updated simulation player

This commit is contained in:
Nalvazhuthi
2025-04-29 17:54:36 +05:30
100 changed files with 5726 additions and 1665 deletions

View File

@@ -15,7 +15,7 @@ import {
} from "../icons/ExportToolsIcons";
import { ArrowIcon, TickIcon } from "../icons/ExportCommonIcons";
import useModuleStore, { useThreeDStore } from "../../store/useModuleStore";
import { handleSaveTemplate } from "../../modules//visualization/functions/handleSaveTemplate";
import { handleSaveTemplate } from "../../modules/visualization/functions/handleSaveTemplate";
import { usePlayButtonStore } from "../../store/usePlayButtonStore";
import useTemplateStore from "../../store/useTemplateStore";
import { useSelectedZoneStore } from "../../store/visualization/useZoneStore";

View File

@@ -1,10 +1,14 @@
import React from "react";
import React, { useState } from "react";
import { ProductionCapacityIcon } from "../../icons/analysis";
const ProductionCapacity = () => {
const ProductionCapacity = ({
progressPercent = 10,
avgProcessTime = "28.4 Secs/unit",
machineUtilization = "78%",
throughputValue = 128,
timeRange = { startTime: "08:00 AM", endTime: "09:00 AM" },
}) => {
const totalBars = 6;
const progressPercent = 50;
const barsToFill = Math.floor((progressPercent / 100) * totalBars);
const partialFillPercent =
((progressPercent / 100) * totalBars - barsToFill) * 100;
@@ -14,8 +18,10 @@ const ProductionCapacity = () => {
<div className="productionCapacity-wrapper analysis-card-wrapper">
<div className="card-header">
<div className="header">
<div className="main-header">Throughput Summary</div>
<div className="sub-header">08:00 - 09:00 AM</div>
<div className="main-header">Production Capacity</div>
<div className="sub-header">
{timeRange.startTime} - {timeRange.endTime}
</div>
</div>
<div className="icon-wrapper">
<ProductionCapacityIcon />
@@ -24,10 +30,10 @@ const ProductionCapacity = () => {
<div className="process-container">
<div className="throughput-value">
<span className="value">128</span> Units/hour
<span className="value">{throughputValue}</span> Units/hour
</div>
{/* Progress Bar */}
{/* Dynamic Progress Bar */}
<div className="progress-bar-wrapper">
{[...Array(totalBars)].map((_, i) => (
<div className="progress-bar" key={i}>
@@ -47,11 +53,11 @@ const ProductionCapacity = () => {
<div className="metrics-section">
<div className="metric">
<span className="label">Avg. Process Time</span>
<span className="value">28.4 Secs/unit</span>
<span className="value">{avgProcessTime}</span>
</div>
<div className="metric">
<span className="label">Machine Utilization</span>
<span className="value">78%</span>
<span className="value">{machineUtilization}</span>
</div>
</div>
</div>

View File

@@ -1,54 +1,68 @@
import React from "react";
import React, { useState } from "react";
import { ROISummaryIcon } from "../../icons/analysis";
import SemiCircleProgress from "./SemiCircleProgress";
const ROISummary = () => {
// Data for the cost breakdown as an array of objects
const costBreakdownData = [
{
item: "Raw Material A",
unitCost: "₹ 10/unit",
laborCost: "₹ 0",
totalCost: "₹ 1000",
sellingPrice: "₹ 1500",
},
{
item: "Labor",
unitCost: "₹ 10/unit",
laborCost: "₹ 500",
totalCost: "₹ 500",
sellingPrice: "N/A",
},
{
item: "Product 1",
unitCost: "₹ 10/unit",
laborCost: "₹ 200",
totalCost: "₹ 200",
sellingPrice: "₹ 2000",
},
{
item: "Machine",
unitCost: "-",
laborCost: "-",
totalCost: "₹ 20,000",
sellingPrice: "N/A",
},
{
item: "Total",
unitCost: "-",
laborCost: "-",
totalCost: "₹ 1,20,000",
sellingPrice: "-",
},
{
item: "Net Profit",
unitCost: "-",
laborCost: "-",
totalCost: "₹ 1,60,000",
sellingPrice: "-",
},
];
const progressValue = 50;
const ROISummary = ({
roiSummaryData = {
productName: "Product name",
roiPercentage: 133,
paybackPeriod: 50.3,
totalCost: "₹ 1,20,000",
revenueGenerated: "₹ 2,80,000",
netProfit: "₹ 1,60,000",
costBreakdown: [
{
item: "Raw Material A",
unitCost: "₹ 10/unit",
laborCost: "₹ 0",
totalCost: "₹ 1000",
sellingPrice: "₹ 1500",
},
{
item: "Labor",
unitCost: "₹ 10/unit",
laborCost: "₹ 500",
totalCost: "₹ 500",
sellingPrice: "N/A",
},
{
item: "Product 1",
unitCost: "₹ 10/unit",
laborCost: "₹ 200",
totalCost: "₹ 200",
sellingPrice: "₹ 2000",
},
{
item: "Machine",
unitCost: "-",
laborCost: "-",
totalCost: "₹ 20,000",
sellingPrice: "N/A",
},
{
item: "Total",
unitCost: "-",
laborCost: "-",
totalCost: "₹ 1,20,000",
sellingPrice: "-",
},
{
item: "Net Profit",
unitCost: "-",
laborCost: "-",
totalCost: "₹ 1,60,000",
sellingPrice: "-",
},
],
},
}) => {
const [isTableOpen, setIsTableOpen] = useState(false); // State to handle the table open/close
// Function to toggle the breakdown table visibility
const toggleTable = () => {
setIsTableOpen(!isTableOpen);
};
return (
<div className="roiSummary-container analysis-card">
<div className="roiSummary-wrapper analysis-card-wrapper">
@@ -63,71 +77,94 @@ const ROISummary = () => {
</div>
<div className="product-info">
<div className="product-label">Product :</div>
<div className="product-name">Product name</div>
<div className="product-name">{roiSummaryData.productName}</div>
</div>
<div className="playBack">
<div className="icon"></div>
<div className="info">
<span>+133%</span> ROI with payback in just <span>50.3</span> months
<span>+{roiSummaryData.roiPercentage}%</span> ROI with payback in
just <span>{roiSummaryData.paybackPeriod}</span> months
</div>
</div>
<div className="roi-details">
<SemiCircleProgress />
<div className="progress-wrapper">
<SemiCircleProgress />
<div className="content">
you're on track to hit it by
<div className="key">July 2029</div>
</div>
</div>
<div className="metrics">
<div className="metric-wrapper">
<div className="metric-item">
<span className="metric-label">Total Cost Incurred</span>
<span className="metric-value"> 1,20,000</span>
<span className="metric-value">{roiSummaryData.totalCost}</span>
</div>
<div className="metric-item">
<span className="metric-label">Revenue Generated</span>
<span className="metric-value"> 2,80,000</span>
<span className="metric-value">
{roiSummaryData.revenueGenerated}
</span>
</div>
</div>
<div className="metric-item net-profit">
<span className="metric-label">Net Profit</span>
<span className="metric-value"> 1,60,000</span>
<span className="metric-value">{roiSummaryData.netProfit}</span>
</div>
</div>
</div>
<div className="cost-breakdown">
<div className="breakdown-header">
<span className="section-number"></span>
<span className="section-title">Cost Breakdown</span>
<span className="expand-icon"></span>
<div className="breakdown-header" onClick={toggleTable}>
<div className="section-wrapper">
<span className="section-number"></span>
<span className="section-title">Cost Breakdown</span>
</div>
<span className={`expand-icon ${isTableOpen ? "open" : ""}`}>
{isTableOpen ? "⌵" : "⌵"}
</span>
</div>
<table className="breakdown-table">
<thead>
<tr>
<th>Item</th>
<th>Unit Cost</th>
<th>Labor Cost</th>
<th>Total Cost</th>
<th>Selling Price</th>
</tr>
</thead>
<tbody>
{costBreakdownData.map((row, index) => (
<tr
key={index}
className={
row.item === "Total"
? "total-row"
: row.item === "Net Profit"
? "net-profit-row"
: ""
}
>
<td>{row.item}</td>
<td>{row.unitCost}</td>
<td>{row.laborCost}</td>
<td>{row.totalCost}</td>
<td>{row.sellingPrice}</td>
<div
className={`breakdown-table-wrapper ${
isTableOpen ? "open" : "closed"
}`}
style={{
transition: "max-height 0.3s ease-in-out",
overflow: "hidden",
}}
>
<table className="breakdown-table">
<thead>
<tr>
<th>Item</th>
<th>Unit Cost</th>
<th>Labor Cost</th>
<th>Total Cost</th>
<th>Selling Price</th>
</tr>
))}
</tbody>
</table>
</thead>
<tbody>
{roiSummaryData.costBreakdown.map((row, index) => (
<tr
key={index}
className={
row.item === "Total"
? "total-row"
: row.item === "Net Profit"
? "net-profit-row"
: ""
}
>
<td>{row.item}</td>
<td>{row.unitCost}</td>
<td>{row.laborCost}</td>
<td>{row.totalCost}</td>
<td>{row.sellingPrice}</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
<div className="tips-section">
<div className="tip-header">

View File

@@ -1,23 +1,25 @@
import React from "react";
const SemiCircleProgress = () => {
const progress = 10;
const clampedProgress = Math.min(Math.max(progress, 0), 100); // clamp 0-100
const progress = 50;
const clampedProgress = Math.min(Math.max(progress, 0), 100);
const gradientProgress = clampedProgress * 0.5;
return (
<div className="semi-circle-wrapper">
<div
className="semi-circle"
style={{
background: `conic-gradient(
from 180deg,
skyblue 0% ${clampedProgress / 2}%,
lightgray ${clampedProgress / 2}% 50%
)`,
background: `conic-gradient(from 270deg, skyblue 0% ${gradientProgress}%, lightgray ${gradientProgress}% 100%)`,
}}
>
<div className="progress-cover"></div>
</div>
<div className="label">{clampedProgress}%</div>
<div className="label-wrapper">
<div className="label">{clampedProgress}%</div>
<div className="label-content">Years</div>
</div>
<div className="content">you're on track to hit it by July 2029</div>
</div>
);
};

View File

@@ -11,21 +11,57 @@ import { PowerIcon, ThroughputSummaryIcon } from "../../icons/analysis";
ChartJS.register(LineElement, CategoryScale, LinearScale, PointElement);
// Helper function to generate random colors
const getRandomColor = () => {
const letters = "0123456789ABCDEF";
let color = "#";
for (let i = 0; i < 6; i++) {
color += letters[Math.floor(Math.random() * 16)];
}
return color;
};
const ThroughputSummary = () => {
const data = {
// Define all data internally within the component
const timeRange = {
startTime: "08:00 AM",
endTime: "09:00 AM",
};
const throughputData = {
labels: ["08:00", "08:10", "08:20", "08:30", "08:40", "08:50", "09:00"],
data: [100, 120, 110, 130, 125, 128, 132],
totalThroughput: 1240,
assetUsage: 85,
};
const energyConsumption = {
energyConsumed: 456,
unit: "KWH",
};
// Dynamic shift data
const shiftUtilization = [
{ shift: 1, percentage: 30 },
{ shift: 2, percentage: 40 },
{ shift: 3, percentage: 30 },
];
// Chart data configuration
const chartData = {
labels: throughputData.labels,
datasets: [
{
label: "Units/hour",
data: [100, 120, 110, 130, 125, 128, 132],
data: throughputData.data,
borderColor: "#B392F0",
tension: 0.4,
pointRadius: 0, // hide points
pointRadius: 0, // Hide points
},
],
};
const options = {
const chartOptions = {
responsive: true,
scales: {
x: {
@@ -57,19 +93,15 @@ const ThroughputSummary = () => {
},
};
const shiftUtilization = {
"shift 1": 25,
"shift 2": 45,
"shift 3": 15,
};
return (
<div className="throughoutSummary analysis-card">
<div className="throughoutSummary-wrapper analysis-card-wrapper">
<div className="card-header">
<div className="header">
<div className="main-header">Throughput Summary</div>
<div className="sub-header">08:00 - 09:00 AM</div>
<div className="sub-header">
{timeRange.startTime} - {timeRange.endTime}
</div>
</div>
<div className="icon-wrapper">
<ThroughputSummaryIcon />
@@ -78,14 +110,15 @@ const ThroughputSummary = () => {
<div className="process-container">
<div className="throughput-value">
<span className="value">1240</span> Units/hour
<span className="value">{throughputData.totalThroughput}</span>{" "}
Units/hour
</div>
<div className="lineChart">
<div className="assetUsage">
<div className="key">Asset usage</div>
<div className="value">85%</div>
<div className="value">{throughputData.assetUsage}%</div>
</div>
<Line data={data} options={options} />
<Line data={chartData} options={chartOptions} />
</div>
</div>
@@ -97,43 +130,41 @@ const ThroughputSummary = () => {
<PowerIcon />
</div>
<div className="value-wrapper">
<div className="value">456</div>
<div className="unit">KWH</div>
<div className="value">{energyConsumption.energyConsumed}</div>
<div className="unit">{energyConsumption.unit}</div>
</div>
</div>
</div>
<div className="shiftUtilization footer-card">
<div className="header">Shift Utilization</div>
<div className="value-container">
<div className="value">85%</div>
<div className="value">{throughputData.assetUsage}%</div>
<div className="progress-wrapper">
<div
className="progress shift-1"
style={{ width: "30%" }}
></div>
<div
className="progress shift-2"
style={{ width: "40%" }}
></div>
<div
className="progress shift-3"
style={{ width: "30%" }}
></div>
{/* Dynamically create progress bars based on shiftUtilization array */}
{shiftUtilization.map((shift) => (
<div
key={shift.shift}
className={`progress shift-${shift.shift}`}
style={{
width: `${shift.percentage}%`,
backgroundColor: getRandomColor(),
}}
></div>
))}
</div>
<div className="progress-indicator">
<div className="shift-wrapper">
<span className="indicator shift-1"></span>
<label>Shift 1</label>
</div>
<div className="shift-wrapper">
<span className="indicator shift-2"></span>
<label>Shift 2</label>
</div>
<div className="shift-wrapper">
<span className="indicator shift-3"></span>
<label>Shift 3</label>
</div>
{/* Dynamically create shift indicators with random colors */}
{shiftUtilization.map((shift) => (
<div className="shift-wrapper" key={shift.shift}>
<span
className={`indicator shift-${shift.shift}`}
style={{ backgroundColor: getRandomColor() }} // Random color for indicator
></span>
<label>Shift {shift.shift}</label>
</div>
))}
</div>
</div>
</div>

View File

@@ -1,47 +1,72 @@
import React, { useState } from "react";
import LabledDropdown from "./LabledDropdown";
import { ArrowIcon } from "../../icons/ExportCommonIcons";
import LabledDropdown from "./LabledDropdown";
const PreviewSelectionWithUpload: React.FC = () => {
const [showPreview, setSetshowPreview] = useState(false);
interface PreviewSelectionWithUploadProps {
preview?: boolean;
upload?: boolean;
label?: string;
onSelect: (option: string) => void;
defaultOption: string;
options: string[];
}
const PreviewSelectionWithUpload: React.FC<PreviewSelectionWithUploadProps> = ({
preview = false,
upload = false,
onSelect,
label,
defaultOption,
options,
}) => {
const [showPreview, setShowPreview] = useState(false);
return (
<div className="preview-selection-with-upload-wrapper">
<div
className="input-header-container"
onClick={() => setSetshowPreview(!showPreview)}
>
<div className="input-header">Preview</div>
<div
className="arrow-container"
style={{ rotate: showPreview ? "0deg" : "90deg" }}
>
<ArrowIcon />
</div>
</div>
{showPreview && (
<div className="canvas-wrapper">
<div className="canvas-container"></div>
{preview && (
<>
<button
className="input-header-container"
onClick={() => setShowPreview(!showPreview)}
>
<div className="input-header">Preview</div>
<div
className="arrow-container"
style={{ rotate: showPreview ? "0deg" : "90deg" }}
>
<ArrowIcon />
</div>
</button>
{showPreview && (
<div className="canvas-wrapper">
<div className="canvas-container"></div>
</div>
)}
</>
)}
{upload && (
<div className="asset-selection-container">
<div className="upload-custom-asset-button">
<div className="title">Upload Product</div>
<input
type="file"
accept=".glb, .gltf"
id="simulation-product-upload"
/>
<label
className="upload-button"
htmlFor="simulation-product-upload"
>
Upload here
</label>
</div>
</div>
)}
<div className="asset-selection-container">
<div className="upload-custom-asset-button">
<div className="title">Upload Product</div>
<input
type="file"
accept=".glb, .gltf"
id="simulation-product-upload"
/>
<label className="upload-button" htmlFor="simulation-product-upload">
Upload here
</label>
</div>
<LabledDropdown
label="Presets"
defaultOption={"Default material"}
options={["Default material", "Product 1", "Product 2"]}
onSelect={(option) => console.log(option)}
/>
</div>
<LabledDropdown
label={label}
defaultOption={defaultOption}
options={options}
onSelect={onSelect}
/>
</div>
);
};

View File

@@ -10,7 +10,7 @@ import {
ArrowIcon,
EyeIcon,
LockIcon,
RmoveIcon,
RemoveIcon,
} from "../../icons/ExportCommonIcons";
import { useThree } from "@react-three/fiber";
import { useFloorItems, useZoneAssetId, useZones } from "../../../store/store";
@@ -142,9 +142,6 @@ const List: React.FC<ListProps> = ({ items = [], remove }) => {
)
);
}
console.log('newName: ', newName);
}
const checkZoneNameDuplicate = (name: string) => {
return zones.some(
@@ -184,7 +181,7 @@ const List: React.FC<ListProps> = ({ items = [], remove }) => {
</div>
{remove && (
<div className="remove option">
<RmoveIcon />
<RemoveIcon />
</div>
)}
{item.assets && item.assets.length > 0 && (
@@ -221,7 +218,7 @@ const List: React.FC<ListProps> = ({ items = [], remove }) => {
</div>
{remove && (
<div className="remove option">
<RmoveIcon />
<RemoveIcon />
</div>
)}
</div>

View File

@@ -1,23 +1,20 @@
import React, { useEffect } from "react";
import {
useEditWidgetOptionsStore,
useLeftData,
useRightClickSelected,
useRightSelected,
useTopData,
} from "../../../store/visualization/useZone3DWidgetStore";
interface EditWidgetOptionProps {
options: string[];
onClick: (option: string) => void;
}
const EditWidgetOption: React.FC<EditWidgetOptionProps> = ({
options,
onClick
}) => {
const { top } = useTopData();
const { left } = useLeftData();
const { setRightSelect } = useRightSelected();
const { setEditWidgetOptions } = useEditWidgetOptionsStore();
useEffect(() => {
@@ -38,10 +35,7 @@ const EditWidgetOption: React.FC<EditWidgetOptionProps> = ({
<div
className="option"
key={index}
onClick={(e) => {
setRightSelect(option);
setEditWidgetOptions(false);
}}
onClick={() => onClick(option)}
>
{option}
</div>

View File

@@ -7,6 +7,15 @@ import {
usePlayButtonStore,
useResetButtonStore,
} from "../../../store/usePlayButtonStore";
import {
DailyProductionIcon,
EndIcon,
ExpandIcon,
HourlySimulationIcon,
MonthlyROI,
SpeedIcon,
StartIcon,
} from "../../icons/ExportCommonIcons";
const SimulationPlayer: React.FC = () => {
const { speed, setSpeed } = useAnimationPlaySpeed();
@@ -30,7 +39,7 @@ const SimulationPlayer: React.FC = () => {
const handleExit = () => {
setPlaySimulation(false);
setIsPlaying(false);
setActiveTool("cursor")
setActiveTool("cursor");
};
// Slider functions starts
@@ -72,70 +81,277 @@ const SimulationPlayer: React.FC = () => {
}, []);
// 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
];
const [expand, setExpand] = useState(false);
// 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 = 80; // percent (example)
const processPlayerRef = useRef<HTMLDivElement>(null);
const processWrapperRef = useRef<HTMLDivElement>(null);
const [playerPosition, setPlayerPosition] = useState(0);
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">
<div className={`simulation-player-container ${expand ? "open" : ""}`}>
<div className="controls-container">
<div
className="simulation-button-container"
onClick={() => {
handleReset();
}}
>
<ResetIcon />
Reset
</div>
<div
className="simulation-button-container"
onClick={() => {
handlePlayStop();
}}
>
<PlayStopIcon />
{playSimulation ? "Play" : "Stop"}
</div>
<div
className="simulation-button-container"
onClick={() => {
handleExit();
}}
>
<ExitIcon />
Exit
</div>
</div>
<div className="speed-control-container">
<div className="min-value">0.5x</div>
<div className="slider-container" ref={sliderRef}>
<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">
<div
className={`slider-handle ${isDragging ? "dragging" : ""}`}
style={{ left: `${calculateHandlePosition()}%` }}
onMouseDown={handleMouseDown}
>
{speed.toFixed(1)}x
<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>
<input
type="range"
min="0.5"
max="8"
step="0.1"
value={speed}
onChange={handleSpeedChange}
className="slider-input"
/>
<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="max-value">8x</div>
<div className="controls-wrapper">
<div
className="simulation-button-container"
onClick={() => {
handleReset();
}}
>
<ResetIcon />
Reset
</div>
<div
className="simulation-button-container"
onClick={() => {
handlePlayStop();
}}
>
<PlayStopIcon />
{playSimulation ? "Play" : "Stop"}
</div>
<div
className="simulation-button-container"
onClick={() => {
handleExit();
}}
>
<ExitIcon />
Exit
</div>
<div
className="simulation-button-container"
onClick={() => setExpand(!expand)}
>
<ExpandIcon />
</div>
</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">0X</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">
<div
className={`slider-handle ${isDragging ? "dragging" : ""}`}
style={{ left: `${calculateHandlePosition()}%` }}
onMouseDown={handleMouseDown}
>
{speed.toFixed(1)}x
</div>
<input
type="range"
min="0.5"
max="8"
step="0.1"
value={speed}
onChange={handleSpeedChange}
className="slider-input"
/>
</div>
<div className="speed-label max-value">8x</div>
</div>
</div>
</div>
<div className="processDisplayer">
<div
className="process-player"
style={{ left: playerPosition, position: "absolute" }}
></div>
<div
className="process-wrapper"
ref={processWrapperRef}
onMouseDown={handleProcessMouseDown}
>
{process.map((item, index) => (
<div
key={index}
className="process"
style={{
width: `${item.completed}%`,
backgroundColor: processColors[index],
}}
></div>
))}
</div>
<div
className="process-player"
ref={processPlayerRef}
style={{ left: playerPosition, position: "absolute" }}
></div>
</div>
</div>
</div>