realTimeVisulization #5

Merged
Vishnu merged 15 commits from realTimeVisulization into main 2025-03-26 08:43:57 +00:00
29 changed files with 46078 additions and 23246 deletions

66133
app/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -3,6 +3,8 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@dnd-kit/core": "^6.3.1",
"@dnd-kit/sortable": "^10.0.0",
"@react-three/csg": "^3.2.0",
"@react-three/drei": "^9.113.0",
"@react-three/fiber": "^8.17.7",

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

View File

@ -0,0 +1,132 @@
export function StarsIcon() {
return (
<svg
width="20"
height="20"
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M9.39075 3.67726C9.58367 3.24444 9.68017 3.02804 9.8145 2.96139C9.93117 2.90349 10.0682 2.90349 10.1848 2.96139C10.3192 3.02804 10.4157 3.24444 10.6086 3.67726L12.1453 7.1247C12.2023 7.25265 12.2308 7.31663 12.275 7.36562C12.314 7.40897 12.3618 7.44364 12.415 7.46738C12.4753 7.49421 12.5449 7.50156 12.6843 7.51626L16.4378 7.91244C16.9091 7.96217 17.1447 7.98704 17.2496 8.09419C17.3407 8.18727 17.383 8.3176 17.364 8.44647C17.3422 8.5948 17.1662 8.75339 16.8142 9.07064L14.0103 11.5975C13.9063 11.6912 13.8542 11.7381 13.8213 11.7952C13.7921 11.8458 13.7738 11.9019 13.7678 11.9599C13.7608 12.0255 13.7753 12.094 13.8044 12.2311L14.5876 15.9233C14.6859 16.3869 14.7351 16.6186 14.6656 16.7515C14.6052 16.867 14.4943 16.9475 14.3659 16.9692C14.2181 16.9942 14.0128 16.8759 13.6023 16.6391L10.3328 14.7533C10.2114 14.6833 10.1508 14.6484 10.0863 14.6346C10.0292 14.6226 9.97017 14.6226 9.91309 14.6346C9.84859 14.6484 9.78792 14.6833 9.66659 14.7533L6.39702 16.6391C5.98654 16.8759 5.7813 16.9942 5.63345 16.9692C5.50503 16.9475 5.39416 16.867 5.3338 16.7515C5.2643 16.6186 5.31346 16.3869 5.41179 15.9233L6.19492 12.2311C6.22399 12.094 6.23852 12.0255 6.23162 11.9599C6.22551 11.9019 6.20729 11.8458 6.17812 11.7952C6.14516 11.7381 6.09313 11.6912 5.98907 11.5975L3.18522 9.07064C2.83321 8.75339 2.6572 8.5948 2.63532 8.44647C2.61632 8.3176 2.65866 8.18727 2.74978 8.09419C2.85468 7.98704 3.0903 7.96217 3.56155 7.91244L7.31513 7.51626C7.45445 7.50156 7.5241 7.49421 7.58433 7.46738C7.63762 7.44364 7.68534 7.40897 7.72439 7.36562C7.76851 7.31663 7.79703 7.25265 7.85407 7.1247L9.39075 3.67726Z"
stroke="#595965"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
);
}
export function DownloadIcon() {
return (
<svg
width="20"
height="20"
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M2.5 11.875C2.84518 11.875 3.125 12.1548 3.125 12.5C3.125 13.6962 3.12633 14.5304 3.21096 15.1599C3.29317 15.7714 3.44354 16.0952 3.67418 16.3258C3.90481 16.5565 4.22863 16.7068 4.8401 16.7891C5.46956 16.8737 6.30383 16.875 7.5 16.875H12.5C13.6962 16.875 14.5304 16.8737 15.1599 16.7891C15.7714 16.7068 16.0952 16.5565 16.3258 16.3258C16.5565 16.0952 16.7068 15.7714 16.7891 15.1599C16.8737 14.5304 16.875 13.6962 16.875 12.5C16.875 12.1548 17.1548 11.875 17.5 11.875C17.8452 11.875 18.125 12.1548 18.125 12.5V12.5458C18.125 13.6854 18.125 14.604 18.0279 15.3265C17.9271 16.0766 17.7113 16.7081 17.2097 17.2097C16.7081 17.7113 16.0766 17.9271 15.3265 18.0279C14.604 18.125 13.6854 18.125 12.5458 18.125H7.45428C6.31462 18.125 5.39602 18.125 4.67354 18.0279C3.92345 17.9271 3.29189 17.7113 2.79029 17.2097C2.28869 16.7081 2.07295 16.0766 1.9721 15.3265C1.87497 14.604 1.87498 13.6854 1.875 12.5458C1.875 12.5305 1.875 12.5152 1.875 12.5C1.875 12.1548 2.15483 11.875 2.5 11.875Z"
fill="#FCFDFD"
/>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M10.0003 13.9583C10.1758 13.9583 10.3432 13.8846 10.4616 13.7551L13.7949 10.1093C14.0278 9.8545 14.0102 9.45917 13.7554 9.22625C13.5007 8.99333 13.1053 9.011 12.8724 9.26575L10.6253 11.7235V2.5C10.6253 2.15483 10.3455 1.875 10.0003 1.875C9.65516 1.875 9.37533 2.15483 9.37533 2.5V11.7235L7.12827 9.26575C6.89535 9.011 6.50002 8.99333 6.24527 9.22625C5.99052 9.45917 5.97281 9.8545 6.20573 10.1093L9.53908 13.7551C9.65749 13.8846 9.82483 13.9583 10.0003 13.9583Z"
fill="#FCFDFD"
/>
</svg>
);
}
export function EyeIconBig() {
return (
<svg
width="22"
height="22"
viewBox="0 0 22 22"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M2.75 11.918C6.05 4.58464 15.95 4.58464 19.25 11.918"
stroke="#2B3344"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M11 15.5859C10.6389 15.5859 10.2813 15.5148 9.94762 15.3766C9.61398 15.2384 9.31082 15.0358 9.05546 14.7805C8.80009 14.5251 8.59753 14.222 8.45933 13.8883C8.32113 13.5547 8.25 13.1971 8.25 12.8359C8.25 12.4748 8.32113 12.1172 8.45933 11.7836C8.59753 11.4499 8.80009 11.1468 9.05546 10.8914C9.31082 10.636 9.61398 10.4335 9.94762 10.2953C10.2813 10.1571 10.6389 10.0859 11 10.0859C11.7293 10.0859 12.4288 10.3757 12.9445 10.8914C13.4603 11.4071 13.75 12.1066 13.75 12.8359C13.75 13.5653 13.4603 14.2648 12.9445 14.7805C12.4288 15.2962 11.7293 15.5859 11 15.5859Z"
stroke="#2B3344"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
);
}
export function CommentsIcon() {
return (
<svg
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g clip-path="url(#clip0_2926_17620)">
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M8 13C7.416 13 6.852 12.932 6.31 12.8165L3.956 14.2315L3.9875 11.912C2.183 10.827 1 9.033 1 7C1 3.6865 4.134 1 8 1C11.866 1 15 3.6865 15 7C15 10.314 11.866 13 8 13ZM8 0C3.582 0 0 3.1345 0 7C0 9.2095 1.1725 11.177 3 12.4595V16L6.5045 13.8735C6.9895 13.9535 7.4885 14 8 14C12.418 14 16 10.866 16 7C16 3.1345 12.418 0 8 0ZM11.5 5.5H4.5C4.224 5.5 4 5.724 4 6C4 6.2765 4.224 6.5 4.5 6.5H11.5C11.776 6.5 12 6.2765 12 6C12 5.724 11.776 5.5 11.5 5.5ZM10.5 8.5H5.5C5.224 8.5 5 8.7235 5 9C5 9.2765 5.224 9.5 5.5 9.5H10.5C10.776 9.5 11 9.2765 11 9C11 8.7235 10.776 8.5 10.5 8.5Z"
fill="#2B3344"
/>
</g>
<defs>
<clipPath id="clip0_2926_17620">
<rect width="16" height="16" fill="white" />
</clipPath>
</defs>
</svg>
);
}
export function VerifiedIcon() {
return (
<svg
width="12"
height="13"
viewBox="0 0 12 13"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M4.7962 2.10014C4.67444 2.2039 4.61356 2.25579 4.54853 2.29937C4.39948 2.39927 4.23209 2.46861 4.05605 2.50336C3.97926 2.51853 3.89952 2.52489 3.74004 2.53761C3.33935 2.56959 3.139 2.58558 2.97186 2.64462C2.58526 2.78117 2.28117 3.08526 2.14462 3.47186C2.08558 3.639 2.06959 3.83935 2.03761 4.24004C2.02489 4.39952 2.01853 4.47926 2.00336 4.55605C1.96861 4.73209 1.89927 4.89948 1.79937 5.04853C1.75579 5.11356 1.70391 5.17444 1.60014 5.2962C1.33942 5.60215 1.20905 5.7551 1.13261 5.91505C0.955796 6.285 0.955796 6.715 1.13261 7.08495C1.20906 7.2449 1.33942 7.39785 1.60014 7.7038C1.70389 7.82555 1.75579 7.88645 1.79937 7.95145C1.89927 8.1005 1.96861 8.2679 2.00336 8.44395C2.01853 8.52075 2.02489 8.6005 2.03761 8.75995C2.06959 9.16065 2.08558 9.361 2.14462 9.52815C2.28117 9.91475 2.58526 10.2189 2.97186 10.3554C3.139 10.4144 3.33935 10.4304 3.74004 10.4624C3.89952 10.4751 3.97926 10.4815 4.05605 10.4966C4.23209 10.5314 4.39948 10.6007 4.54853 10.7007C4.61356 10.7442 4.67444 10.7961 4.7962 10.8998C5.10215 11.1606 5.2551 11.2909 5.41505 11.3674C5.785 11.5442 6.215 11.5442 6.58495 11.3674C6.7449 11.2909 6.89785 11.1606 7.2038 10.8998C7.32555 10.7961 7.38645 10.7442 7.45145 10.7007C7.6005 10.6007 7.7679 10.5314 7.94395 10.4966C8.02075 10.4815 8.1005 10.4751 8.25995 10.4624C8.66065 10.4304 8.861 10.4144 9.02815 10.3554C9.41475 10.2189 9.71885 9.91475 9.8554 9.52815C9.9144 9.361 9.9304 9.16065 9.9624 8.75995C9.9751 8.6005 9.9815 8.52075 9.99665 8.44395C10.0314 8.2679 10.1007 8.1005 10.2007 7.95145C10.2442 7.88645 10.2961 7.82555 10.3998 7.7038C10.6606 7.39785 10.7909 7.2449 10.8674 7.08495C11.0442 6.715 11.0442 6.285 10.8674 5.91505C10.7909 5.7551 10.6606 5.60215 10.3998 5.2962C10.2961 5.17444 10.2442 5.11356 10.2007 5.04853C10.1007 4.89948 10.0314 4.73209 9.99665 4.55605C9.9815 4.47926 9.9751 4.39952 9.9624 4.24004C9.9304 3.83935 9.9144 3.639 9.8554 3.47186C9.71885 3.08526 9.41475 2.78117 9.02815 2.64462C8.861 2.58558 8.66065 2.56959 8.25995 2.53761C8.1005 2.52489 8.02075 2.51853 7.94395 2.50336C7.7679 2.46861 7.6005 2.39927 7.45145 2.29937C7.38645 2.25579 7.32555 2.20391 7.2038 2.10014C6.89785 1.83942 6.7449 1.70906 6.58495 1.63261C6.215 1.4558 5.785 1.4558 5.41505 1.63261C5.2551 1.70905 5.10215 1.83942 4.7962 2.10014ZM8.18675 5.43157C8.34565 5.27265 8.34565 5.015 8.18675 4.85608C8.02785 4.69717 7.77015 4.69717 7.61125 4.85608L5.18615 7.2812L4.38873 6.4838C4.22982 6.3249 3.97216 6.3249 3.81325 6.4838C3.65433 6.6427 3.65433 6.90035 3.81325 7.0593L4.89839 8.14445C5.0573 8.30335 5.31495 8.30335 5.4739 8.14445L8.18675 5.43157Z"
fill="#6F42C1"
/>
</svg>
);
}
export function StarsIconSmall() {
return (
<svg
width="13"
height="13"
viewBox="0 0 13 13"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M6.43387 2.70558C6.54962 2.44589 6.60752 2.31604 6.68812 2.27606C6.75812 2.24131 6.84032 2.24131 6.91032 2.27606C6.99092 2.31604 7.04882 2.44589 7.16457 2.70558L8.08657 4.77404C8.12082 4.85081 8.13792 4.8892 8.16442 4.91859C8.18782 4.9446 8.21647 4.96541 8.24842 4.97965C8.28457 4.99575 8.32637 5.00016 8.40997 5.00898L10.6621 5.24668C10.9449 5.27652 11.0862 5.29144 11.1492 5.35574C11.2038 5.41158 11.2292 5.48978 11.2178 5.5671C11.2047 5.6561 11.0991 5.75125 10.8879 5.9416L9.20557 7.4577C9.14317 7.51395 9.11192 7.5421 9.09217 7.57635C9.07467 7.6067 9.06372 7.64035 9.06007 7.67515C9.05592 7.7145 9.06462 7.7556 9.08207 7.83785L9.55197 10.0532C9.61097 10.3314 9.64047 10.4704 9.59877 10.5501C9.56252 10.6194 9.49602 10.6677 9.41897 10.6808C9.33027 10.6958 9.20712 10.6248 8.96082 10.4827L6.99907 9.3512C6.92627 9.3092 6.88987 9.28825 6.85117 9.28C6.81692 9.27275 6.78152 9.27275 6.74727 9.28C6.70857 9.28825 6.67217 9.3092 6.59937 9.3512L4.63763 10.4827C4.39134 10.6248 4.26819 10.6958 4.17949 10.6808C4.10243 10.6677 4.03591 10.6194 3.99969 10.5501C3.958 10.4704 3.98749 10.3314 4.04649 10.0532L4.51637 7.83785C4.53381 7.7556 4.54253 7.7145 4.53839 7.67515C4.53472 7.64035 4.52379 7.6067 4.50629 7.57635C4.48651 7.5421 4.45529 7.51395 4.39286 7.4577L2.71055 5.9416C2.49934 5.75125 2.39374 5.6561 2.38061 5.5671C2.36921 5.48978 2.39461 5.41158 2.44928 5.35574C2.51222 5.29144 2.6536 5.27652 2.93635 5.24668L5.18849 5.00898C5.27208 5.00016 5.31387 4.99575 5.35001 4.97965C5.38199 4.96541 5.41062 4.9446 5.43405 4.91859C5.46052 4.8892 5.47763 4.85081 5.51186 4.77404L6.43387 2.70558Z"
stroke="#595965"
stroke-width="0.6"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
);
}

View File

@ -5,7 +5,7 @@ import Widgets3D from "./Widgets3D";
import WidgetsFloating from "./WidgetsFloating";
const Widgets = () => {
const [activeOption, setActiveOption] = useState("Floating");
const [activeOption, setActiveOption] = useState("2D");
const handleToggleClick = (option: string) => {
setActiveOption(option);

View File

@ -9,7 +9,6 @@ const chartTypes: ChartType[] = [
"line",
"pie",
"doughnut",
"radar",
"polarArea",
];

View File

@ -21,11 +21,11 @@ import ZoneProperties from "./properties/ZoneProperties";
const SideBarRight: React.FC = () => {
const { activeModule } = useModuleStore();
const [activeList] = useState("properties");
const { toggleUI } = useToggleStore();
const { selectedActionSphere } = useSelectedActionSphere();
const { subModule, setSubModule } = useSubModuleStore();
const { toggleUI } = useToggleStore();
// Reset subModule whenever activeModule changes
// Reset activeList whenever activeModule changes
useEffect(() => {
if (activeModule !== "simulation") setSubModule("properties");
if (activeModule === "simulation") setSubModule("mechanics");
@ -37,14 +37,14 @@ const SideBarRight: React.FC = () => {
{toggleUI && (
<div className="sidebar-actions-container">
{/* {activeModule === "builder" && ( */}
<div
className={`sidebar-action-list ${
subModule === "properties" ? "active" : ""
}`}
onClick={() => setSubModule("properties")}
>
<PropertiesIcon isActive={subModule === "properties"} />
</div>
<div
className={`sidebar-action-list ${
subModule === "properties" ? "active" : ""
}`}
onClick={() => setSubModule("properties")}
>
<PropertiesIcon isActive={subModule === "properties"} />
</div>
{/* )} */}
{activeModule === "simulation" && (
<>
@ -54,7 +54,7 @@ const SideBarRight: React.FC = () => {
}`}
onClick={() => setSubModule("mechanics")}
>
<MechanicsIcon isActive={subModule === "mechanics"} />
<MechanicsIcon isActive={activeList === "mechanics"} />
</div>
<div
className={`sidebar-action-list ${
@ -62,7 +62,7 @@ const SideBarRight: React.FC = () => {
}`}
onClick={() => setSubModule("simulations")}
>
<SimulationIcon isActive={subModule === "simulations"} />
<SimulationIcon isActive={activeList === "simulations"} />
</div>
<div
className={`sidebar-action-list ${
@ -70,7 +70,7 @@ const SideBarRight: React.FC = () => {
}`}
onClick={() => setSubModule("analysis")}
>
<AnalysisIcon isActive={subModule === "analysis"} />
<AnalysisIcon isActive={activeList === "analysis"} />
</div>
</>
)}
@ -78,7 +78,7 @@ const SideBarRight: React.FC = () => {
)}
{/* process builder */}
{toggleUI &&
subModule === "properties" &&
activeList === "properties" &&
activeModule !== "visualization" && (
<div className="sidebar-right-container">
<div className="sidebar-right-content-container">
@ -114,7 +114,7 @@ const SideBarRight: React.FC = () => {
</div>
</div>
)}
{subModule === "simulations" && (
{activeList === "simulations" && (
<div className="sidebar-right-container">
<div className="sidebar-right-content-container">
<Simulations />

View File

@ -17,7 +17,12 @@ import { handleSaveTemplate } from "../../modules/visualization/handleSaveTempla
import { usePlayButtonStore } from "../../store/usePlayButtonStore";
import useTemplateStore from "../../store/useTemplateStore";
import { useSelectedZoneStore } from "../../store/useZoneStore";
import { useAddAction, useDeleteModels, useSelectedWallItem, useToggleView } from "../../store/store";
import {
useAddAction,
useDeleteModels,
useSelectedWallItem,
useToggleView,
} from "../../store/store";
const Tools: React.FC = () => {
const { templates } = useTemplateStore();
@ -39,7 +44,6 @@ const Tools: React.FC = () => {
const { setAddAction } = useAddAction();
const { setSelectedWallItem } = useSelectedWallItem();
// Reset activeTool whenever activeModule changes
useEffect(() => {
setActiveTool(activeSubTool);
@ -52,8 +56,7 @@ const Tools: React.FC = () => {
setDeleteModels(false);
setAddAction(null);
setToggleView(true);
}
else {
} else {
setToggleView(false);
}
setToggleThreeD(!toggleThreeD);
@ -68,196 +71,225 @@ const Tools: React.FC = () => {
setOpenDrop(false); // Close the dropdown
}
};
const handleEscKeyPress = (event: KeyboardEvent) => {
if (event.key === "Escape") {
setIsPlaying(false); // Set isPlaying to false when Escape key is pressed
}
};
document.addEventListener("mousedown", handleOutsideClick);
document.addEventListener("keydown", handleEscKeyPress); // Listen for ESC key
return () => {
document.removeEventListener("mousedown", handleOutsideClick);
document.removeEventListener("keydown", handleEscKeyPress); // Clean up the event listener
};
}, []);
return (
<div className="tools-container">
<div className="drop-down-icons">
<div className="activeDropicon">
{activeSubTool == "cursor" && (
<div
className={`tool-button ${activeTool === "cursor" ? "active" : ""
}`}
onClick={() => {
setActiveTool("cursor");
}}
>
<CursorIcon isActive={activeTool === "cursor"} />
</div>
)}
{activeSubTool == "free-hand" && (
<div
className={`tool-button ${activeTool === "free-hand" ? "active" : ""
}`}
onClick={() => {
setActiveTool("free-hand");
}}
>
<FreeMoveIcon isActive={activeTool === "free-hand"} />
</div>
)}
{activeModule !== "visualization" && (
<div
className="drop-down-option-button"
ref={dropdownRef}
onClick={() => {
setOpenDrop(!openDrop);
console.log(openDrop);
}}
>
<ArrowIcon />
{openDrop && (
<div className="drop-down-container">
<>
{!isPlaying ? (
<>
<div className="tools-container">
<div className="drop-down-icons">
<div className="activeDropicon">
{activeSubTool == "cursor" && (
<div
className="option-list"
className={`tool-button ${
activeTool === "cursor" ? "active" : ""
}`}
onClick={() => {
setOpenDrop(false);
setActiveTool("cursor");
setActiveSubTool("cursor");
}}
>
<div className="active-option">
{activeSubTool === "cursor" && <TickIcon />}
</div>
<CursorIcon isActive={false} />
<div className="option">Cursor</div>
<CursorIcon isActive={activeTool === "cursor"} />
</div>
)}
{activeSubTool == "free-hand" && (
<div
className={`tool-button ${
activeTool === "free-hand" ? "active" : ""
}`}
onClick={() => {
setActiveTool("free-hand");
}}
>
<FreeMoveIcon isActive={activeTool === "free-hand"} />
</div>
)}
{activeModule !== "visualization" && (
<div
className="drop-down-option-button"
ref={dropdownRef}
onClick={() => {
setOpenDrop(!openDrop);
console.log(openDrop);
}}
>
<ArrowIcon />
{openDrop && (
<div className="drop-down-container">
<div
className="option-list"
onClick={() => {
setOpenDrop(false);
setActiveTool("cursor");
setActiveSubTool("cursor");
}}
>
<div className="active-option">
{activeSubTool === "cursor" && <TickIcon />}
</div>
<CursorIcon isActive={false} />
<div className="option">Cursor</div>
</div>
<div
className="option-list"
onClick={() => {
setOpenDrop(false);
setActiveTool("free-hand");
setActiveSubTool("free-hand");
}}
>
<div className="active-option">
{activeSubTool === "free-hand" && <TickIcon />}
</div>
<FreeMoveIcon isActive={false} />
<div className="option">Free Hand</div>
</div>
</div>
)}
</div>
)}
</div>
</div>
{!toggleThreeD && activeModule === "builder" && (
<>
<div className="split"></div>
<div className="draw-tools">
<div
className={`tool-button ${
activeTool === "draw-wall" ? "active" : ""
}`}
onClick={() => {
setActiveTool("draw-wall");
}}
>
<WallIcon isActive={activeTool === "draw-wall"} />
</div>
<div
className="option-list"
className={`tool-button ${
activeTool === "draw-zone" ? "active" : ""
}`}
onClick={() => {
setOpenDrop(false);
setActiveTool("free-hand");
setActiveSubTool("free-hand");
setActiveTool("draw-zone");
}}
>
<div className="active-option">
{activeSubTool === "free-hand" && <TickIcon />}
</div>
<FreeMoveIcon isActive={false} />
<div className="option">Free Hand</div>
<ZoneIcon isActive={activeTool === "draw-zone"} />
</div>
<div
className={`tool-button ${
activeTool === "draw-aisle" ? "active" : ""
}`}
onClick={() => {
setActiveTool("draw-aisle");
}}
>
<AsileIcon isActive={activeTool === "draw-aisle"} />
</div>
<div
className={`tool-button ${
activeTool === "draw-floor" ? "active" : ""
}`}
onClick={() => {
setActiveTool("draw-floor");
}}
>
<FloorIcon isActive={activeTool === "draw-floor"} />
</div>
</div>
)}
</div>
)}
</div>
</div>
{!toggleThreeD && activeModule === "builder" && (
<>
<div className="split"></div>
<div className="draw-tools">
<div
className={`tool-button ${activeTool === "draw-wall" ? "active" : ""
</>
)}
{activeModule === "simulation" && (
<>
<div className="split"></div>
<div className="draw-tools">
<div
className={`tool-button ${
activeTool === "pen" ? "active" : ""
}`}
onClick={() => {
setActiveTool("pen");
}}
>
<PenIcon isActive={activeTool === "pen"} />
</div>
</div>
</>
)}
{activeModule === "visualization" && (
<>
<div className="split"></div>
<div className="draw-tools">
<div
className={`tool-button`}
onClick={() =>
handleSaveTemplate({
addTemplate,
selectedZone,
templates,
})
}
>
<SaveTemplateIcon isActive={false} />
</div>
</div>
</>
)}
<div className="split"></div>
<div className="general-options">
<div
className={`tool-button ${
activeTool === "comment" ? "active" : ""
}`}
onClick={() => {
setActiveTool("draw-wall");
}}
>
<WallIcon isActive={activeTool === "draw-wall"} />
</div>
<div
className={`tool-button ${activeTool === "draw-zone" ? "active" : ""
onClick={() => {
setActiveTool("comment");
}}
>
<CommentIcon isActive={activeTool === "comment"} />
</div>
<div
className={`tool-button ${
activeTool === "play" ? "active" : ""
}`}
onClick={() => {
setActiveTool("draw-zone");
}}
>
<ZoneIcon isActive={activeTool === "draw-zone"} />
onClick={() => {
setIsPlaying(!isPlaying);
}}
>
<PlayIcon isActive={activeTool === "play"} />
</div>
</div>
<div className="split"></div>
<div
className={`tool-button ${activeTool === "draw-aisle" ? "active" : ""
}`}
onClick={() => {
setActiveTool("draw-aisle");
}}
className={`toggle-threed-button${
toggleThreeD ? " toggled" : ""
}`}
onClick={toggleSwitch}
>
<AsileIcon isActive={activeTool === "draw-aisle"} />
</div>
<div
className={`tool-button ${activeTool === "draw-floor" ? "active" : ""
}`}
onClick={() => {
setActiveTool("draw-floor");
}}
>
<FloorIcon isActive={activeTool === "draw-floor"} />
<div className={`toggle-option${!toggleThreeD ? " active" : ""}`}>
2d
</div>
<div className={`toggle-option${toggleThreeD ? " active" : ""}`}>
3d
</div>
</div>
</div>
</>
) : (
<div className="exitPlay" onClick={() => setIsPlaying(false)}>
X
</div>
)}
{activeModule === "simulation" && (
<>
<div className="split"></div>
<div className="draw-tools">
<div
className={`tool-button ${activeTool === "pen" ? "active" : ""}`}
onClick={() => {
setActiveTool("pen");
}}
>
<PenIcon isActive={activeTool === "pen"} />
</div>
</div>
</>
)}
{activeModule === "visualization" && (
<>
<div className="split"></div>
<div className="draw-tools">
<div
className={`tool-button`}
onClick={() =>
handleSaveTemplate({
addTemplate,
selectedZone,
templates,
})
}
>
<SaveTemplateIcon isActive={false} />
</div>
</div>
</>
)}
<div className="split"></div>
<div className="general-options">
<div
className={`tool-button ${activeTool === "comment" ? "active" : ""}`}
onClick={() => {
setActiveTool("comment");
}}
>
<CommentIcon isActive={activeTool === "comment"} />
</div>
<div
className={`tool-button ${activeTool === "play" ? "active" : ""}`}
onClick={() => {
setActiveTool("play");
setIsPlaying(!isPlaying);
}}
>
<PlayIcon isActive={activeTool === "play"} />
</div>
</div>
<div className="split"></div>
<div
className={`toggle-threed-button${toggleThreeD ? " toggled" : ""}`}
onClick={toggleSwitch}
>
<div className={`toggle-option${!toggleThreeD ? " active" : ""}`}>
2d
</div>
<div className={`toggle-option${toggleThreeD ? " active" : ""}`}>
3d
</div>
</div>
</div>
</>
);
};

View File

@ -1,192 +1,193 @@
import React from "react";
import {
CleanPannel,
EyeIcon,
LockIcon,
} from "../../icons/RealTimeVisulationIcons";
// Define the type for `Side`
type Side = "top" | "bottom" | "left" | "right";
// Define the type for the props passed to the Buttons component
interface ButtonsProps {
selectedZone: {
zoneName: string;
activeSides: Side[];
panelOrder: Side[];
lockedPanels: Side[];
widgets: {
id: string;
type: string;
title: string;
panel: Side;
data: any;
}[];
};
setSelectedZone: React.Dispatch<
React.SetStateAction<{
zoneName: string;
activeSides: Side[];
panelOrder: Side[];
lockedPanels: Side[];
widgets: {
id: string;
type: string;
title: string;
panel: Side;
data: any;
}[];
}>
>;
hiddenPanels: Side[]; // Add this prop for hidden panels
setHiddenPanels: React.Dispatch<React.SetStateAction<Side[]>>; // Add this prop for updating hidden panels
}
const AddButtons: React.FC<ButtonsProps> = ({
selectedZone,
setSelectedZone,
setHiddenPanels,
hiddenPanels,
}) => {
// Local state to track hidden panels
// Function to toggle lock/unlock a panel
const toggleLockPanel = (side: Side) => {
const newLockedPanels = selectedZone.lockedPanels.includes(side)
? selectedZone.lockedPanels.filter((panel) => panel !== side)
: [...selectedZone.lockedPanels, side];
const updatedZone = {
...selectedZone,
lockedPanels: newLockedPanels,
};
// Update the selectedZone state
setSelectedZone(updatedZone);
};
// 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
const cleanPanel = (side: Side) => {
const cleanedWidgets = selectedZone.widgets.filter(
(widget) => widget.panel !== side
);
const updatedZone = {
...selectedZone,
widgets: cleanedWidgets,
};
// Update the selectedZone state
setSelectedZone(updatedZone);
};
// Function to handle "+" button click
const handlePlusButtonClick = (side: Side) => {
if (selectedZone.activeSides.includes(side)) {
// If the panel is already active, remove all widgets and close the panel
const cleanedWidgets = selectedZone.widgets.filter(
(widget) => widget.panel !== side
);
const newActiveSides = selectedZone.activeSides.filter((s) => s !== side);
const updatedZone = {
...selectedZone,
widgets: cleanedWidgets,
activeSides: newActiveSides,
panelOrder: newActiveSides,
};
// Update the selectedZone state
setSelectedZone(updatedZone);
} else {
// If the panel is not active, activate it
const newActiveSides = [...selectedZone.activeSides, side];
const updatedZone = {
...selectedZone,
activeSides: newActiveSides,
panelOrder: newActiveSides,
};
// Update the selectedZone state
setSelectedZone(updatedZone);
}
};
return (
<div>
{(["top", "right", "bottom", "left"] as Side[]).map((side) => (
<div key={side} className={`side-button-container ${side}`}>
{/* "+" Button */}
<button
className={`side-button ${side}`}
onClick={() => handlePlusButtonClick(side)}
title={
selectedZone.activeSides.includes(side)
? `Remove all items and close ${side} panel`
: `Activate ${side} panel`
}
>
+
</button>
{/* Extra Buttons */}
{selectedZone.activeSides.includes(side) && (
<div className="extra-Bs">
{/* Hide Panel */}
<div
className={`icon ${
hiddenPanels.includes(side) ? "active" : ""
}`}
title={
hiddenPanels.includes(side) ? "Show Panel" : "Hide Panel"
}
onClick={() => toggleVisibility(side)}
>
<EyeIcon />
</div>
{/* Clean Panel */}
<div
className="icon"
title="Clean Panel"
onClick={() => cleanPanel(side)}
>
<CleanPannel />
</div>
{/* Lock/Unlock Panel */}
<div
className={`icon ${
selectedZone.lockedPanels.includes(side) ? "active" : ""
}`}
title={
selectedZone.lockedPanels.includes(side)
? "Unlock Panel"
: "Lock Panel"
}
onClick={() => toggleLockPanel(side)}
>
<LockIcon />
</div>
</div>
)}
</div>
))}
</div>
);
};
export default AddButtons;
import React from "react";
import {
CleanPannel,
EyeIcon,
LockIcon,
} from "../../icons/RealTimeVisulationIcons";
// Define the type for `Side`
type Side = "top" | "bottom" | "left" | "right";
// Define the type for the props passed to the Buttons component
interface ButtonsProps {
selectedZone: {
zoneName: string;
activeSides: Side[];
panelOrder: Side[];
lockedPanels: Side[];
widgets: {
id: string;
type: string;
title: string;
panel: Side;
data: any;
}[];
};
setSelectedZone: React.Dispatch<
React.SetStateAction<{
zoneName: string;
activeSides: Side[];
panelOrder: Side[];
lockedPanels: Side[];
widgets: {
id: string;
type: string;
title: string;
panel: Side;
data: any;
}[];
}>
>;
hiddenPanels: Side[]; // Add this prop for hidden panels
setHiddenPanels: React.Dispatch<React.SetStateAction<Side[]>>; // Add this prop for updating hidden panels
}
const AddButtons: React.FC<ButtonsProps> = ({
selectedZone,
setSelectedZone,
setHiddenPanels,
hiddenPanels,
}) => {
// Local state to track hidden panels
// Function to toggle lock/unlock a panel
const toggleLockPanel = (side: Side) => {
const newLockedPanels = selectedZone.lockedPanels.includes(side)
? selectedZone.lockedPanels.filter((panel) => panel !== side)
: [...selectedZone.lockedPanels, side];
const updatedZone = {
...selectedZone,
lockedPanels: newLockedPanels,
};
// Update the selectedZone state
setSelectedZone(updatedZone);
};
// 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
const cleanPanel = (side: Side) => {
const cleanedWidgets = selectedZone.widgets.filter(
(widget) => widget.panel !== side
);
const updatedZone = {
...selectedZone,
widgets: cleanedWidgets,
};
// Update the selectedZone state
setSelectedZone(updatedZone);
};
// Function to handle "+" button click
const handlePlusButtonClick = (side: Side) => {
if (selectedZone.activeSides.includes(side)) {
// If the panel is already active, remove all widgets and close the panel
const cleanedWidgets = selectedZone.widgets.filter(
(widget) => widget.panel !== side
);
const newActiveSides = selectedZone.activeSides.filter((s) => s !== side);
const updatedZone = {
...selectedZone,
widgets: cleanedWidgets,
activeSides: newActiveSides,
panelOrder: newActiveSides,
};
// Update the selectedZone state
console.log('updatedZone: ', updatedZone);
setSelectedZone(updatedZone);
} else {
// If the panel is not active, activate it
const newActiveSides = [...selectedZone.activeSides, side];
const updatedZone = {
...selectedZone,
activeSides: newActiveSides,
panelOrder: newActiveSides,
};
// Update the selectedZone state
console.log('updatedZone: ', updatedZone);
setSelectedZone(updatedZone);
}
};
return (
<div>
{(["top", "right", "bottom", "left"] as Side[]).map((side) => (
<div key={side} className={`side-button-container ${side}`}>
<button
className={`side-button ${side}`}
onClick={() => handlePlusButtonClick(side)}
title={
selectedZone.activeSides.includes(side)
? `Remove all items and close ${side} panel`
: `Activate ${side} panel`
}
>
+
</button>
{/* Extra Buttons */}
{selectedZone.activeSides.includes(side) && (
<div className="extra-Bs">
{/* Hide Panel */}
<div
className={`icon ${hiddenPanels.includes(side) ? "active" : ""
}`}
title={
hiddenPanels.includes(side) ? "Show Panel" : "Hide Panel"
}
onClick={() => toggleVisibility(side)}
>
<EyeIcon
fill={hiddenPanels.includes(side) ? "white" : "#1D1E21"}
/>
</div>
{/* Clean Panel */}
<div
className="icon"
title="Clean Panel"
onClick={() => cleanPanel(side)}
>
<CleanPannel />
</div>
{/* Lock/Unlock Panel */}
<div
className={`icon ${selectedZone.lockedPanels.includes(side) ? "active" : ""
}`}
title={
selectedZone.lockedPanels.includes(side)
? "Unlock Panel"
: "Lock Panel"
}
onClick={() => toggleLockPanel(side)}
>
<LockIcon fill={selectedZone.lockedPanels.includes(side) ? "#ffffff" : "#1D1E21"} />
</div>
</div>
)}
</div>
))}
</div>
);
};
export default AddButtons;

View File

@ -152,21 +152,29 @@ const DisplayZone: React.FC<DisplayZoneProps> = ({
return (
<div
ref={containerRef}
className={`zoon-wrapper ${
selectedZone.activeSides.includes("bottom") && "bottom"
}`}
className={`zoon-wrapper ${selectedZone.activeSides.includes("bottom") && "bottom"
}`}
>
{Object.keys(zonesData).map((zoneName, index) => (
<div
key={index}
className={`zone ${
selectedZone.zoneName === zoneName ? "active" : ""
className={`zone ${selectedZone.zoneName === zoneName ? "active" : ""
}`}
onClick={() => {
console.log('zoneName: ', zoneName);
setSelectedZone({
zoneName,
...zonesData[zoneName],
});
activeSides: zonesData[zoneName].activeSides || [],
panelOrder: zonesData[zoneName].panelOrder || [],
lockedPanels: zonesData[zoneName].lockedPanels || [],
widgets: zonesData[zoneName].widgets || [],
})
// setSelectedZone({
// zoneName,
// ...zonesData[zoneName],
// });
console.log(selectedZone);
}}
>
{zoneName}

View File

@ -1,82 +1,165 @@
import { useWidgetStore } from "../../../store/useWidgetStore";
import PieGraphComponent from "../charts/PieGraphComponent";
import BarGraphComponent from "../charts/BarGraphComponent";
import LineGraphComponent from "../charts/LineGraphComponent";
export const DraggableWidget = ({ widget }: { widget: any }) => {
const { selectedChartId, setSelectedChartId } = useWidgetStore();
const handlePointerDown = () => {
if (selectedChartId?.id !== widget.id) {
setSelectedChartId(widget);
}
};
return (
<>
<div
key={widget.id}
className={`chart-container ${
selectedChartId?.id === widget.id && "activeChart"
}`}
onPointerDown={handlePointerDown}
>
{widget.type === "progress" ? (
// <ProgressCard title={widget.title} data={widget.data} />
<></>
) : (
<>
{widget.type === "line" && (
<LineGraphComponent
type={widget.type}
title={widget.title}
fontSize={widget.fontSize}
fontWeight={widget.fontWeight}
data={{
measurements: [
{ name: "testDevice", fields: "powerConsumption" },
{ name: "furnace", fields: "powerConsumption" },
],
interval: 1000,
duration: "1h",
}}
/>
)}
{widget.type === "bar" && (
<BarGraphComponent
type={widget.type}
title={widget.title}
fontSize={widget.fontSize}
fontWeight={widget.fontWeight}
data={{
measurements: [
{ name: "testDevice", fields: "powerConsumption" },
{ name: "furnace", fields: "powerConsumption" },
],
interval: 1000,
duration: "1h",
}}
/>
)}
{widget.type === "pie" && (
<PieGraphComponent
type={widget.type}
title={widget.title}
fontSize={widget.fontSize}
fontWeight={widget.fontWeight}
data={{
measurements: [
{ name: "testDevice", fields: "powerConsumption" },
{ name: "furnace", fields: "powerConsumption" },
],
interval: 1000,
duration: "1h",
}}
/>
)}
</>
)}
</div>
</>
);
};
import { useWidgetStore } from "../../../store/useWidgetStore";
import ProgressCard from "../realTimeVis/charts/ProgressCard";
import PieGraphComponent from "../realTimeVis/charts/PieGraphComponent";
import BarGraphComponent from "../realTimeVis/charts/BarGraphComponent";
import LineGraphComponent from "../realTimeVis/charts/LineGraphComponent";
import RadarGraphComponent from "../realTimeVis/charts/RadarGraphComponent";
import DoughnutGraphComponent from "../realTimeVis/charts/DoughnutGraphComponent";
import PolarAreaGraphComponent from "../realTimeVis/charts/PolarAreaGraphComponent";
export const DraggableWidget = ({
widget,
hiddenPanels, // Add this prop to track hidden panels
index, onReorder
}: {
widget: any;
hiddenPanels: string[]; // Array of hidden panel names
index: number; onReorder: (fromIndex: number, toIndex: number) => void
}) => {
const { selectedChartId, setSelectedChartId } = useWidgetStore();
const handlePointerDown = () => {
if (selectedChartId?.id !== widget.id) {
setSelectedChartId(widget);
}
};
// Determine if the widget's panel is hidden
const isPanelHidden = hiddenPanels.includes(widget.panel);
const handleDragStart = (event: React.DragEvent<HTMLDivElement>) => {
event.dataTransfer.setData('text/plain', index.toString()); // Store the index of the dragged widget
};
const handleDragEnter = (event: React.DragEvent<HTMLDivElement>) => {
event.preventDefault(); // Allow drop
};
const handleDragOver = (event: React.DragEvent<HTMLDivElement>) => {
event.preventDefault(); // Allow drop
};
const handleDrop = (event: React.DragEvent<HTMLDivElement>) => {
event.preventDefault();
const fromIndex = parseInt(event.dataTransfer.getData('text/plain'), 10); // Get the dragged widget's index
const toIndex = index; // The index of the widget where the drop occurred
if (fromIndex !== toIndex) {
onReorder(fromIndex, toIndex); // Call the reorder function passed as a prop
}
};
return (
<>
<div
draggable
key={widget.id}
className={`chart-container ${selectedChartId?.id === widget.id && "activeChart"
}`}
onPointerDown={handlePointerDown}
onDragStart={handleDragStart}
onDragEnter={handleDragEnter}
onDragOver={handleDragOver}
onDrop={handleDrop}
style={{
opacity: isPanelHidden ? 0 : 1, // Set opacity to 0 if the panel is hidden
pointerEvents: isPanelHidden ? "none" : "auto", // Disable interaction when hidden
}}
>
{/* Render charts based on widget type */}
{widget.type === "progress" && (
<ProgressCard title={widget.title} data={widget.data} />
)}
{widget.type === "line" && (
<LineGraphComponent
type={widget.type}
title={widget.title}
fontSize={widget.fontSize}
fontWeight={widget.fontWeight}
data={{
measurements: [
{ name: "testDevice", fields: "powerConsumption" },
{ name: "furnace", fields: "powerConsumption" },
],
interval: 1000,
duration: "1h",
}}
/>
)}
{widget.type === "bar" && (
<BarGraphComponent
type={widget.type}
title={widget.title}
fontSize={widget.fontSize}
fontWeight={widget.fontWeight}
data={{
measurements: [
{ name: "testDevice", fields: "powerConsumption" },
{ name: "furnace", fields: "powerConsumption" },
],
interval: 1000,
duration: "1h",
}}
/>
)}
{/* {widget.type === "radar" && (
<RadarGraphComponent
type={widget.type}
title={widget.title}
fontSize={widget.fontSize}
fontWeight={widget.fontWeight}
data={widget.data.measurements.map((item: any) => item.fields)}
/>
)} */}
{widget.type === "pie" && (
<PieGraphComponent
type={widget.type}
title={widget.title}
fontSize={widget.fontSize}
fontWeight={widget.fontWeight}
data={{
measurements: [
{ name: "testDevice", fields: "powerConsumption" },
{ name: "furnace", fields: "powerConsumption" },
],
interval: 1000,
duration: "1h",
}}
/>
)}
{widget.type === "doughnut" && (
<DoughnutGraphComponent
type={widget.type}
title={widget.title}
fontSize={widget.fontSize}
fontWeight={widget.fontWeight}
data={{
measurements: [
{ name: "testDevice", fields: "powerConsumption" },
{ name: "furnace", fields: "powerConsumption" },
],
interval: 1000,
duration: "1h",
}}
/>
)}
{widget.type === "polarArea" && (
<PolarAreaGraphComponent
type={widget.type}
title={widget.title}
fontSize={widget.fontSize}
fontWeight={widget.fontWeight}
data={{
measurements: [
{ name: "testDevice", fields: "powerConsumption" },
{ name: "furnace", fields: "powerConsumption" },
],
interval: 1000,
duration: "1h",
}}
/>
)}
</div>
</>
);
};

View File

@ -1,203 +1,245 @@
import React, { useEffect, useMemo, useRef, useState } from "react";
import { useWidgetStore } from "../../../store/useWidgetStore";
import { usePlayButtonStore } from "../../../store/usePlayButtonStore";
import { DraggableWidget } from "./DraggableWidget";
type Side = "top" | "bottom" | "left" | "right";
interface Widget {
id: string;
type: string;
title: string;
panel: Side;
data: any;
}
interface PanelProps {
selectedZone: {
zoneName: string;
activeSides: Side[];
panelOrder: Side[];
lockedPanels: Side[];
widgets: Widget[];
};
setSelectedZone: React.Dispatch<
React.SetStateAction<{
zoneName: string;
activeSides: Side[];
panelOrder: Side[];
lockedPanels: Side[];
widgets: Widget[];
}>
>;
}
const generateUniqueId = () =>
`${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
const Panel: React.FC<PanelProps> = ({ selectedZone, setSelectedZone }) => {
const panelRefs = useRef<{ [side in Side]?: HTMLDivElement }>({});
const [panelDimensions, setPanelDimensions] = useState<{
[side in Side]?: { width: number; height: number };
}>({});
const getPanelStyle = useMemo(
() => (side: Side) => {
const currentIndex = selectedZone.panelOrder.indexOf(side);
const previousPanels = selectedZone.panelOrder.slice(0, currentIndex);
const leftActive = previousPanels.includes("left");
const rightActive = previousPanels.includes("right");
const topActive = previousPanels.includes("top");
const bottomActive = previousPanels.includes("bottom");
switch (side) {
case "top":
case "bottom":
return {
width: `calc(100% - ${
(leftActive ? 204 : 0) + (rightActive ? 204 : 0)
}px)`,
left: leftActive ? "204px" : "0",
right: rightActive ? "204px" : "0",
[side]: "0",
height: "200px",
};
case "left":
case "right":
return {
height: `calc(100% - ${
(topActive ? 204 : 0) + (bottomActive ? 204 : 0)
}px)`,
top: topActive ? "204px" : "0",
bottom: bottomActive ? "204px" : "0",
[side]: "0",
width: "200px",
};
default:
return {};
}
},
[selectedZone.panelOrder]
);
const handleDrop = (e: React.DragEvent, panel: Side) => {
e.preventDefault();
const { draggedAsset } = useWidgetStore.getState();
if (!draggedAsset) return;
if (isPanelLocked(panel)) return;
const currentWidgetsCount = getCurrentWidgetCount(panel);
const maxCapacity = calculatePanelCapacity(panel);
if (currentWidgetsCount >= maxCapacity) return;
addWidgetToPanel(draggedAsset, panel);
};
// Helper functions
const isPanelLocked = (panel: Side) =>
selectedZone.lockedPanels.includes(panel);
const getCurrentWidgetCount = (panel: Side) =>
selectedZone.widgets.filter(w => w.panel === panel).length;
const calculatePanelCapacity = (panel: Side) => {
const CHART_WIDTH = 200;
const CHART_HEIGHT = 200;
const FALLBACK_HORIZONTAL_CAPACITY = 5;
const FALLBACK_VERTICAL_CAPACITY = 3;
const dimensions = panelDimensions[panel];
if (!dimensions) {
return panel === "top" || panel === "bottom"
? FALLBACK_HORIZONTAL_CAPACITY
: FALLBACK_VERTICAL_CAPACITY;
}
return panel === "top" || panel === "bottom"
? Math.floor(dimensions.width / CHART_WIDTH)
: Math.floor(dimensions.height / CHART_HEIGHT);
};
const addWidgetToPanel = (asset: any, panel: Side) => {
const newWidget = {
...asset,
id: generateUniqueId(),
panel,
};
setSelectedZone(prev => ({
...prev,
widgets: [...prev.widgets, newWidget]
}));
};
useEffect(() => {
const observers: ResizeObserver[] = [];
const currentPanelRefs = panelRefs.current;
selectedZone.activeSides.forEach((side) => {
const element = currentPanelRefs[side];
if (element) {
const observer = new ResizeObserver((entries) => {
for (const entry of entries) {
const { width, height } = entry.contentRect;
setPanelDimensions((prev) => ({
...prev,
[side]: { width, height },
}));
}
});
observer.observe(element);
observers.push(observer);
}
});
return () => {
observers.forEach((observer) => observer.disconnect());
};
}, [selectedZone.activeSides]);
const { isPlaying } = usePlayButtonStore();
return (
<>
{selectedZone.activeSides.map((side) => (
<div
key={side}
className={`panel ${side}-panel absolute ${isPlaying && ""}`}
style={getPanelStyle(side)}
onDrop={(e) => handleDrop(e, side)}
onDragOver={(e) => e.preventDefault()}
ref={(el) => {
if (el) {
panelRefs.current[side] = el;
} else {
delete panelRefs.current[side];
}
}}
>
<div
className={`panel-content ${isPlaying && "fullScreen"}`}
style={{
pointerEvents: selectedZone.lockedPanels.includes(side)
? "none"
: "auto",
opacity: selectedZone.lockedPanels.includes(side) ? "0.8" : "1",
}}
>
<>{}</>
{selectedZone.widgets
.filter((w) => w.panel === side)
.map((widget) => (
<DraggableWidget widget={widget} key={widget.id} />
))}
</div>
</div>
))}
</>
);
};
export default Panel;
import React, { useEffect, useMemo, useRef, useState } from "react";
import { useWidgetStore } from "../../../store/useWidgetStore";
import { usePlayButtonStore } from "../../../store/usePlayButtonStore";
import { DraggableWidget } from "./DraggableWidget";
import { arrayMove } from "@dnd-kit/sortable";
type Side = "top" | "bottom" | "left" | "right";
interface Widget {
id: string;
type: string;
title: string;
panel: Side;
data: any;
}
interface PanelProps {
selectedZone: {
zoneName: string;
activeSides: Side[];
panelOrder: Side[];
lockedPanels: Side[];
widgets: Widget[];
};
setSelectedZone: React.Dispatch<
React.SetStateAction<{
zoneName: string;
activeSides: Side[];
panelOrder: Side[];
lockedPanels: Side[];
widgets: Widget[];
}>
>;
hiddenPanels: string[];
}
const generateUniqueId = () =>
`${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
const Panel: React.FC<PanelProps> = ({
selectedZone,
setSelectedZone,
hiddenPanels,
}) => {
const panelRefs = useRef<{ [side in Side]?: HTMLDivElement }>({});
const [panelDimensions, setPanelDimensions] = useState<{
[side in Side]?: { width: number; height: number };
}>({});
const { isPlaying } = usePlayButtonStore();
const getPanelStyle = useMemo(
() => (side: Side) => {
const currentIndex = selectedZone.panelOrder.indexOf(side);
const previousPanels = selectedZone.panelOrder.slice(0, currentIndex);
const leftActive = previousPanels.includes("left");
const rightActive = previousPanels.includes("right");
const topActive = previousPanels.includes("top");
const bottomActive = previousPanels.includes("bottom");
const panelSize = isPlaying ? 300 : 210;
switch (side) {
case "top":
case "bottom":
return {
width: `calc(100% - ${(leftActive ? panelSize : 0) + (rightActive ? panelSize : 0)
}px)`,
height: `${panelSize - 5}px`,
left: leftActive ? `${panelSize}px` : "0",
right: rightActive ? `${panelSize}px` : "0",
[side]: "0",
};
case "left":
case "right":
return {
width: `${panelSize - 5}px`,
height: `calc(100% - ${(topActive ? panelSize : 0) + (bottomActive ? panelSize : 0)
}px)`,
top: topActive ? `${panelSize}px` : "0",
bottom: bottomActive ? `${panelSize}px` : "0",
[side]: "0",
};
default:
return {};
}
},
[selectedZone.panelOrder, isPlaying]
);
const handleDrop = (e: React.DragEvent, panel: Side) => {
e.preventDefault();
const { draggedAsset } = useWidgetStore.getState();
if (!draggedAsset) return;
if (isPanelLocked(panel)) return;
const currentWidgetsCount = getCurrentWidgetCount(panel);
const maxCapacity = calculatePanelCapacity(panel);
if (currentWidgetsCount >= maxCapacity) return;
console.log('draggedAsset: ', draggedAsset);
console.log('panel: ', panel);
addWidgetToPanel(draggedAsset, panel);
};
const isPanelLocked = (panel: Side) =>
selectedZone.lockedPanels.includes(panel);
const getCurrentWidgetCount = (panel: Side) =>
selectedZone.widgets.filter((w) => w.panel === panel).length;
const calculatePanelCapacity = (panel: Side) => {
const CHART_WIDTH = 150;
const CHART_HEIGHT = 150;
const FALLBACK_HORIZONTAL_CAPACITY = 5;
const FALLBACK_VERTICAL_CAPACITY = 3;
const dimensions = panelDimensions[panel];
if (!dimensions) {
return panel === "top" || panel === "bottom"
? FALLBACK_HORIZONTAL_CAPACITY
: FALLBACK_VERTICAL_CAPACITY;
}
return panel === "top" || panel === "bottom"
? Math.floor(dimensions.width / CHART_WIDTH)
: Math.floor(dimensions.height / CHART_HEIGHT);
};
const addWidgetToPanel = (asset: any, panel: Side) => {
const newWidget = {
...asset,
id: generateUniqueId(),
panel,
};
setSelectedZone((prev) => ({
...prev,
widgets: [...prev.widgets, newWidget],
}));
};
useEffect(() => {
const observers: ResizeObserver[] = [];
const currentPanelRefs = panelRefs.current;
selectedZone.activeSides.forEach((side) => {
const element = currentPanelRefs[side];
if (element) {
const observer = new ResizeObserver((entries) => {
for (const entry of entries) {
const { width, height } = entry.contentRect;
setPanelDimensions((prev) => ({
...prev,
[side]: { width, height },
}));
}
});
observer.observe(element);
observers.push(observer);
}
});
return () => {
observers.forEach((observer) => observer.disconnect());
};
}, [selectedZone.activeSides]);
const handleReorder = (fromIndex: number, toIndex: number, panel: Side) => {
if (!selectedZone) return; // Ensure selectedZone is not null
console.log('selectedZone: ', selectedZone);
setSelectedZone((prev) => {
if (!prev) return prev; // Ensure prev is not null
// Filter widgets for the specified panel
const widgetsInPanel = prev.widgets.filter((w) => w.panel === panel);
// Reorder widgets within the same panel
const reorderedWidgets = arrayMove(widgetsInPanel, fromIndex, toIndex);
// Merge the reordered widgets back into the full list while preserving the order
const updatedWidgets = prev.widgets
.filter((widget) => widget.panel !== panel) // Keep widgets from other panels
.concat(reorderedWidgets); // Add the reordered widgets for the specified panel
return {
...prev,
widgets: updatedWidgets,
};
});
};
return (
<>
{selectedZone.activeSides.map((side) => (
<div
key={side}
className={`panel ${side}-panel absolute ${isPlaying && ""}`}
style={getPanelStyle(side)}
onDrop={(e) => handleDrop(e, side)}
onDragOver={(e) => e.preventDefault()}
ref={(el) => {
if (el) {
panelRefs.current[side] = el;
} else {
delete panelRefs.current[side];
}
}}
>
<div
className={`panel-content ${isPlaying && "fullScreen"}`}
style={{
pointerEvents: selectedZone.lockedPanels.includes(side)
? "none"
: "auto",
opacity: selectedZone.lockedPanels.includes(side) ? "0.8" : "1",
}}
>
{selectedZone.widgets
.filter((w) => w.panel === side)
.map((widget, index) => (
<DraggableWidget
hiddenPanels={hiddenPanels}
widget={widget}
key={widget.id}
index={index}
onReorder={(fromIndex, toIndex) =>
handleReorder(fromIndex, toIndex, side)
}
/>
))}
</div>
</div>
))}
</>
);
};
export default Panel;

View File

@ -1,122 +1,147 @@
import React, { useEffect, useState, useRef } from "react";
import { usePlayButtonStore } from "../../../store/usePlayButtonStore";
import Panel from "./Panel";
import AddButtons from "./AddButtons";
import { useSelectedZoneStore } from "../../../store/useZoneStore";
import DisplayZone from "./DisplayZone";
import Scene from "../../../modules/scene/scene";
import useModuleStore from "../../../store/useModuleStore";
type Side = "top" | "bottom" | "left" | "right";
interface Widget {
id: string;
type: string;
title: string;
panel: Side;
data: any;
}
const RealTimeVisulization: React.FC = () => {
const [hiddenPanels, setHiddenPanels] = React.useState<Side[]>([]);
const containerRef = useRef<HTMLDivElement>(null);
const [zonesData, setZonesData] = useState<{
[key: string]: {
activeSides: Side[];
panelOrder: Side[];
lockedPanels: Side[];
widgets: Widget[];
};
}>({
"Manufacturing unit": {
activeSides: [],
panelOrder: [],
lockedPanels: [],
widgets: [],
},
"Assembly unit": {
activeSides: [],
panelOrder: [],
lockedPanels: [],
widgets: [],
},
"Packing unit": {
activeSides: [],
panelOrder: [],
lockedPanels: [],
widgets: [],
},
Warehouse: {
activeSides: [],
panelOrder: [],
lockedPanels: [],
widgets: [],
},
Inventory: {
activeSides: [],
panelOrder: [],
lockedPanels: [],
widgets: [],
},
});
const { isPlaying } = usePlayButtonStore();
const { activeModule } = useModuleStore();
const { selectedZone, setSelectedZone } = useSelectedZoneStore();
useEffect(() => {
setZonesData((prev) => ({
...prev,
[selectedZone.zoneName]: selectedZone,
}));
}, [selectedZone]);
return (
<div
ref={containerRef}
id="real-time-vis-canvas"
className="realTime-viz canvas"
style={{
height: isPlaying || activeModule !== "visualization" ? "100vh" : "",
width: isPlaying || activeModule !== "visualization" ? "100vw" : "",
left: isPlaying || activeModule !== "visualization" ? "0%" : "",
}}
>
<div
className="scene-container"
style={{
height: "100%",
width: "100%",
borderRadius: isPlaying || activeModule !== "visualization" ? "" : "6px",
}}
>
<Scene />
</div>
{activeModule === "visualization" && (
<>
<DisplayZone
zonesData={zonesData}
selectedZone={selectedZone}
setSelectedZone={setSelectedZone}
/>
{!isPlaying && (
<AddButtons
hiddenPanels={hiddenPanels}
setHiddenPanels={setHiddenPanels}
selectedZone={selectedZone}
setSelectedZone={setSelectedZone}
/>
)}
<Panel
selectedZone={selectedZone}
setSelectedZone={setSelectedZone}
/>
</>
)}
</div>
);
};
export default RealTimeVisulization;
import React, { useEffect, useState, useRef } from "react";
import { usePlayButtonStore } from "../../../store/usePlayButtonStore";
import Panel from "./Panel";
import AddButtons from "./AddButtons";
import { useSelectedZoneStore } from "../../../store/useZoneStore";
import DisplayZone from "./DisplayZone";
import Scene from "../../../modules/scene/scene";
import useModuleStore from "../../../store/useModuleStore";
import { getZonesApi } from "../../../services/realTimeVisulization/zoneData/getZones";
type Side = "top" | "bottom" | "left" | "right";
type FormattedZoneData = Record<
string,
{
activeSides: Side[];
panelOrder: Side[];
lockedPanels: Side[];
zoneCentrePoint: number[];
widgets: Widget[];
}
>;
type Widget = {
id: string;
type: string;
title: string;
panel: Side;
data: any;
};
type Zone = {
zoneId: string;
zoneName: string;
points: number[][];
layer: number;
};
const RealTimeVisulization: React.FC = () => {
const [hiddenPanels, setHiddenPanels] = React.useState<Side[]>([]);
const containerRef = useRef<HTMLDivElement>(null);
const { isPlaying } = usePlayButtonStore();
const { activeModule } = useModuleStore();
const [zonesData, setZonesData] = useState<FormattedZoneData>({});
const { selectedZone, setSelectedZone } = useSelectedZoneStore();
useEffect(() => {
async function GetZoneData() {
try {
const response: { data: Zone[] } | undefined = await getZonesApi(
"hexrfactory"
);
if (!response || !response.data) {
return;
}
const formattedData = response?.data?.reduce<FormattedZoneData>(
(acc, zone) => {
acc[zone.zoneName] = {
activeSides: [],
panelOrder: [],
lockedPanels: [],
zoneCentrePoint: [],
widgets: [],
};
return acc;
},
{}
);
setZonesData(formattedData);
} catch (error) { }
}
GetZoneData();
}, []);
useEffect(() => {
console.log('zonesData: ', zonesData);
}, [zonesData]);
useEffect(() => {
setZonesData((prev) => {
if (!selectedZone) return prev;
return {
...prev,
[selectedZone.zoneName]: {
...prev[selectedZone.zoneName], // Keep existing properties
activeSides: selectedZone.activeSides || [],
panelOrder: selectedZone.panelOrder || [],
lockedPanels: selectedZone.lockedPanels || [],
widgets: selectedZone.widgets || [],
},
};
});
}, [selectedZone]);
return (
<div
ref={containerRef}
id="real-time-vis-canvas"
className={`realTime-viz canvas ${isPlaying ? "playingFlase" : ""}`}
style={{
height: isPlaying || activeModule !== "visualization" ? "100vh" : "",
width: isPlaying || activeModule !== "visualization" ? "100vw" : "",
left: isPlaying || activeModule !== "visualization" ? "0%" : "",
}}
>
<div
className="scene-container"
style={{
height: "100%",
width: "100%",
borderRadius: isPlaying || activeModule !== "visualization" ? "" : "6px",
}}
>
<Scene />
</div>
{activeModule === "visualization" && (
<>
<DisplayZone
zonesData={zonesData}
selectedZone={selectedZone}
setSelectedZone={setSelectedZone}
/>
{!isPlaying && (
<AddButtons
hiddenPanels={hiddenPanels}
setHiddenPanels={setHiddenPanels}
selectedZone={selectedZone}
setSelectedZone={setSelectedZone}
/>
)}
<Panel
selectedZone={selectedZone}
setSelectedZone={setSelectedZone}
hiddenPanels={hiddenPanels}
/>
</>
)}
</div>
);
};
export default RealTimeVisulization;

View File

@ -1,7 +1,8 @@
import React, { useState } from "react";
import React, { useEffect, useState } from "react";
import List from "./List";
import { AddIcon, ArrowIcon, FocusIcon } from "../../icons/ExportCommonIcons";
import KebabMenuListMultiSelect from "./KebebMenuListMultiSelect";
import { getZonesApi } from "../../../services/realTimeVisulization/zoneData/getZones";
interface DropDownListProps {
value?: string; // Value to display in the DropDownList
@ -33,6 +34,19 @@ const DropDownList: React.FC<DropDownListProps> = ({
const handleToggle = () => {
setIsOpen((prev) => !prev); // Toggle the state
};
const [zoneDataList, setZoneDataList] = useState<{ id: string; name: string }[]>([]);
useEffect(() => {
async function GetZoneData() {
const response = await getZonesApi("hexrfactory")
console.log('response: ', response.data);
setZoneDataList([{ id: "1", name: "zone1" },
{ id: "2", name: "Zone 2" },])
}
GetZoneData()
}, [])
return (
<div className="dropdown-list-container">
@ -79,7 +93,7 @@ const DropDownList: React.FC<DropDownListProps> = ({
value="Zones"
showKebabMenu={false}
showAddIcon={false}
items={[]}
items={zoneDataList}
/>
</>
)}

View File

@ -8,6 +8,7 @@ interface ListProps {
}
const List: React.FC<ListProps> = ({ items = [] }) => {
console.log('items: ', items);
return (
<>
{items.length > 0 ? (

View File

@ -1,3 +1,5 @@
import { StockIncreseIcon } from "../../../icons/RealTimeVisulationIcons";
const ProgressCard = ({
title,
data,
@ -16,7 +18,9 @@ const ProgressCard = ({
</span>
<div className="stock-description">{stock.description}</div>
</span>
<div className="icon">Icon</div>
<div className="icon">
<StockIncreseIcon />
</div>
</div>
))}
</div>

View File

@ -1,12 +1,9 @@
import React, { useMemo } from "react";
import { Radar } from "react-chartjs-2";
import {
ChartOptions,
ChartData,
RadialLinearScaleOptions,
} from "chart.js";
import { ChartOptions, ChartData, RadialLinearScaleOptions } from "chart.js";
interface ChartComponentProps {
type: string;
title: string;
fontFamily?: string;
fontSize?: string;
@ -19,7 +16,7 @@ const RadarGraphComponent = ({
fontFamily,
fontSize,
fontWeight = "Regular",
data,
data, // Now guaranteed to be number[]
}: ChartComponentProps) => {
// Memoize Font Weight Mapping
const chartFontWeightMap = useMemo(
@ -31,14 +28,15 @@ const RadarGraphComponent = ({
[]
);
const fontSizeValue = useMemo(() => (fontSize ? parseInt(fontSize) : 12), [
fontSize,
]);
const fontSizeValue = useMemo(
() => (fontSize ? parseInt(fontSize) : 12),
[fontSize]
);
const fontWeightValue = useMemo(() => chartFontWeightMap[fontWeight], [
fontWeight,
chartFontWeightMap,
]);
const fontWeightValue = useMemo(
() => chartFontWeightMap[fontWeight],
[fontWeight, chartFontWeightMap]
);
const chartFontStyle = useMemo(
() => ({

View File

@ -0,0 +1,57 @@
import React from "react";
import {
CommentsIcon,
DownloadIcon,
EyeIconBig,
StarsIconSmall,
VerifiedIcon,
} from "../../components/icons/marketPlaceIcons";
import assetImage from "../../assets/image/image.png";
const Card: React.FC = () => {
return (
<div className="card-container">
<div className="icon">
<DownloadIcon />
</div>
<div className="image-container">
<img src={assetImage} alt="" />
</div>
<div className="assets-container">
<div className="name-container">
<div className="asstes-container">Asset name</div>
<div className="assets-date">Uploaded on-12 Jan 23</div>
</div>
<div className="details">
<div className="content">
<EyeIconBig />
1.5k
</div>
<div className="content">
<CommentsIcon />
32
</div>
</div>
</div>
<div className="vendor-icon">
HEXR FACTORY <VerifiedIcon />
</div>
<div className="stars-container">
<div className="stars-wrapper">
<StarsIconSmall />
<StarsIconSmall />
<StarsIconSmall />
<StarsIconSmall />
<StarsIconSmall />
</div>
<div className="units">
36,500/<span>unit</span>
</div>
</div>
<div className="buy-now-button">Buy now</div>
</div>
);
};
export default Card;

View File

@ -0,0 +1,18 @@
import React from "react";
import Card from "./Card";
const CardsContainer: React.FC = () => {
const array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
return (
<div className="cards-container-container">
<div className="header">Products You May Like</div>
<div className="cards-wrapper-container">
{array.map((index) => (
<Card key={index} />
))}
</div>
</div>
);
};
export default CardsContainer;

View File

@ -0,0 +1,39 @@
import React, { useState } from "react";
// import RegularDropDown from "./ui/inputs/RegularDropDown";
import Search from "../../components/ui/inputs/Search";
import { StarsIcon } from "../../components/icons/marketPlaceIcons";
import RegularDropDown from "../../components/ui/inputs/RegularDropDown";
const FilterSearch: React.FC = () => {
const [activeOption, setActiveOption] = useState("Sort by"); // State for active option
const handleSelect = (option: string) => {
setActiveOption(option);
};
return (
<div className="filter-search-container">
<Search onChange={() => {}} />
<RegularDropDown
header={activeOption}
options={["Alphabet ascending", "Alphabet descending", ""]}
onSelect={handleSelect}
search={false}
/>
<div className="button">Free</div>
<div className="button">Animated</div>
<div className="rating-container">
<div className="label">Rating</div>
<div className="stars">
<StarsIcon />
<StarsIcon />
<StarsIcon />
<StarsIcon />
<StarsIcon />
</div>
</div>
</div>
);
};
export default FilterSearch;

View File

@ -0,0 +1,18 @@
import React from "react";
import FilterSearch from "./FilterSearch";
import CardsContainer from "./CardsContainer";
const MarketPlace = () => {
return (
<div className="marketplace-wrapper">
<div className="marketplace-container">
<div className="marketPlace">
<FilterSearch />
<CardsContainer />
</div>
</div>
</div>
);
};
export default MarketPlace;

View File

@ -15,7 +15,9 @@ import {
useZones,
} from "../store/store";
import { useNavigate } from "react-router-dom";
import { usePlayButtonStore } from "../store/usePlayButtonStore";
import SimulationUI from "../modules/simulation/simulationUI";
import MarketPlace from "../modules/market/MarketPlace";
const Project: React.FC = () => {
let navigate = useNavigate();
@ -44,19 +46,21 @@ const Project: React.FC = () => {
navigate("/");
}
}, []);
const { isPlaying } = usePlayButtonStore();
return (
<div className="project-main">
<ModuleToggle />
<SideBarLeft />
<SideBarRight />
{!isPlaying && (
<>
<ModuleToggle />
<SideBarLeft />
<SideBarRight />
</>
)}
{activeModule === "market" && <MarketPlace />}
<RealTimeVisulization />
{activeModule !== "market" && <Tools />}
{/* <SimulationUI /> */}
<div
className="canvas-container"
style={{ height: "100vh", width: "100vw" }}
></div>
</div>
);
};

View File

@ -0,0 +1,25 @@
let url_Backend_dwinzo = `http://185.100.212.76:5000`;
export const getZonesApi = async (organization: string) => {
try {
const response = await fetch(`${url_Backend_dwinzo}/api/v2/findZones/${organization}`, {
method: "GET",
headers: {
"Content-Type": "application/json",
},
});
// if (!response.ok) {
// throw new Error("Failed to get Zones");
// }
const result = await response.json();
return result;
} catch (error) {
if (error instanceof Error) {
throw new Error(error.message);
} else {
throw new Error("An unknown error occurred");
}
}
};

View File

@ -25,7 +25,7 @@ interface SelectedZoneStore {
export const useSelectedZoneStore = create<SelectedZoneStore>((set) => ({
selectedZone: {
zoneName: "Manufacturing unit",
zoneName: "",
activeSides: [],
panelOrder: [],
lockedPanels: [],

View File

@ -0,0 +1,187 @@
@use "../../abstracts/variables" as *;
@use "../../abstracts/mixins.scss" as *;
.marketplace-wrapper {
height: 100vh;
width: 100vw;
z-index: #{$z-index-marketplace};
background-color: var(--background-color-secondary);
position: absolute;
left: 0;
padding: 100px 50px;
.marketplace-container {
padding: 20px 2px;
height: calc(100vh - 120px);
background-color: var(--background-color);
box-shadow: #{$box-shadow-medium};
border-radius: #{$border-radius-extra-large};
}
.marketPlace {
width: 100%;
height: 100%;
overflow: auto;
left: calc(120px / 2);
top: 100px;
padding: 14px;
padding-bottom: 60px;
display: flex;
flex-direction: column;
gap: 24px;
.filter-search-container {
width: 100%;
display: flex;
align-items: center;
gap: 12px;
.search-wrapper {
min-width: 60%;
max-width: 684px;
padding: 0;
border-radius: $border-radius-large ;
.search-container {
border: none !important;
box-shadow: $box-shadow-medium;
border-radius: $border-radius-large ;
input {
border: none !important;
outline: none;
}
}
}
.regularDropdown-container {
max-width: 159px;
}
.button {
padding: 5px 20px;
border: 1px solid var(--accent-color);
border-radius: 14px;
}
.rating-container {
display: flex;
align-items: center;
gap: 6px;
.stars {
display: flex;
align-items: center;
}
}
}
.cards-container-container {
padding: 0px 20px;
display: flex;
flex-direction: column;
gap: 6px;
.header {
color: var(--text-color);
font-weight: $medium-weight;
font-size: $xlarge;
}
.cards-wrapper-container {
display: flex;
flex-wrap: wrap;
gap: 28px;
.card-container {
width: calc(25% - 23px);
border-radius: 18px;
padding: 12px;
box-shadow: 0px 2px 10.5px 0px #0000000D;
border: 1px solid var(--background-accent-transparent, #E0DFFF80);
position: relative;
display: flex;
flex-direction: column;
justify-content: center;
gap: 6px;
.icon {
position: absolute;
top: 12px;
left: 12px;
width: 30px;
height: 30px;
border-radius: 10px;
padding: 5px;
background-color: var(--accent-color);
}
.image-container {
width: 100%;
display: flex;
justify-content: center;
}
.assets-container {
display: flex;
justify-content: space-between;
.name-container {
display: flex;
flex-direction: column;
gap: 3px;
.asstes-container {
font-weight: #{$bold-weight};
font-size: $regular ;
}
.assets-date {
color: var(--accent-color);
font-size: $small;
}
}
.details {
display: flex;
align-items: center;
gap: 10px;
.content {
display: flex;
align-items: center;
gap: 6px;
}
}
}
.vendor-icon {
font-weight: #{$bold-weight};
font-size: $regular ;
}
.stars-container {
display: flex;
justify-content: space-between;
}
.buy-now-button {
width: 100%;
background-color: var(--background-color-secondary);
border-radius: $border-radius-extra-large ;
padding: 8px 0;
@include flex-center;
color: var(--accent-color);
&:hover {
cursor: pointer;
}
}
}
}
}
}
}

View File

@ -4,7 +4,7 @@
.tools-container {
@include flex-center;
position: fixed;
bottom: 32px;
bottom: 50px;
left: 50%;
transform: translate(-50%, 0);
padding: 8px;
@ -15,12 +15,14 @@
transition: width 0.2s;
background-color: var(--background-color);
z-index: #{$z-index-default};
.split {
height: 20px;
width: 2px;
border-radius: 2px;
background: var(--highlight-accent-color);
}
.draw-tools,
.general-options,
.activeDropicon {
@ -30,42 +32,46 @@
width: 0;
opacity: 0;
animation: expandWidth 0.2s ease-in-out forwards;
.tool-button {
@include flex-center;
height: 28px;
width: 28px;
cursor: pointer;
border-radius: #{$border-radius-small};
&:hover {
background: color-mix(
in srgb,
var(--highlight-accent-color) 60%,
transparent
);
background: color-mix(in srgb,
var(--highlight-accent-color) 60%,
transparent);
}
}
.active {
background-color: var(--accent-color);
&:hover {
background-color: var(--accent-color);
}
}
}
.activeDropicon {
gap: 2px;
.drop-down-option-button {
@include flex-center;
height: 28px;
cursor: pointer;
border-radius: #{$border-radius-small};
position: relative;
&:hover {
background: color-mix(
in srgb,
var(--highlight-accent-color) 60%,
transparent
);
background: color-mix(in srgb,
var(--highlight-accent-color) 60%,
transparent);
}
.drop-down-container {
position: absolute;
bottom: 40px;
@ -74,6 +80,7 @@
padding: 8px;
border-radius: #{$border-radius-large};
background: var(--background-color);
.option-list {
margin: 4px 0;
display: flex;
@ -82,18 +89,22 @@
border-radius: #{$border-radius-medium};
gap: 6px;
padding: 4px;
&:hover {
background-color: var(--highlight-accent-color);
color: var(--accent-color);
path {
stroke: var(--accent-color);
}
}
.active-option {
height: 12px;
width: 12px;
@include flex-center;
}
.option {
color: inherit;
}
@ -101,6 +112,7 @@
}
}
}
.toggle-threed-button {
@include flex-center;
padding: 3px;
@ -108,12 +120,14 @@
background-color: var(--highlight-accent-color);
gap: 2px;
position: relative;
.toggle-option {
font-size: var(--font-size-small);
padding: 2px;
z-index: 1;
transition: all 0.2s;
}
&::after {
content: "";
position: absolute;
@ -125,24 +139,56 @@
border-radius: #{$border-radius-small};
transition: all 0.2s;
}
.active {
color: var(--highlight-accent-color);
}
}
.toggled {
&::after {
left: 24px;
}
}
}
.exitPlay {
width: 30px;
height: 30px;
border-radius: 50%;
background-color: var(--accent-color);
cursor: pointer;
@include flex-center;
position: fixed;
bottom: 60px;
left: 50%;
transform: translate(-50%, 0);
transition: background-color 0.3s, transform 0.3s;
color: var(--background-color);
// transform: none;
z-index: 100;
&:hover {
background-color: var(--accent-color);
}
}
@keyframes expandWidth {
from {
width: 0;
opacity: 0;
}
to {
width: fit-content;
opacity: 1;
}
}
}

View File

@ -21,6 +21,7 @@
@use 'components/visualization/floating/energyConsumed';
@use 'components/visualization/ui/styledWidgets';
@use './components/visualization/floating/common';
@use './components/marketPlace/marketPlace.scss';
// layout
@use 'layout/loading';

View File

@ -1,396 +1,403 @@
@use "../abstracts/variables.scss" as *;
// Main Container
.realTime-viz {
background-color: var(--background-color);
border-radius: 20px;
box-shadow: $box-shadow-medium;
width: calc(100% - (320px + 270px + 90px));
height: calc(100% - (200px + 80px));
position: absolute;
top: 50%;
left: calc(270px + 45px);
transform: translate(0, -50%);
border-radius: #{$border-radius-medium};
transition: all 0.2s;
z-index: #{$z-index-default};
.scene-container {
overflow: hidden;
}
.icon {
display: flex;
align-items: center;
position: relative;
}
.icons-container {
.icon {
&:first-child {
&::after {
display: none;
}
}
}
}
.zoon-wrapper {
display: flex;
background-color: var(--background-color);
position: absolute;
bottom: 10px;
left: 50%;
transform: translate(-50%, 0);
gap: 6px;
padding: 4px;
border-radius: 8px;
max-width: 80%;
overflow: auto;
max-width: calc(100% - 450px);
&::-webkit-scrollbar {
display: none;
}
.zone {
width: auto;
background-color: var(--background-color);
border-radius: 6px;
padding: 4px 8px;
white-space: nowrap;
font-size: $small;
}
.active {
background-color: var(--accent-color);
color: var(--background-color);
// color: #FCFDFD !important;
}
}
.zoon-wrapper.bottom {
bottom: 210px;
}
@media (max-width: 1024px) {
width: 80%; // Increase width to take more space on smaller screens
height: 500px; // Reduce height to fit smaller screens
left: 50%; // Center horizontally
.main-container {
margin: 0 15px; // Reduce margin for better spacing
}
.zoon-wrapper {
bottom: 5px; // Adjust position for smaller screens
&.bottom {
bottom: 150px; // Adjust for bottom placement
}
}
}
.content-container {
display: flex;
height: 100vh;
transition: all 0.3s ease;
}
.main-container {
position: relative;
flex: 1;
height: 600px;
background-color: rgb(235, 235, 235);
margin: 0 30px;
transition: height 0.3s ease, margin 0.3s ease;
.zoon-wrapper {
display: flex;
background-color: rgba(224, 223, 255, 0.5);
position: absolute;
bottom: 10px;
left: 50%;
transform: translate(-50%, 0);
gap: 6px;
padding: 4px;
border-radius: 8px;
max-width: 80%;
overflow: auto;
transition: transform 0.3s ease;
&::-webkit-scrollbar {
display: none;
}
.zone {
width: auto;
background-color: $background-color;
border-radius: 6px;
padding: 4px 8px;
white-space: nowrap;
cursor: pointer;
transition: background-color 0.3s ease;
&.active {
background-color: var(--primary-color);
color: var(--accent-color);
}
}
&.bottom {
bottom: 210px;
}
}
}
.panel {
position: absolute;
background: white;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
transition: all 0.3s ease;
border-radius: 6px;
overflow: visible !important;
.panel-content {
position: relative;
height: 100%;
padding: 10px;
overflow: auto;
display: flex;
flex-direction: column;
gap: 10px;
background-color: var(--background-color);
&::-webkit-scrollbar {
display: none;
}
.chart-container {
width: 100%;
height: 200px;
max-height: 100%;
border: 1px dotted #a9a9a9;
border-radius: 8px;
box-shadow: 0px 2px 6px 0px rgba(60, 60, 67, 0.1);
padding: 6px 0;
background-color: white;
}
.close-btn {
position: absolute;
top: 5px;
right: 5px;
background: none;
border: none;
cursor: pointer;
color: var(--primary-color);
}
}
&.top-panel,
&.bottom-panel {
left: 0;
right: 0;
.fullScreen {
background-color: red;
}
.panel-content {
display: flex;
flex-direction: row;
.chart-container {
height: 100%;
width: 200px;
}
}
}
&.top-panel {
top: 0;
}
&.bottom-panel {
bottom: 0;
}
&.left-panel {
left: 0;
top: 0;
bottom: 0;
.chart-container {
width: 100%;
height: 180px;
}
}
&.right-panel {
right: 0;
top: 0;
bottom: 0;
}
}
}
// Side Buttons
.side-button-container {
position: absolute;
display: flex;
background-color: var(--background-color);
padding: 5px;
border-radius: 8px;
transition: transform 0.3s ease;
.extra-Bs {
display: flex;
align-items: center;
gap: 12px;
.icon {
display: flex;
align-items: center;
justify-content: center;
width: 18px;
height: 18px;
border-radius: 4px;
}
.active {
background-color: var(--accent-color);
}
&:hover {
cursor: pointer;
}
}
.side-button {
cursor: pointer;
transition: background-color 0.3s ease;
width: 18px;
height: 18px;
display: flex;
justify-content: center;
// align-items: center;
background-color: var(--accent-color);
border: none;
color: var(--background-color);
border-radius: 4px;
}
&.top {
top: -30px;
left: 50%;
transform: translateX(-50%);
flex-direction: row;
gap: 6px;
}
&.right {
right: -30px;
top: 50%;
transform: translateY(-50%);
flex-direction: column;
gap: 6px;
}
&.bottom {
bottom: -30px;
left: 50%;
transform: translateX(-50%);
flex-direction: row;
gap: 6px;
}
&.left {
left: -30px;
top: 50%;
transform: translateY(-50%);
flex-direction: column;
gap: 6px;
}
}
.right.side-button-container {
.extra-Bs {
flex-direction: column;
}
}
.left.side-button-container {
.extra-Bs {
flex-direction: column;
}
}
// Theme Container
.theme-container {
width: 250px;
padding: 12px;
box-shadow: 1px -3px 4px 0px rgba(0, 0, 0, 0.11);
border-radius: 8px;
background-color: white;
position: absolute;
top: 20px;
right: -100%;
transform: translate(-0%, 0);
h2 {
font-size: 12px;
margin-bottom: 8px;
color: #2b3344;
}
.theme-preset-wrapper {
display: flex;
gap: 5px;
flex-wrap: wrap;
.theme-preset {
display: flex;
gap: 2px;
margin-bottom: 10px;
border: 1px solid $border-color;
padding: 5px 10px;
border-radius: 4px;
transition: border 0.3s ease;
&.active {
border: 1px solid var(--primary-color);
&::after {
content: "";
position: absolute;
top: 1px;
left: 1px;
width: 10px;
height: 10px;
background-color: var(--primary-color);
border-radius: 50%;
}
}
}
}
.custom-color {
display: flex;
justify-content: space-between;
.color-displayer {
display: flex;
gap: 5px;
align-items: center;
border: 1px solid var(--accent-color);
border-radius: 4px;
padding: 0 5px;
input {
border: none;
outline: none;
border-radius: 50%;
}
}
}
}
@use "../abstracts/variables.scss" as *;
// Main Container
.realTime-viz {
background-color: var(--background-color);
border-radius: 20px;
box-shadow: $box-shadow-medium;
width: calc(100% - (320px + 270px + 90px));
height: calc(100% - (200px + 80px));
position: absolute;
top: 50%;
left: calc(270px + 45px);
transform: translate(0, -50%);
border-radius: #{$border-radius-medium};
transition: all 0.2s;
z-index: #{$z-index-default};
.scene-container {
overflow: hidden;
}
.icon {
display: flex;
align-items: center;
position: relative;
}
.icons-container {
.icon {
&:first-child {
&::after {
display: none;
}
}
}
}
.zoon-wrapper {
display: flex;
background-color: var(--background-color);
position: absolute;
bottom: 10px;
left: 50%;
transform: translate(-50%, 0);
gap: 6px;
padding: 4px;
border-radius: 8px;
max-width: 80%;
overflow: auto;
max-width: calc(100% - 450px);
&::-webkit-scrollbar {
display: none;
}
.zone {
width: auto;
background-color: var(--background-color);
border-radius: 6px;
padding: 4px 8px;
white-space: nowrap;
font-size: $small;
}
.active {
background-color: var(--accent-color);
color: var(--background-color);
// color: #FCFDFD !important;
}
}
.zoon-wrapper.bottom {
bottom: 210px;
}
@media (max-width: 1024px) {
width: 80%; // Increase width to take more space on smaller screens
height: 500px; // Reduce height to fit smaller screens
left: 50%; // Center horizontally
.main-container {
margin: 0 15px; // Reduce margin for better spacing
}
.zoon-wrapper {
bottom: 5px; // Adjust position for smaller screens
&.bottom {
bottom: 150px; // Adjust for bottom placement
}
}
}
.content-container {
display: flex;
height: 100vh;
transition: all 0.3s ease;
}
.main-container {
position: relative;
flex: 1;
height: 600px;
background-color: rgb(235, 235, 235);
margin: 0 30px;
transition: height 0.3s ease, margin 0.3s ease;
.zoon-wrapper {
display: flex;
background-color: rgba(224, 223, 255, 0.5);
position: absolute;
bottom: 10px;
left: 50%;
transform: translate(-50%, 0);
gap: 6px;
padding: 4px;
border-radius: 8px;
max-width: 80%;
overflow: auto;
transition: transform 0.3s ease;
&::-webkit-scrollbar {
display: none;
}
.zone {
width: auto;
background-color: $background-color;
border-radius: 6px;
padding: 4px 8px;
white-space: nowrap;
cursor: pointer;
transition: background-color 0.3s ease;
&.active {
background-color: var(--primary-color);
color: var(--accent-color);
}
}
&.bottom {
bottom: 210px;
}
}
}
.panel {
position: absolute;
background: white;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
transition: all 0.3s ease;
border-radius: 6px;
overflow: visible !important;
.panel-content {
position: relative;
height: 100%;
padding: 10px;
overflow: auto;
display: flex;
flex-direction: column;
gap: 10px;
background-color: var(--background-color);
&::-webkit-scrollbar {
display: none;
}
.chart-container {
width: 100%;
height: 24% !important;
min-height: 150px;
max-height: 100%;
border: 1px dotted #a9a9a9;
border-radius: 8px;
box-shadow: 0px 2px 6px 0px rgba(60, 60, 67, 0.1);
padding: 6px 0;
background-color: white;
}
.close-btn {
position: absolute;
top: 5px;
right: 5px;
background: none;
border: none;
cursor: pointer;
color: var(--primary-color);
}
}
&.top-panel,
&.bottom-panel {
left: 0;
right: 0;
.panel-content {
display: flex;
flex-direction: row;
height: 100%;
.chart-container {
height: 100% !important;
width: 20%;
min-width: 150px;
}
}
}
&.top-panel {
top: 0;
}
&.bottom-panel {
bottom: 0;
}
&.left-panel {
left: 0;
top: 0;
bottom: 0;
.chart-container {
width: 100%;
height: 180px;
}
}
&.right-panel {
right: 0;
top: 0;
bottom: 0;
}
}
}
.playingFlase{
.zoon-wrapper{
bottom: 300px !important;
}
}
// Side Buttons
.side-button-container {
position: absolute;
display: flex;
background-color: var(--background-color);
padding: 5px;
border-radius: 8px;
transition: transform 0.3s ease;
.extra-Bs {
display: flex;
align-items: center;
gap: 12px;
.icon {
display: flex;
align-items: center;
justify-content: center;
width: 18px;
height: 18px;
border-radius: 4px;
}
.active {
background-color: var(--accent-color);
}
&:hover {
cursor: pointer;
}
}
.side-button {
cursor: pointer;
transition: background-color 0.3s ease;
width: 18px;
height: 18px;
display: flex;
justify-content: center;
// align-items: center;
background-color: var(--accent-color);
border: none;
color: var(--background-color);
border-radius: 4px;
}
&.top {
top: -30px;
left: 50%;
transform: translateX(-50%);
flex-direction: row;
gap: 6px;
}
&.right {
right: -30px;
top: 50%;
transform: translateY(-50%);
flex-direction: column;
gap: 6px;
}
&.bottom {
bottom: -30px;
left: 50%;
transform: translateX(-50%);
flex-direction: row;
gap: 6px;
}
&.left {
left: -30px;
top: 50%;
transform: translateY(-50%);
flex-direction: column;
gap: 6px;
}
}
.right.side-button-container {
.extra-Bs {
flex-direction: column;
}
}
.left.side-button-container {
.extra-Bs {
flex-direction: column;
}
}
// Theme Container
.theme-container {
width: 250px;
padding: 12px;
box-shadow: 1px -3px 4px 0px rgba(0, 0, 0, 0.11);
border-radius: 8px;
background-color: white;
position: absolute;
top: 20px;
right: -100%;
transform: translate(-0%, 0);
h2 {
font-size: 12px;
margin-bottom: 8px;
color: #2b3344;
}
.theme-preset-wrapper {
display: flex;
gap: 5px;
flex-wrap: wrap;
.theme-preset {
display: flex;
gap: 2px;
margin-bottom: 10px;
border: 1px solid $border-color;
padding: 5px 10px;
border-radius: 4px;
transition: border 0.3s ease;
&.active {
border: 1px solid var(--primary-color);
&::after {
content: "";
position: absolute;
top: 1px;
left: 1px;
width: 10px;
height: 10px;
background-color: var(--primary-color);
border-radius: 50%;
}
}
}
}
.custom-color {
display: flex;
justify-content: space-between;
.color-displayer {
display: flex;
gap: 5px;
align-items: center;
border: 1px solid var(--accent-color);
border-radius: 4px;
padding: 0 5px;
input {
border: none;
outline: none;
border-radius: 50%;
}
}
}
}

6
package-lock.json generated Normal file
View File

@ -0,0 +1,6 @@
{
"name": "Dwinzo_dev",
"lockfileVersion": 3,
"requires": true,
"packages": {}
}