Merge remote-tracking branch 'origin/main' into ui
This commit is contained in:
@@ -75,7 +75,7 @@ export function CommentsIcon() {
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<g clip-path="url(#clip0_2926_17620)">
|
||||
<g clipPath="url(#clip0_2926_17620)">
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
|
||||
@@ -11,7 +11,7 @@ const CardsScene = () => {
|
||||
<Canvas>
|
||||
{/* 3d-cards */}
|
||||
|
||||
{/* <Throughput /> */}
|
||||
<Throughput />
|
||||
{/* <ReturnOfInvestment /> */}
|
||||
{/* <ProductionCapacity /> */}
|
||||
{/* <StateWorking /> */}
|
||||
|
||||
@@ -108,7 +108,7 @@ const SideBarRight: React.FC = () => {
|
||||
{subModule === "mechanics" && !selectedActionSphere && (
|
||||
<div className="sidebar-right-container">
|
||||
<div className="sidebar-right-content-container">
|
||||
<MachineMechanics />
|
||||
{/* <MachineMechanics /> */}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useRef, useState } from "react";
|
||||
import React, { useRef, useState, useMemo, useEffect } from "react";
|
||||
import {
|
||||
AddIcon,
|
||||
InfoIcon,
|
||||
@@ -11,80 +11,332 @@ import LabledDropdown from "../../../ui/inputs/LabledDropdown";
|
||||
import RegularDropDown from "../../../ui/inputs/RegularDropDown";
|
||||
import { handleResize } from "../../../../functions/handleResizePannel";
|
||||
import EyeDropInput from "../../../ui/inputs/EyeDropInput";
|
||||
import { useSelectedActionSphere } from "../../../../store/store";
|
||||
import { useSelectedActionSphere, useSelectedPath, useSimulationPaths } from "../../../../store/store";
|
||||
import * as THREE from 'three';
|
||||
import InputToggle from "../../../ui/inputs/InputToggle";
|
||||
|
||||
const MachineMechanics: React.FC = () => {
|
||||
const { selectedActionSphere } = useSelectedActionSphere();
|
||||
console.log("selectedActionSphere: ", selectedActionSphere);
|
||||
const [actionList, setActionList] = useState<string[]>([]);
|
||||
const [triggerList, setTriggerList] = useState<string[]>([]);
|
||||
const [selectedItem, setSelectedItem] = useState<{
|
||||
type: "action" | "trigger";
|
||||
name: string;
|
||||
} | null>(null);
|
||||
const { selectedPath, setSelectedPath } = useSelectedPath();
|
||||
const { simulationPaths, setSimulationPaths } = useSimulationPaths();
|
||||
|
||||
const actionsContainerRef = useRef<HTMLDivElement>(null);
|
||||
const triggersContainerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const selectedPoint = useMemo(() => {
|
||||
if (!selectedActionSphere) return null;
|
||||
return simulationPaths.flatMap((path) => path.points).find((point) => point.uuid === selectedActionSphere.point.uuid);
|
||||
}, [selectedActionSphere, simulationPaths]);
|
||||
|
||||
const handleAddAction = () => {
|
||||
setActionList([...actionList, `Action ${actionList.length + 1}`]);
|
||||
if (!selectedActionSphere) return;
|
||||
|
||||
const updatedPaths = simulationPaths.map((path) => ({
|
||||
...path,
|
||||
points: path.points.map((point) => {
|
||||
if (point.uuid === selectedActionSphere.point.uuid) {
|
||||
const actionIndex = point.actions.length;
|
||||
const newAction = {
|
||||
uuid: THREE.MathUtils.generateUUID(),
|
||||
name: `Action ${actionIndex + 1}`,
|
||||
type: 'Inherit',
|
||||
material: 'Inherit',
|
||||
delay: 'Inherit',
|
||||
spawnInterval: 'Inherit',
|
||||
isUsed: false
|
||||
};
|
||||
|
||||
return { ...point, actions: [...point.actions, newAction] };
|
||||
}
|
||||
return point;
|
||||
}),
|
||||
}));
|
||||
|
||||
setSimulationPaths(updatedPaths);
|
||||
};
|
||||
|
||||
const handleDeleteAction = (uuid: string) => {
|
||||
if (!selectedActionSphere) return;
|
||||
|
||||
const updatedPaths = simulationPaths.map((path) => ({
|
||||
...path,
|
||||
points: path.points.map((point) =>
|
||||
point.uuid === selectedActionSphere.point.uuid
|
||||
? { ...point, actions: point.actions.filter(action => action.uuid !== uuid) }
|
||||
: point
|
||||
),
|
||||
}));
|
||||
|
||||
setSimulationPaths(updatedPaths);
|
||||
};
|
||||
|
||||
const handleActionSelect = (uuid: string, actionType: string) => {
|
||||
if (!selectedActionSphere) return;
|
||||
|
||||
const updatedPaths = simulationPaths.map((path) => ({
|
||||
...path,
|
||||
points: path.points.map((point) =>
|
||||
point.uuid === selectedActionSphere.point.uuid
|
||||
? {
|
||||
...point,
|
||||
actions: point.actions.map((action) =>
|
||||
action.uuid === uuid
|
||||
? {
|
||||
...action,
|
||||
type: actionType,
|
||||
// Reset dependent fields when type changes
|
||||
material: actionType === 'Spawn' || actionType === 'Swap' ? 'Inherit' : action.material,
|
||||
delay: actionType === 'Delay' ? 'Inherit' : action.delay,
|
||||
spawnInterval: actionType === 'Spawn' ? 'Inherit' : action.spawnInterval
|
||||
}
|
||||
: action
|
||||
),
|
||||
}
|
||||
: point
|
||||
),
|
||||
}));
|
||||
|
||||
setSimulationPaths(updatedPaths);
|
||||
|
||||
// Update the selected item to reflect changes
|
||||
if (selectedItem?.type === "action" && selectedItem.item.uuid === uuid) {
|
||||
const updatedAction = updatedPaths
|
||||
.flatMap(path => path.points)
|
||||
.find(p => p.uuid === selectedActionSphere.point.uuid)
|
||||
?.actions.find(a => a.uuid === uuid);
|
||||
|
||||
if (updatedAction) {
|
||||
setSelectedItem({
|
||||
type: "action",
|
||||
item: updatedAction
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Modified handleMaterialSelect to ensure it only applies to relevant action types
|
||||
const handleMaterialSelect = (uuid: string, material: string) => {
|
||||
if (!selectedActionSphere) return;
|
||||
|
||||
const updatedPaths = simulationPaths.map((path) => ({
|
||||
...path,
|
||||
points: path.points.map((point) =>
|
||||
point.uuid === selectedActionSphere.point.uuid
|
||||
? {
|
||||
...point,
|
||||
actions: point.actions.map((action) =>
|
||||
action.uuid === uuid &&
|
||||
(action.type === 'Spawn' || action.type === 'Swap')
|
||||
? { ...action, material }
|
||||
: action
|
||||
),
|
||||
}
|
||||
: point
|
||||
),
|
||||
}));
|
||||
|
||||
setSimulationPaths(updatedPaths);
|
||||
|
||||
// Update selected item if it's the current action
|
||||
if (selectedItem?.type === "action" && selectedItem.item.uuid === uuid) {
|
||||
setSelectedItem({
|
||||
...selectedItem,
|
||||
item: {
|
||||
...selectedItem.item,
|
||||
material
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleDelayChange = (uuid: string, delay: number | string) => {
|
||||
if (!selectedActionSphere) return;
|
||||
|
||||
const updatedPaths = simulationPaths.map((path) => ({
|
||||
...path,
|
||||
points: path.points.map((point) =>
|
||||
point.uuid === selectedActionSphere.point.uuid
|
||||
? {
|
||||
...point,
|
||||
actions: point.actions.map((action) =>
|
||||
action.uuid === uuid ? { ...action, delay } : action
|
||||
),
|
||||
}
|
||||
: point
|
||||
),
|
||||
}));
|
||||
|
||||
setSimulationPaths(updatedPaths);
|
||||
};
|
||||
|
||||
const handleSpawnIntervalChange = (uuid: string, spawnInterval: number | string) => {
|
||||
if (!selectedActionSphere) return;
|
||||
|
||||
const updatedPaths = simulationPaths.map((path) => ({
|
||||
...path,
|
||||
points: path.points.map((point) =>
|
||||
point.uuid === selectedActionSphere.point.uuid
|
||||
? {
|
||||
...point,
|
||||
actions: point.actions.map((action) =>
|
||||
action.uuid === uuid ? { ...action, spawnInterval } : action
|
||||
),
|
||||
}
|
||||
: point
|
||||
),
|
||||
}));
|
||||
|
||||
setSimulationPaths(updatedPaths);
|
||||
};
|
||||
|
||||
const handleSpeedChange = (speed: number) => {
|
||||
if (!selectedPath) return;
|
||||
|
||||
const updatedPaths = simulationPaths.map((path) =>
|
||||
path.modeluuid === selectedPath.path.modeluuid ? { ...path, speed } : path
|
||||
);
|
||||
|
||||
setSimulationPaths(updatedPaths);
|
||||
setSelectedPath({ ...selectedPath, path: { ...selectedPath.path, speed } });
|
||||
};
|
||||
|
||||
const handleAddTrigger = () => {
|
||||
setTriggerList([...triggerList, `Trigger ${triggerList.length + 1}`]);
|
||||
if (!selectedActionSphere) return;
|
||||
|
||||
const updatedPaths = simulationPaths.map((path) => ({
|
||||
...path,
|
||||
points: path.points.map((point) => {
|
||||
if (point.uuid === selectedActionSphere.point.uuid) {
|
||||
const triggerIndex = point.triggers.length;
|
||||
const newTrigger = {
|
||||
uuid: THREE.MathUtils.generateUUID(),
|
||||
name: `Trigger ${triggerIndex + 1}`,
|
||||
type: '',
|
||||
isUsed: false
|
||||
};
|
||||
|
||||
return { ...point, triggers: [...point.triggers, newTrigger] };
|
||||
}
|
||||
return point;
|
||||
}),
|
||||
}));
|
||||
|
||||
setSimulationPaths(updatedPaths);
|
||||
};
|
||||
|
||||
const handleRemoveAction = (index: number) => {
|
||||
setActionList(actionList.filter((_, i) => i !== index));
|
||||
if (
|
||||
selectedItem?.type === "action" &&
|
||||
selectedItem.name === actionList[index]
|
||||
) {
|
||||
setSelectedItem(null);
|
||||
const handleDeleteTrigger = (uuid: string) => {
|
||||
if (!selectedActionSphere) return;
|
||||
|
||||
const updatedPaths = simulationPaths.map((path) => ({
|
||||
...path,
|
||||
points: path.points.map((point) =>
|
||||
point.uuid === selectedActionSphere.point.uuid
|
||||
? { ...point, triggers: point.triggers.filter(trigger => trigger.uuid !== uuid) }
|
||||
: point
|
||||
),
|
||||
}));
|
||||
|
||||
setSimulationPaths(updatedPaths);
|
||||
};
|
||||
|
||||
const handleTriggerSelect = (uuid: string, triggerType: string) => {
|
||||
if (!selectedActionSphere) return;
|
||||
|
||||
const updatedPaths = simulationPaths.map((path) => ({
|
||||
...path,
|
||||
points: path.points.map((point) =>
|
||||
point.uuid === selectedActionSphere.point.uuid
|
||||
? {
|
||||
...point,
|
||||
triggers: point.triggers.map((trigger) =>
|
||||
trigger.uuid === uuid ? { ...trigger, type: triggerType } : trigger
|
||||
),
|
||||
}
|
||||
: point
|
||||
),
|
||||
}));
|
||||
|
||||
setSimulationPaths(updatedPaths);
|
||||
};
|
||||
|
||||
// Update the toggle handlers to immediately update the selected item
|
||||
const handleActionToggle = (uuid: string) => {
|
||||
if (!selectedActionSphere) return;
|
||||
|
||||
const updatedPaths = simulationPaths.map((path) => ({
|
||||
...path,
|
||||
points: path.points.map((point) =>
|
||||
point.uuid === selectedActionSphere.point.uuid
|
||||
? {
|
||||
...point,
|
||||
actions: point.actions.map((action) => ({
|
||||
...action,
|
||||
isUsed: action.uuid === uuid ? !action.isUsed : false,
|
||||
})),
|
||||
}
|
||||
: point
|
||||
),
|
||||
}));
|
||||
|
||||
setSimulationPaths(updatedPaths);
|
||||
|
||||
// Immediately update the selected item if it's the one being toggled
|
||||
if (selectedItem?.type === "action" && selectedItem.item.uuid === uuid) {
|
||||
setSelectedItem({
|
||||
...selectedItem,
|
||||
item: {
|
||||
...selectedItem.item,
|
||||
isUsed: !selectedItem.item.isUsed
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleRemoveTrigger = (index: number) => {
|
||||
setTriggerList(triggerList.filter((_, i) => i !== index));
|
||||
if (
|
||||
selectedItem?.type === "trigger" &&
|
||||
selectedItem.name === triggerList[index]
|
||||
) {
|
||||
setSelectedItem(null);
|
||||
// Do the same for trigger toggle
|
||||
const handleTriggerToggle = (uuid: string) => {
|
||||
if (!selectedActionSphere) return;
|
||||
|
||||
const updatedPaths = simulationPaths.map((path) => ({
|
||||
...path,
|
||||
points: path.points.map((point) =>
|
||||
point.uuid === selectedActionSphere.point.uuid
|
||||
? {
|
||||
...point,
|
||||
triggers: point.triggers.map((trigger) => ({
|
||||
...trigger,
|
||||
isUsed: trigger.uuid === uuid ? !trigger.isUsed : false,
|
||||
})),
|
||||
}
|
||||
: point
|
||||
),
|
||||
}));
|
||||
|
||||
setSimulationPaths(updatedPaths);
|
||||
|
||||
// Immediately update the selected item if it's the one being toggled
|
||||
if (selectedItem?.type === "trigger" && selectedItem.item.uuid === uuid) {
|
||||
setSelectedItem({
|
||||
...selectedItem,
|
||||
item: {
|
||||
...selectedItem.item,
|
||||
isUsed: !selectedItem.item.isUsed
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleSelectItem = (type: "action" | "trigger", name: string) => {
|
||||
setSelectedItem({ type, name });
|
||||
};
|
||||
const [selectedItem, setSelectedItem] = useState<{ type: "action" | "trigger"; item: any; } | null>(null);
|
||||
|
||||
const [processes, setProcesses] = useState<string[]>([]);
|
||||
const [activeProcess, setActiveProcesses] = useState<string>();
|
||||
|
||||
const handleSelect = (option: string) => {
|
||||
setActiveProcesses(option); // Update the active option state
|
||||
};
|
||||
const handleAddProcess = () => {
|
||||
const newProcess = `Process ${processes.length + 1}`; // Generate new process name dynamically
|
||||
setProcesses((prevProcesses) => [...prevProcesses, newProcess]); // Update the state with the new process
|
||||
};
|
||||
useEffect(() => {
|
||||
setSelectedItem(null); // Reset selectedItem when selectedActionSphere changes
|
||||
}, [selectedActionSphere]);
|
||||
|
||||
return (
|
||||
<div className="machine-mechanics-container">
|
||||
<div className="machine-mechanics-header">
|
||||
{selectedActionSphere?.path?.modelName || "path name not found"}
|
||||
{selectedActionSphere?.path?.modelName || "point name not found"}
|
||||
</div>
|
||||
{/* <div className="process-list-container">
|
||||
<div className="label">Process:</div>
|
||||
<RegularDropDown
|
||||
header={activeProcess || "add process ->"}
|
||||
options={processes}
|
||||
onSelect={handleSelect}
|
||||
/>
|
||||
<div className="add-new-process" onClick={handleAddProcess}>
|
||||
<AddIcon />
|
||||
</div>
|
||||
</div> */}
|
||||
|
||||
<div className="machine-mechanics-content-container">
|
||||
<div className="actions">
|
||||
<div className="header">
|
||||
@@ -99,25 +351,24 @@ const MachineMechanics: React.FC = () => {
|
||||
style={{ height: "120px" }}
|
||||
>
|
||||
<div className="list-container">
|
||||
{actionList.map((action, index) => (
|
||||
{selectedPoint?.actions.map((action) => (
|
||||
<div
|
||||
key={index}
|
||||
className={`list-item ${
|
||||
selectedItem?.type === "action" &&
|
||||
selectedItem.name === action
|
||||
? "active"
|
||||
: ""
|
||||
}`}
|
||||
key={action.uuid}
|
||||
className={`list-item ${selectedItem?.type === "action" &&
|
||||
selectedItem.item?.uuid === action.uuid
|
||||
? "active"
|
||||
: ""
|
||||
}`}
|
||||
>
|
||||
<div
|
||||
className="value"
|
||||
onClick={() => handleSelectItem("action", action)}
|
||||
onClick={() => setSelectedItem({ type: "action", item: action })}
|
||||
>
|
||||
<RenameInput value={action} />
|
||||
<RenameInput value={action.name} />
|
||||
</div>
|
||||
<div
|
||||
className="remove-button"
|
||||
onClick={() => handleRemoveAction(index)}
|
||||
onClick={() => handleDeleteAction(action.uuid)}
|
||||
>
|
||||
<RemoveIcon />
|
||||
</div>
|
||||
@@ -146,25 +397,24 @@ const MachineMechanics: React.FC = () => {
|
||||
style={{ height: "120px" }}
|
||||
>
|
||||
<div className="list-container">
|
||||
{triggerList.map((trigger, index) => (
|
||||
{selectedPoint?.triggers.map((trigger) => (
|
||||
<div
|
||||
key={index}
|
||||
className={`list-item ${
|
||||
selectedItem?.type === "trigger" &&
|
||||
selectedItem.name === trigger
|
||||
? "active"
|
||||
: ""
|
||||
}`}
|
||||
key={trigger.uuid}
|
||||
className={`list-item ${selectedItem?.type === "trigger" &&
|
||||
selectedItem.item?.uuid === trigger.uuid
|
||||
? "active"
|
||||
: ""
|
||||
}`}
|
||||
>
|
||||
<div
|
||||
className="value"
|
||||
onClick={() => handleSelectItem("trigger", trigger)}
|
||||
onClick={() => setSelectedItem({ type: "trigger", item: trigger })}
|
||||
>
|
||||
<RenameInput value={trigger} />
|
||||
<RenameInput value={trigger.name} />
|
||||
</div>
|
||||
<div
|
||||
className="remove-button"
|
||||
onClick={() => handleRemoveTrigger(index)}
|
||||
onClick={() => handleDeleteTrigger(trigger.uuid)}
|
||||
>
|
||||
<RemoveIcon />
|
||||
</div>
|
||||
@@ -183,28 +433,101 @@ const MachineMechanics: React.FC = () => {
|
||||
<div className="selected-properties-container">
|
||||
{selectedItem && (
|
||||
<>
|
||||
<div className="properties-header">{selectedItem.name}</div>
|
||||
<LabledDropdown
|
||||
defaultOption="On-hit"
|
||||
options={["On-hit", "Buffer"]}
|
||||
/>
|
||||
<InputWithDropDown
|
||||
label="Speed"
|
||||
value=""
|
||||
activeOption=".mm"
|
||||
onChange={() => {}}
|
||||
/>
|
||||
<EyeDropInput />
|
||||
<div className="properties-header">{selectedItem.item.name}</div>
|
||||
|
||||
{selectedItem.type === "action" && (
|
||||
<>
|
||||
<InputToggle
|
||||
inputKey="enableTrigger"
|
||||
label="Enable Trigger"
|
||||
value={selectedItem.item.isUsed}
|
||||
onClick={() => handleActionToggle(selectedItem.item.uuid)}
|
||||
/>
|
||||
<LabledDropdown
|
||||
defaultOption={selectedItem.item.type}
|
||||
options={["Inherit", "Spawn", "Swap", "Despawn", "Delay"]}
|
||||
onSelect={(option) => handleActionSelect(selectedItem.item.uuid, option)}
|
||||
/>
|
||||
|
||||
{/* Only show material dropdown for Spawn/Swap actions */}
|
||||
{(selectedItem.item.type === 'Spawn' || selectedItem.item.type === 'Swap') && (
|
||||
<LabledDropdown
|
||||
label={selectedItem.item.type === 'Spawn' ? 'Spawn Material' : 'Swap Material'}
|
||||
defaultOption={selectedItem.item.material}
|
||||
options={["Inherit", "Crate", "Box"]}
|
||||
onSelect={(option) => handleMaterialSelect(selectedItem.item.uuid, option)}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Only show delay input for Delay actions */}
|
||||
{selectedItem.item.type === 'Delay' && (
|
||||
<InputWithDropDown
|
||||
label="Delay Time"
|
||||
value={selectedItem.item.delay === 'Inherit'
|
||||
? undefined
|
||||
: selectedItem.item.delay}
|
||||
onChange={(value) => {
|
||||
const numValue = parseInt(value);
|
||||
handleDelayChange(
|
||||
selectedItem.item.uuid,
|
||||
!value ? 'Inherit' : numValue
|
||||
);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Only show spawn interval for Spawn actions */}
|
||||
{selectedItem.item.type === 'Spawn' && (
|
||||
<InputWithDropDown
|
||||
label="Spawn Interval"
|
||||
min={0}
|
||||
defaultValue={selectedItem.item.spawnInterval === "Inherit" ? "" : selectedItem.item.spawnInterval.toString()}
|
||||
value={selectedItem.item.spawnInterval === "Inherit" ? "" : selectedItem.item.spawnInterval.toString()}
|
||||
onChange={(value) => {
|
||||
handleSpawnIntervalChange(selectedItem.item.uuid, (value === "") ? "Inherit" : parseInt(value));
|
||||
}}
|
||||
/>
|
||||
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
{selectedItem.type === "trigger" && (
|
||||
<>
|
||||
<InputToggle
|
||||
inputKey="enableTrigger"
|
||||
label="Enable Trigger"
|
||||
value={selectedItem.item.isUsed}
|
||||
onClick={() => handleTriggerToggle(selectedItem.item.uuid)}
|
||||
/>
|
||||
|
||||
<LabledDropdown
|
||||
defaultOption={selectedItem.item.type || "Select Trigger Type"}
|
||||
options={["On-Hit", "Buffer"]}
|
||||
onSelect={(option) => handleTriggerSelect(selectedItem.item.uuid, option)}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
{selectedPath && !selectedItem && (
|
||||
<div className="speed-control">
|
||||
<InputWithDropDown
|
||||
label="Path Speed"
|
||||
value={selectedPath.path.speed.toString()}
|
||||
onChange={(value) => handleSpeedChange(parseFloat(value))}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="footer">
|
||||
<InfoIcon />
|
||||
By Selecting Path, you can create Object Triggers.
|
||||
By selecting points, you can create events and triggers.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default MachineMechanics;
|
||||
export default MachineMechanics;
|
||||
@@ -1,115 +1,115 @@
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import MultiLevelDropdown from '../../../../ui/inputs/MultiLevelDropDown'
|
||||
import { AddIcon } from '../../../../icons/ExportCommonIcons'
|
||||
import RegularDropDown from '../../../../ui/inputs/RegularDropDown'
|
||||
import useChartStore from '../../../../../store/useChartStore'
|
||||
import axios from 'axios'
|
||||
|
||||
type Props = {}
|
||||
|
||||
const LineGrapInput = (props: Props) => {
|
||||
const [dropDowndata, setDropDownData] = useState({})
|
||||
const [selections, setSelections] = useState<Record<string, { name: string, fields: string }>>({})
|
||||
const [selectedOption, setSelectedOption] = useState('1h')
|
||||
const { measurements, setMeasurements, updateDuration, duration } = useChartStore();
|
||||
|
||||
const handleSelectDuration = (option: string) => {
|
||||
updateDuration(option); // Normalize for key matching
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const fetchZoneData = async () => {
|
||||
try {
|
||||
const response = await axios.get('http://192.168.0.192:5010/getinput');
|
||||
if (response.status === 200) {
|
||||
console.log('dropdown data:', response.data);
|
||||
setDropDownData(response.data)
|
||||
} else {
|
||||
console.log('Unexpected response:', response);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('There was an error!', error);
|
||||
}
|
||||
};
|
||||
fetchZoneData();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
console.log(selections);
|
||||
}, [selections])
|
||||
|
||||
const handleSelect = (inputKey: string, selectedData: { name: string, fields: string } | null) => {
|
||||
setSelections(prev => {
|
||||
if (selectedData === null) {
|
||||
const newSelections = { ...prev };
|
||||
delete newSelections[inputKey];
|
||||
return newSelections;
|
||||
} else {
|
||||
return {
|
||||
...prev,
|
||||
[inputKey]: selectedData
|
||||
};
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
interface Measurement {
|
||||
name: string;
|
||||
fields: string;
|
||||
}
|
||||
|
||||
interface InputData {
|
||||
[key: string]: Measurement;
|
||||
}
|
||||
|
||||
const extractMeasurements = (input: InputData): Measurement[] => {
|
||||
return Object.values(input);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const measurementsData = extractMeasurements(selections);
|
||||
setMeasurements(measurementsData);
|
||||
}, [selections]);
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="inputs-wrapper">
|
||||
{[...Array(6)].map((_, index) => {
|
||||
const inputKey = `input${index + 1}`;
|
||||
return (
|
||||
<div key={index} className="datas">
|
||||
<div className="datas__label">Input {index + 1}</div>
|
||||
<div className="datas__class">
|
||||
<MultiLevelDropdown
|
||||
data={dropDowndata}
|
||||
onSelect={(selectedData) => handleSelect(inputKey, selectedData)}
|
||||
onUnselect={() => handleSelect(inputKey, null)}
|
||||
selectedValue={selections[inputKey]}
|
||||
/>
|
||||
<div className="icon">
|
||||
<AddIcon />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div>
|
||||
<div className="datas">
|
||||
<div className="datas__label">duration</div>
|
||||
<div className="datas__class">
|
||||
<RegularDropDown
|
||||
header={duration}
|
||||
options={["1h", "2h", "12h"]}
|
||||
onSelect={handleSelectDuration}
|
||||
search={false}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import MultiLevelDropdown from '../../../../ui/inputs/MultiLevelDropDown'
|
||||
import { AddIcon } from '../../../../icons/ExportCommonIcons'
|
||||
import RegularDropDown from '../../../../ui/inputs/RegularDropDown'
|
||||
import useChartStore from '../../../../../store/useChartStore'
|
||||
import axios from 'axios'
|
||||
|
||||
type Props = {}
|
||||
|
||||
const LineGrapInput = (props: Props) => {
|
||||
const [dropDowndata, setDropDownData] = useState({})
|
||||
const [selections, setSelections] = useState<Record<string, { name: string, fields: string }>>({})
|
||||
const [selectedOption, setSelectedOption] = useState('1h')
|
||||
const { measurements, setMeasurements, updateDuration, duration } = useChartStore();
|
||||
|
||||
const handleSelectDuration = (option: string) => {
|
||||
updateDuration(option); // Normalize for key matching
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const fetchZoneData = async () => {
|
||||
try {
|
||||
const response = await axios.get('http://192.168.0.192:5010/getinput');
|
||||
if (response.status === 200) {
|
||||
console.log('dropdown data:', response.data);
|
||||
setDropDownData(response.data)
|
||||
} else {
|
||||
console.log('Unexpected response:', response);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('There was an error!', error);
|
||||
}
|
||||
};
|
||||
fetchZoneData();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
console.log(selections);
|
||||
}, [selections])
|
||||
|
||||
const handleSelect = (inputKey: string, selectedData: { name: string, fields: string } | null) => {
|
||||
setSelections(prev => {
|
||||
if (selectedData === null) {
|
||||
const newSelections = { ...prev };
|
||||
delete newSelections[inputKey];
|
||||
return newSelections;
|
||||
} else {
|
||||
return {
|
||||
...prev,
|
||||
[inputKey]: selectedData
|
||||
};
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
interface Measurement {
|
||||
name: string;
|
||||
fields: string;
|
||||
}
|
||||
|
||||
interface InputData {
|
||||
[key: string]: Measurement;
|
||||
}
|
||||
|
||||
const extractMeasurements = (input: InputData): Measurement[] => {
|
||||
return Object.values(input);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const measurementsData = extractMeasurements(selections);
|
||||
setMeasurements(measurementsData);
|
||||
}, [selections]);
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="inputs-wrapper">
|
||||
{[...Array(6)].map((_, index) => {
|
||||
const inputKey = `input${index + 1}`;
|
||||
return (
|
||||
<div key={index} className="datas">
|
||||
<div className="datas__label">Input {index + 1}</div>
|
||||
<div className="datas__class">
|
||||
<MultiLevelDropdown
|
||||
data={dropDowndata}
|
||||
onSelect={(selectedData) => handleSelect(inputKey, selectedData)}
|
||||
onUnselect={() => handleSelect(inputKey, null)}
|
||||
selectedValue={selections[inputKey]}
|
||||
/>
|
||||
<div className="icon">
|
||||
<AddIcon />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div>
|
||||
<div className="datas">
|
||||
<div className="datas__label">duration</div>
|
||||
<div className="datas__class">
|
||||
<RegularDropDown
|
||||
header={duration}
|
||||
options={["1h", "2h", "12h"]}
|
||||
onSelect={handleSelectDuration}
|
||||
search={false}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default LineGrapInput
|
||||
@@ -1,77 +1,77 @@
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import MultiLevelDropdown from '../../../../ui/inputs/MultiLevelDropDown'
|
||||
import { AddIcon } from '../../../../icons/ExportCommonIcons'
|
||||
import axios from 'axios'
|
||||
|
||||
type Props = {}
|
||||
|
||||
const PieChartInput = (props: Props) => {
|
||||
const [dropDowndata, setDropDownData] = useState({})
|
||||
const [selections, setSelections] = useState<Record<string, {name: string, fields: string}>>({})
|
||||
|
||||
useEffect(() => {
|
||||
const fetchZoneData = async () => {
|
||||
try {
|
||||
const response = await axios.get('http://192.168.0.192:5010/getinput');
|
||||
if (response.status === 200) {
|
||||
console.log('dropdown data:', response.data);
|
||||
setDropDownData(response.data)
|
||||
} else {
|
||||
console.log('Unexpected response:', response);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('There was an error!', error);
|
||||
}
|
||||
};
|
||||
fetchZoneData();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {console.log(selections);
|
||||
},[selections])
|
||||
|
||||
const handleSelect = (inputKey: string, selectedData: {name: string, fields: string} | null) => {
|
||||
setSelections(prev => {
|
||||
if (selectedData === null) {
|
||||
const newSelections = {...prev};
|
||||
delete newSelections[inputKey];
|
||||
return newSelections;
|
||||
} else {
|
||||
return {
|
||||
...prev,
|
||||
[inputKey]: selectedData
|
||||
};
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="inputs-wrapper">
|
||||
{[...Array(3)].map((_, index) => {
|
||||
const inputKey = `input${index+1}`;
|
||||
return (
|
||||
<div key={index} className="datas">
|
||||
<div className="datas__label">Input {index+1}</div>
|
||||
<div className="datas__class">
|
||||
<MultiLevelDropdown
|
||||
data={dropDowndata}
|
||||
onSelect={(selectedData) => handleSelect(inputKey, selectedData)}
|
||||
onUnselect={() => handleSelect(inputKey, null)}
|
||||
selectedValue={selections[inputKey]}
|
||||
/>
|
||||
<div className="icon">
|
||||
<AddIcon />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div>
|
||||
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import MultiLevelDropdown from '../../../../ui/inputs/MultiLevelDropDown'
|
||||
import { AddIcon } from '../../../../icons/ExportCommonIcons'
|
||||
import axios from 'axios'
|
||||
|
||||
type Props = {}
|
||||
|
||||
const PieChartInput = (props: Props) => {
|
||||
const [dropDowndata, setDropDownData] = useState({})
|
||||
const [selections, setSelections] = useState<Record<string, {name: string, fields: string}>>({})
|
||||
|
||||
useEffect(() => {
|
||||
const fetchZoneData = async () => {
|
||||
try {
|
||||
const response = await axios.get('http://192.168.0.192:5010/getinput');
|
||||
if (response.status === 200) {
|
||||
console.log('dropdown data:', response.data);
|
||||
setDropDownData(response.data)
|
||||
} else {
|
||||
console.log('Unexpected response:', response);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('There was an error!', error);
|
||||
}
|
||||
};
|
||||
fetchZoneData();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {console.log(selections);
|
||||
},[selections])
|
||||
|
||||
const handleSelect = (inputKey: string, selectedData: {name: string, fields: string} | null) => {
|
||||
setSelections(prev => {
|
||||
if (selectedData === null) {
|
||||
const newSelections = {...prev};
|
||||
delete newSelections[inputKey];
|
||||
return newSelections;
|
||||
} else {
|
||||
return {
|
||||
...prev,
|
||||
[inputKey]: selectedData
|
||||
};
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="inputs-wrapper">
|
||||
{[...Array(3)].map((_, index) => {
|
||||
const inputKey = `input${index+1}`;
|
||||
return (
|
||||
<div key={index} className="datas">
|
||||
<div className="datas__label">Input {index+1}</div>
|
||||
<div className="datas__class">
|
||||
<MultiLevelDropdown
|
||||
data={dropDowndata}
|
||||
onSelect={(selectedData) => handleSelect(inputKey, selectedData)}
|
||||
onUnselect={() => handleSelect(inputKey, null)}
|
||||
selectedValue={selections[inputKey]}
|
||||
/>
|
||||
<div className="icon">
|
||||
<AddIcon />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div>
|
||||
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default PieChartInput
|
||||
@@ -7,7 +7,6 @@ interface LoadingPageProps {
|
||||
}
|
||||
|
||||
const LoadingPage: React.FC<LoadingPageProps> = ({ progress }) => {
|
||||
// Ensure progress stays within 0-100 range
|
||||
const validatedProgress = Math.min(100, Math.max(0, progress));
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import React, { useEffect, useRef, useState, useCallback } from "react";
|
||||
import { Widget } from "../../../store/useWidgetStore";
|
||||
import { MoveArrowLeft, MoveArrowRight } from "../../icons/SimulationIcons";
|
||||
import { InfoIcon } from "../../icons/ExportCommonIcons";
|
||||
import { useDroppedObjectsStore } from "../../../store/useDroppedObjectsStore";
|
||||
|
||||
// Define the type for `Side`
|
||||
type Side = "top" | "bottom" | "left" | "right";
|
||||
@@ -154,33 +156,40 @@ const DisplayZone: React.FC<DisplayZoneProps> = ({
|
||||
)}
|
||||
|
||||
{/* Zones Wrapper */}
|
||||
<div ref={containerRef} className="zones-wrapper">
|
||||
{Object.keys(zonesData).map((zoneName, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className={`zone ${
|
||||
selectedZone.zoneName === zoneName ? "active" : ""
|
||||
}`}
|
||||
onClick={() => {
|
||||
console.log("zoneName: ", zoneName);
|
||||
setSelectedZone({
|
||||
zoneName,
|
||||
activeSides: zonesData[zoneName].activeSides || [],
|
||||
panelOrder: zonesData[zoneName].panelOrder || [],
|
||||
lockedPanels: zonesData[zoneName].lockedPanels || [],
|
||||
widgets: zonesData[zoneName].widgets || [],
|
||||
zoneId: zonesData[zoneName]?.zoneId || "",
|
||||
zoneViewPortTarget:
|
||||
zonesData[zoneName].zoneViewPortTarget || [],
|
||||
zoneViewPortPosition:
|
||||
zonesData[zoneName].zoneViewPortPosition || [],
|
||||
});
|
||||
}}
|
||||
>
|
||||
{zoneName}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
{Object.keys(zonesData).length !== 0 ? (
|
||||
<div ref={containerRef} className="zones-wrapper">
|
||||
{Object.keys(zonesData).map((zoneName, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className={`zone ${
|
||||
selectedZone.zoneName === zoneName ? "active" : ""
|
||||
}`}
|
||||
onClick={() => {
|
||||
useDroppedObjectsStore.getState().setZone(zoneName, zonesData[zoneName]?.zoneId);
|
||||
setSelectedZone({
|
||||
zoneName,
|
||||
activeSides: zonesData[zoneName].activeSides || [],
|
||||
panelOrder: zonesData[zoneName].panelOrder || [],
|
||||
lockedPanels: zonesData[zoneName].lockedPanels || [],
|
||||
widgets: zonesData[zoneName].widgets || [],
|
||||
zoneId: zonesData[zoneName]?.zoneId || "",
|
||||
zoneViewPortTarget:
|
||||
zonesData[zoneName].zoneViewPortTarget || [],
|
||||
zoneViewPortPosition:
|
||||
zonesData[zoneName].zoneViewPortPosition || [],
|
||||
});
|
||||
}}
|
||||
>
|
||||
{zoneName}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div className="no-zone">
|
||||
<InfoIcon />
|
||||
No zones? Create one!
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Right Arrow */}
|
||||
{showRightArrow && (
|
||||
|
||||
@@ -1,89 +1,215 @@
|
||||
// import { useState } from "react";
|
||||
// import { useThree } from "@react-three/fiber";
|
||||
// import * as THREE from "three";
|
||||
|
||||
|
||||
|
||||
// const DroppedObjects = () => {
|
||||
// const { camera } = useThree(); // Now inside Canvas ✅
|
||||
// const [objects, setObjects] = useState<{ id: number; position: [number, number, number] }[]>([]);
|
||||
|
||||
// // Function to convert drop event into 3D position
|
||||
// const handleDrop = (event: DragEvent) => {
|
||||
// event.preventDefault();
|
||||
|
||||
// const data = event.dataTransfer?.getData("text/plain");
|
||||
// if (!data) return;
|
||||
|
||||
// try {
|
||||
// const cardData = JSON.parse(data);
|
||||
// if (!cardData.className.includes("floating total-card")) {
|
||||
// console.log("Drop rejected: Incorrect element.");
|
||||
// return;
|
||||
// }
|
||||
|
||||
// // Convert 2D drop position to 3D world coordinates
|
||||
// const x = (event.clientX / window.innerWidth) * 2 - 1;
|
||||
// const y = -(event.clientY / window.innerHeight) * 2 + 1;
|
||||
|
||||
// // Raycasting to determine the drop position in 3D
|
||||
// const raycaster = new THREE.Raycaster();
|
||||
// const mouseVector = new THREE.Vector2(x, y);
|
||||
// raycaster.setFromCamera(mouseVector, camera);
|
||||
|
||||
// // Intersect with a ground plane (assume y = 0)
|
||||
// const groundPlane = new THREE.Plane(new THREE.Vector3(0, 1, 0), 0);
|
||||
// const intersection = new THREE.Vector3();
|
||||
// raycaster.ray.intersectPlane(groundPlane, intersection);
|
||||
|
||||
// console.log("Spawn Object at:", intersection);
|
||||
|
||||
// // Add the dropped object to the scene state
|
||||
// setObjects((prev) => [...prev, { id: Date.now(), position: [intersection.x, intersection.y, intersection.z] }]);
|
||||
// } catch (error) {
|
||||
// console.error("Invalid data:", error);
|
||||
// }
|
||||
// };
|
||||
|
||||
// return (
|
||||
// <group>
|
||||
// {/* Render dropped objects as green boxes */}
|
||||
// {objects.map((obj) => (
|
||||
// <mesh key={obj.id} position={obj.position}>
|
||||
// <boxGeometry args={[1, 1, 1]} />
|
||||
// <meshStandardMaterial color="green" />
|
||||
// </mesh>
|
||||
// ))}
|
||||
// </group>
|
||||
// );
|
||||
// };
|
||||
|
||||
import { Html } from "@react-three/drei";
|
||||
import { useDroppedObjectsStore } from "../../../store/store";
|
||||
|
||||
import { WalletIcon } from "../../icons/3dChartIcons";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { Line } from "react-chartjs-2";
|
||||
import { useDroppedObjectsStore, Zones } from "../../../store/useDroppedObjectsStore";
|
||||
|
||||
const DroppedObjects: React.FC = () => {
|
||||
const objects = useDroppedObjectsStore((state) => state.objects); // Get objects from Zustand store
|
||||
console.log('objects: ', objects);
|
||||
const zones = useDroppedObjectsStore((state) => state.zones);
|
||||
|
||||
const updateObjectPosition = useDroppedObjectsStore((state) => state.updateObjectPosition);
|
||||
const [draggingIndex, setDraggingIndex] = useState<{ zone: string; index: number } | null>(null);
|
||||
const [offset, setOffset] = useState<[number, number] | null>(null);
|
||||
const positionRef = useRef<[number, number] | null>(null);
|
||||
const animationRef = useRef<number | null>(null);
|
||||
|
||||
// useEffect(() => {
|
||||
// const initialZones: Record<string, Zones> = {
|
||||
// "Zone 1": {
|
||||
// zoneName: "Zone 1",
|
||||
// zoneId: "2e996073-546c-470c-8323-55bd3700c6aa",
|
||||
// objects: [
|
||||
// {
|
||||
// header: "Today’s Money",
|
||||
// value: 53000, // ✅ Converted to number
|
||||
// per: "+55%",
|
||||
// className: "floating total-card",
|
||||
// position: [146, 214], // ✅ No need for 'as' here
|
||||
// },
|
||||
// {
|
||||
// header: "New Clients",
|
||||
// value: 250, // ✅ Converted to number
|
||||
// per: "+12%",
|
||||
// className: "floating total-card",
|
||||
// position: [344, 295],
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// };
|
||||
|
||||
// useDroppedObjectsStore.setState({ zones: initialZones });
|
||||
// }, []);
|
||||
|
||||
const zoneEntries = Object.entries(zones);
|
||||
if (zoneEntries.length === 0) return null; // No zone, nothing to render
|
||||
const [zoneName, zone] = zoneEntries[0]; // Only render the first zone
|
||||
|
||||
function handlePointerDown(event: React.PointerEvent, index: number) {
|
||||
const obj = zone.objects[index];
|
||||
const offsetX = event.clientX - obj.position[1];
|
||||
const offsetY = event.clientY - obj.position[0];
|
||||
|
||||
setDraggingIndex({ zone: zoneName, index });
|
||||
setOffset([offsetY, offsetX]);
|
||||
}
|
||||
|
||||
function handlePointerMove(event: React.PointerEvent) {
|
||||
if (!draggingIndex || !offset) return;
|
||||
|
||||
const container = document.getElementById("real-time-vis-canvas");
|
||||
if (!container) return;
|
||||
|
||||
const rect = container.getBoundingClientRect();
|
||||
|
||||
let newX = event.clientX - offset[1];
|
||||
let newY = event.clientY - offset[0];
|
||||
|
||||
newX = Math.max(0, Math.min(rect.width - 50, newX));
|
||||
newY = Math.max(0, Math.min(rect.height - 50, newY));
|
||||
|
||||
positionRef.current = [newY, newX];
|
||||
|
||||
if (!animationRef.current) {
|
||||
animationRef.current = requestAnimationFrame(() => {
|
||||
if (positionRef.current) {
|
||||
updateObjectPosition(zoneName, draggingIndex.index, positionRef.current);
|
||||
}
|
||||
animationRef.current = null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function handlePointerUp() {
|
||||
setDraggingIndex(null);
|
||||
setOffset(null);
|
||||
if (animationRef.current) {
|
||||
cancelAnimationFrame(animationRef.current);
|
||||
animationRef.current = null;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{objects.map((obj, index) => (
|
||||
<group key={index} position={[Math.random() * 5, Math.random() * 5, 0]}>
|
||||
<Html wrapperClass={obj.className}>
|
||||
<div style={{ padding: "10px", background: "#fff", borderRadius: "6px" }}>
|
||||
<div className="header">{obj.header}</div>
|
||||
<div className="data-values">
|
||||
<div className="value">{obj.value}</div>
|
||||
<div className="per">{obj.per}</div>
|
||||
<div onPointerMove={handlePointerMove} onPointerUp={handlePointerUp}>
|
||||
{zone.objects.map((obj, index) => (
|
||||
<div
|
||||
key={`${zoneName}-${index}`}
|
||||
className={obj.className}
|
||||
style={{
|
||||
top: obj.position[0] + "px",
|
||||
left: obj.position[1] + "px",
|
||||
transition: draggingIndex?.index === index ? "none" : "transform 0.1s ease-out",
|
||||
}}
|
||||
onPointerDown={(event) => handlePointerDown(event, index)}
|
||||
>
|
||||
{obj.className === "floating total-card" ? (
|
||||
<div>
|
||||
<div className="header-wrapper">
|
||||
<div className="header">{obj.header}</div>
|
||||
<div className="data-values">
|
||||
<div className="value">{obj.value}</div>
|
||||
<div className="per">{obj.per}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="icon">
|
||||
<WalletIcon />
|
||||
</div>
|
||||
</div>
|
||||
</Html>
|
||||
</group>
|
||||
) : obj.className === "warehouseThroughput floating" ? (
|
||||
<div>
|
||||
<div className="header">
|
||||
<h2>Warehouse Throughput</h2>
|
||||
<p>
|
||||
<span>(+5) more</span> in 2025
|
||||
</p>
|
||||
</div>
|
||||
<div className="lineGraph" style={{ height: "100%" }}>
|
||||
{/* <Line data={lineGraphData} options={lineGraphOptions} /> */}
|
||||
</div>
|
||||
</div>
|
||||
) : obj.className === "fleetEfficiency floating" ? (
|
||||
<div>
|
||||
<h2 className="header">Fleet Efficiency</h2>
|
||||
<div className="progressContainer">
|
||||
<div className="progress">
|
||||
<div className="barOverflow">
|
||||
<div
|
||||
className="bar"
|
||||
style={{ transform: `rotate(${obj.value}deg)` }}
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="scaleLabels">
|
||||
<span>0%</span>
|
||||
<div className="centerText">
|
||||
<div className="percentage">{obj.per}%</div>
|
||||
<div className="status">Optimal</div>
|
||||
</div>
|
||||
<span>100%</span>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
export default DroppedObjects;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// import { Html } from "@react-three/drei";
|
||||
// import { useDroppedObjectsStore } from "../../../store/store";
|
||||
// import { CartIcon, DocumentIcon, GlobeIcon, WalletIcon } from "../../icons/3dChartIcons";
|
||||
// import SimpleCard from "../realTimeVis/floating/SimpleCard";
|
||||
|
||||
// const ICON_MAP: Record<string, React.ComponentType<React.SVGProps<SVGSVGElement>>> = {
|
||||
// WalletIcon,
|
||||
// GlobeIcon,
|
||||
// DocumentIcon,
|
||||
// CartIcon
|
||||
// };
|
||||
// const DroppedObjects: React.FC = () => {
|
||||
// const objects = useDroppedObjectsStore((state) => state.objects); // Get objects from Zustand store
|
||||
//
|
||||
|
||||
// return (
|
||||
// <>
|
||||
// {objects.map((obj, index) => {
|
||||
// const IconComponent = obj.Icon || WalletIcon; // Use obj.Icon directly if it exists
|
||||
// return (
|
||||
// <SimpleCard
|
||||
// key={index}
|
||||
// position={obj.position}
|
||||
// header={obj.header}
|
||||
// icon={IconComponent} // ✅ No need to look it up in ICON_MAP
|
||||
// value={obj.value}
|
||||
// per={obj.per}
|
||||
// />
|
||||
// );
|
||||
// })}
|
||||
// </>
|
||||
// );
|
||||
// };
|
||||
|
||||
// export default DroppedObjects;
|
||||
|
||||
@@ -6,8 +6,10 @@ import { useSelectedZoneStore } from "../../../store/useZoneStore";
|
||||
import DisplayZone from "./DisplayZone";
|
||||
import Scene from "../../../modules/scene/scene";
|
||||
import useModuleStore from "../../../store/useModuleStore";
|
||||
import { useDroppedObjectsStore, useZones } from "../../../store/store";
|
||||
|
||||
import DroppedObjects from "./DroppedFloatingWidgets";
|
||||
import { useDroppedObjectsStore } from "../../../store/useDroppedObjectsStore";
|
||||
import { useZones } from "../../../store/store";
|
||||
|
||||
|
||||
type Side = "top" | "bottom" | "left" | "right";
|
||||
@@ -80,36 +82,36 @@ const RealTimeVisulization: React.FC = () => {
|
||||
});
|
||||
}, [selectedZone]);
|
||||
|
||||
// const handleDrop = (event: React.DragEvent<HTMLDivElement>) => {
|
||||
// console.log("Drop event fired! ✅");
|
||||
// event.preventDefault();
|
||||
|
||||
// const data = event.dataTransfer.getData("text/plain");
|
||||
// if (!data) {
|
||||
// console.log("❌ No data received on drop!");
|
||||
// return;
|
||||
// }
|
||||
|
||||
// try {
|
||||
// const droppedData = JSON.parse(data);
|
||||
// console.log("✅ Dropped Data:", droppedData);
|
||||
|
||||
// console.log('droppedData: ', droppedData);
|
||||
// setDroppedObjects((prev) => [...prev, droppedData]); // ✅ Add to state
|
||||
// console.log(droppedObjects);
|
||||
// } catch (error) {
|
||||
// console.error("❌ Error parsing dropped data:", error);
|
||||
// }
|
||||
// };
|
||||
const handleDrop = (event: React.DragEvent<HTMLDivElement>) => {
|
||||
event.preventDefault();
|
||||
const data = event.dataTransfer.getData("text/plain"); // Use "text/plain" to match the drag event
|
||||
|
||||
if (data) {
|
||||
const droppedData = JSON.parse(data);
|
||||
useDroppedObjectsStore.getState().addObject(droppedData); // Add to Zustand store
|
||||
const data = event.dataTransfer.getData("text/plain");
|
||||
if (!data || !selectedZone.zoneName) return;
|
||||
|
||||
const droppedData = JSON.parse(data);
|
||||
const canvasElement = document.getElementById("real-time-vis-canvas");
|
||||
if (!canvasElement) return;
|
||||
|
||||
const canvasRect = canvasElement.getBoundingClientRect();
|
||||
const relativeX = event.clientX - canvasRect.left;
|
||||
const relativeY = event.clientY - canvasRect.top;
|
||||
|
||||
const newObject = {
|
||||
...droppedData,
|
||||
position: [relativeY, relativeX], // Y first because of top/left style
|
||||
};
|
||||
|
||||
console.log("newObject: ", newObject);
|
||||
|
||||
// Only set zone if it’s not already in the store (prevents overwriting objects)
|
||||
const existingZone = useDroppedObjectsStore.getState().zones[selectedZone.zoneName];
|
||||
if (!existingZone) {
|
||||
useDroppedObjectsStore.getState().setZone(selectedZone.zoneName, selectedZone.zoneId);
|
||||
}
|
||||
|
||||
// Add the dropped object to the zone
|
||||
useDroppedObjectsStore.getState().addObject(selectedZone.zoneName, newObject);
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -133,9 +135,10 @@ const RealTimeVisulization: React.FC = () => {
|
||||
onDrop={(event) => handleDrop(event)}
|
||||
onDragOver={(event) => event.preventDefault()}
|
||||
>
|
||||
|
||||
|
||||
<Scene />
|
||||
</div>
|
||||
<DroppedObjects />
|
||||
{activeModule === "visualization" && (
|
||||
<>
|
||||
<DisplayZone
|
||||
|
||||
@@ -47,12 +47,9 @@ export default function ZoneCentreTarget() {
|
||||
const worldUp = new THREE.Vector3(0, 0, 1);
|
||||
const right = new THREE.Vector3().crossVectors(worldUp, direction).normalize();
|
||||
const up = new THREE.Vector3().crossVectors(direction, right).normalize();
|
||||
|
||||
const offsetPosition = up.clone().multiplyScalar(20);
|
||||
|
||||
camPosition.add(offsetPosition);
|
||||
|
||||
|
||||
const setCam = async () => {
|
||||
controls.setLookAt(centrePoint[0], 100, centrePoint[2], ...centrePoint, true);
|
||||
setTimeout(() => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import React from "react";
|
||||
|
||||
interface InputToggleProps {
|
||||
label: string; // Represents the toggle state (on/off)
|
||||
@@ -7,23 +7,18 @@ interface InputToggleProps {
|
||||
inputKey: string;
|
||||
}
|
||||
|
||||
// Update InputToggle.tsx to be fully controlled
|
||||
const InputToggle: React.FC<InputToggleProps> = ({
|
||||
label,
|
||||
onClick,
|
||||
value = false,
|
||||
inputKey,
|
||||
}) => {
|
||||
const [activeValue, setActiveValue] = useState<boolean>(value);
|
||||
|
||||
// Remove internal state and use the value prop directly
|
||||
function handleOnClick() {
|
||||
setActiveValue(!activeValue);
|
||||
if (onClick) onClick();
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setActiveValue(value);
|
||||
}, [value]);
|
||||
|
||||
return (
|
||||
<div className="input-toggle-container">
|
||||
<label htmlFor={`toogle-input-${inputKey}`} className="label">
|
||||
@@ -33,15 +28,16 @@ const InputToggle: React.FC<InputToggleProps> = ({
|
||||
<div
|
||||
className="check-box-style"
|
||||
style={{
|
||||
left: activeValue ? "50%" : "2px",
|
||||
background: activeValue ? "" : "var(--text-disabled)",
|
||||
left: value ? "50%" : "2px",
|
||||
background: value ? "" : "var(--text-disabled)",
|
||||
}}
|
||||
></div>
|
||||
<input
|
||||
type="checkbox"
|
||||
name=""
|
||||
id={`toogle-input-${inputKey}`}
|
||||
defaultChecked={value}
|
||||
checked={value}
|
||||
readOnly
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -4,21 +4,27 @@ import RenameInput from "./RenameInput";
|
||||
type InputWithDropDownProps = {
|
||||
label: string;
|
||||
value: string;
|
||||
min?: number
|
||||
defaultValue?: string;
|
||||
options?: string[]; // Array of dropdown options
|
||||
activeOption?: string; // The currently active dropdown option
|
||||
onClick?: () => void;
|
||||
onChange: (newValue: string) => void;
|
||||
editableLabel?: boolean;
|
||||
placeholder?: string; // New placeholder prop
|
||||
};
|
||||
|
||||
const InputWithDropDown: React.FC<InputWithDropDownProps> = ({
|
||||
label,
|
||||
value,
|
||||
min,
|
||||
defaultValue,
|
||||
options,
|
||||
activeOption,
|
||||
onClick,
|
||||
onChange,
|
||||
editableLabel = false,
|
||||
placeholder = "Inherit", // Default empty placeholder
|
||||
}) => {
|
||||
const separatedWords = label
|
||||
.split(/(?=[A-Z])/)
|
||||
@@ -38,11 +44,13 @@ const InputWithDropDown: React.FC<InputWithDropDownProps> = ({
|
||||
)}
|
||||
<div className="input default" id={separatedWords}>
|
||||
<input
|
||||
type="text"
|
||||
min={min}
|
||||
type="number"
|
||||
defaultValue={value}
|
||||
onChange={(e) => {
|
||||
onChange(e.target.value);
|
||||
}}
|
||||
placeholder={placeholder} // Added placeholder prop
|
||||
/>
|
||||
|
||||
{activeOption && (
|
||||
@@ -73,4 +81,4 @@ const InputWithDropDown: React.FC<InputWithDropDownProps> = ({
|
||||
);
|
||||
};
|
||||
|
||||
export default InputWithDropDown;
|
||||
export default InputWithDropDown;
|
||||
@@ -1,29 +1,49 @@
|
||||
import React, { useState } from "react";
|
||||
import React, { useState, useEffect } from "react";
|
||||
import RegularDropDown from "./RegularDropDown";
|
||||
|
||||
type LabledDropdownProps = {
|
||||
defaultOption: string; // Initial active option
|
||||
options: string[]; // Array of dropdown options
|
||||
label?: string; // Customizable label text
|
||||
onSelect?: (option: string) => void; // Callback when option is selected
|
||||
className?: string; // Additional className for styling
|
||||
disabled?: boolean; // Disable dropdown
|
||||
search?: boolean; // Enable/disable search functionality
|
||||
};
|
||||
|
||||
const LabledDropdown: React.FC<LabledDropdownProps> = ({ defaultOption, options }) => {
|
||||
const [activeOption, setActiveOption] = useState(defaultOption); // State for active option
|
||||
const LabledDropdown: React.FC<LabledDropdownProps> = ({
|
||||
defaultOption,
|
||||
options,
|
||||
label = "Type",
|
||||
onSelect,
|
||||
className = "",
|
||||
search = false
|
||||
}) => {
|
||||
const [activeOption, setActiveOption] = useState(defaultOption);
|
||||
|
||||
// Update active option if defaultOption changes
|
||||
useEffect(() => {
|
||||
setActiveOption(defaultOption);
|
||||
}, [defaultOption]);
|
||||
|
||||
const handleSelect = (option: string) => {
|
||||
setActiveOption(option); // Update the active option state
|
||||
setActiveOption(option);
|
||||
if (onSelect) {
|
||||
onSelect(option);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="value-field-container">
|
||||
<div className="label">Type</div>
|
||||
<div className={`value-field-container ${className}`}>
|
||||
<div className="label">{label}</div>
|
||||
<RegularDropDown
|
||||
header={activeOption} // Display the current active option
|
||||
options={options} // Use the options from props
|
||||
onSelect={handleSelect} // Handle option selection
|
||||
search = {false}
|
||||
header={activeOption}
|
||||
options={options}
|
||||
onSelect={handleSelect}
|
||||
search={search}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default LabledDropdown;
|
||||
export default LabledDropdown;
|
||||
@@ -201,18 +201,16 @@ const LineGraphComponent = ({
|
||||
const { measurements, setMeasurements, updateDuration, duration } = useChartStore();
|
||||
|
||||
useEffect(() => {
|
||||
if ( measurements.length > 0 ) {
|
||||
const socket = io("http://192.168.0.192:5010");
|
||||
|
||||
const socket = io("http://192.168.0.192:5010");
|
||||
|
||||
if ( measurements.length > 0 ) {
|
||||
var inputes = {
|
||||
measurements: measurements,
|
||||
duration: duration,
|
||||
interval: 1000,
|
||||
}
|
||||
|
||||
console.log('graphHHHHHHHHHHHHHHHHHHHHHHHHHHHHH',inputes);
|
||||
|
||||
|
||||
// Start stream
|
||||
const startStream = () => {
|
||||
socket.emit("lineInput", inputes);
|
||||
@@ -226,23 +224,24 @@ const LineGraphComponent = ({
|
||||
|
||||
// Extract timestamps and values
|
||||
const labels = responceData.time;
|
||||
const datasets = data.measurements.map((measurement: any) => ({
|
||||
label: `${measurement.name}.${measurement.fields}`,
|
||||
data: responceData[`${measurement.name}.${measurement.fields}`]?.values || [],
|
||||
backgroundColor: themeColor[0] || "#5c87df",
|
||||
borderColor: themeColor[1] || "#ffffff",
|
||||
borderWidth: 2,
|
||||
// fill: false,
|
||||
}));
|
||||
const datasets = measurements.map((measurement: any) => {
|
||||
const key = `${measurement.name}.${measurement.fields}`;
|
||||
return {
|
||||
label: key,
|
||||
data: responceData[key]?.values ?? [], // Ensure it exists
|
||||
backgroundColor: themeColor[0] || "#5c87df",
|
||||
borderColor: themeColor[1] || "#ffffff",
|
||||
};
|
||||
});
|
||||
|
||||
setChartData({ labels, datasets });
|
||||
});
|
||||
}
|
||||
|
||||
return () => {
|
||||
socket.off("lineOutput");
|
||||
socket.emit("stop_stream"); // Stop streaming when component unmounts
|
||||
};
|
||||
}
|
||||
}, [measurements, duration]);
|
||||
|
||||
// useEffect(() => {
|
||||
|
||||
@@ -4,8 +4,24 @@ const FleetEfficiency = () => {
|
||||
// Calculate the rotation angle for the progress bar
|
||||
const rotationAngle = -90 + progress * 3.6; // Progress starts from the left (-90°)
|
||||
|
||||
const handleDragStart = (event: React.DragEvent<HTMLDivElement>) => {
|
||||
const rect = event.currentTarget.getBoundingClientRect(); // Get position
|
||||
|
||||
const cardData = JSON.stringify({
|
||||
className: event.currentTarget.className,
|
||||
position: [rect.top, rect.left], // Store position
|
||||
value: rotationAngle, // Example value (you can change if dynamic)
|
||||
per: progress,
|
||||
|
||||
});
|
||||
|
||||
console.log("Dragged Data:", cardData);
|
||||
event.dataTransfer.setData("text/plain", cardData);
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<div className="fleetEfficiency floating">
|
||||
<div className="fleetEfficiency floating" draggable onDragStart={handleDragStart}>
|
||||
<h2 className="header">Fleet Efficiency</h2>
|
||||
|
||||
<div className="progressContainer">
|
||||
|
||||
@@ -5,6 +5,7 @@ interface SimpleCardProps {
|
||||
icon: React.ComponentType<React.SVGProps<SVGSVGElement>>; // React component for SVG icon
|
||||
value: string;
|
||||
per: string; // Percentage change
|
||||
position?: [number, number]
|
||||
}
|
||||
|
||||
const SimpleCard: React.FC<SimpleCardProps> = ({
|
||||
@@ -12,23 +13,30 @@ const SimpleCard: React.FC<SimpleCardProps> = ({
|
||||
icon: Icon,
|
||||
value,
|
||||
per,
|
||||
position = [0, 0],
|
||||
}) => {
|
||||
|
||||
const handleDragStart = (event: React.DragEvent<HTMLDivElement>) => {
|
||||
const rect = event.currentTarget.getBoundingClientRect(); // Get position
|
||||
const cardData = JSON.stringify({
|
||||
header,
|
||||
value,
|
||||
per,
|
||||
className: event.currentTarget.className,
|
||||
icon: Icon,
|
||||
className: event.currentTarget.className,
|
||||
position: [rect.top, rect.left], // ✅ Store position
|
||||
});
|
||||
|
||||
console.log("Dragging Data:", cardData); // ✅ Debugging log
|
||||
|
||||
|
||||
event.dataTransfer.setData("text/plain", cardData);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="floating total-card" draggable onDragStart={handleDragStart}>
|
||||
<div
|
||||
className="floating total-card"
|
||||
draggable
|
||||
onDragStart={handleDragStart}
|
||||
style={{ top: position[0], left: position[1] }} // No need for ?? 0 if position is guaranteed
|
||||
>
|
||||
<div className="header-wrapper">
|
||||
<div className="header">{header}</div>
|
||||
<div className="data-values">
|
||||
|
||||
@@ -111,8 +111,27 @@ const WarehouseThroughput = () => {
|
||||
},
|
||||
};
|
||||
|
||||
const handleDragStart = (event: React.DragEvent<HTMLDivElement>) => {
|
||||
const rect = event.currentTarget.getBoundingClientRect(); // Get element position
|
||||
|
||||
const cardData = JSON.stringify({
|
||||
header: "Warehouse Throughput", // Static header
|
||||
value: "+5", // Example value (you can change if dynamic)
|
||||
per: "2025", // Example percentage or date
|
||||
icon: "📊", // Placeholder for an icon (if needed)
|
||||
className: event.currentTarget.className,
|
||||
position: [rect.top, rect.left], // ✅ Store initial position
|
||||
lineGraphData, // ✅ Include chart data
|
||||
lineGraphOptions, // ✅ Include chart options
|
||||
});
|
||||
|
||||
|
||||
event.dataTransfer.setData("text/plain", cardData);
|
||||
// event.dataTransfer.effectAllowed = "move"; // Improve drag effect
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="warehouseThroughput floating" draggable>
|
||||
<div className="warehouseThroughput floating" draggable onDragStart={handleDragStart}>
|
||||
<div className="header">
|
||||
<h2>Warehouse Throughput</h2>
|
||||
<p>
|
||||
|
||||
Reference in New Issue
Block a user