Merge remote-tracking branch 'origin/ui' into simulation
This commit is contained in:
@@ -220,8 +220,8 @@ const ProductionCapacity: React.FC<ProductionCapacityProps> = ({
|
|||||||
// e.stopPropagation();
|
// e.stopPropagation();
|
||||||
}}
|
}}
|
||||||
wrapperClass="pointer-none"
|
wrapperClass="pointer-none"
|
||||||
className={`${selectedChartId?.id === id ? "activeChart" : ""}`}
|
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={`productionCapacity-wrapper card ${selectedChartId?.id === id ? "activeChart" : ""}`}
|
className={`productionCapacity-wrapper card ${selectedChartId?.id === id ? "activeChart" : ""}`}
|
||||||
onClick={() => setSelectedChartId({ id: id, type: type })}
|
onClick={() => setSelectedChartId({ id: id, type: type })}
|
||||||
|
|||||||
@@ -46,20 +46,32 @@ interface ReturnOfInvestmentProps {
|
|||||||
rotation: [number, number, number];
|
rotation: [number, number, number];
|
||||||
onContextMenu?: (event: React.MouseEvent) => void;
|
onContextMenu?: (event: React.MouseEvent) => void;
|
||||||
}
|
}
|
||||||
const ReturnOfInvestment: React.FC<ReturnOfInvestmentProps> = ({ id, type, position, rotation, onContextMenu }) => {
|
const ReturnOfInvestment: React.FC<ReturnOfInvestmentProps> = ({
|
||||||
|
id,
|
||||||
|
type,
|
||||||
|
position,
|
||||||
|
rotation,
|
||||||
|
onContextMenu,
|
||||||
|
}) => {
|
||||||
const { selectedChartId, setSelectedChartId } = useWidgetStore();
|
const { selectedChartId, setSelectedChartId } = useWidgetStore();
|
||||||
const { measurements: chartMeasurements, duration: chartDuration, name: widgetName } = useChartStore();
|
const {
|
||||||
|
measurements: chartMeasurements,
|
||||||
|
duration: chartDuration,
|
||||||
|
name: widgetName,
|
||||||
|
} = useChartStore();
|
||||||
const [measurements, setmeasurements] = useState<any>({});
|
const [measurements, setmeasurements] = useState<any>({});
|
||||||
const [duration, setDuration] = useState("1h")
|
const [duration, setDuration] = useState("1h");
|
||||||
const [name, setName] = useState("Widget")
|
const [name, setName] = useState("Widget");
|
||||||
const [chartData, setChartData] = useState<{ labels: string[]; datasets: any[] }>({
|
const [chartData, setChartData] = useState<{
|
||||||
|
labels: string[];
|
||||||
|
datasets: any[];
|
||||||
|
}>({
|
||||||
labels: [],
|
labels: [],
|
||||||
datasets: [],
|
datasets: [],
|
||||||
});
|
});
|
||||||
const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
|
const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
|
||||||
const email = localStorage.getItem("email") || "";
|
const email = localStorage.getItem("email") || "";
|
||||||
const organization = email?.split("@")[1]?.split(".")[0]
|
const organization = email?.split("@")[1]?.split(".")[0];
|
||||||
// Improved sample data for the smooth curve graph (single day)
|
// Improved sample data for the smooth curve graph (single day)
|
||||||
const graphData: ChartData<"line"> = {
|
const graphData: ChartData<"line"> = {
|
||||||
labels: [
|
labels: [
|
||||||
@@ -129,7 +141,8 @@ const ReturnOfInvestment: React.FC<ReturnOfInvestmentProps> = ({ id, type, posit
|
|||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!iotApiUrl || !measurements || Object.keys(measurements).length === 0) return;
|
if (!iotApiUrl || !measurements || Object.keys(measurements).length === 0)
|
||||||
|
return;
|
||||||
|
|
||||||
const socket = io(`http://${iotApiUrl}`);
|
const socket = io(`http://${iotApiUrl}`);
|
||||||
|
|
||||||
@@ -139,7 +152,6 @@ const ReturnOfInvestment: React.FC<ReturnOfInvestmentProps> = ({ id, type, posit
|
|||||||
interval: 1000,
|
interval: 1000,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const startStream = () => {
|
const startStream = () => {
|
||||||
socket.emit("lineInput", inputData);
|
socket.emit("lineInput", inputData);
|
||||||
};
|
};
|
||||||
@@ -157,8 +169,10 @@ const ReturnOfInvestment: React.FC<ReturnOfInvestmentProps> = ({ id, type, posit
|
|||||||
return {
|
return {
|
||||||
label: datasetKey,
|
label: datasetKey,
|
||||||
data: responseData[datasetKey]?.values ?? [],
|
data: responseData[datasetKey]?.values ?? [],
|
||||||
borderColor: index === 0 ? "rgba(75, 192, 192, 1)" : "rgba(255, 99, 132, 1)", // Light blue color
|
borderColor:
|
||||||
backgroundColor: index === 0 ? "rgba(75, 192, 192, 0.2)" : "rgba(255, 99, 132, 0.2)",
|
index === 0 ? "rgba(75, 192, 192, 1)" : "rgba(255, 99, 132, 1)", // Light blue color
|
||||||
|
backgroundColor:
|
||||||
|
index === 0 ? "rgba(75, 192, 192, 0.2)" : "rgba(255, 99, 132, 0.2)",
|
||||||
fill: true,
|
fill: true,
|
||||||
tension: 0.4, // Smooth curve effect
|
tension: 0.4, // Smooth curve effect
|
||||||
pointRadius: 0, // Hide dots
|
pointRadius: 0, // Hide dots
|
||||||
@@ -177,14 +191,15 @@ const ReturnOfInvestment: React.FC<ReturnOfInvestmentProps> = ({ id, type, posit
|
|||||||
}, [measurements, duration, iotApiUrl]);
|
}, [measurements, duration, iotApiUrl]);
|
||||||
|
|
||||||
const fetchSavedInputes = async () => {
|
const fetchSavedInputes = async () => {
|
||||||
|
|
||||||
if (id !== "") {
|
if (id !== "") {
|
||||||
try {
|
try {
|
||||||
const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/widget3D/${id}/${organization}`);
|
const response = await axios.get(
|
||||||
|
`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/widget3D/${id}/${organization}`
|
||||||
|
);
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
setmeasurements(response.data.Data.measurements)
|
setmeasurements(response.data.Data.measurements);
|
||||||
setDuration(response.data.Data.duration)
|
setDuration(response.data.Data.duration);
|
||||||
setName(response.data.widgetName)
|
setName(response.data.widgetName);
|
||||||
} else {
|
} else {
|
||||||
console.log("Unexpected response:", response);
|
console.log("Unexpected response:", response);
|
||||||
}
|
}
|
||||||
@@ -192,7 +207,7 @@ const ReturnOfInvestment: React.FC<ReturnOfInvestmentProps> = ({ id, type, posit
|
|||||||
console.error("There was an error!", error);
|
console.error("There was an error!", error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchSavedInputes();
|
fetchSavedInputes();
|
||||||
@@ -202,8 +217,7 @@ const ReturnOfInvestment: React.FC<ReturnOfInvestmentProps> = ({ id, type, posit
|
|||||||
if (selectedChartId?.id === id) {
|
if (selectedChartId?.id === id) {
|
||||||
fetchSavedInputes();
|
fetchSavedInputes();
|
||||||
}
|
}
|
||||||
}
|
}, [chartMeasurements, chartDuration, widgetName]);
|
||||||
, [chartMeasurements, chartDuration, widgetName])
|
|
||||||
const rotationDegrees = {
|
const rotationDegrees = {
|
||||||
x: (rotation[0] * 180) / Math.PI,
|
x: (rotation[0] * 180) / Math.PI,
|
||||||
y: (rotation[1] * 180) / Math.PI,
|
y: (rotation[1] * 180) / Math.PI,
|
||||||
@@ -215,27 +229,32 @@ const ReturnOfInvestment: React.FC<ReturnOfInvestmentProps> = ({ id, type, posit
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Html position={[position[0], position[1], position[2]]}
|
<Html
|
||||||
|
position={[position[0], position[1], position[2]]}
|
||||||
scale={[0.5, 0.5, 0.5]}
|
scale={[0.5, 0.5, 0.5]}
|
||||||
transform
|
transform
|
||||||
zIndexRange={[1, 0]}
|
zIndexRange={[1, 0]}
|
||||||
sprite
|
sprite
|
||||||
style={{
|
style={{
|
||||||
transform: transformStyle.transform,
|
transform: transformStyle.transform,
|
||||||
transformStyle: 'preserve-3d',
|
transformStyle: "preserve-3d",
|
||||||
transition: 'transform 0.1s ease-out'
|
transition: "transform 0.1s ease-out",
|
||||||
|
|
||||||
}}
|
}}
|
||||||
className={`${selectedChartId?.id === id ? "activeChart" : ""}`}
|
|
||||||
>
|
>
|
||||||
<div className="returnOfInvestment card"
|
<div
|
||||||
|
className={`returnOfInvestment card ${
|
||||||
|
selectedChartId?.id === id ? "activeChart" : ""
|
||||||
|
}`}
|
||||||
onClick={() => setSelectedChartId({ id: id, type: type })}
|
onClick={() => setSelectedChartId({ id: id, type: type })}
|
||||||
onContextMenu={onContextMenu}
|
onContextMenu={onContextMenu}
|
||||||
>
|
>
|
||||||
<div className="header">Return of Investment</div>
|
<div className="header">Return of Investment</div>
|
||||||
<div className="lineGraph charts">
|
<div className="lineGraph charts">
|
||||||
{/* Smooth curve graph with two datasets */}
|
{/* Smooth curve graph with two datasets */}
|
||||||
<SmoothLineGraphComponent data={Object.keys(measurements).length > 0 ? chartData : graphData} options={graphOptions} />
|
<SmoothLineGraphComponent
|
||||||
|
data={Object.keys(measurements).length > 0 ? chartData : graphData}
|
||||||
|
options={graphOptions}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="returns-wrapper">
|
<div className="returns-wrapper">
|
||||||
<div className="icon">
|
<div className="icon">
|
||||||
|
|||||||
@@ -13,16 +13,26 @@ interface StateWorkingProps {
|
|||||||
rotation: [number, number, number];
|
rotation: [number, number, number];
|
||||||
onContextMenu?: (event: React.MouseEvent) => void;
|
onContextMenu?: (event: React.MouseEvent) => void;
|
||||||
}
|
}
|
||||||
const StateWorking: React.FC<StateWorkingProps> = ({ id, type, position, rotation, onContextMenu }) => {
|
const StateWorking: React.FC<StateWorkingProps> = ({
|
||||||
|
id,
|
||||||
|
type,
|
||||||
|
position,
|
||||||
|
rotation,
|
||||||
|
onContextMenu,
|
||||||
|
}) => {
|
||||||
const { selectedChartId, setSelectedChartId } = useWidgetStore();
|
const { selectedChartId, setSelectedChartId } = useWidgetStore();
|
||||||
const { measurements: chartMeasurements, duration: chartDuration, name: widgetName } = useChartStore();
|
const {
|
||||||
|
measurements: chartMeasurements,
|
||||||
|
duration: chartDuration,
|
||||||
|
name: widgetName,
|
||||||
|
} = useChartStore();
|
||||||
const [measurements, setmeasurements] = useState<any>({});
|
const [measurements, setmeasurements] = useState<any>({});
|
||||||
const [duration, setDuration] = useState("1h")
|
const [duration, setDuration] = useState("1h");
|
||||||
const [name, setName] = useState("Widget")
|
const [name, setName] = useState("Widget");
|
||||||
const [datas, setDatas] = useState<any>({});
|
const [datas, setDatas] = useState<any>({});
|
||||||
const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
|
const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
|
||||||
const email = localStorage.getItem("email") || "";
|
const email = localStorage.getItem("email") || "";
|
||||||
const organization = email?.split("@")[1]?.split(".")[0]
|
const organization = email?.split("@")[1]?.split(".")[0];
|
||||||
// const datas = [
|
// const datas = [
|
||||||
// { key: "Oil Tank:", value: "24/341" },
|
// { key: "Oil Tank:", value: "24/341" },
|
||||||
// { key: "Oil Refin:", value: 36.023 },
|
// { key: "Oil Refin:", value: 36.023 },
|
||||||
@@ -33,7 +43,8 @@ const StateWorking: React.FC<StateWorkingProps> = ({ id, type, position, rotatio
|
|||||||
// ];
|
// ];
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!iotApiUrl || !measurements || Object.keys(measurements).length === 0) return;
|
if (!iotApiUrl || !measurements || Object.keys(measurements).length === 0)
|
||||||
|
return;
|
||||||
const socket = io(`http://${iotApiUrl}`);
|
const socket = io(`http://${iotApiUrl}`);
|
||||||
const inputData = {
|
const inputData = {
|
||||||
measurements,
|
measurements,
|
||||||
@@ -46,7 +57,6 @@ const StateWorking: React.FC<StateWorkingProps> = ({ id, type, position, rotatio
|
|||||||
socket.on("connect", startStream);
|
socket.on("connect", startStream);
|
||||||
socket.on("lastOutput", (response) => {
|
socket.on("lastOutput", (response) => {
|
||||||
const responseData = response;
|
const responseData = response;
|
||||||
|
|
||||||
|
|
||||||
setDatas(responseData);
|
setDatas(responseData);
|
||||||
});
|
});
|
||||||
@@ -59,14 +69,15 @@ const StateWorking: React.FC<StateWorkingProps> = ({ id, type, position, rotatio
|
|||||||
}, [measurements, duration, iotApiUrl]);
|
}, [measurements, duration, iotApiUrl]);
|
||||||
|
|
||||||
const fetchSavedInputes = async () => {
|
const fetchSavedInputes = async () => {
|
||||||
|
|
||||||
if (id !== "") {
|
if (id !== "") {
|
||||||
try {
|
try {
|
||||||
const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/widget3D/${id}/${organization}`);
|
const response = await axios.get(
|
||||||
|
`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/widget3D/${id}/${organization}`
|
||||||
|
);
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
setmeasurements(response.data.Data.measurements)
|
setmeasurements(response.data.Data.measurements);
|
||||||
setDuration(response.data.Data.duration)
|
setDuration(response.data.Data.duration);
|
||||||
setName(response.data.widgetName)
|
setName(response.data.widgetName);
|
||||||
} else {
|
} else {
|
||||||
console.log("Unexpected response:", response);
|
console.log("Unexpected response:", response);
|
||||||
}
|
}
|
||||||
@@ -74,10 +85,7 @@ const StateWorking: React.FC<StateWorkingProps> = ({ id, type, position, rotatio
|
|||||||
console.error("There was an error!", error);
|
console.error("There was an error!", error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchSavedInputes();
|
fetchSavedInputes();
|
||||||
@@ -87,8 +95,7 @@ const StateWorking: React.FC<StateWorkingProps> = ({ id, type, position, rotatio
|
|||||||
if (selectedChartId?.id === id) {
|
if (selectedChartId?.id === id) {
|
||||||
fetchSavedInputes();
|
fetchSavedInputes();
|
||||||
}
|
}
|
||||||
}
|
}, [chartMeasurements, chartDuration, widgetName]);
|
||||||
, [chartMeasurements, chartDuration, widgetName])
|
|
||||||
|
|
||||||
const rotationDegrees = {
|
const rotationDegrees = {
|
||||||
x: (rotation[0] * 180) / Math.PI,
|
x: (rotation[0] * 180) / Math.PI,
|
||||||
@@ -100,20 +107,22 @@ const StateWorking: React.FC<StateWorkingProps> = ({ id, type, position, rotatio
|
|||||||
transform: `rotateX(${rotationDegrees.x}deg) rotateY(${rotationDegrees.y}deg) rotateZ(${rotationDegrees.z}deg)`,
|
transform: `rotateX(${rotationDegrees.x}deg) rotateY(${rotationDegrees.y}deg) rotateZ(${rotationDegrees.z}deg)`,
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<Html position={[position[0], position[1], position[2]]}
|
<Html
|
||||||
|
position={[position[0], position[1], position[2]]}
|
||||||
scale={[0.5, 0.5, 0.5]}
|
scale={[0.5, 0.5, 0.5]}
|
||||||
transform
|
transform
|
||||||
zIndexRange={[1, 0]}
|
zIndexRange={[1, 0]}
|
||||||
sprite
|
sprite
|
||||||
style={{
|
style={{
|
||||||
transform: transformStyle.transform,
|
transform: transformStyle.transform,
|
||||||
transformStyle: 'preserve-3d',
|
transformStyle: "preserve-3d",
|
||||||
transition: 'transform 0.1s ease-out'
|
transition: "transform 0.1s ease-out",
|
||||||
|
|
||||||
}}
|
}}
|
||||||
className={`${selectedChartId?.id === id ? "activeChart" : ""}`}
|
|
||||||
>
|
>
|
||||||
<div className="stateWorking-wrapper card"
|
<div
|
||||||
|
className={`stateWorking-wrapper card ${
|
||||||
|
selectedChartId?.id === id ? "activeChart" : ""
|
||||||
|
}`}
|
||||||
onClick={() => setSelectedChartId({ id: id, type: type })}
|
onClick={() => setSelectedChartId({ id: id, type: type })}
|
||||||
onContextMenu={onContextMenu}
|
onContextMenu={onContextMenu}
|
||||||
>
|
>
|
||||||
@@ -121,12 +130,10 @@ const StateWorking: React.FC<StateWorkingProps> = ({ id, type, position, rotatio
|
|||||||
<div className="header">
|
<div className="header">
|
||||||
<span>State</span>
|
<span>State</span>
|
||||||
<span>
|
<span>
|
||||||
{datas?.input1 ? datas.input1 : 'input1'} <span>.</span>
|
{datas?.input1 ? datas.input1 : "input1"} <span>.</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="img">
|
<div className="img">{/* <img src={image} alt="" /> */}</div>
|
||||||
{/* <img src={image} alt="" /> */}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
{/* Data */}
|
{/* Data */}
|
||||||
<div className="data-wrapper">
|
<div className="data-wrapper">
|
||||||
@@ -137,28 +144,52 @@ const StateWorking: React.FC<StateWorkingProps> = ({ id, type, position, rotatio
|
|||||||
</div>
|
</div>
|
||||||
))} */}
|
))} */}
|
||||||
<div className="data-table">
|
<div className="data-table">
|
||||||
<div className="data">{measurements?.input2?.fields ? measurements.input2.fields : 'input2'}</div>
|
<div className="data">
|
||||||
<div className="key">{datas?.input2 ? datas.input2 : 'data'}</div>
|
{measurements?.input2?.fields
|
||||||
|
? measurements.input2.fields
|
||||||
|
: "input2"}
|
||||||
|
</div>
|
||||||
|
<div className="key">{datas?.input2 ? datas.input2 : "data"}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="data-table">
|
<div className="data-table">
|
||||||
<div className="data">{measurements?.input3?.fields ? measurements.input3.fields : 'input3'}</div>
|
<div className="data">
|
||||||
<div className="key">{datas?.input3 ? datas.input3 : 'data'}</div>
|
{measurements?.input3?.fields
|
||||||
|
? measurements.input3.fields
|
||||||
|
: "input3"}
|
||||||
|
</div>
|
||||||
|
<div className="key">{datas?.input3 ? datas.input3 : "data"}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="data-table">
|
<div className="data-table">
|
||||||
<div className="data">{measurements?.input4?.fields ? measurements.input4.fields : 'input4'}</div>
|
<div className="data">
|
||||||
<div className="key">{datas?.input4 ? datas.input4 : 'data'}</div>
|
{measurements?.input4?.fields
|
||||||
|
? measurements.input4.fields
|
||||||
|
: "input4"}
|
||||||
|
</div>
|
||||||
|
<div className="key">{datas?.input4 ? datas.input4 : "data"}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="data-table">
|
<div className="data-table">
|
||||||
<div className="data">{measurements?.input5?.fields ? measurements.input5.fields : 'input5'}</div>
|
<div className="data">
|
||||||
<div className="key">{datas?.input5 ? datas.input5 : 'data'}</div>
|
{measurements?.input5?.fields
|
||||||
|
? measurements.input5.fields
|
||||||
|
: "input5"}
|
||||||
|
</div>
|
||||||
|
<div className="key">{datas?.input5 ? datas.input5 : "data"}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="data-table">
|
<div className="data-table">
|
||||||
<div className="data">{measurements?.input6?.fields ? measurements.input6.fields : 'input6'}</div>
|
<div className="data">
|
||||||
<div className="key">{datas?.input6 ? datas.input6 : 'data'}</div>
|
{measurements?.input6?.fields
|
||||||
|
? measurements.input6.fields
|
||||||
|
: "input6"}
|
||||||
|
</div>
|
||||||
|
<div className="key">{datas?.input6 ? datas.input6 : "data"}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="data-table">
|
<div className="data-table">
|
||||||
<div className="data">{measurements?.input7?.fields ? measurements.input7.fields : 'input7'}</div>
|
<div className="data">
|
||||||
<div className="key">{datas?.input7 ? datas.input7 : 'data'}</div>
|
{measurements?.input7?.fields
|
||||||
|
? measurements.input7.fields
|
||||||
|
: "input7"}
|
||||||
|
</div>
|
||||||
|
<div className="key">{datas?.input7 ? datas.input7 : "data"}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -167,5 +198,3 @@ const StateWorking: React.FC<StateWorkingProps> = ({ id, type, position, rotatio
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default StateWorking;
|
export default StateWorking;
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -49,20 +49,32 @@ interface ThroughputProps {
|
|||||||
onContextMenu?: (event: React.MouseEvent) => void;
|
onContextMenu?: (event: React.MouseEvent) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Throughput: React.FC<ThroughputProps> = ({ id, type, position, rotation, onContextMenu }) => {
|
const Throughput: React.FC<ThroughputProps> = ({
|
||||||
|
id,
|
||||||
|
type,
|
||||||
|
position,
|
||||||
|
rotation,
|
||||||
|
onContextMenu,
|
||||||
|
}) => {
|
||||||
const { selectedChartId, setSelectedChartId } = useWidgetStore();
|
const { selectedChartId, setSelectedChartId } = useWidgetStore();
|
||||||
const { measurements: chartMeasurements, duration: chartDuration, name: widgetName } = useChartStore();
|
const {
|
||||||
|
measurements: chartMeasurements,
|
||||||
|
duration: chartDuration,
|
||||||
|
name: widgetName,
|
||||||
|
} = useChartStore();
|
||||||
const [measurements, setmeasurements] = useState<any>({});
|
const [measurements, setmeasurements] = useState<any>({});
|
||||||
const [duration, setDuration] = useState("1h")
|
const [duration, setDuration] = useState("1h");
|
||||||
const [name, setName] = useState("Widget")
|
const [name, setName] = useState("Widget");
|
||||||
const [chartData, setChartData] = useState<{ labels: string[]; datasets: any[] }>({
|
const [chartData, setChartData] = useState<{
|
||||||
|
labels: string[];
|
||||||
|
datasets: any[];
|
||||||
|
}>({
|
||||||
labels: [],
|
labels: [],
|
||||||
datasets: [],
|
datasets: [],
|
||||||
});
|
});
|
||||||
const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
|
const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
|
||||||
const email = localStorage.getItem("email") || "";
|
const email = localStorage.getItem("email") || "";
|
||||||
const organization = email?.split("@")[1]?.split(".")[0]
|
const organization = email?.split("@")[1]?.split(".")[0];
|
||||||
|
|
||||||
// Sample data for the line graph
|
// Sample data for the line graph
|
||||||
const graphData: ChartData<"line"> = {
|
const graphData: ChartData<"line"> = {
|
||||||
@@ -112,7 +124,8 @@ const Throughput: React.FC<ThroughputProps> = ({ id, type, position, rotation, o
|
|||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!iotApiUrl || !measurements || Object.keys(measurements).length === 0) return;
|
if (!iotApiUrl || !measurements || Object.keys(measurements).length === 0)
|
||||||
|
return;
|
||||||
|
|
||||||
const socket = io(`http://${iotApiUrl}`);
|
const socket = io(`http://${iotApiUrl}`);
|
||||||
|
|
||||||
@@ -122,7 +135,6 @@ const Throughput: React.FC<ThroughputProps> = ({ id, type, position, rotation, o
|
|||||||
interval: 1000,
|
interval: 1000,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const startStream = () => {
|
const startStream = () => {
|
||||||
socket.emit("lineInput", inputData);
|
socket.emit("lineInput", inputData);
|
||||||
};
|
};
|
||||||
@@ -157,14 +169,15 @@ const Throughput: React.FC<ThroughputProps> = ({ id, type, position, rotation, o
|
|||||||
}, [measurements, duration, iotApiUrl]);
|
}, [measurements, duration, iotApiUrl]);
|
||||||
|
|
||||||
const fetchSavedInputes = async () => {
|
const fetchSavedInputes = async () => {
|
||||||
|
|
||||||
if (id !== "") {
|
if (id !== "") {
|
||||||
try {
|
try {
|
||||||
const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/widget3D/${id}/${organization}`);
|
const response = await axios.get(
|
||||||
|
`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/widget3D/${id}/${organization}`
|
||||||
|
);
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
setmeasurements(response.data.Data.measurements)
|
setmeasurements(response.data.Data.measurements);
|
||||||
setDuration(response.data.Data.duration)
|
setDuration(response.data.Data.duration);
|
||||||
setName(response.data.widgetName)
|
setName(response.data.widgetName);
|
||||||
} else {
|
} else {
|
||||||
console.log("Unexpected response:", response);
|
console.log("Unexpected response:", response);
|
||||||
}
|
}
|
||||||
@@ -172,7 +185,7 @@ const Throughput: React.FC<ThroughputProps> = ({ id, type, position, rotation, o
|
|||||||
console.error("There was an error!", error);
|
console.error("There was an error!", error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchSavedInputes();
|
fetchSavedInputes();
|
||||||
@@ -182,8 +195,7 @@ const Throughput: React.FC<ThroughputProps> = ({ id, type, position, rotation, o
|
|||||||
if (selectedChartId?.id === id) {
|
if (selectedChartId?.id === id) {
|
||||||
fetchSavedInputes();
|
fetchSavedInputes();
|
||||||
}
|
}
|
||||||
}
|
}, [chartMeasurements, chartDuration, widgetName]);
|
||||||
, [chartMeasurements, chartDuration, widgetName])
|
|
||||||
const rotationDegrees = {
|
const rotationDegrees = {
|
||||||
x: (rotation[0] * 180) / Math.PI,
|
x: (rotation[0] * 180) / Math.PI,
|
||||||
y: (rotation[1] * 180) / Math.PI,
|
y: (rotation[1] * 180) / Math.PI,
|
||||||
@@ -195,20 +207,22 @@ const Throughput: React.FC<ThroughputProps> = ({ id, type, position, rotation, o
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Html position={[position[0], position[1], position[2]]}
|
<Html
|
||||||
|
position={[position[0], position[1], position[2]]}
|
||||||
scale={[0.5, 0.5, 0.5]}
|
scale={[0.5, 0.5, 0.5]}
|
||||||
transform
|
transform
|
||||||
zIndexRange={[1, 0]}
|
zIndexRange={[1, 0]}
|
||||||
sprite
|
sprite
|
||||||
style={{
|
style={{
|
||||||
transform: transformStyle.transform,
|
transform: transformStyle.transform,
|
||||||
transformStyle: 'preserve-3d',
|
transformStyle: "preserve-3d",
|
||||||
transition: 'transform 0.1s ease-out'
|
transition: "transform 0.1s ease-out",
|
||||||
|
|
||||||
}}
|
}}
|
||||||
className={`${selectedChartId?.id === id ? "activeChart" : ""}`}
|
|
||||||
>
|
>
|
||||||
<div className="throughput-wrapper"
|
<div
|
||||||
|
className={`throughput-wrapper card ${
|
||||||
|
selectedChartId?.id === id ? "activeChart" : ""
|
||||||
|
}`}
|
||||||
onClick={() => setSelectedChartId({ id: id, type: type })}
|
onClick={() => setSelectedChartId({ id: id, type: type })}
|
||||||
onContextMenu={onContextMenu}
|
onContextMenu={onContextMenu}
|
||||||
>
|
>
|
||||||
@@ -235,7 +249,10 @@ const Throughput: React.FC<ThroughputProps> = ({ id, type, position, rotation, o
|
|||||||
</div>
|
</div>
|
||||||
<div className="line-graph">
|
<div className="line-graph">
|
||||||
{/* Line graph using react-chartjs-2 */}
|
{/* Line graph using react-chartjs-2 */}
|
||||||
<LineGraphComponent data={Object.keys(measurements).length > 0 ? chartData : graphData} options={graphOptions} />
|
<LineGraphComponent
|
||||||
|
data={Object.keys(measurements).length > 0 ? chartData : graphData}
|
||||||
|
options={graphOptions}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="footer">
|
<div className="footer">
|
||||||
You made an extra <span className="value">$1256.13</span> this month
|
You made an extra <span className="value">$1256.13</span> this month
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ const Assets: React.FC = () => {
|
|||||||
<div className="assets-container">
|
<div className="assets-container">
|
||||||
{categoryAssets &&
|
{categoryAssets &&
|
||||||
categoryAssets?.map((asset: any, index: number) => (
|
categoryAssets?.map((asset: any, index: number) => (
|
||||||
<div key={index} className="assets">
|
<div key={index} className="assets" id={asset.filename}>
|
||||||
<img
|
<img
|
||||||
src={asset?.thumbnail}
|
src={asset?.thumbnail}
|
||||||
alt={asset.filename}
|
alt={asset.filename}
|
||||||
@@ -172,6 +172,7 @@ const Assets: React.FC = () => {
|
|||||||
<div className="assets-wrapper">
|
<div className="assets-wrapper">
|
||||||
<div
|
<div
|
||||||
className="back-button"
|
className="back-button"
|
||||||
|
id="asset-backButtom"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSelectedCategory(null);
|
setSelectedCategory(null);
|
||||||
setCategoryAssets([]);
|
setCategoryAssets([]);
|
||||||
@@ -183,7 +184,7 @@ const Assets: React.FC = () => {
|
|||||||
<div className="assets-container">
|
<div className="assets-container">
|
||||||
{categoryAssets &&
|
{categoryAssets &&
|
||||||
categoryAssets?.map((asset: any, index: number) => (
|
categoryAssets?.map((asset: any, index: number) => (
|
||||||
<div key={index} className="assets">
|
<div key={index} className="assets" id={asset.filename}>
|
||||||
<img
|
<img
|
||||||
src={asset?.thumbnail}
|
src={asset?.thumbnail}
|
||||||
alt={asset.filename}
|
alt={asset.filename}
|
||||||
@@ -223,6 +224,7 @@ const Assets: React.FC = () => {
|
|||||||
<div
|
<div
|
||||||
key={index}
|
key={index}
|
||||||
className="category"
|
className="category"
|
||||||
|
id={category}
|
||||||
onClick={() => fetchCategoryAssets(category)}
|
onClick={() => fetchCategoryAssets(category)}
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import useTemplateStore from "../../../../store/useTemplateStore";
|
|||||||
import { useSelectedZoneStore } from "../../../../store/useZoneStore";
|
import { useSelectedZoneStore } from "../../../../store/useZoneStore";
|
||||||
import { getTemplateData } from "../../../../services/realTimeVisulization/zoneData/getTemplate";
|
import { getTemplateData } from "../../../../services/realTimeVisulization/zoneData/getTemplate";
|
||||||
import { useSocketStore } from "../../../../store/store";
|
import { useSocketStore } from "../../../../store/store";
|
||||||
|
import RenameInput from "../../../ui/inputs/RenameInput";
|
||||||
|
|
||||||
const Templates = () => {
|
const Templates = () => {
|
||||||
const { templates, removeTemplate, setTemplates } = useTemplateStore();
|
const { templates, removeTemplate, setTemplates } = useTemplateStore();
|
||||||
@@ -34,7 +35,10 @@ const Templates = () => {
|
|||||||
templateID: id,
|
templateID: id,
|
||||||
};
|
};
|
||||||
if (visualizationSocket) {
|
if (visualizationSocket) {
|
||||||
visualizationSocket.emit("v2:viz-template:deleteTemplate", deleteTemplate);
|
visualizationSocket.emit(
|
||||||
|
"v2:viz-template:deleteTemplate",
|
||||||
|
deleteTemplate
|
||||||
|
);
|
||||||
}
|
}
|
||||||
removeTemplate(id);
|
removeTemplate(id);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -65,11 +69,15 @@ const Templates = () => {
|
|||||||
widgets: template.widgets,
|
widgets: template.widgets,
|
||||||
});
|
});
|
||||||
|
|
||||||
useDroppedObjectsStore.getState().setZone(selectedZone.zoneName, selectedZone.zoneId);
|
useDroppedObjectsStore
|
||||||
|
.getState()
|
||||||
|
.setZone(selectedZone.zoneName, selectedZone.zoneId);
|
||||||
|
|
||||||
if (Array.isArray(template.floatingWidget)) {
|
if (Array.isArray(template.floatingWidget)) {
|
||||||
template.floatingWidget.forEach((val: any) => {
|
template.floatingWidget.forEach((val: any) => {
|
||||||
useDroppedObjectsStore.getState().addObject(selectedZone.zoneName, val);
|
useDroppedObjectsStore
|
||||||
|
.getState()
|
||||||
|
.addObject(selectedZone.zoneName, val);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -79,7 +87,7 @@ const Templates = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="template-list">
|
<div className="template-list">
|
||||||
{templates.map((template) => (
|
{templates.map((template, index) => (
|
||||||
<div key={template.id} className="template-item">
|
<div key={template.id} className="template-item">
|
||||||
{template?.snapshot && (
|
{template?.snapshot && (
|
||||||
<div className="template-image-container">
|
<div className="template-image-container">
|
||||||
@@ -96,7 +104,8 @@ const Templates = () => {
|
|||||||
onClick={() => handleLoadTemplate(template)}
|
onClick={() => handleLoadTemplate(template)}
|
||||||
className="template-name"
|
className="template-name"
|
||||||
>
|
>
|
||||||
{template.name}
|
{/* {`Template ${index + 1}`} */}
|
||||||
|
<RenameInput value={`Template ${index + 1}`} />
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={() => handleDeleteTemplate(template.id)}
|
onClick={() => handleDeleteTemplate(template.id)}
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
import React, { useEffect } from "react";
|
import React from "react";
|
||||||
import {
|
import {
|
||||||
CleanPannel,
|
CleanPannel,
|
||||||
EyeIcon,
|
EyeIcon,
|
||||||
LockIcon,
|
LockIcon,
|
||||||
} from "../../icons/RealTimeVisulationIcons";
|
} from "../../icons/RealTimeVisulationIcons";
|
||||||
import { panelData } from "../../../services/realTimeVisulization/zoneData/panel";
|
|
||||||
import { AddIcon } from "../../icons/ExportCommonIcons";
|
import { AddIcon } from "../../icons/ExportCommonIcons";
|
||||||
import { deletePanelApi } from "../../../services/realTimeVisulization/zoneData/deletePanel";
|
|
||||||
import { useSocketStore } from "../../../store/store";
|
import { useSocketStore } from "../../../store/store";
|
||||||
import { clearPanel } from "../../../services/realTimeVisulization/zoneData/clearPanel";
|
import { clearPanel } from "../../../services/realTimeVisulization/zoneData/clearPanel";
|
||||||
import { lockPanel } from "../../../services/realTimeVisulization/zoneData/lockPanel";
|
import { lockPanel } from "../../../services/realTimeVisulization/zoneData/lockPanel";
|
||||||
@@ -14,6 +12,11 @@ import { lockPanel } from "../../../services/realTimeVisulization/zoneData/lockP
|
|||||||
// Define the type for `Side`
|
// Define the type for `Side`
|
||||||
type Side = "top" | "bottom" | "left" | "right";
|
type Side = "top" | "bottom" | "left" | "right";
|
||||||
|
|
||||||
|
// Define the type for HiddenPanels, where keys are zone IDs and values are arrays of hidden sides
|
||||||
|
interface HiddenPanels {
|
||||||
|
[zoneId: string]: Side[];
|
||||||
|
}
|
||||||
|
|
||||||
// Define the type for the props passed to the Buttons component
|
// Define the type for the props passed to the Buttons component
|
||||||
interface ButtonsProps {
|
interface ButtonsProps {
|
||||||
selectedZone: {
|
selectedZone: {
|
||||||
@@ -37,7 +40,6 @@ interface ButtonsProps {
|
|||||||
zoneName: string;
|
zoneName: string;
|
||||||
activeSides: Side[];
|
activeSides: Side[];
|
||||||
panelOrder: Side[];
|
panelOrder: Side[];
|
||||||
|
|
||||||
lockedPanels: Side[];
|
lockedPanels: Side[];
|
||||||
zoneId: string;
|
zoneId: string;
|
||||||
zoneViewPortTarget: number[];
|
zoneViewPortTarget: number[];
|
||||||
@@ -51,8 +53,8 @@ interface ButtonsProps {
|
|||||||
}[];
|
}[];
|
||||||
}>
|
}>
|
||||||
>;
|
>;
|
||||||
hiddenPanels: Side[]; // Add this prop for hidden panels
|
hiddenPanels: HiddenPanels; // Updated prop type
|
||||||
setHiddenPanels: React.Dispatch<React.SetStateAction<Side[]>>; // Add this prop for updating hidden panels
|
setHiddenPanels: React.Dispatch<React.SetStateAction<HiddenPanels>>; // Updated prop type
|
||||||
}
|
}
|
||||||
|
|
||||||
const AddButtons: React.FC<ButtonsProps> = ({
|
const AddButtons: React.FC<ButtonsProps> = ({
|
||||||
@@ -63,11 +65,33 @@ const AddButtons: React.FC<ButtonsProps> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const { visualizationSocket } = useSocketStore();
|
const { visualizationSocket } = useSocketStore();
|
||||||
|
|
||||||
// Local state to track hidden panels
|
// Function to toggle visibility of a panel
|
||||||
|
const toggleVisibility = (side: Side) => {
|
||||||
|
const isHidden = hiddenPanels[selectedZone.zoneId]?.includes(side) ?? false;
|
||||||
|
|
||||||
|
if (isHidden) {
|
||||||
|
// If the panel is already hidden, remove it from the hiddenPanels array for this zone
|
||||||
|
setHiddenPanels((prevHiddenPanels) => ({
|
||||||
|
...prevHiddenPanels,
|
||||||
|
[selectedZone.zoneId]: prevHiddenPanels[selectedZone.zoneId].filter(
|
||||||
|
(panel) => panel !== side
|
||||||
|
),
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
// If the panel is visible, add it to the hiddenPanels array for this zone
|
||||||
|
setHiddenPanels((prevHiddenPanels) => ({
|
||||||
|
...prevHiddenPanels,
|
||||||
|
[selectedZone.zoneId]: [
|
||||||
|
...(prevHiddenPanels[selectedZone.zoneId] || []),
|
||||||
|
side,
|
||||||
|
],
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Function to toggle lock/unlock a panel
|
// Function to toggle lock/unlock a panel
|
||||||
const toggleLockPanel = async (side: Side) => {
|
const toggleLockPanel = async (side: Side) => {
|
||||||
console.log('side: ', side);
|
// console.log('side: ', side);
|
||||||
const email = localStorage.getItem("email") || "";
|
const email = localStorage.getItem("email") || "";
|
||||||
const organization = email?.split("@")[1]?.split(".")[0]; // Fallback value
|
const organization = email?.split("@")[1]?.split(".")[0]; // Fallback value
|
||||||
//add api
|
//add api
|
||||||
@@ -99,23 +123,10 @@ const AddButtons: React.FC<ButtonsProps> = ({
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Function to toggle visibility of a panel
|
|
||||||
const toggleVisibility = (side: Side) => {
|
|
||||||
|
|
||||||
const isHidden = hiddenPanels.includes(side);
|
|
||||||
if (isHidden) {
|
|
||||||
// If the panel is already hidden, remove it from the hiddenPanels array
|
|
||||||
setHiddenPanels(hiddenPanels.filter((panel) => panel !== side));
|
|
||||||
} else {
|
|
||||||
// If the panel is visible, add it to the hiddenPanels array
|
|
||||||
setHiddenPanels([...hiddenPanels, side]);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Function to clean all widgets from a panel
|
// Function to clean all widgets from a panel
|
||||||
const cleanPanel = async (side: Side) => {
|
const cleanPanel = async (side: Side) => {
|
||||||
//add api
|
//add api
|
||||||
console.log('side: ', side);
|
// console.log('side: ', side);
|
||||||
const email = localStorage.getItem("email") || "";
|
const email = localStorage.getItem("email") || "";
|
||||||
const organization = email?.split("@")[1]?.split(".")[0]; // Fallback value
|
const organization = email?.split("@")[1]?.split(".")[0]; // Fallback value
|
||||||
|
|
||||||
@@ -135,7 +146,7 @@ const AddButtons: React.FC<ButtonsProps> = ({
|
|||||||
widgets: cleanedWidgets,
|
widgets: cleanedWidgets,
|
||||||
};
|
};
|
||||||
// Update the selectedZone state
|
// Update the selectedZone state
|
||||||
console.log('updatedZone: ', updatedZone);
|
// console.log('updatedZone: ', updatedZone);
|
||||||
setSelectedZone(updatedZone);
|
setSelectedZone(updatedZone);
|
||||||
|
|
||||||
// let response = await clearPanel(selectedZone.zoneId, organization, side)
|
// let response = await clearPanel(selectedZone.zoneId, organization, side)
|
||||||
@@ -196,8 +207,6 @@ const AddButtons: React.FC<ButtonsProps> = ({
|
|||||||
//
|
//
|
||||||
// }
|
// }
|
||||||
} else {
|
} else {
|
||||||
setHiddenPanels(hiddenPanels.filter((panel) => panel !== side));
|
|
||||||
|
|
||||||
// Panel does not exist: Create panel
|
// Panel does not exist: Create panel
|
||||||
try {
|
try {
|
||||||
// Get email and organization safely with a default fallback
|
// Get email and organization safely with a default fallback
|
||||||
@@ -234,7 +243,6 @@ const AddButtons: React.FC<ButtonsProps> = ({
|
|||||||
} catch (error) { }
|
} catch (error) { }
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div>
|
<div>
|
||||||
@@ -261,16 +269,21 @@ const AddButtons: React.FC<ButtonsProps> = ({
|
|||||||
<div className="extra-Bs">
|
<div className="extra-Bs">
|
||||||
{/* Hide Panel */}
|
{/* Hide Panel */}
|
||||||
<div
|
<div
|
||||||
className={`icon ${hiddenPanels.includes(side) ? "active" : ""
|
className={`icon ${
|
||||||
}`}
|
hiddenPanels[selectedZone.zoneId]?.includes(side)
|
||||||
|
? "active"
|
||||||
|
: ""
|
||||||
|
}`}
|
||||||
title={
|
title={
|
||||||
hiddenPanels.includes(side) ? "Show Panel" : "Hide Panel"
|
hiddenPanels[selectedZone.zoneId]?.includes(side)
|
||||||
|
? "Show Panel"
|
||||||
|
: "Hide Panel"
|
||||||
}
|
}
|
||||||
onClick={() => toggleVisibility(side)}
|
onClick={() => toggleVisibility(side)}
|
||||||
>
|
>
|
||||||
<EyeIcon
|
<EyeIcon
|
||||||
fill={
|
fill={
|
||||||
hiddenPanels.includes(side)
|
hiddenPanels[selectedZone.zoneId]?.includes(side)
|
||||||
? "var(--primary-color)"
|
? "var(--primary-color)"
|
||||||
: "var(--text-color)"
|
: "var(--text-color)"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -88,12 +88,15 @@ export const DraggableWidget = ({
|
|||||||
|
|
||||||
const chartWidget = useRef<HTMLDivElement>(null);
|
const chartWidget = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
const isPanelHidden = hiddenPanels.includes(widget.panel);
|
OuterClick({
|
||||||
|
contextClassName: [
|
||||||
// OuterClick({
|
"chart-container",
|
||||||
// contextClassName: ["chart-container", "floating", "sidebar-right-wrapper"],
|
"floating",
|
||||||
// setMenuVisible: () => setSelectedChartId(null),
|
"sidebar-right-wrapper",
|
||||||
// });
|
"card",
|
||||||
|
],
|
||||||
|
setMenuVisible: () => setSelectedChartId(null),
|
||||||
|
});
|
||||||
|
|
||||||
const deleteSelectedChart = async () => {
|
const deleteSelectedChart = async () => {
|
||||||
try {
|
try {
|
||||||
@@ -397,4 +400,4 @@ export const DraggableWidget = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// by using canvasDimensions.height canvasDimensions.width dynamically div value insted of static 6 and 4 calculate according to canvasDimensions.width canvasDimensions.height
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ interface PanelProps {
|
|||||||
widgets: Widget[];
|
widgets: Widget[];
|
||||||
}>
|
}>
|
||||||
>;
|
>;
|
||||||
hiddenPanels: string[];
|
hiddenPanels: any;
|
||||||
setZonesData: React.Dispatch<React.SetStateAction<any>>;
|
setZonesData: React.Dispatch<React.SetStateAction<any>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,7 +139,12 @@ const Panel: React.FC<PanelProps> = ({
|
|||||||
const handleDrop = (e: React.DragEvent, panel: Side) => {
|
const handleDrop = (e: React.DragEvent, panel: Side) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const { draggedAsset } = useWidgetStore.getState();
|
const { draggedAsset } = useWidgetStore.getState();
|
||||||
if (!draggedAsset || isPanelLocked(panel)) return;
|
if (
|
||||||
|
!draggedAsset ||
|
||||||
|
isPanelLocked(panel) ||
|
||||||
|
hiddenPanels[selectedZone.zoneId]?.includes(panel)
|
||||||
|
)
|
||||||
|
return;
|
||||||
|
|
||||||
const currentWidgetsCount = getCurrentWidgetCount(panel);
|
const currentWidgetsCount = getCurrentWidgetCount(panel);
|
||||||
const maxCapacity = calculatePanelCapacity(panel);
|
const maxCapacity = calculatePanelCapacity(panel);
|
||||||
@@ -261,7 +266,7 @@ const Panel: React.FC<PanelProps> = ({
|
|||||||
{`
|
{`
|
||||||
:root {
|
:root {
|
||||||
--topWidth: ${topWidth};
|
--topWidth: ${topWidth};
|
||||||
--bottomWidth: ${bottomWidth};
|
--bottomWidth: ${bottomWidth} ;
|
||||||
--leftHeight: ${leftHeight};
|
--leftHeight: ${leftHeight};
|
||||||
--rightHeight: ${rightHeight};
|
--rightHeight: ${rightHeight};
|
||||||
|
|
||||||
@@ -278,7 +283,7 @@ const Panel: React.FC<PanelProps> = ({
|
|||||||
key={side}
|
key={side}
|
||||||
id="panel-wrapper"
|
id="panel-wrapper"
|
||||||
className={`panel ${side}-panel absolute ${
|
className={`panel ${side}-panel absolute ${
|
||||||
hiddenPanels.includes(side) ? "hidePanel" : ""
|
hiddenPanels[selectedZone.zoneId]?.includes(side) ? "hidePanel" : ""
|
||||||
}`}
|
}`}
|
||||||
style={getPanelStyle(side)}
|
style={getPanelStyle(side)}
|
||||||
onDrop={(e) => handleDrop(e, side)}
|
onDrop={(e) => handleDrop(e, side)}
|
||||||
@@ -294,9 +299,11 @@ const Panel: React.FC<PanelProps> = ({
|
|||||||
<div
|
<div
|
||||||
className={`panel-content ${isPlaying && "fullScreen"}`}
|
className={`panel-content ${isPlaying && "fullScreen"}`}
|
||||||
style={{
|
style={{
|
||||||
pointerEvents: selectedZone.lockedPanels.includes(side)
|
pointerEvents:
|
||||||
? "none"
|
selectedZone.lockedPanels.includes(side) ||
|
||||||
: "auto",
|
hiddenPanels[selectedZone.zoneId]?.includes(side)
|
||||||
|
? "none"
|
||||||
|
: "auto",
|
||||||
opacity: selectedZone.lockedPanels.includes(side) ? "0.8" : "1",
|
opacity: selectedZone.lockedPanels.includes(side) ? "0.8" : "1",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -53,8 +53,13 @@ type Widget = {
|
|||||||
data: any;
|
data: any;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Define the type for HiddenPanels, where keys are zone IDs and values are arrays of hidden sides
|
||||||
|
interface HiddenPanels {
|
||||||
|
[zoneId: string]: Side[];
|
||||||
|
}
|
||||||
|
|
||||||
const RealTimeVisulization: React.FC = () => {
|
const RealTimeVisulization: React.FC = () => {
|
||||||
const [hiddenPanels, setHiddenPanels] = React.useState<Side[]>([]);
|
const [hiddenPanels, setHiddenPanels] = React.useState<HiddenPanels>({});
|
||||||
const containerRef = useRef<HTMLDivElement>(null);
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
const { isPlaying } = usePlayButtonStore();
|
const { isPlaying } = usePlayButtonStore();
|
||||||
const { activeModule } = useModuleStore();
|
const { activeModule } = useModuleStore();
|
||||||
@@ -74,7 +79,6 @@ const RealTimeVisulization: React.FC = () => {
|
|||||||
const { widgetSubOption, setWidgetSubOption } = useWidgetSubOption();
|
const { widgetSubOption, setWidgetSubOption } = useWidgetSubOption();
|
||||||
const { visualizationSocket } = useSocketStore();
|
const { visualizationSocket } = useSocketStore();
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function GetZoneData() {
|
async function GetZoneData() {
|
||||||
const email = localStorage.getItem("email") || "";
|
const email = localStorage.getItem("email") || "";
|
||||||
@@ -101,7 +105,7 @@ const RealTimeVisulization: React.FC = () => {
|
|||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
setZonesData(formattedData);
|
setZonesData(formattedData);
|
||||||
} catch (error) { }
|
} catch (error) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
GetZoneData();
|
GetZoneData();
|
||||||
@@ -229,8 +233,6 @@ const RealTimeVisulization: React.FC = () => {
|
|||||||
};
|
};
|
||||||
}, [setRightClickSelected]);
|
}, [setRightClickSelected]);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div
|
<div
|
||||||
@@ -263,7 +265,6 @@ const RealTimeVisulization: React.FC = () => {
|
|||||||
}}
|
}}
|
||||||
onDrop={(event) => handleDrop(event)}
|
onDrop={(event) => handleDrop(event)}
|
||||||
onDragOver={(event) => event.preventDefault()}
|
onDragOver={(event) => event.preventDefault()}
|
||||||
|
|
||||||
>
|
>
|
||||||
<Scene />
|
<Scene />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,55 +1,20 @@
|
|||||||
|
import html2canvas from "html2canvas";
|
||||||
|
|
||||||
export const captureVisualization = async (): Promise<string | null> => {
|
export const captureVisualization = async (): Promise<string | null> => {
|
||||||
const container = document.getElementById("real-time-vis-canvas");
|
const container = document.getElementById("real-time-vis-canvas");
|
||||||
if (!container) return null;
|
if (!container) return null;
|
||||||
|
|
||||||
const canvas = document.createElement("canvas");
|
try {
|
||||||
const ctx = canvas.getContext("2d");
|
// Use html2canvas to capture the container
|
||||||
if (!ctx) return null;
|
const canvas = await html2canvas(container, {
|
||||||
|
scale: 1, // Adjust scale for higher/lower resolution
|
||||||
const rect = container.getBoundingClientRect();
|
|
||||||
canvas.width = rect.width;
|
|
||||||
canvas.height = rect.height;
|
|
||||||
|
|
||||||
// Draw background
|
|
||||||
ctx.fillStyle = getComputedStyle(container).backgroundColor;
|
|
||||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
||||||
|
|
||||||
// Capture all canvas elements
|
|
||||||
const canvases = container.querySelectorAll("canvas");
|
|
||||||
canvases.forEach((childCanvas) => {
|
|
||||||
const childRect = childCanvas.getBoundingClientRect();
|
|
||||||
const x = childRect.left - rect.left;
|
|
||||||
const y = childRect.top - rect.top;
|
|
||||||
ctx.drawImage(childCanvas, x, y, childRect.width, childRect.height);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Capture SVG elements
|
// Convert the canvas to a data URL (PNG format)
|
||||||
const svgs = container.querySelectorAll("svg");
|
const dataUrl = canvas.toDataURL("image/png");
|
||||||
for (const svg of Array.from(svgs)) {
|
return dataUrl;
|
||||||
const svgString = new XMLSerializer().serializeToString(svg);
|
} catch (error) {
|
||||||
const svgBlob = new Blob([svgString], { type: "image/svg+xml" });
|
console.error("Error capturing visualization:", error);
|
||||||
const url = URL.createObjectURL(svgBlob);
|
return null;
|
||||||
|
}
|
||||||
try {
|
|
||||||
const img = await new Promise<HTMLImageElement>((resolve, reject) => {
|
|
||||||
const image = new Image();
|
|
||||||
image.onload = () => resolve(image);
|
|
||||||
image.onerror = reject;
|
|
||||||
image.src = url;
|
|
||||||
});
|
|
||||||
|
|
||||||
const svgRect = svg.getBoundingClientRect();
|
|
||||||
ctx.drawImage(
|
|
||||||
img,
|
|
||||||
svgRect.left - rect.left,
|
|
||||||
svgRect.top - rect.top,
|
|
||||||
svgRect.width,
|
|
||||||
svgRect.height
|
|
||||||
);
|
|
||||||
} finally {
|
|
||||||
URL.revokeObjectURL(url);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return canvas.toDataURL("image/png");
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ export const handleSaveTemplate = async ({
|
|||||||
try {
|
try {
|
||||||
// Check if the selected zone has any widgets
|
// Check if the selected zone has any widgets
|
||||||
if (!selectedZone.panelOrder || selectedZone.panelOrder.length === 0) {
|
if (!selectedZone.panelOrder || selectedZone.panelOrder.length === 0) {
|
||||||
console.warn("No widgets found in the selected zone.");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,8 +64,7 @@ export const handleSaveTemplate = async ({
|
|||||||
floatingWidget,
|
floatingWidget,
|
||||||
widgets3D,
|
widgets3D,
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log('newTemplate: ', newTemplate);
|
|
||||||
// Extract organization from email
|
// Extract organization from email
|
||||||
const email = localStorage.getItem("email") || "";
|
const email = localStorage.getItem("email") || "";
|
||||||
const organization = email.includes("@")
|
const organization = email.includes("@")
|
||||||
@@ -74,7 +72,6 @@ export const handleSaveTemplate = async ({
|
|||||||
: "";
|
: "";
|
||||||
|
|
||||||
if (!organization) {
|
if (!organization) {
|
||||||
console.error("Organization could not be determined from email.");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let saveTemplate = {
|
let saveTemplate = {
|
||||||
@@ -89,13 +86,13 @@ export const handleSaveTemplate = async ({
|
|||||||
try {
|
try {
|
||||||
addTemplate(newTemplate);
|
addTemplate(newTemplate);
|
||||||
// const response = await saveTemplateApi(organization, newTemplate);
|
// const response = await saveTemplateApi(organization, newTemplate);
|
||||||
// console.log("Save API Response:", response);
|
//
|
||||||
|
|
||||||
// Add template only if API call succeeds
|
// Add template only if API call succeeds
|
||||||
} catch (apiError) {
|
} catch (apiError) {
|
||||||
// console.error("Error saving template to API:", apiError);
|
//
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// console.error("Error in handleSaveTemplate:", error);
|
//
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -81,9 +81,12 @@
|
|||||||
|
|
||||||
.returnOfInvestment {
|
.returnOfInvestment {
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
|
min-width: 150px;
|
||||||
|
|
||||||
.charts {
|
.charts {
|
||||||
|
width: 100%;
|
||||||
height: 200px;
|
height: 200px;
|
||||||
|
min-width: 150px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.returns-wrapper {
|
.returns-wrapper {
|
||||||
@@ -126,6 +129,12 @@
|
|||||||
gap: 6px;
|
gap: 6px;
|
||||||
border-radius: 5.2px;
|
border-radius: 5.2px;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
height: 150px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
font-size: $small;
|
font-size: $small;
|
||||||
text-align: start;
|
text-align: start;
|
||||||
|
|||||||
@@ -75,6 +75,7 @@
|
|||||||
max-width: 80%;
|
max-width: 80%;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
max-width: calc(100% - 500px);
|
max-width: calc(100% - 500px);
|
||||||
|
min-width: 150px;
|
||||||
z-index: 3;
|
z-index: 3;
|
||||||
transform: translate(-50%, -10%);
|
transform: translate(-50%, -10%);
|
||||||
|
|
||||||
@@ -363,6 +364,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.panel.hidePanel {
|
.panel.hidePanel {
|
||||||
|
pointer-events: none;
|
||||||
opacity: 0.1;
|
opacity: 0.1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user