added socket for chart
This commit is contained in:
parent
932ab54631
commit
507698a96e
File diff suppressed because it is too large
Load Diff
|
@ -65,6 +65,7 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^22.9.1",
|
"@types/node": "^22.9.1",
|
||||||
"@types/three": "^0.169.0",
|
"@types/three": "^0.169.0",
|
||||||
|
"axios": "^1.8.4",
|
||||||
"cypress": "^13.14.2",
|
"cypress": "^13.14.2",
|
||||||
"dotenv": "^16.4.5",
|
"dotenv": "^16.4.5",
|
||||||
"husky": "^9.1.6",
|
"husky": "^9.1.6",
|
||||||
|
|
|
@ -0,0 +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>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LineGrapInput
|
|
@ -0,0 +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>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PieChartInput
|
|
@ -2,6 +2,7 @@ import { useEffect, useState } from "react";
|
||||||
import { useWidgetStore } from "../../../../../store/useWidgetStore";
|
import { useWidgetStore } from "../../../../../store/useWidgetStore";
|
||||||
import { AddIcon, RemoveIcon } from "../../../../icons/ExportCommonIcons";
|
import { AddIcon, RemoveIcon } from "../../../../icons/ExportCommonIcons";
|
||||||
import MultiLevelDropDown from "../../../../ui/inputs/MultiLevelDropDown";
|
import MultiLevelDropDown from "../../../../ui/inputs/MultiLevelDropDown";
|
||||||
|
import LineGrapInput from "../IotInputCards/LineGrapInput";
|
||||||
|
|
||||||
// Define the data structure for demonstration purposes
|
// Define the data structure for demonstration purposes
|
||||||
const DATA_STRUCTURE = {
|
const DATA_STRUCTURE = {
|
||||||
|
@ -123,41 +124,10 @@ const Data = () => {
|
||||||
<div className="sideBarHeader">{selectedChartId?.title}</div>
|
<div className="sideBarHeader">{selectedChartId?.title}</div>
|
||||||
)}
|
)}
|
||||||
{/* Render groups dynamically */}
|
{/* Render groups dynamically */}
|
||||||
{chartDataGroups[selectedChartId?.id]?.map((group) => (
|
{
|
||||||
<div key={group.id} className="inputs-wrapper">
|
chartDataGroups[selectedChartId?.id] && <LineGrapInput />
|
||||||
{group.children.map((child, index) => (
|
}
|
||||||
<div key={child.id} className="datas">
|
|
||||||
<div className="datas__label">Input {index + 1}</div>
|
|
||||||
<div className="datas__class">
|
|
||||||
<MultiLevelDropDown data={DATA_STRUCTURE} />
|
|
||||||
{/* Add Icon */}
|
|
||||||
{group.children.length < 7 && (
|
|
||||||
<div
|
|
||||||
className="icon"
|
|
||||||
onClick={() => handleAddClick(group.id)} // Pass groupId to handleAddClick
|
|
||||||
>
|
|
||||||
<AddIcon />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{/* Remove Icon */}
|
|
||||||
|
|
||||||
<span
|
|
||||||
className={`datas__separator ${
|
|
||||||
group.children.length > 1 ? "" : "disable"
|
|
||||||
}`}
|
|
||||||
onClick={(e) => {
|
|
||||||
e.stopPropagation(); // Prevent event bubbling
|
|
||||||
removeChild(group.id, child.id); // Pass groupId and childId to removeChild
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<RemoveIcon />
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
|
|
||||||
{/* Info Box */}
|
{/* Info Box */}
|
||||||
<div className="infoBox">
|
<div className="infoBox">
|
||||||
<span className="infoIcon">i</span>
|
<span className="infoIcon">i</span>
|
||||||
|
@ -173,3 +143,40 @@ const Data = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Data;
|
export default Data;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// {chartDataGroups[selectedChartId?.id]?.map((group) => (
|
||||||
|
// <div key={group.id} className="inputs-wrapper">
|
||||||
|
// {group.children.map((child, index) => (
|
||||||
|
// <div key={child.id} className="datas">
|
||||||
|
// <div className="datas__label">Input {index + 1}</div>
|
||||||
|
// <div className="datas__class">
|
||||||
|
// <MultiLevelDropDown data={DATA_STRUCTURE} />
|
||||||
|
// {/* Add Icon */}
|
||||||
|
// {group.children.length < 7 && (
|
||||||
|
// <div
|
||||||
|
// className="icon"
|
||||||
|
// onClick={() => handleAddClick(group.id)} // Pass groupId to handleAddClick
|
||||||
|
// >
|
||||||
|
// <AddIcon />
|
||||||
|
// </div>
|
||||||
|
// )}
|
||||||
|
// {/* Remove Icon */}
|
||||||
|
|
||||||
|
// <span
|
||||||
|
// className={`datas__separator ${
|
||||||
|
// group.children.length > 1 ? "" : "disable"
|
||||||
|
// }`}
|
||||||
|
// onClick={(e) => {
|
||||||
|
// e.stopPropagation(); // Prevent event bubbling
|
||||||
|
// removeChild(group.id, child.id); // Pass groupId and childId to removeChild
|
||||||
|
// }}
|
||||||
|
// >
|
||||||
|
// <RemoveIcon />
|
||||||
|
// </span>
|
||||||
|
// </div>
|
||||||
|
// </div>
|
||||||
|
// ))}
|
||||||
|
// </div>
|
||||||
|
// ))}
|
||||||
|
|
|
@ -14,7 +14,7 @@ export const DraggableWidget = ({
|
||||||
widget: any;
|
widget: any;
|
||||||
hiddenPanels: string[]; // Array of hidden panel names
|
hiddenPanels: string[]; // Array of hidden panel names
|
||||||
}) => {
|
}) => {
|
||||||
console.log("widget: ", widget.type);
|
// console.log("widget: ", widget.type);
|
||||||
const { selectedChartId, setSelectedChartId } = useWidgetStore();
|
const { selectedChartId, setSelectedChartId } = useWidgetStore();
|
||||||
|
|
||||||
const handlePointerDown = () => {
|
const handlePointerDown = () => {
|
||||||
|
|
|
@ -1,99 +1,213 @@
|
||||||
|
// import React, { useState, useRef, useEffect } from "react";
|
||||||
|
|
||||||
|
// // Dropdown Item Component
|
||||||
|
// const DropdownItem = ({
|
||||||
|
// label,
|
||||||
|
// href,
|
||||||
|
// onClick,
|
||||||
|
// }: {
|
||||||
|
// label: string;
|
||||||
|
// href?: string;
|
||||||
|
// onClick?: () => void;
|
||||||
|
// }) => (
|
||||||
|
// <a
|
||||||
|
// href={href || "#"}
|
||||||
|
// className="dropdown-item"
|
||||||
|
// onClick={(e) => {
|
||||||
|
// e.preventDefault();
|
||||||
|
// onClick?.();
|
||||||
|
// }}
|
||||||
|
// >
|
||||||
|
// {label}
|
||||||
|
// </a>
|
||||||
|
// );
|
||||||
|
|
||||||
|
// // Nested Dropdown Component
|
||||||
|
// const NestedDropdown = ({
|
||||||
|
// label,
|
||||||
|
// children,
|
||||||
|
// onSelect,
|
||||||
|
// }: {
|
||||||
|
// label: string;
|
||||||
|
// children: React.ReactNode;
|
||||||
|
// onSelect: (selectedLabel: string) => void;
|
||||||
|
// }) => {
|
||||||
|
// const [open, setOpen] = useState(false);
|
||||||
|
|
||||||
|
// return (
|
||||||
|
// <div className="nested-dropdown">
|
||||||
|
// {/* Dropdown Trigger */}
|
||||||
|
// <div
|
||||||
|
// className={`dropdown-trigger ${open ? "open" : ""}`}
|
||||||
|
// onClick={() => setOpen(!open)} // Toggle submenu on click
|
||||||
|
// >
|
||||||
|
// {label} <span className="icon">{open ? "▼" : "▶"}</span>
|
||||||
|
// </div>
|
||||||
|
|
||||||
|
// {/* Submenu */}
|
||||||
|
// {open && (
|
||||||
|
// <div className="submenu">
|
||||||
|
// {React.Children.map(children, (child) => {
|
||||||
|
// if (React.isValidElement(child)) {
|
||||||
|
// // Clone the element and pass the `onSelect` prop only if it's expected
|
||||||
|
// return React.cloneElement(child as React.ReactElement<any>, { onSelect });
|
||||||
|
// }
|
||||||
|
// return child; // Return non-element children as-is
|
||||||
|
// })}
|
||||||
|
// </div>
|
||||||
|
// )}
|
||||||
|
// </div>
|
||||||
|
// );
|
||||||
|
// };
|
||||||
|
|
||||||
|
// // Recursive Function to Render Nested Data
|
||||||
|
// const renderNestedData = (
|
||||||
|
// data: Record<string, any>,
|
||||||
|
// onSelect: (selectedLabel: string) => void
|
||||||
|
// ) => {
|
||||||
|
// return Object.entries(data).map(([key, value]) => {
|
||||||
|
// if (typeof value === "object" && !Array.isArray(value)) {
|
||||||
|
// // If the value is an object, render it as a nested dropdown
|
||||||
|
// return (
|
||||||
|
// <NestedDropdown key={key} label={key} onSelect={onSelect}>
|
||||||
|
// {renderNestedData(value, onSelect)}
|
||||||
|
// </NestedDropdown>
|
||||||
|
// );
|
||||||
|
// } else if (Array.isArray(value)) {
|
||||||
|
// // If the value is an array, render each item as a dropdown item
|
||||||
|
// return value.map((item, index) => (
|
||||||
|
// <DropdownItem key={index} label={item} onClick={() => onSelect(item)} />
|
||||||
|
// ));
|
||||||
|
// } else {
|
||||||
|
// // If the value is a simple string, render it as a dropdown item
|
||||||
|
// return (
|
||||||
|
// <DropdownItem key={key} label={value} onClick={() => onSelect(value)} />
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// };
|
||||||
|
|
||||||
|
// // Main Multi-Level Dropdown Component
|
||||||
|
// const MultiLevelDropdown = ({ data }: { data: Record<string, any> }) => {
|
||||||
|
// const [open, setOpen] = useState(false);
|
||||||
|
// const [selectedLabel, setSelectedLabel] = useState("Dropdown trigger");
|
||||||
|
// const dropdownRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
// // Handle outer click to close the dropdown
|
||||||
|
// useEffect(() => {
|
||||||
|
// const handleClickOutside = (event: MouseEvent) => {
|
||||||
|
// if (
|
||||||
|
// dropdownRef.current &&
|
||||||
|
// !dropdownRef.current.contains(event.target as Node)
|
||||||
|
// ) {
|
||||||
|
// setOpen(false);
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
|
||||||
|
// document.addEventListener("mousedown", handleClickOutside);
|
||||||
|
// return () => {
|
||||||
|
// document.removeEventListener("mousedown", handleClickOutside);
|
||||||
|
// };
|
||||||
|
// }, []);
|
||||||
|
|
||||||
|
// // Handle selection of an item
|
||||||
|
// const handleSelect = (selectedLabel: string) => {
|
||||||
|
// setSelectedLabel(selectedLabel); // Update the dropdown trigger text
|
||||||
|
// setOpen(false); // Close the dropdown
|
||||||
|
// };
|
||||||
|
|
||||||
|
// return (
|
||||||
|
// <div className="multi-level-dropdown" ref={dropdownRef}>
|
||||||
|
// {/* Dropdown Trigger Button */}
|
||||||
|
// <button
|
||||||
|
// className={`dropdown-button ${open ? "open" : ""}`}
|
||||||
|
// onClick={() => setOpen(!open)} // Toggle main menu on click
|
||||||
|
// >
|
||||||
|
// {selectedLabel} <span className="icon">▾</span>
|
||||||
|
// </button>
|
||||||
|
|
||||||
|
// {/* Dropdown Menu */}
|
||||||
|
// {open && (
|
||||||
|
// <div className="dropdown-menu">
|
||||||
|
// <div className="dropdown-content">
|
||||||
|
// {renderNestedData(data, handleSelect)}
|
||||||
|
// </div>
|
||||||
|
// </div>
|
||||||
|
// )}
|
||||||
|
// </div>
|
||||||
|
// );
|
||||||
|
// };
|
||||||
|
|
||||||
|
// export default MultiLevelDropdown;
|
||||||
|
|
||||||
import React, { useState, useRef, useEffect } from "react";
|
import React, { useState, useRef, useEffect } from "react";
|
||||||
|
|
||||||
// Dropdown Item Component
|
// Dropdown Item Component
|
||||||
const DropdownItem = ({
|
const DropdownItem = ({
|
||||||
label,
|
label,
|
||||||
href,
|
|
||||||
onClick,
|
onClick,
|
||||||
}: {
|
}: {
|
||||||
label: string;
|
label: string;
|
||||||
href?: string;
|
onClick: () => void;
|
||||||
onClick?: () => void;
|
|
||||||
}) => (
|
}) => (
|
||||||
<a
|
<div className="dropdown-item" onClick={onClick}>
|
||||||
href={href || "#"}
|
|
||||||
className="dropdown-item"
|
|
||||||
onClick={(e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
onClick?.();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{label}
|
{label}
|
||||||
</a>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
// Nested Dropdown Component
|
// Nested Dropdown Component
|
||||||
const NestedDropdown = ({
|
const NestedDropdown = ({
|
||||||
label,
|
label,
|
||||||
children,
|
fields,
|
||||||
onSelect,
|
onSelect,
|
||||||
}: {
|
}: {
|
||||||
label: string;
|
label: string;
|
||||||
children: React.ReactNode;
|
fields: string[];
|
||||||
onSelect: (selectedLabel: string) => void;
|
onSelect: (selectedData: { name: string; fields: string }) => void;
|
||||||
}) => {
|
}) => {
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="nested-dropdown">
|
<div className="nested-dropdown">
|
||||||
{/* Dropdown Trigger */}
|
|
||||||
<div
|
<div
|
||||||
className={`dropdown-trigger ${open ? "open" : ""}`}
|
className={`dropdown-trigger ${open ? "open" : ""}`}
|
||||||
onClick={() => setOpen(!open)} // Toggle submenu on click
|
onClick={() => setOpen(!open)}
|
||||||
>
|
>
|
||||||
{label} <span className="icon">{open ? "▼" : "▶"}</span>
|
{label} <span className="icon">{open ? "▼" : "▶"}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Submenu */}
|
|
||||||
{open && (
|
{open && (
|
||||||
<div className="submenu">
|
<div className="submenu">
|
||||||
{React.Children.map(children, (child) => {
|
{fields.map((field) => (
|
||||||
if (React.isValidElement(child)) {
|
<DropdownItem
|
||||||
// Clone the element and pass the `onSelect` prop only if it's expected
|
key={field}
|
||||||
return React.cloneElement(child as React.ReactElement<any>, { onSelect });
|
label={field}
|
||||||
}
|
onClick={() => onSelect({ name: label, fields: field })}
|
||||||
return child; // Return non-element children as-is
|
/>
|
||||||
})}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Recursive Function to Render Nested Data
|
// Props type for MultiLevelDropdown
|
||||||
const renderNestedData = (
|
interface MultiLevelDropdownProps {
|
||||||
data: Record<string, any>,
|
data: Record<string, any>;
|
||||||
onSelect: (selectedLabel: string) => void
|
onSelect: (selectedData: { name: string; fields: string }) => void;
|
||||||
) => {
|
onUnselect: () => void;
|
||||||
return Object.entries(data).map(([key, value]) => {
|
selectedValue?: { name: string; fields: string };
|
||||||
if (typeof value === "object" && !Array.isArray(value)) {
|
}
|
||||||
// If the value is an object, render it as a nested dropdown
|
|
||||||
return (
|
|
||||||
<NestedDropdown key={key} label={key} onSelect={onSelect}>
|
|
||||||
{renderNestedData(value, onSelect)}
|
|
||||||
</NestedDropdown>
|
|
||||||
);
|
|
||||||
} else if (Array.isArray(value)) {
|
|
||||||
// If the value is an array, render each item as a dropdown item
|
|
||||||
return value.map((item, index) => (
|
|
||||||
<DropdownItem key={index} label={item} onClick={() => onSelect(item)} />
|
|
||||||
));
|
|
||||||
} else {
|
|
||||||
// If the value is a simple string, render it as a dropdown item
|
|
||||||
return (
|
|
||||||
<DropdownItem key={key} label={value} onClick={() => onSelect(value)} />
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// Main Multi-Level Dropdown Component
|
// Main Multi-Level Dropdown Component
|
||||||
const MultiLevelDropdown = ({ data }: { data: Record<string, any> }) => {
|
const MultiLevelDropdown = ({
|
||||||
|
data,
|
||||||
|
onSelect,
|
||||||
|
onUnselect,
|
||||||
|
selectedValue
|
||||||
|
}: MultiLevelDropdownProps) => {
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const [selectedLabel, setSelectedLabel] = useState("Dropdown trigger");
|
|
||||||
const dropdownRef = useRef<HTMLDivElement>(null);
|
const dropdownRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
// Handle outer click to close the dropdown
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleClickOutside = (event: MouseEvent) => {
|
const handleClickOutside = (event: MouseEvent) => {
|
||||||
if (
|
if (
|
||||||
|
@ -103,34 +217,51 @@ const MultiLevelDropdown = ({ data }: { data: Record<string, any> }) => {
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
document.addEventListener("mousedown", handleClickOutside);
|
document.addEventListener("mousedown", handleClickOutside);
|
||||||
return () => {
|
return () => {
|
||||||
document.removeEventListener("mousedown", handleClickOutside);
|
document.removeEventListener("mousedown", handleClickOutside);
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// Handle selection of an item
|
// Handle item selection
|
||||||
const handleSelect = (selectedLabel: string) => {
|
const handleItemSelect = (selectedData: { name: string; fields: string }) => {
|
||||||
setSelectedLabel(selectedLabel); // Update the dropdown trigger text
|
onSelect(selectedData);
|
||||||
setOpen(false); // Close the dropdown
|
setOpen(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Handle unselect
|
||||||
|
const handleItemUnselect = () => {
|
||||||
|
onUnselect();
|
||||||
|
setOpen(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Determine the display label
|
||||||
|
const displayLabel = selectedValue
|
||||||
|
? `${selectedValue.name} - ${selectedValue.fields}`
|
||||||
|
: "Dropdown trigger";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="multi-level-dropdown" ref={dropdownRef}>
|
<div className="multi-level-dropdown" ref={dropdownRef}>
|
||||||
{/* Dropdown Trigger Button */}
|
|
||||||
<button
|
<button
|
||||||
className={`dropdown-button ${open ? "open" : ""}`}
|
className={`dropdown-button ${open ? "open" : ""}`}
|
||||||
onClick={() => setOpen(!open)} // Toggle main menu on click
|
onClick={() => setOpen(!open)}
|
||||||
>
|
>
|
||||||
{selectedLabel} <span className="icon">▾</span>
|
{displayLabel} <span className="icon">▾</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{/* Dropdown Menu */}
|
|
||||||
{open && (
|
{open && (
|
||||||
<div className="dropdown-menu">
|
<div className="dropdown-menu">
|
||||||
<div className="dropdown-content">
|
<div className="dropdown-content">
|
||||||
{renderNestedData(data, handleSelect)}
|
{/* Unselect Option */}
|
||||||
|
<DropdownItem label="Unselect" onClick={handleItemUnselect} />
|
||||||
|
{/* Nested Dropdown Items */}
|
||||||
|
{Object.entries(data).map(([key, value]) => (
|
||||||
|
<NestedDropdown
|
||||||
|
key={key}
|
||||||
|
label={key}
|
||||||
|
fields={Object.keys(value)}
|
||||||
|
onSelect={handleItemSelect}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
@ -138,4 +269,5 @@ const MultiLevelDropdown = ({ data }: { data: Record<string, any> }) => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default MultiLevelDropdown;
|
export default MultiLevelDropdown;
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,107 @@
|
||||||
import { useMemo } from "react";
|
// import { useMemo } from "react";
|
||||||
import { Line } from "react-chartjs-2";
|
// import { Line } from "react-chartjs-2";
|
||||||
|
|
||||||
|
// interface ChartComponentProps {
|
||||||
|
// type: any;
|
||||||
|
// title: string;
|
||||||
|
// fontFamily?: string;
|
||||||
|
// fontSize?: string;
|
||||||
|
// fontWeight?: "Light" | "Regular" | "Bold";
|
||||||
|
// data: any;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// const LineGraphComponent = ({
|
||||||
|
// title,
|
||||||
|
// fontFamily,
|
||||||
|
// fontSize,
|
||||||
|
// fontWeight = "Regular",
|
||||||
|
// }: ChartComponentProps) => {
|
||||||
|
// // Memoize Font Weight Mapping
|
||||||
|
// const chartFontWeightMap = useMemo(
|
||||||
|
// () => ({
|
||||||
|
// Light: "lighter" as const,
|
||||||
|
// Regular: "normal" as const,
|
||||||
|
// Bold: "bold" as const,
|
||||||
|
// }),
|
||||||
|
// []
|
||||||
|
// );
|
||||||
|
|
||||||
|
// // Parse and Memoize Font Size
|
||||||
|
// const fontSizeValue = useMemo(
|
||||||
|
// () => (fontSize ? parseInt(fontSize) : 12),
|
||||||
|
// [fontSize]
|
||||||
|
// );
|
||||||
|
|
||||||
|
// // Determine and Memoize Font Weight
|
||||||
|
// const fontWeightValue = useMemo(
|
||||||
|
// () => chartFontWeightMap[fontWeight],
|
||||||
|
// [fontWeight, chartFontWeightMap]
|
||||||
|
// );
|
||||||
|
|
||||||
|
// // Memoize Chart Font Style
|
||||||
|
// const chartFontStyle = useMemo(
|
||||||
|
// () => ({
|
||||||
|
// family: fontFamily || "Arial",
|
||||||
|
// size: fontSizeValue,
|
||||||
|
// weight: fontWeightValue,
|
||||||
|
// }),
|
||||||
|
// [fontFamily, fontSizeValue, fontWeightValue]
|
||||||
|
// );
|
||||||
|
|
||||||
|
// const options = useMemo(
|
||||||
|
// () => ({
|
||||||
|
// responsive: true,
|
||||||
|
// maintainAspectRatio: false,
|
||||||
|
// plugins: {
|
||||||
|
// title: {
|
||||||
|
// display: true,
|
||||||
|
// text: title,
|
||||||
|
// font: chartFontStyle,
|
||||||
|
// },
|
||||||
|
// legend: {
|
||||||
|
// display: false,
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// scales: {
|
||||||
|
// x: {
|
||||||
|
// ticks: {
|
||||||
|
// display: true, // This hides the x-axis labels
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// }),
|
||||||
|
// [title, chartFontStyle]
|
||||||
|
// );
|
||||||
|
|
||||||
|
// const chartData = {
|
||||||
|
// labels: ["January", "February", "March", "April", "May", "June", "July"],
|
||||||
|
// datasets: [
|
||||||
|
// {
|
||||||
|
// label: "My First Dataset",
|
||||||
|
// data: [65, 59, 80, 81, 56, 55, 40],
|
||||||
|
// backgroundColor: "#6f42c1", // Updated to #6f42c1 (Purple)
|
||||||
|
// borderColor: "#ffffff", // Keeping border color white
|
||||||
|
// borderWidth: 2,
|
||||||
|
// fill: false,
|
||||||
|
// },
|
||||||
|
// ],
|
||||||
|
// };
|
||||||
|
|
||||||
|
// return <Line data={chartData} options={options} />;
|
||||||
|
// };
|
||||||
|
|
||||||
|
// export default LineGraphComponent;
|
||||||
|
|
||||||
|
|
||||||
|
import React, { useEffect, useRef, useMemo, useState } from "react";
|
||||||
|
import { Chart } from "chart.js/auto";
|
||||||
|
import { useThemeStore } from "../../../../store/useThemeStore";
|
||||||
|
import io from "socket.io-client";
|
||||||
|
import { Line } from 'react-chartjs-2';
|
||||||
|
import useChartStore from "../../../../store/useChartStore";
|
||||||
|
|
||||||
|
// WebSocket Connection
|
||||||
|
// const socket = io("http://localhost:5000"); // Adjust to your backend URL
|
||||||
|
|
||||||
interface ChartComponentProps {
|
interface ChartComponentProps {
|
||||||
type: any;
|
type: any;
|
||||||
|
@ -11,86 +113,153 @@ interface ChartComponentProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
const LineGraphComponent = ({
|
const LineGraphComponent = ({
|
||||||
|
type,
|
||||||
title,
|
title,
|
||||||
fontFamily,
|
fontFamily,
|
||||||
fontSize,
|
fontSize,
|
||||||
fontWeight = "Regular",
|
fontWeight = "Regular",
|
||||||
|
data,
|
||||||
}: ChartComponentProps) => {
|
}: ChartComponentProps) => {
|
||||||
// Memoize Font Weight Mapping
|
const canvasRef = useRef<HTMLCanvasElement>(null);
|
||||||
const chartFontWeightMap = useMemo(
|
const { themeColor } = useThemeStore();
|
||||||
() => ({
|
const [chartData, setChartData] = useState<{ labels: string[]; datasets: any[] }>({
|
||||||
Light: "lighter" as const,
|
labels: [],
|
||||||
Regular: "normal" as const,
|
datasets: [],
|
||||||
Bold: "bold" as const,
|
});
|
||||||
}),
|
|
||||||
[]
|
|
||||||
);
|
|
||||||
|
|
||||||
// Parse and Memoize Font Size
|
// Memoize Theme Colors to Prevent Unnecessary Recalculations
|
||||||
const fontSizeValue = useMemo(
|
const buttonActionColor = useMemo(
|
||||||
() => (fontSize ? parseInt(fontSize) : 12),
|
() => themeColor[0] || "#5c87df",
|
||||||
[fontSize]
|
[themeColor]
|
||||||
);
|
);
|
||||||
|
const buttonAbortColor = useMemo(
|
||||||
// Determine and Memoize Font Weight
|
() => themeColor[1] || "#ffffff",
|
||||||
const fontWeightValue = useMemo(
|
[themeColor]
|
||||||
() => chartFontWeightMap[fontWeight],
|
);
|
||||||
[fontWeight, chartFontWeightMap]
|
|
||||||
);
|
// Memoize Font Weight Mapping
|
||||||
|
const chartFontWeightMap = useMemo(
|
||||||
// Memoize Chart Font Style
|
() => ({
|
||||||
const chartFontStyle = useMemo(
|
Light: "lighter" as const,
|
||||||
() => ({
|
Regular: "normal" as const,
|
||||||
family: fontFamily || "Arial",
|
Bold: "bold" as const,
|
||||||
size: fontSizeValue,
|
}),
|
||||||
weight: fontWeightValue,
|
[]
|
||||||
}),
|
);
|
||||||
[fontFamily, fontSizeValue, fontWeightValue]
|
|
||||||
);
|
// Parse and Memoize Font Size
|
||||||
|
const fontSizeValue = useMemo(
|
||||||
const options = useMemo(
|
() => (fontSize ? parseInt(fontSize) : 12),
|
||||||
() => ({
|
[fontSize]
|
||||||
responsive: true,
|
);
|
||||||
maintainAspectRatio: false,
|
|
||||||
plugins: {
|
// Determine and Memoize Font Weight
|
||||||
title: {
|
const fontWeightValue = useMemo(
|
||||||
display: true,
|
() => chartFontWeightMap[fontWeight],
|
||||||
text: title,
|
[fontWeight, chartFontWeightMap]
|
||||||
font: chartFontStyle,
|
);
|
||||||
},
|
|
||||||
legend: {
|
// Memoize Chart Font Style
|
||||||
display: false,
|
const chartFontStyle = useMemo(
|
||||||
},
|
() => ({
|
||||||
},
|
family: fontFamily || "Arial",
|
||||||
scales: {
|
size: fontSizeValue,
|
||||||
x: {
|
weight: fontWeightValue,
|
||||||
ticks: {
|
}),
|
||||||
display: true, // This hides the x-axis labels
|
[fontFamily, fontSizeValue, fontWeightValue]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Memoize Chart Data
|
||||||
|
// const data = useMemo(() => propsData, [propsData]);
|
||||||
|
|
||||||
|
// Memoize Chart Options
|
||||||
|
const options = useMemo(
|
||||||
|
() => ({
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
plugins: {
|
||||||
|
title: {
|
||||||
|
display: true,
|
||||||
|
text: title,
|
||||||
|
font: chartFontStyle,
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
display: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
scales: {
|
||||||
}),
|
x: {
|
||||||
[title, chartFontStyle]
|
ticks: {
|
||||||
);
|
display: true, // This hides the x-axis labels
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
[title, chartFontStyle]
|
||||||
|
);
|
||||||
|
|
||||||
const chartData = {
|
const { measurements, setMeasurements, updateDuration, duration } = useChartStore();
|
||||||
labels: ["January", "February", "March", "April", "May", "June", "July"],
|
|
||||||
datasets: [
|
useEffect(() => {
|
||||||
{
|
if ( measurements.length > 0 ) {
|
||||||
label: "My First Dataset",
|
const socket = io("http://192.168.0.192:5010");
|
||||||
data: [65, 59, 80, 81, 56, 55, 40],
|
|
||||||
backgroundColor: "#6f42c1", // Updated to #6f42c1 (Purple)
|
var inputes = {
|
||||||
borderColor: "#ffffff", // Keeping border color white
|
measurements: measurements,
|
||||||
|
duration: duration,
|
||||||
|
interval: 1000,
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('graphHHHHHHHHHHHHHHHHHHHHHHHHHHHHH',inputes);
|
||||||
|
|
||||||
|
|
||||||
|
// Start stream
|
||||||
|
const startStream = () => {
|
||||||
|
socket.emit("lineInput", inputes);
|
||||||
|
}
|
||||||
|
|
||||||
|
socket.on('connect', startStream);
|
||||||
|
|
||||||
|
socket.on("lineOutput", (response) => {
|
||||||
|
const responceData = response.data;
|
||||||
|
console.log("Received data:", responceData);
|
||||||
|
|
||||||
|
// 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,
|
borderWidth: 2,
|
||||||
fill: false,
|
// fill: false,
|
||||||
},
|
}));
|
||||||
],
|
|
||||||
};
|
setChartData({ labels, datasets });
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
socket.off("lineOutput");
|
||||||
|
socket.emit("stop_stream"); // Stop streaming when component unmounts
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, [measurements, duration]);
|
||||||
|
|
||||||
|
// useEffect(() => {
|
||||||
|
// if (!canvasRef.current) return;
|
||||||
|
// const ctx = canvasRef.current.getContext("2d");
|
||||||
|
// if (!ctx) return;
|
||||||
|
|
||||||
|
// const chart = new Chart(ctx, {
|
||||||
|
// type,
|
||||||
|
// data: chartData,
|
||||||
|
// options: options,
|
||||||
|
// });
|
||||||
|
|
||||||
|
// return () => chart.destroy();
|
||||||
|
// }, [chartData, type, title]);
|
||||||
|
|
||||||
return <Line data={chartData} options={options} />;
|
return <Line data={chartData} options={options} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default LineGraphComponent;
|
export default LineGraphComponent;
|
||||||
|
|
||||||
|
|
||||||
// like this
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
import { create } from "zustand";
|
||||||
|
|
||||||
|
interface Measurement {
|
||||||
|
name: string;
|
||||||
|
fields: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MeasurementStore {
|
||||||
|
measurements: Measurement[];
|
||||||
|
interval: number;
|
||||||
|
duration: string;
|
||||||
|
setMeasurements: (newMeasurements: Measurement[]) => void;
|
||||||
|
updateDuration: (newDuration: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const useChartStore = create<MeasurementStore>((set) => ({
|
||||||
|
measurements: [],
|
||||||
|
interval: 1000,
|
||||||
|
duration: "1h",
|
||||||
|
|
||||||
|
setMeasurements: (newMeasurements) =>
|
||||||
|
set(() => ({ measurements: newMeasurements })),
|
||||||
|
|
||||||
|
updateDuration: (newDuration) =>
|
||||||
|
set(() => ({ duration: newDuration })),
|
||||||
|
}));
|
||||||
|
|
||||||
|
export default useChartStore;
|
Loading…
Reference in New Issue