+
+
ROI Summary
@@ -14,6 +75,111 @@ const ROISummary = () => {
+
+
Product :
+
{roiSummaryData.productName}
+
+
+
+
+ +{roiSummaryData.roiPercentage}% ROI with payback in
+ just {roiSummaryData.paybackPeriod} months
+
+
+
+
+
+
+ you're on track to hit it by
+
July 2029
+
+
+
+
+
+ Total Cost Incurred
+ {roiSummaryData.totalCost}
+
+
+ Revenue Generated
+
+ {roiSummaryData.revenueGenerated}
+
+
+
+
+ Net Profit
+ {roiSummaryData.netProfit}
+
+
+
+
+
+
+ â‘
+ Cost Breakdown
+
+
+
+ {isTableOpen ? "⌵" : "⌵"}
+
+
+
+
+
+
+ Item |
+ Unit Cost |
+ Labor Cost |
+ Total Cost |
+ Selling Price |
+
+
+
+ {roiSummaryData.costBreakdown.map((row, index) => (
+
+ {row.item} |
+ {row.unitCost} |
+ {row.laborCost} |
+ {row.totalCost} |
+ {row.sellingPrice} |
+
+ ))}
+
+
+
+
+
+
+ 💡
+ How to improve ROI?
+
+
+ Increase CNC utilization by 10%{" "}
+ to shave 0.5 months of payback
+ period
+
+
+
);
diff --git a/app/src/components/ui/analysis/SemiCircleProgress.tsx b/app/src/components/ui/analysis/SemiCircleProgress.tsx
new file mode 100644
index 0000000..084636b
--- /dev/null
+++ b/app/src/components/ui/analysis/SemiCircleProgress.tsx
@@ -0,0 +1,27 @@
+import React from "react";
+
+const SemiCircleProgress = () => {
+ const progress = 50;
+ const clampedProgress = Math.min(Math.max(progress, 0), 100);
+ const gradientProgress = clampedProgress * 0.5;
+
+ return (
+
+
+
+
{clampedProgress}%
+
Years
+
+
you're on track to hit it by July 2029
+
+ );
+};
+
+export default SemiCircleProgress;
diff --git a/app/src/components/ui/analysis/ThroughputSummary.tsx b/app/src/components/ui/analysis/ThroughputSummary.tsx
index cb4fac5..b92a82d 100644
--- a/app/src/components/ui/analysis/ThroughputSummary.tsx
+++ b/app/src/components/ui/analysis/ThroughputSummary.tsx
@@ -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 (
Throughput Summary
-
08:00 - 09:00 AM
+
+ {timeRange.startTime} - {timeRange.endTime}
+
@@ -78,14 +110,15 @@ const ThroughputSummary = () => {
- 1240 Units/hour
+ {throughputData.totalThroughput}{" "}
+ Units/hour
Asset usage
-
85%
+
{throughputData.assetUsage}%
-
+
@@ -97,43 +130,41 @@ const ThroughputSummary = () => {
-
456
-
KWH
+
{energyConsumption.energyConsumed}
+
{energyConsumption.unit}
Shift Utilization
-
85%
+
{throughputData.assetUsage}%
-
-
-
+ {/* Dynamically create progress bars based on shiftUtilization array */}
+ {shiftUtilization.map((shift) => (
+
+ ))}
+
-
-
-
-
-
-
-
-
-
-
-
-
+ {/* Dynamically create shift indicators with random colors */}
+ {shiftUtilization.map((shift) => (
+
+
+
+
+ ))}
diff --git a/app/src/components/ui/inputs/InputToggle.tsx b/app/src/components/ui/inputs/InputToggle.tsx
index 941d486..5e69291 100644
--- a/app/src/components/ui/inputs/InputToggle.tsx
+++ b/app/src/components/ui/inputs/InputToggle.tsx
@@ -24,11 +24,16 @@ const InputToggle: React.FC
= ({
-
+
diff --git a/app/src/components/ui/simulation/simulationPlayer.tsx b/app/src/components/ui/simulation/simulationPlayer.tsx
index fb59d3a..a23652b 100644
--- a/app/src/components/ui/simulation/simulationPlayer.tsx
+++ b/app/src/components/ui/simulation/simulationPlayer.tsx
@@ -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();
@@ -31,7 +40,7 @@ const SimulationPlayer: React.FC = () => {
const handleExit = () => {
setPlaySimulation(false);
setIsPlaying(false);
- setActiveTool("cursor")
+ setActiveTool("cursor");
};
// Slider functions starts
@@ -73,70 +82,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
([]);
+
+ // 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(null);
+ const processWrapperRef = useRef(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 (
-
+
-
{
- handleReset();
- }}
- >
-
- Reset
-
-
{
- handlePlayStop();
- }}
- >
-
- {playSimulation ? "Play" : "Stop"}
-
-
{
- handleExit();
- }}
- >
-
- Exit
-
-
-
-
0.5x
-
-
-
-
-
-
-
-
-
-
-
-
- {speed.toFixed(1)}x
+
+ {/* hourlySimulation */}
+
+
+
+
+
+
Hourly Simulation
-
+
+
+ {/* dailyProduction */}
+
+
+
+
+
+
Daily Production
+
+
+
+ {/* monthlyROI */}
+
-
8x
+
+
{
+ handleReset();
+ }}
+ >
+
+ Reset
+
+
{
+ handlePlayStop();
+ }}
+ >
+
+ {playSimulation ? "Play" : "Stop"}
+
+
{
+ handleExit();
+ }}
+ >
+
+ Exit
+
+
setExpand(!expand)}
+ >
+
+
+
+
+
+
+
+
+
+
+
+
23 April ,25
+
04:41 PM
+
+
+
+
+ {intervals.map((label, index) => {
+ const segmentProgress = (index / totalSegments) * 100;
+ const isFilled = progress >= segmentProgress;
+ return (
+
+
+ {index < intervals.length - 1 && (
+ = ((index + 1) / totalSegments) * 100
+ ? "filled"
+ : ""
+ }`}
+ >
+ )}
+
+ );
+ })}
+
+
+
+
+
+
+
+
+
0X
+
+
+
+
+
+
+
+
+
+
+
+ {speed.toFixed(1)}x
+
+
+
+
8x
+
+
+
+
+
+
+ {process.map((item, index) => (
+
+ ))}
+
+
diff --git a/app/src/functions/collabUserIcon.tsx b/app/src/functions/collabUserIcon.tsx
deleted file mode 100644
index 9e6802b..0000000
--- a/app/src/functions/collabUserIcon.tsx
+++ /dev/null
@@ -1,31 +0,0 @@
-import React from "react";
-import CustomAvatar from "./users/Avatar";
-
-interface CollabUserIconProps {
- userName: string;
- userImage?: string;
- color: string;
-}
-
-const CollabUserIcon: React.FC
= ({
- userImage,
- userName,
- color,
-}) => {
- return (
-
-
- {userImage ? (
-

- ) : (
-
- )}
-
-
- {userName}
-
-
- );
-};
-
-export default CollabUserIcon;
diff --git a/app/src/modules/builder/groups/zoneGroup.tsx b/app/src/modules/builder/groups/zoneGroup.tsx
index d218534..1eadd24 100644
--- a/app/src/modules/builder/groups/zoneGroup.tsx
+++ b/app/src/modules/builder/groups/zoneGroup.tsx
@@ -69,7 +69,9 @@ const ZoneGroup: React.FC = () => {
},
transparent: true,
depthWrite: false,
- }), []);
+ }),
+ []
+ );
useEffect(() => {
const fetchZones = async () => {
@@ -148,6 +150,7 @@ const ZoneGroup: React.FC = () => {
}
}, [toolMode, toggleView]);
+ // eslint-disable-next-line react-hooks/exhaustive-deps
const addZoneToBackend = async (zone: {
zoneId: string;
zoneName: string;
@@ -503,6 +506,15 @@ const ZoneGroup: React.FC = () => {
draggedSphere,
movePoint,
activeLayer,
+ raycaster,
+ pointer,
+ controls,
+ plane,
+ setZones,
+ setZonePoints,
+ addZoneToBackend,
+ handleDeleteZone,
+ updateZoneToBackend,
]);
useFrame(() => {
@@ -551,6 +563,7 @@ const ZoneGroup: React.FC = () => {
key={index}
position={midpoint}
rotation={[0, -angle, 0]}
+ visible={false}
>
{
const navigate = useNavigate();
@@ -20,13 +20,14 @@ const CamModelsGroup = () => {
const { socket } = useSocketStore();
const { activeModule } = useModuleStore();
+ // eslint-disable-next-line react-hooks/exhaustive-deps
const loader = new GLTFLoader();
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath("three/examples/jsm/libs/draco/gltf/");
loader.setDRACOLoader(dracoLoader);
const [cams, setCams] = useState([]);
- const [models, setModels] = useState>({});
+ const [models, setModels] = useState>({});
const dedupeCams = (cams: any[]) => {
const seen = new Set();
@@ -102,6 +103,7 @@ const CamModelsGroup = () => {
});
socket.on("cameraUpdateResponse", (data: any) => {
+
if (
!groupRef.current ||
socket.id === data.socketId ||
@@ -122,6 +124,11 @@ const CamModelsGroup = () => {
data.data.rotation.y,
data.data.rotation.z
),
+ target: new THREE.Vector3(
+ data.data.target.x,
+ data.data.target.y,
+ data.data.target.z
+ ),
},
}));
});
@@ -131,7 +138,7 @@ const CamModelsGroup = () => {
socket.off("userDisConnectResponse");
socket.off("cameraUpdateResponse");
};
- }, [socket]);
+ }, [email, loader, navigate, setActiveUsers, socket]);
useFrame(() => {
if (!groupRef.current) return;
@@ -217,9 +224,11 @@ const CamModelsGroup = () => {
position={[-0.015, 0, 0.7]}
>