added donut and pole area chart and added its iot data
|
@ -4138,6 +4138,7 @@
|
|||
"version": "10.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz",
|
||||
"integrity": "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.10.4",
|
||||
|
|
After Width: | Height: | Size: 722 KiB |
After Width: | Height: | Size: 62 KiB |
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 320 KiB |
After Width: | Height: | Size: 6.9 KiB |
After Width: | Height: | Size: 7.9 KiB |
After Width: | Height: | Size: 5.0 KiB |
After Width: | Height: | Size: 1.2 MiB |
|
@ -0,0 +1,255 @@
|
|||
export function NotificationIcon() {
|
||||
return (
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M10 12.8335C10 13.3639 9.78927 13.8726 9.4142 14.2477C9.03913 14.6228 8.5304 14.8335 8 14.8335C7.4696 14.8335 6.96087 14.6228 6.58579 14.2477C6.21072 13.8726 6 13.3639 6 12.8335"
|
||||
stroke="var(--text-color)"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M3.72064 12.1667C3.39434 12.0974 3.10586 11.9084 2.91209 11.6369C2.71832 11.3653 2.63337 11.0311 2.67399 10.7L3.34066 5.29332C3.51087 4.18175 4.07672 3.16901 4.93414 2.44143C5.79156 1.71384 6.88287 1.32036 8.00734 1.33332C9.1318 1.32036 10.2231 1.71384 11.0805 2.44143C11.9379 3.16901 12.5038 4.18175 12.674 5.29332L13.3407 10.7C13.3815 11.0301 13.2975 11.3636 13.1051 11.635C12.9127 11.9063 12.6257 12.096 12.3007 12.1667C9.47907 12.8297 6.54222 12.8297 3.72064 12.1667Z"
|
||||
stroke="var(--text-color)"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function HomeIcon() {
|
||||
return (
|
||||
<svg
|
||||
width="16"
|
||||
height="17"
|
||||
viewBox="0 0 16 17"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M6.91304 13.5V12.8333C6.91304 12.281 7.36076 11.8333 7.91304 11.8333H8.95652C9.50881 11.8333 9.95652 12.281 9.95652 12.8333V13.5C9.95652 14.0523 10.4042 14.5 10.9565 14.5H12C12.5523 14.5 13 14.0523 13 13.5V7.38889L8.21739 2.5L3 7.38889V13.5C3 14.0523 3.44772 14.5 4 14.5H5.91304C6.46533 14.5 6.91304 14.0523 6.91304 13.5Z"
|
||||
stroke="var(--text-color)"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function ProjectsIcon() {
|
||||
return (
|
||||
<svg
|
||||
width="16"
|
||||
height="17"
|
||||
viewBox="0 0 16 17"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M12.1538 1.5H5.69231C5.20268 1.5 4.7331 1.68437 4.38688 2.01256C4.04066 2.34075 3.84615 2.78587 3.84615 3.25C3.35652 3.25 2.88695 3.43437 2.54073 3.76256C2.1945 4.09075 2 4.53587 2 5V13.75C2 14.2141 2.1945 14.6592 2.54073 14.9874C2.88695 15.3156 3.35652 15.5 3.84615 15.5H10.3077C10.7973 15.5 11.2669 15.3156 11.6131 14.9874C11.9593 14.6592 12.1538 14.2141 12.1538 13.75C12.6435 13.75 13.1131 13.5656 13.4593 13.2374C13.8055 12.9092 14 12.4641 14 12V3.25C14 2.78587 13.8055 2.34075 13.4593 2.01256C13.1131 1.68437 12.6435 1.5 12.1538 1.5ZM12.1538 12.875V5C12.1538 4.53587 11.9593 4.09075 11.6131 3.76256C11.2669 3.43437 10.7973 3.25 10.3077 3.25H4.76923C4.76923 3.01794 4.86648 2.79538 5.03959 2.63128C5.2127 2.46719 5.44749 2.375 5.69231 2.375H12.1538C12.3987 2.375 12.6334 2.46719 12.8066 2.63128C12.9797 2.79538 13.0769 3.01794 13.0769 3.25V12C13.0769 12.2321 12.9797 12.4546 12.8066 12.6187C12.6334 12.7828 12.3987 12.875 12.1538 12.875ZM2.92308 5C2.92308 4.76794 3.02033 4.54538 3.19344 4.38128C3.36655 4.21719 3.60134 4.125 3.84615 4.125H10.3077C10.5525 4.125 10.7873 4.21719 10.9604 4.38128C11.1335 4.54538 11.2308 4.76794 11.2308 5V13.75C11.2308 13.9821 11.1335 14.2046 10.9604 14.3687C10.7873 14.5328 10.5525 14.625 10.3077 14.625H3.84615C3.60134 14.625 3.36655 14.5328 3.19344 14.3687C3.02033 14.2046 2.92308 13.9821 2.92308 13.75V5Z"
|
||||
fill="var(--text-color)"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function TutorialsIcon() {
|
||||
return (
|
||||
<svg
|
||||
width="16"
|
||||
height="17"
|
||||
viewBox="0 0 16 17"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<circle
|
||||
cx="8.157"
|
||||
cy="8.35866"
|
||||
r="6.17928"
|
||||
stroke="var(--text-color)"
|
||||
strokeWidth="0.562865"
|
||||
/>
|
||||
<path
|
||||
d="M7.31894 7.8336L7.30273 7.72125C10.0583 7.32407 11.5796 5.74901 12.1058 5.09033L12.1945 5.1612C11.6598 5.83032 10.1146 7.43067 7.31894 7.8336Z"
|
||||
fill="var(--text-color)"
|
||||
stroke="var(--text-color)"
|
||||
strokeWidth="0.562865"
|
||||
/>
|
||||
<path
|
||||
d="M7.3313 8.19434C7.56713 8.19434 7.7583 8.00316 7.7583 7.76734C7.7583 7.53151 7.56713 7.34033 7.3313 7.34033C7.09547 7.34033 6.9043 7.53151 6.9043 7.76734C6.9043 8.00316 7.09547 8.19434 7.3313 8.19434Z"
|
||||
fill="var(--text-color)"
|
||||
stroke="var(--text-color)"
|
||||
strokeWidth="0.562865"
|
||||
/>
|
||||
<path
|
||||
d="M12.134 5.56787C12.3699 5.56787 12.561 5.3767 12.561 5.14087C12.561 4.90504 12.3699 4.71387 12.134 4.71387C11.8982 4.71387 11.707 4.90504 11.707 5.14087C11.707 5.3767 11.8982 5.56787 12.134 5.56787Z"
|
||||
fill="var(--text-color)"
|
||||
stroke="var(--text-color)"
|
||||
strokeWidth="0.562865"
|
||||
/>
|
||||
<path
|
||||
d="M5.67763 13.0492C5.15009 12.385 4.31304 10.9992 4.63359 9.18018L4.74534 9.20001C4.43251 10.9751 5.25078 12.3292 5.76636 12.9785L5.67763 13.0492Z"
|
||||
fill="var(--text-color)"
|
||||
stroke="var(--text-color)"
|
||||
strokeWidth="0.562865"
|
||||
/>
|
||||
<path
|
||||
d="M4.68921 9.63867C4.92504 9.63867 5.11621 9.4475 5.11621 9.21167C5.11621 8.97584 4.92504 8.78467 4.68921 8.78467C4.45338 8.78467 4.26221 8.97584 4.26221 9.21167C4.26221 9.4475 4.45338 9.63867 4.68921 9.63867Z"
|
||||
fill="var(--text-color)"
|
||||
stroke="var(--text-color)"
|
||||
strokeWidth="0.562865"
|
||||
/>
|
||||
<path
|
||||
d="M5.70923 13.4238C5.94506 13.4238 6.13623 13.2327 6.13623 12.9968C6.13623 12.761 5.94506 12.5698 5.70923 12.5698C5.4734 12.5698 5.28223 12.761 5.28223 12.9968C5.28223 13.2327 5.4734 13.4238 5.70923 13.4238Z"
|
||||
fill="var(--text-color)"
|
||||
stroke="var(--text-color)"
|
||||
strokeWidth="0.562865"
|
||||
/>
|
||||
<path
|
||||
d="M12.4429 9.6101L12.3293 9.60692C12.369 8.18736 11.8263 6.82867 10.801 5.7813C9.73352 4.69047 8.2434 4.07147 6.70876 4.0804L6.70801 3.96684C8.27081 3.96078 9.79333 4.58917 10.8822 5.70181C11.9291 6.77143 12.4833 8.1595 12.4429 9.6101Z"
|
||||
fill="var(--text-color)"
|
||||
stroke="var(--text-color)"
|
||||
strokeWidth="0.562865"
|
||||
/>
|
||||
<path
|
||||
d="M12.3792 10.0142C12.615 10.0142 12.8062 9.82299 12.8062 9.58716C12.8062 9.35133 12.615 9.16016 12.3792 9.16016C12.1433 9.16016 11.9521 9.35133 11.9521 9.58716C11.9521 9.82299 12.1433 10.0142 12.3792 10.0142Z"
|
||||
fill="var(--text-color)"
|
||||
stroke="var(--text-color)"
|
||||
strokeWidth="0.562865"
|
||||
/>
|
||||
<path
|
||||
d="M6.72974 4.4585C6.96556 4.4585 7.15674 4.26732 7.15674 4.0315C7.15674 3.79567 6.96556 3.60449 6.72974 3.60449C6.49391 3.60449 6.30273 3.79567 6.30273 4.0315C6.30273 4.26732 6.49391 4.4585 6.72974 4.4585Z"
|
||||
fill="var(--text-color)"
|
||||
stroke="var(--text-color)"
|
||||
strokeWidth="0.562865"
|
||||
/>
|
||||
<path
|
||||
d="M8.93738 12.7561C5.51197 12.5871 3.18964 10.3443 2.15833 8.30167C1.40017 6.79989 1.28161 5.33657 1.84898 4.48256C2.21511 3.93139 2.7529 3.64179 3.57572 3.69903L3.45825 3.81649C2.67632 3.76183 2.28567 4.03042 1.94346 4.5454C1.39865 5.36549 1.51979 6.78505 2.25963 8.25049C3.27641 10.2647 5.56617 12.476 8.94298 12.6426L8.93738 12.7561Z"
|
||||
fill="var(--text-color)"
|
||||
stroke="var(--text-color)"
|
||||
strokeWidth="0.562865"
|
||||
/>
|
||||
<path
|
||||
d="M4.12372 13.5272C2.54078 13.2677 1.46328 11.9915 1.38697 10.4835C1.31368 9.03292 2.2066 7.3084 4.36675 6.72559L4.39628 6.83521C2.2973 7.40152 1.42936 9.07259 1.50053 10.4778C1.54767 11.4101 2.02721 12.4642 3.18398 12.9967C3.46147 13.1244 3.45807 13.0965 3.77132 13.2139L4.12372 13.5272Z"
|
||||
fill="var(--text-color)"
|
||||
stroke="var(--text-color)"
|
||||
strokeWidth="0.562865"
|
||||
/>
|
||||
<path
|
||||
d="M4.36157 7.21436C4.5974 7.21436 4.78858 7.02318 4.78858 6.78735C4.78858 6.55153 4.5974 6.36035 4.36157 6.36035C4.12575 6.36035 3.93457 6.55153 3.93457 6.78735C3.93457 7.02318 4.12575 7.21436 4.36157 7.21436Z"
|
||||
fill="var(--text-color)"
|
||||
stroke="var(--text-color)"
|
||||
strokeWidth="0.562865"
|
||||
/>
|
||||
<path
|
||||
d="M11.5155 15.1026C11.4663 15.1026 11.4165 15.1003 11.3659 15.0956C10.4065 15.0064 9.53752 14.1202 9.04102 12.7247L9.14807 12.6865C9.62928 14.0393 10.4622 14.8976 11.3764 14.9825C11.8685 15.0293 12.3041 14.8309 12.5152 14.4681C12.7337 14.0928 12.7265 13.7453 12.3977 13.2744L12.5152 13.1569C12.8703 13.6657 12.8548 14.1099 12.6133 14.5252C12.4016 14.8889 11.9895 15.1026 11.5155 15.1026Z"
|
||||
fill="var(--text-color)"
|
||||
stroke="var(--text-color)"
|
||||
strokeWidth="0.562865"
|
||||
/>
|
||||
<path
|
||||
d="M8.9187 13.1187C9.15453 13.1187 9.34571 12.9275 9.34571 12.6917C9.34571 12.4558 9.15453 12.2646 8.9187 12.2646C8.68287 12.2646 8.4917 12.4558 8.4917 12.6917C8.4917 12.9275 8.68287 13.1187 8.9187 13.1187Z"
|
||||
fill="var(--text-color)"
|
||||
stroke="var(--text-color)"
|
||||
strokeWidth="0.562865"
|
||||
/>
|
||||
<path
|
||||
d="M10.0987 3.65056L9.99072 3.61513C10.2487 2.83274 10.7045 2.32867 11.2414 2.23252C11.572 2.17286 11.8969 2.28566 12.0886 2.52597C12.2781 2.76339 12.4042 2.98817 12.2684 3.30782L12.1509 3.19035C12.2699 2.91023 12.1625 2.80064 12.0001 2.59683C11.8344 2.38923 11.5514 2.29248 11.2616 2.34441C10.7669 2.43284 10.3432 2.90906 10.0987 3.65056Z"
|
||||
fill="var(--text-color)"
|
||||
stroke="var(--text-color)"
|
||||
strokeWidth="0.562865"
|
||||
/>
|
||||
<path
|
||||
d="M10.0535 4.04004C10.2893 4.04004 10.4805 3.84887 10.4805 3.61304C10.4805 3.37721 10.2893 3.18604 10.0535 3.18604C9.81764 3.18604 9.62646 3.37721 9.62646 3.61304C9.62646 3.84887 9.81764 4.04004 10.0535 4.04004Z"
|
||||
fill="var(--text-color)"
|
||||
stroke="var(--text-color)"
|
||||
strokeWidth="0.562865"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function DocumentationIcon() {
|
||||
return (
|
||||
<svg
|
||||
width="16"
|
||||
height="17"
|
||||
viewBox="0 0 16 17"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M8 5.10589C7.2666 4.17245 6.13604 3.23901 3.33413 3.17051C3.15009 3.16602 3 3.3155 3 3.4996C3 4.86525 3 10.0354 3 11.5645C3 11.7486 3.1501 11.8932 3.33409 11.8992C6.13603 11.9908 7.2666 13.233 8 14.1665M8 5.10589C8.7334 4.17245 9.86393 3.23901 12.6659 3.17051C12.8499 3.16602 13 3.31214 13 3.49624C13 5.02281 13 10.0374 13 11.564C13 11.7481 12.8499 11.8932 12.6659 11.8992C9.864 11.9908 8.7334 13.233 8 14.1665M8 5.10589V14.1665"
|
||||
stroke="var(--text-color)"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M12.8232 4.5H14.333C14.5171 4.5 14.6663 4.64924 14.6663 4.83333V13.526C14.6663 13.7957 14.3485 13.9749 14.102 13.8654C13.5719 13.6299 12.6873 13.3421 11.5291 13.3421C9.56827 13.3421 7.99967 14.5 7.99967 14.5C7.99967 14.5 6.43105 13.3421 4.47026 13.3421C3.31197 13.3421 2.42738 13.6299 1.89732 13.8654C1.65079 13.9749 1.33301 13.7957 1.33301 13.526V4.83333C1.33301 4.64924 1.48225 4.5 1.66634 4.5H3.17615"
|
||||
stroke="var(--text-color)"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function HelpIcon() {
|
||||
return (
|
||||
<svg
|
||||
width="12"
|
||||
height="13"
|
||||
viewBox="0 0 12 13"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<g clipPath="url(#clip0_764_1941)">
|
||||
<path
|
||||
d="M6 12.5C2.6862 12.5 0 9.8138 0 6.5C0 3.1862 2.6862 0.5 6 0.5C9.3138 0.5 12 3.1862 12 6.5C12 9.8138 9.3138 12.5 6 12.5ZM3.552 4.8404V4.9016C3.552 4.98117 3.58361 5.05747 3.63987 5.11373C3.69613 5.16999 3.77244 5.2016 3.852 5.2016H4.4502C4.48952 5.2016 4.52845 5.19386 4.56478 5.17881C4.6011 5.16376 4.63411 5.14171 4.66191 5.11391C4.68971 5.08611 4.71176 5.0531 4.72681 5.01678C4.74186 4.98045 4.7496 4.94152 4.7496 4.9022C4.7496 4.1282 5.3484 3.7148 6.1536 3.7148C6.9384 3.7148 7.4544 4.1282 7.4544 4.7168C7.4544 5.2736 7.1652 5.5322 6.4428 5.8628L6.2364 5.9552C5.6274 6.224 5.4 6.626 5.4 7.3286V7.4C5.4 7.47957 5.43161 7.55587 5.48787 7.61213C5.54413 7.66839 5.62044 7.7 5.7 7.7H6.2982C6.33752 7.7 6.37645 7.69226 6.41278 7.67721C6.4491 7.66216 6.48211 7.64011 6.50991 7.61231C6.53771 7.58451 6.55976 7.5515 6.57481 7.51518C6.58986 7.47885 6.5976 7.43992 6.5976 7.4006C6.5976 7.091 6.6804 6.9668 6.9276 6.8534L7.1346 6.7604C8.0016 6.368 8.652 5.852 8.652 4.7264V4.6646C8.652 3.4778 7.62 2.6 6.1536 2.6C4.6668 2.6 3.552 3.4568 3.552 4.8404ZM5.1 9.4946C5.1 10.0148 5.4954 10.4 5.9946 10.4C6.5046 10.4 6.9 10.0148 6.9 9.4946C6.9 8.9744 6.5046 8.6 5.9946 8.6C5.4954 8.6 5.1 8.9744 5.1 9.4946Z"
|
||||
fill="var(--text-color)"
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_764_1941">
|
||||
<rect
|
||||
width="12"
|
||||
height="12"
|
||||
fill="white"
|
||||
transform="translate(0 0.5)"
|
||||
/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function LogoutIcon() {
|
||||
return (
|
||||
<svg
|
||||
width="12"
|
||||
height="12"
|
||||
viewBox="0 0 12 12"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M4 3.5C4.00605 2.41248 4.05428 1.82353 4.43847 1.43934C4.87781 1 5.58489 1 6.99914 1H7.49914C8.91334 1 9.62044 1 10.0598 1.43934C10.4991 1.87868 10.4991 2.58578 10.4991 4V8C10.4991 9.4142 10.4991 10.1213 10.0598 10.5606C9.62044 11 8.91334 11 7.49914 11H6.99914C5.58489 11 4.87781 11 4.43847 10.5606C4.05428 10.1764 4.00605 9.5875 4 8.5"
|
||||
stroke="var(--text-color)"
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
<path
|
||||
opacity="0.5"
|
||||
d="M4 9.75C2.82149 9.75 2.23223 9.75 1.86611 9.3839C1.5 9.01775 1.5 8.4285 1.5 7.25V4.75C1.5 3.57149 1.5 2.98224 1.86611 2.61612C2.23223 2.25 2.82149 2.25 4 2.25"
|
||||
stroke="var(--text-color)"
|
||||
/>
|
||||
<path
|
||||
d="M7.5 6H3M3 6L4 7M3 6L4 5"
|
||||
stroke="var(--text-color)"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
|
@ -526,21 +526,21 @@ export const KebabIcon = () => {
|
|||
cy="1.35112"
|
||||
rx="1.4993"
|
||||
ry="1.27477"
|
||||
fill="#E1E1E1"
|
||||
fill="var(--text-color)"
|
||||
/>
|
||||
<ellipse
|
||||
cx="6.04868"
|
||||
cy="1.35131"
|
||||
rx="1.4993"
|
||||
ry="1.27477"
|
||||
fill="#E1E1E1"
|
||||
fill="var(--text-color)"
|
||||
/>
|
||||
<ellipse
|
||||
cx="10.5476"
|
||||
cy="1.35131"
|
||||
rx="1.4993"
|
||||
ry="1.27477"
|
||||
fill="#E1E1E1"
|
||||
fill="var(--text-color)"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
@ -559,7 +559,7 @@ export const DublicateIcon = () => {
|
|||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M14.3125 11.375C14.3125 11.7545 14.0045 12.0625 13.625 12.0625H8.125C7.7455 12.0625 7.4375 11.7545 7.4375 11.375V5.875C7.4375 5.4955 7.7455 5.1875 8.125 5.1875H13.625C14.0045 5.1875 14.3125 5.4955 14.3125 5.875V11.375ZM13.625 4.5H8.125C7.36566 4.5 6.75 5.11566 6.75 5.875V11.375C6.75 12.1343 7.36566 12.75 8.125 12.75H13.625C14.3843 12.75 15 12.1343 15 11.375V5.875C15 5.11566 14.3843 4.5 13.625 4.5ZM11.5625 14.125C11.5625 14.5045 11.2545 14.8125 10.875 14.8125H5.375C4.9955 14.8125 4.6875 14.5045 4.6875 14.125V8.625C4.6875 8.2455 4.9955 7.9375 5.375 7.9375H6.0625V7.25H5.375C4.61566 7.25 4 7.86566 4 8.625V14.125C4 14.8843 4.61566 15.5 5.375 15.5H10.875C11.6343 15.5 12.25 14.8843 12.25 14.125V13.4375H11.5625V14.125Z"
|
||||
fill="#6F42C1"
|
||||
fill="var(--text-color)"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
@ -576,31 +576,31 @@ export const DeleteIcon = () => {
|
|||
>
|
||||
<path
|
||||
d="M8.33301 10V13.3334"
|
||||
stroke="#5D5F63"
|
||||
stroke="var(--text-color)"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M11 10V13.3334"
|
||||
stroke="#5D5F63"
|
||||
stroke="var(--text-color)"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M4.33301 6.66406H15"
|
||||
stroke="#5D5F63"
|
||||
stroke="var(--text-color)"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M5.66699 8.66406V13.9976C5.66699 15.1022 6.56245 15.9976 7.66705 15.9976H11.6672C12.7718 15.9976 13.6672 15.1022 13.6672 13.9976V8.66406"
|
||||
stroke="#5D5F63"
|
||||
stroke="var(--text-color)"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M7.66699 5.33337C7.66699 4.59697 8.26396 4 9.00037 4H10.3337C11.0702 4 11.6671 4.59697 11.6671 5.33337V6.66675H7.66699V5.33337Z"
|
||||
stroke="#5D5F63"
|
||||
stroke="var(--text-color)"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
|
|
|
@ -636,3 +636,32 @@ export function SaveTemplateIcon({ isActive }: { isActive: boolean }) {
|
|||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function MeasureToolIcon({ isActive }: { isActive: boolean }) {
|
||||
return (
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M7.07495 10.248C7.58308 10.3976 8.18027 10.4818 8.82142 10.4821C9.67666 10.4811 10.4522 10.3335 11.0449 10.0758C11.341 9.94594 11.5943 9.78867 11.7886 9.5888C11.9812 9.39173 12.1201 9.13327 12.1197 8.84166C12.1201 8.55 11.9812 8.29125 11.7886 8.09447C11.4962 7.79625 11.0754 7.58705 10.5676 7.43531C10.0598 7.28569 9.46267 7.20117 8.82147 7.20117C7.96628 7.20183 7.19069 7.34939 6.59767 7.60748C6.30152 7.73737 6.0483 7.89459 5.854 8.09447C5.66144 8.29125 5.5225 8.55 5.52283 8.84166C5.5225 9.13327 5.66139 9.39169 5.854 9.5888C6.14627 9.88669 6.56748 10.0962 7.07495 10.248ZM6.36348 8.58806C6.51831 8.42423 6.84391 8.241 7.27586 8.11561C7.70744 7.98811 8.24336 7.9102 8.82142 7.91058C9.59177 7.9095 10.288 8.0498 10.7608 8.25764C10.9971 8.36016 11.1755 8.48002 11.2787 8.58811C11.384 8.69859 11.41 8.77795 11.4103 8.8417C11.41 8.90508 11.384 8.98477 11.2787 9.09525C11.1242 9.25875 10.7986 9.44231 10.3667 9.5677C9.93508 9.6952 9.39953 9.77278 8.82142 9.77278C8.05075 9.77344 7.35452 9.63351 6.88169 9.42572C6.64544 9.32316 6.46703 9.20334 6.36353 9.09525C6.25825 8.98477 6.23256 8.90508 6.23223 8.8417C6.23251 8.77791 6.2582 8.69855 6.36348 8.58806Z"
|
||||
fill={isActive ? "var(--highlight-accent-color)" : "var(--text-color)"}
|
||||
/>
|
||||
<path
|
||||
d="M13.5094 5.1167C13.5115 5.1174 13.5139 5.11806 13.516 5.11876L13.5433 5.12673L13.5094 5.1167Z"
|
||||
fill={isActive ? "var(--highlight-accent-color)" : "var(--text-color)"}
|
||||
/>
|
||||
<path
|
||||
d="M4.13305 18.8834C4.13028 18.8827 4.12747 18.8817 4.12471 18.8806L4.09039 18.8706L4.13305 18.8834Z"
|
||||
fill={isActive ? "var(--highlight-accent-color)" : "var(--text-color)"}
|
||||
/>
|
||||
<path
|
||||
d="M17.4073 11.4456V8.97325C17.4077 8.60876 17.3207 8.25954 17.1727 7.94355C17.0242 7.62686 16.8165 7.34174 16.5698 7.08811C15.8326 6.33495 14.751 5.79382 13.4528 5.40542L13.4458 5.40345C12.1412 5.01874 10.604 4.80163 8.9538 4.80127C6.75295 4.80325 4.75594 5.18436 3.23696 5.84525L3.23628 5.84561C2.47778 6.17821 1.82951 6.58256 1.33717 7.08842C1.0909 7.34174 0.883095 7.62686 0.734718 7.94355C0.586655 8.25958 0.499642 8.6088 0.500001 8.9733V15.0268C0.499642 15.3912 0.586655 15.7402 0.734718 16.0562C0.883095 16.3729 1.0909 16.658 1.33721 16.9116C2.07451 17.6648 3.15529 18.2059 4.4529 18.5939L4.46153 18.5966C5.76646 18.9814 7.30337 19.1981 8.95385 19.1988H23.5V11.4457H17.4073V11.4456ZM2.19167 7.91665C2.70288 7.38486 3.62108 6.88991 4.79849 6.54437C5.97562 6.19681 7.4093 5.99067 8.95385 5.99098C11.013 5.98999 12.8759 6.35844 14.1944 6.93573C14.8537 7.22319 15.3745 7.56311 15.7157 7.91665C16.0586 8.27283 16.2173 8.62367 16.2177 8.97325C16.2173 9.32243 16.0586 9.67368 15.7157 10.0299C15.2045 10.5613 14.2863 11.0562 13.1089 11.4018C11.9318 11.7497 10.4984 11.9555 8.9538 11.9555C6.89436 11.9562 5.0315 11.5878 3.71295 11.0108C3.05367 10.7233 2.53285 10.3834 2.19162 10.0299C1.84869 9.67368 1.69003 9.32243 1.68967 8.97325C1.69007 8.62372 1.84874 8.27283 2.19167 7.91665ZM16.2176 10.488V11.9555H13.6018C13.9049 11.8566 14.1973 11.751 14.4665 11.6335C15.1832 11.3198 15.7758 10.9441 16.2043 10.5026L16.2176 10.488ZM22.3103 18.009H20.2707V15.0058H19.5909V18.009H18.0898V16.3824H17.4099V18.009H15.9085V15.0058H15.2287V18.009H13.7272V16.3824H13.0474V18.009H11.5463V15.0058H10.8665V18.009H9.36542V16.3824H8.68558V18.003C8.16938 17.9951 7.66778 17.9645 7.18415 17.9128V15.0058H6.5043V17.8251C5.43642 17.6624 4.48039 17.4002 3.71295 17.0643C3.05367 16.7768 2.53285 16.4369 2.19162 16.0834C1.84869 15.7271 1.69003 15.3759 1.68967 15.0267V10.488L1.70296 10.5026C2.34598 11.1632 3.35609 11.683 4.60586 12.0538C5.85604 12.4229 7.34848 12.6351 8.95376 12.6354H22.3102V18.009H22.3103Z"
|
||||
fill={isActive ? "var(--highlight-accent-color)" : "var(--text-color)"}
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -179,8 +179,8 @@ export function MoveArrowRight() {
|
|||
<path
|
||||
d="M9.58363 6.16637L15.4173 12L9.58363 17.8336"
|
||||
stroke="var(--accent-color)"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
@ -198,8 +198,8 @@ export function MoveArrowLeft() {
|
|||
<path
|
||||
d="M14.4164 6.16637L8.58274 12L14.4164 17.8336"
|
||||
stroke="var(--accent-color)"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
import React from "react";
|
||||
import { KebabIcon } from "../../icons/ExportCommonIcons";
|
||||
import img from "../../../assets/image/image.png"
|
||||
|
||||
const DashboardCard:React.FC = () => {
|
||||
return (
|
||||
<div className="dashboard-card-container">
|
||||
<div className="preview-container">
|
||||
<img src={img} alt="" />
|
||||
</div>
|
||||
<div className="project-details-container">
|
||||
<div className="project-details">
|
||||
<div className="project-name">Untitled</div>
|
||||
<div className="project-data">24-12-2025</div>
|
||||
</div>
|
||||
<div className="users-list-container">
|
||||
<div className="user-profile">V</div>
|
||||
<KebabIcon />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardCard;
|
|
@ -0,0 +1,21 @@
|
|||
import React from "react";
|
||||
import DashboardCard from "./DashboardCard";
|
||||
import DashboardNavBar from "./DashboardNavBar";
|
||||
import MarketPlaceBanner from "./MarketPlaceBanner";
|
||||
|
||||
const DashboardHome: React.FC = () => {
|
||||
return (
|
||||
<div className="dashboard-home-container">
|
||||
<DashboardNavBar page={"home"} />
|
||||
<MarketPlaceBanner />
|
||||
<div className="container">
|
||||
<div className="header">Recents</div>
|
||||
<div className="cards-container">
|
||||
<DashboardCard />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardHome;
|
|
@ -0,0 +1,21 @@
|
|||
import React from "react";
|
||||
import { CartIcon } from "../../icons/ExportModuleIcons";
|
||||
import Search from "../../ui/inputs/Search";
|
||||
|
||||
interface DashboardNavBarProps {
|
||||
page: React.ReactNode;
|
||||
}
|
||||
|
||||
const DashboardNavBar: React.FC<DashboardNavBarProps> = ({ page }) => {
|
||||
return (
|
||||
<div className="dashboard-navbar-container">
|
||||
<div className="title">{page}</div>
|
||||
<div className="market-place-button">
|
||||
<CartIcon isActive /> Market Place
|
||||
</div>
|
||||
<Search onChange={() => {}} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardNavBar;
|
|
@ -0,0 +1,44 @@
|
|||
import React from "react";
|
||||
import banner from "../../../assets/image/banner.png";
|
||||
|
||||
const MarketPlaceBanner = () => {
|
||||
return (
|
||||
<div className="market-place-banner-container">
|
||||
{/* market place banner */}
|
||||
<img src={banner} alt="" />
|
||||
<div className="hero-text">
|
||||
NEW
|
||||
<br /> FALL
|
||||
<br /> COLLECTION
|
||||
</div>
|
||||
<div className="context">Unlock Creativity with Premium 3D Assets!</div>
|
||||
<div className="arrow-context">
|
||||
<svg
|
||||
width="169"
|
||||
height="120"
|
||||
viewBox="0 0 169 120"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M167.189 2C154.638 36.335 104.466 106.204 4.18872 111"
|
||||
stroke="white"
|
||||
stroke-width="3"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M10.662 118.326L1.59439 111.524L9.47334 103.374"
|
||||
stroke="white"
|
||||
stroke-width="3"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div className="explore-button">Explore Now</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default MarketPlaceBanner;
|
|
@ -0,0 +1,69 @@
|
|||
import React from "react";
|
||||
import {
|
||||
DocumentationIcon,
|
||||
HelpIcon,
|
||||
HomeIcon,
|
||||
LogoutIcon,
|
||||
NotificationIcon,
|
||||
ProjectsIcon,
|
||||
TutorialsIcon,
|
||||
} from "../../icons/DashboardIcon";
|
||||
import { SettingsIcon, TrashIcon } from "../../icons/ExportCommonIcons";
|
||||
|
||||
const SidePannel: React.FC = () => {
|
||||
const userName = localStorage.getItem("userName") || "Anonymous";
|
||||
return (
|
||||
<div className="side-pannel-container">
|
||||
<div className="side-pannel-header">
|
||||
<div className="user-container">
|
||||
<div className="user-profile">{userName[0]}</div>
|
||||
<div className="user-name">{userName}</div>
|
||||
</div>
|
||||
<div className="notifications-container">
|
||||
<NotificationIcon />
|
||||
</div>
|
||||
</div>
|
||||
<div className="new-project-button">+ New project</div>
|
||||
<div className="side-bar-content-container">
|
||||
<div className="side-bar-options-container">
|
||||
<div className="option-list active">
|
||||
<HomeIcon />
|
||||
Home
|
||||
</div>
|
||||
<div className="option-list" title="coming soon">
|
||||
<ProjectsIcon />
|
||||
Projects
|
||||
</div>
|
||||
<div className="option-list" title="coming soon">
|
||||
<TrashIcon />
|
||||
Trash
|
||||
</div>
|
||||
<div className="option-list" title="coming soon">
|
||||
<TutorialsIcon />
|
||||
Tutorials
|
||||
</div>
|
||||
<div className="option-list" title="coming soon">
|
||||
<DocumentationIcon />
|
||||
Documentation
|
||||
</div>
|
||||
</div>
|
||||
<div className="side-bar-options-container" title="coming soon">
|
||||
<div className="option-list">
|
||||
<SettingsIcon />
|
||||
Settings
|
||||
</div>
|
||||
<div className="option-list" style={{cursor: "pointer"}}>
|
||||
<LogoutIcon />
|
||||
Log out
|
||||
</div>
|
||||
<div className="option-list">
|
||||
<HelpIcon />
|
||||
Help & Feedback
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SidePannel;
|
|
@ -1,13 +1,105 @@
|
|||
import React, { useState } from "react";
|
||||
import Search from "../../ui/inputs/Search";
|
||||
|
||||
import vehicle from "../../../assets/image/vehicles.png";
|
||||
import workStation from "../../../assets/image/workStation.png";
|
||||
import machines from "../../../assets/image/machines.png";
|
||||
import feneration from "../../../assets/image/feneration.png";
|
||||
import worker from "../../../assets/image/worker.png";
|
||||
const Assets: React.FC = () => {
|
||||
const [searchValue, setSearchValue] = useState<string>("");
|
||||
const [selectedCategory, setSelectedCategory] = useState<string | null>(null);
|
||||
|
||||
const handleSearchChange = (value: string) => {
|
||||
setSearchValue(value);
|
||||
console.log(value); // Log the search value if needed
|
||||
setSelectedCategory(null); // Reset selected category when search changes
|
||||
};
|
||||
|
||||
const categoryList = [
|
||||
{
|
||||
assetName: "Doors",
|
||||
assetImage: "",
|
||||
category: "Feneration",
|
||||
categoryImage: feneration,
|
||||
},
|
||||
{
|
||||
assetName: "Windows",
|
||||
assetImage: "",
|
||||
category: "Feneration",
|
||||
categoryImage: feneration,
|
||||
},
|
||||
{
|
||||
assetName: "Pillars",
|
||||
assetImage: "",
|
||||
category: "Feneration",
|
||||
categoryImage: feneration,
|
||||
},
|
||||
{
|
||||
assetName: "Forklift",
|
||||
assetImage: "",
|
||||
category: "Vehicles",
|
||||
categoryImage: vehicle,
|
||||
},
|
||||
{
|
||||
assetName: "AGV",
|
||||
assetImage: "",
|
||||
category: "Vehicles",
|
||||
categoryImage: vehicle,
|
||||
},
|
||||
{
|
||||
assetName: "Pallet",
|
||||
assetImage: "",
|
||||
category: "Workstation",
|
||||
categoryImage: workStation,
|
||||
},
|
||||
{
|
||||
assetName: "Controller",
|
||||
assetImage: "",
|
||||
category: "Workstation",
|
||||
categoryImage: workStation,
|
||||
},
|
||||
{
|
||||
assetName: "Conveyor",
|
||||
assetImage: "",
|
||||
category: "Workstation",
|
||||
categoryImage: workStation,
|
||||
},
|
||||
{
|
||||
assetName: "VMC",
|
||||
assetImage: "",
|
||||
category: "Machines",
|
||||
categoryImage: machines,
|
||||
},
|
||||
{
|
||||
assetName: "CMC",
|
||||
assetImage: "",
|
||||
category: "Machines",
|
||||
categoryImage: machines,
|
||||
},
|
||||
{
|
||||
assetName: "Male Worker",
|
||||
assetImage: "",
|
||||
category: "Workers",
|
||||
categoryImage: worker,
|
||||
},
|
||||
{
|
||||
assetName: "Female Worker",
|
||||
assetImage: "",
|
||||
category: "Workers",
|
||||
categoryImage: worker,
|
||||
},
|
||||
];
|
||||
|
||||
// Get unique categories
|
||||
const uniqueCategories = Array.from(
|
||||
new Set(categoryList.map((asset) => asset.category))
|
||||
);
|
||||
|
||||
// Filter assets based on the selected category
|
||||
const filteredAssets =
|
||||
selectedCategory !== null
|
||||
? categoryList.filter((asset) => asset.category === selectedCategory)
|
||||
: [];
|
||||
|
||||
return (
|
||||
<div className="assets-container">
|
||||
<Search onChange={handleSearchChange} />
|
||||
|
@ -15,11 +107,58 @@ const Assets: React.FC = () => {
|
|||
<div className="searched-content">
|
||||
<p>Results for "{searchValue}"</p>
|
||||
</div>
|
||||
) : selectedCategory ? (
|
||||
<div className="assets-wrapper">
|
||||
{/* Back Button */}
|
||||
<div
|
||||
className="back-button"
|
||||
onClick={() => setSelectedCategory(null)}
|
||||
>
|
||||
← Back
|
||||
</div>
|
||||
<h2>{selectedCategory}</h2>
|
||||
<div className="assets-container">
|
||||
{filteredAssets.map((asset, index) => (
|
||||
<div key={index} className="assets">
|
||||
<img
|
||||
src={asset.assetImage}
|
||||
alt={asset.assetName}
|
||||
className="asset-image"
|
||||
/>
|
||||
<div className="asset-name">{asset.assetName}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
<div className="assets-wrapper">
|
||||
<h2>Categories</h2>
|
||||
<div className="categories-container">
|
||||
{uniqueCategories.map((category, index) => {
|
||||
const categoryInfo = categoryList.find(
|
||||
(asset) => asset.category === category
|
||||
);
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
className="category"
|
||||
onClick={() => setSelectedCategory(category)}
|
||||
>
|
||||
<img
|
||||
src={categoryInfo?.categoryImage || ""}
|
||||
alt={category}
|
||||
className="category-image"
|
||||
/>
|
||||
<div className="category-name">{category}</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Assets;
|
||||
|
||||
|
|
|
@ -22,7 +22,10 @@ const Header: React.FC = () => {
|
|||
<div
|
||||
className={`toggle-sidebar-ui-button ${!toggleUI ? "active" : ""}`}
|
||||
onClick={() => {
|
||||
if (activeModule !== "market") setToggleUI(!toggleUI);
|
||||
if (activeModule !== "market") {
|
||||
setToggleUI(!toggleUI);
|
||||
localStorage.setItem("navBarUi", JSON.stringify(!toggleUI));
|
||||
}
|
||||
}}
|
||||
>
|
||||
<ToggleSidebarIcon />
|
||||
|
|
|
@ -5,7 +5,6 @@ const Templates = () => {
|
|||
const { templates, removeTemplate } = useTemplateStore();
|
||||
const { setSelectedZone } = useSelectedZoneStore();
|
||||
|
||||
console.log("templates: ", templates);
|
||||
const handleDeleteTemplate = (id: string) => {
|
||||
removeTemplate(id);
|
||||
};
|
||||
|
|
|
@ -3,24 +3,26 @@ import ToggleHeader from "../../../../ui/inputs/ToggleHeader";
|
|||
import Widgets2D from "./Widgets2D";
|
||||
import Widgets3D from "./Widgets3D";
|
||||
import WidgetsFloating from "./WidgetsFloating";
|
||||
import { useWidgetSubOption } from "../../../../../store/store";
|
||||
|
||||
const Widgets = () => {
|
||||
const [activeOption, setActiveOption] = useState("2D");
|
||||
const { widgetSubOption, setWidgetSubOption } = useWidgetSubOption();
|
||||
|
||||
const handleToggleClick = (option: string) => {
|
||||
setActiveOption(option);
|
||||
setWidgetSubOption(option);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="widget-left-sideBar">
|
||||
<ToggleHeader
|
||||
options={["2D", "3D", "Floating"]}
|
||||
activeOption={activeOption}
|
||||
activeOption={widgetSubOption}
|
||||
handleClick={handleToggleClick}
|
||||
/>
|
||||
{activeOption === "2D" && <Widgets2D />}
|
||||
{activeOption === "3D" && <Widgets3D />}
|
||||
{activeOption === "Floating" && <WidgetsFloating />}
|
||||
{widgetSubOption === "2D" && <Widgets2D />}
|
||||
{widgetSubOption === "3D" && <Widgets3D />}
|
||||
{widgetSubOption === "Floating" && <WidgetsFloating />}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -24,9 +24,8 @@ const Widgets3D = () => {
|
|||
let crt = e.target
|
||||
if (crt instanceof HTMLElement) {
|
||||
const widget = crt.cloneNode(true) as HTMLElement;
|
||||
console.log('widget: ', widget);
|
||||
e.dataTransfer.setDragImage(widget,0,0)
|
||||
e.dataTransfer.effectAllowed="move"
|
||||
e.dataTransfer.setDragImage(widget, 0, 0)
|
||||
e.dataTransfer.effectAllowed = "move"
|
||||
}
|
||||
}}
|
||||
onPointerDown={() => {
|
||||
|
@ -41,7 +40,7 @@ const Widgets3D = () => {
|
|||
className="widget-image"
|
||||
src={widget.img}
|
||||
alt={widget.name}
|
||||
// draggable={false}
|
||||
// draggable={false}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
|
|
|
@ -1,49 +1,70 @@
|
|||
import React from "react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { AppDockIcon } from "../../icons/HeaderIcons";
|
||||
import orgImg from "../../../assets/orgTemp.png"
|
||||
import orgImg from "../../../assets/orgTemp.png";
|
||||
import { useActiveUsers } from "../../../store/store";
|
||||
import { getAvatarColor } from "../../../modules/collaboration/users/functions/getAvatarColor";
|
||||
import { ActiveUser } from "../../../types/users";
|
||||
import CollaborationPopup from "../../templates/CollaborationPopup";
|
||||
|
||||
const Header: React.FC = () => {
|
||||
const guestUsers = [
|
||||
{ value: "Nazria", color: "#43C06D" },
|
||||
{ value: "Name1", color: "#0050EB" },
|
||||
{ value: "Abigail", color: "#FF6600" },
|
||||
{ value: "Jack", color: "#488EF6" },
|
||||
]; // Example guest users array
|
||||
const { activeUsers } = useActiveUsers();
|
||||
const userName = localStorage.getItem("userName") || "Anonymous";
|
||||
|
||||
const guestUsers: ActiveUser[] = activeUsers.filter(
|
||||
(user: ActiveUser) => user.userName !== userName
|
||||
);
|
||||
|
||||
const [userManagement, setUserManagement] = useState(false);
|
||||
|
||||
return (
|
||||
<div className="header-container">
|
||||
<div className="options-container">
|
||||
<div className="share-button">Share</div>
|
||||
<div className="app-docker-button">
|
||||
<AppDockIcon />
|
||||
<>
|
||||
{userManagement && (
|
||||
<CollaborationPopup setUserManagement={setUserManagement} />
|
||||
)}
|
||||
<div className="header-container">
|
||||
<div className="options-container">
|
||||
<div
|
||||
className="share-button"
|
||||
onClick={() => {
|
||||
setUserManagement(true);
|
||||
}}
|
||||
>
|
||||
Share
|
||||
</div>
|
||||
<div className="app-docker-button">
|
||||
<AppDockIcon />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="split"></div>
|
||||
<div className="users-container">
|
||||
<div className="guest-users-container">
|
||||
{guestUsers.length > 3 && (
|
||||
<div className="other-guest">+{guestUsers.length - 3}</div>
|
||||
)}
|
||||
{guestUsers.slice(0, 3).map((user, index) => (
|
||||
<div className="split"></div>
|
||||
<div className="users-container">
|
||||
<div className="guest-users-container">
|
||||
{guestUsers.length > 3 && (
|
||||
<div className="other-guest">+{guestUsers.length - 3}</div>
|
||||
)}
|
||||
{guestUsers.slice(0, 3).map((user, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="user-profile"
|
||||
style={{ background: getAvatarColor(index) }}
|
||||
>
|
||||
{user.userName[0]}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="user-profile-container">
|
||||
<div
|
||||
key={index}
|
||||
className="user-profile"
|
||||
style={{ background: user.color }}
|
||||
style={{ background: "var(--accent-color)" }}
|
||||
>
|
||||
{user.value[0]}
|
||||
{userName[0]}
|
||||
</div>
|
||||
<div className="user-organization">
|
||||
<img src={orgImg} alt="" />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="user-profile-container">
|
||||
<div className="user-profile" style={{ background: "#48AC2A" }}>
|
||||
V
|
||||
</div>
|
||||
<div className="user-organization">
|
||||
<img src={orgImg} alt="" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -14,7 +14,10 @@ import ConveyorMechanics from "./mechanics/ConveyorMechanics";
|
|||
import Visualization from "./visualization/Visualization";
|
||||
import Analysis from "./analysis/Analysis";
|
||||
import Simulations from "./simulation/Simulations";
|
||||
import { useSelectedActionSphere } from "../../../store/store";
|
||||
import {
|
||||
useSelectedActionSphere,
|
||||
useselectedFloorItem,
|
||||
} from "../../../store/store";
|
||||
import GlobalProperties from "./properties/GlobalProperties";
|
||||
import AsstePropertiies from "./properties/AssetProperties";
|
||||
import ZoneProperties from "./properties/ZoneProperties";
|
||||
|
@ -25,6 +28,7 @@ const SideBarRight: React.FC = () => {
|
|||
const { toggleUI } = useToggleStore();
|
||||
const { selectedActionSphere } = useSelectedActionSphere();
|
||||
const { subModule, setSubModule } = useSubModuleStore();
|
||||
const { selectedFloorItem } = useselectedFloorItem();
|
||||
// Reset activeList whenever activeModule changes
|
||||
useEffect(() => {
|
||||
if (activeModule !== "simulation") setSubModule("properties");
|
||||
|
@ -38,8 +42,9 @@ const SideBarRight: React.FC = () => {
|
|||
<div className="sidebar-actions-container">
|
||||
{/* {activeModule === "builder" && ( */}
|
||||
<div
|
||||
className={`sidebar-action-list ${subModule === "properties" ? "active" : ""
|
||||
}`}
|
||||
className={`sidebar-action-list ${
|
||||
subModule === "properties" ? "active" : ""
|
||||
}`}
|
||||
onClick={() => setSubModule("properties")}
|
||||
>
|
||||
<PropertiesIcon isActive={subModule === "properties"} />
|
||||
|
@ -48,22 +53,25 @@ const SideBarRight: React.FC = () => {
|
|||
{activeModule === "simulation" && (
|
||||
<>
|
||||
<div
|
||||
className={`sidebar-action-list ${subModule === "mechanics" ? "active" : ""
|
||||
}`}
|
||||
className={`sidebar-action-list ${
|
||||
subModule === "mechanics" ? "active" : ""
|
||||
}`}
|
||||
onClick={() => setSubModule("mechanics")}
|
||||
>
|
||||
<MechanicsIcon isActive={subModule === "mechanics"} />
|
||||
</div>
|
||||
<div
|
||||
className={`sidebar-action-list ${subModule === "simulations" ? "active" : ""
|
||||
}`}
|
||||
className={`sidebar-action-list ${
|
||||
subModule === "simulations" ? "active" : ""
|
||||
}`}
|
||||
onClick={() => setSubModule("simulations")}
|
||||
>
|
||||
<SimulationIcon isActive={subModule === "simulations"} />
|
||||
</div>
|
||||
<div
|
||||
className={`sidebar-action-list ${subModule === "analysis" ? "active" : ""
|
||||
}`}
|
||||
className={`sidebar-action-list ${
|
||||
subModule === "analysis" ? "active" : ""
|
||||
}`}
|
||||
onClick={() => setSubModule("analysis")}
|
||||
>
|
||||
<AnalysisIcon isActive={subModule === "analysis"} />
|
||||
|
@ -75,12 +83,21 @@ const SideBarRight: React.FC = () => {
|
|||
{/* process builder */}
|
||||
{toggleUI &&
|
||||
subModule === "properties" &&
|
||||
activeModule !== "visualization" && (
|
||||
activeModule !== "visualization" &&
|
||||
!selectedFloorItem && (
|
||||
<div className="sidebar-right-container">
|
||||
<div className="sidebar-right-content-container">
|
||||
<GlobalProperties />
|
||||
{/* <ZoneProperties /> */}
|
||||
{/* <AsstePropertiies /> */}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{toggleUI &&
|
||||
subModule === "properties" &&
|
||||
activeModule !== "visualization" &&
|
||||
selectedFloorItem && (
|
||||
<div className="sidebar-right-container">
|
||||
<div className="sidebar-right-content-container">
|
||||
<AsstePropertiies />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
@ -89,9 +106,7 @@ const SideBarRight: React.FC = () => {
|
|||
activeModule === "builder" && (
|
||||
<div className="sidebar-right-container">
|
||||
<div className="sidebar-right-content-container">
|
||||
{/* <GlobalProperties /> */}
|
||||
<ZoneProperties />
|
||||
{/* <AsstePropertiies /> */}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
@ -99,20 +114,24 @@ const SideBarRight: React.FC = () => {
|
|||
|
||||
{toggleUI && activeModule === "simulation" && (
|
||||
<>
|
||||
{subModule === "mechanics" && selectedActionSphere && selectedActionSphere.path.type === "Conveyor" && (
|
||||
<div className="sidebar-right-container">
|
||||
<div className="sidebar-right-content-container">
|
||||
<ConveyorMechanics />
|
||||
{subModule === "mechanics" &&
|
||||
selectedActionSphere &&
|
||||
selectedActionSphere.path.type === "Conveyor" && (
|
||||
<div className="sidebar-right-container">
|
||||
<div className="sidebar-right-content-container">
|
||||
<ConveyorMechanics />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{subModule === "mechanics" && selectedActionSphere && selectedActionSphere.path.type === "Vehicle" && (
|
||||
<div className="sidebar-right-container">
|
||||
<div className="sidebar-right-content-container">
|
||||
{/* <VehicleMechanics /> */}
|
||||
)}
|
||||
{subModule === "mechanics" &&
|
||||
selectedActionSphere &&
|
||||
selectedActionSphere.path.type === "Vehicle" && (
|
||||
<div className="sidebar-right-container">
|
||||
<div className="sidebar-right-content-container">
|
||||
<VehicleMechanics />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
)}
|
||||
{subModule === "mechanics" && !selectedActionSphere && (
|
||||
<div className="sidebar-right-container">
|
||||
<div className="sidebar-right-content-container">
|
||||
|
|
|
@ -4,12 +4,16 @@ interface PositionInputProps {
|
|||
onChange: (value: string) => void; // Callback for value change
|
||||
placeholder?: string; // Optional placeholder
|
||||
type?: string; // Input type (e.g., text, number, email)
|
||||
value1?: number;
|
||||
value2?: number;
|
||||
}
|
||||
|
||||
const PositionInput: React.FC<PositionInputProps> = ({
|
||||
onChange,
|
||||
placeholder = "Enter value", // Default placeholder
|
||||
type = "number", // Default type
|
||||
value1 = "number",
|
||||
value2 = "number",
|
||||
}) => {
|
||||
return (
|
||||
<div className="custom-input-container">
|
||||
|
@ -22,6 +26,7 @@ const PositionInput: React.FC<PositionInputProps> = ({
|
|||
type={type}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
placeholder={placeholder}
|
||||
value={value2}
|
||||
/>
|
||||
</div>
|
||||
<div className="input-container">
|
||||
|
@ -31,6 +36,7 @@ const PositionInput: React.FC<PositionInputProps> = ({
|
|||
type={type}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
placeholder={placeholder}
|
||||
value={value1}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -4,17 +4,19 @@ interface RotationInputProps {
|
|||
onChange: (value: string) => void; // Callback for value change
|
||||
placeholder?: string; // Optional placeholder
|
||||
type?: string; // Input type (e.g., text, number, email)
|
||||
value?: number;
|
||||
}
|
||||
|
||||
const RotationInput: React.FC<RotationInputProps> = ({
|
||||
onChange,
|
||||
placeholder = "Enter value", // Default placeholder
|
||||
type = "number", // Default type
|
||||
value = "number",
|
||||
}) => {
|
||||
return (
|
||||
<div className="custom-input-container">
|
||||
<div className="header">Rotation</div>
|
||||
<div className="inputs-container" style={{display: "block"}}>
|
||||
<div className="inputs-container" style={{ display: "block" }}>
|
||||
<div className="input-container">
|
||||
<div className="custom-input-label">Rotate : </div>
|
||||
<input
|
||||
|
@ -22,6 +24,7 @@ const RotationInput: React.FC<RotationInputProps> = ({
|
|||
type={type}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
placeholder={placeholder}
|
||||
value={value}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -8,9 +8,7 @@ import {
|
|||
import RenameInput from "../../../ui/inputs/RenameInput";
|
||||
import InputWithDropDown from "../../../ui/inputs/InputWithDropDown";
|
||||
import LabledDropdown from "../../../ui/inputs/LabledDropdown";
|
||||
import RegularDropDown from "../../../ui/inputs/RegularDropDown";
|
||||
import { handleResize } from "../../../../functions/handleResizePannel";
|
||||
import EyeDropInput from "../../../ui/inputs/EyeDropInput";
|
||||
import { useSelectedActionSphere, useSelectedPath, useSimulationPaths } from "../../../../store/store";
|
||||
import * as THREE from 'three';
|
||||
import * as Types from '../../../../types/world/worldTypes';
|
||||
|
@ -218,7 +216,7 @@ const ConveyorMechanics: React.FC = () => {
|
|||
setSimulationPaths(updatedPaths);
|
||||
};
|
||||
|
||||
const handleSpeedChange = (speed: number) => {
|
||||
const handleSpeedChange = (speed: number | string) => {
|
||||
if (!selectedPath) return;
|
||||
|
||||
const updatedPaths = simulationPaths.map((path) =>
|
||||
|
@ -243,6 +241,7 @@ const ConveyorMechanics: React.FC = () => {
|
|||
uuid: THREE.MathUtils.generateUUID(),
|
||||
name: `Trigger ${triggerIndex + 1}`,
|
||||
type: '',
|
||||
bufferTime: 0,
|
||||
isUsed: false
|
||||
};
|
||||
|
||||
|
@ -298,8 +297,19 @@ const ConveyorMechanics: React.FC = () => {
|
|||
);
|
||||
|
||||
setSimulationPaths(updatedPaths);
|
||||
|
||||
// Ensure the selectedItem is updated immediately
|
||||
const updatedTrigger = updatedPaths
|
||||
.flatMap((path) => (path.type === "Conveyor" ? path.points : []))
|
||||
.flatMap((point) => point.triggers)
|
||||
.find((trigger) => trigger.uuid === uuid);
|
||||
|
||||
if (updatedTrigger) {
|
||||
setSelectedItem({ type: "trigger", item: updatedTrigger });
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Update the toggle handlers to immediately update the selected item
|
||||
const handleActionToggle = (uuid: string) => {
|
||||
if (!selectedActionSphere) return;
|
||||
|
@ -373,17 +383,61 @@ const ConveyorMechanics: React.FC = () => {
|
|||
}
|
||||
};
|
||||
|
||||
const handleTriggerBufferTimeChange = (uuid: string, bufferTime: number) => {
|
||||
if (!selectedActionSphere) return;
|
||||
|
||||
const updatedPaths = simulationPaths.map((path) =>
|
||||
path.type === "Conveyor"
|
||||
? {
|
||||
...path,
|
||||
points: path.points.map((point) =>
|
||||
point.uuid === selectedActionSphere.point.uuid
|
||||
? {
|
||||
...point,
|
||||
triggers: point.triggers.map((trigger) =>
|
||||
trigger.uuid === uuid ? { ...trigger, bufferTime } : trigger
|
||||
),
|
||||
}
|
||||
: point
|
||||
),
|
||||
}
|
||||
: path
|
||||
);
|
||||
|
||||
setSimulationPaths(updatedPaths);
|
||||
|
||||
// Immediately update selectedItem if it's the currently selected trigger
|
||||
if (selectedItem?.type === "trigger" && selectedItem.item.uuid === uuid) {
|
||||
setSelectedItem({
|
||||
...selectedItem,
|
||||
item: {
|
||||
...selectedItem.item,
|
||||
bufferTime
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const [selectedItem, setSelectedItem] = useState<{ type: "action" | "trigger"; item: any; } | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
setSelectedItem(null); // Reset selectedItem when selectedActionSphere changes
|
||||
setSelectedItem(null);
|
||||
}, [selectedActionSphere]);
|
||||
|
||||
return (
|
||||
<div className="machine-mechanics-container">
|
||||
<div className="machine-mechanics-header">
|
||||
{selectedActionSphere?.path?.modelName || "point name not found"}
|
||||
</div>
|
||||
{!selectedPath &&
|
||||
<div className="machine-mechanics-header">
|
||||
{selectedActionSphere?.path?.modelName || "point name not found"}
|
||||
</div>
|
||||
}
|
||||
|
||||
{selectedPath &&
|
||||
|
||||
<div className="machine-mechanics-header">
|
||||
{selectedPath.path.modelName || "path name not found"}
|
||||
</div>
|
||||
}
|
||||
|
||||
<div className="machine-mechanics-content-container">
|
||||
{!selectedPath &&
|
||||
|
@ -559,25 +613,45 @@ const ConveyorMechanics: React.FC = () => {
|
|||
options={["On-Hit", "Buffer"]}
|
||||
onSelect={(option) => handleTriggerSelect(selectedItem.item.uuid, option)}
|
||||
/>
|
||||
|
||||
{selectedItem.item.type === "Buffer" && (
|
||||
<InputWithDropDown
|
||||
label="Buffer Time"
|
||||
value={selectedItem.item.bufferTime.toString()}
|
||||
onChange={(value) => {
|
||||
handleTriggerBufferTimeChange(selectedItem.item.uuid, parseInt(value));
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
</>
|
||||
)}
|
||||
|
||||
{selectedPath && !selectedItem && (
|
||||
<div className="speed-control">
|
||||
<div key={selectedPath?.path.modeluuid || "none"} className="speed-control">
|
||||
<InputWithDropDown
|
||||
label="Conveyor Speed"
|
||||
value={selectedPath.path.speed.toString()}
|
||||
onChange={(value) => handleSpeedChange(parseFloat(value))}
|
||||
min={0}
|
||||
value={selectedPath.path.speed === "Inherit" ? "" : selectedPath.path.speed.toString()}
|
||||
onChange={(value) => handleSpeedChange((value === "") ? "Inherit" : parseInt(value))}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="footer">
|
||||
<InfoIcon />
|
||||
By selecting points, you can create events and triggers.
|
||||
</div>
|
||||
{!selectedPath && (
|
||||
<div className="footer">
|
||||
<InfoIcon />
|
||||
Configure the point's action and trigger properties.
|
||||
</div>
|
||||
)}
|
||||
{selectedPath && (
|
||||
<div className="footer">
|
||||
<InfoIcon />
|
||||
Configure the path properties.
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -1,561 +1,158 @@
|
|||
import React, { useRef, useState, useMemo, useEffect } from "react";
|
||||
import {
|
||||
AddIcon,
|
||||
InfoIcon,
|
||||
RemoveIcon,
|
||||
ResizeHeightIcon,
|
||||
} from "../../../icons/ExportCommonIcons";
|
||||
import RenameInput from "../../../ui/inputs/RenameInput";
|
||||
import React, { useRef, useMemo } from "react";
|
||||
import { InfoIcon } from "../../../icons/ExportCommonIcons";
|
||||
import InputWithDropDown from "../../../ui/inputs/InputWithDropDown";
|
||||
import LabledDropdown from "../../../ui/inputs/LabledDropdown";
|
||||
import RegularDropDown from "../../../ui/inputs/RegularDropDown";
|
||||
import { handleResize } from "../../../../functions/handleResizePannel";
|
||||
import EyeDropInput from "../../../ui/inputs/EyeDropInput";
|
||||
import { useSelectedActionSphere, useSelectedPath, useSimulationPaths } from "../../../../store/store";
|
||||
import * as THREE from 'three';
|
||||
import { useSelectedActionSphere, useSimulationPaths } from "../../../../store/store";
|
||||
import * as Types from '../../../../types/world/worldTypes';
|
||||
import InputToggle from "../../../ui/inputs/InputToggle";
|
||||
import LabledDropdown from "../../../ui/inputs/LabledDropdown";
|
||||
|
||||
const VehicleMechanics: React.FC = () => {
|
||||
const { selectedActionSphere } = useSelectedActionSphere();
|
||||
const { selectedPath, setSelectedPath } = useSelectedPath();
|
||||
const { simulationPaths, setSimulationPaths } = useSimulationPaths();
|
||||
|
||||
const actionsContainerRef = useRef<HTMLDivElement>(null);
|
||||
const triggersContainerRef = useRef<HTMLDivElement>(null);
|
||||
const propertiesContainerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const selectedPoint = useMemo(() => {
|
||||
if (!selectedActionSphere) return null;
|
||||
return simulationPaths
|
||||
.filter((path): path is Types.ConveyorEventsSchema => path.type === "Conveyor")
|
||||
.flatMap((path) => path.points)
|
||||
.find((point) => point.uuid === selectedActionSphere.point.uuid);
|
||||
const { selectedPoint, connectedPointUuids } = useMemo(() => {
|
||||
if (!selectedActionSphere?.point?.uuid) return { selectedPoint: null, connectedPointUuids: [] };
|
||||
|
||||
const vehiclePaths = simulationPaths.filter(
|
||||
(path): path is Types.VehicleEventsSchema => path.type === "Vehicle"
|
||||
);
|
||||
|
||||
const point = vehiclePaths.find(
|
||||
(path) => path.point.uuid === selectedActionSphere.point.uuid
|
||||
)?.point;
|
||||
|
||||
if (!point) return { selectedPoint: null, connectedPointUuids: [] };
|
||||
|
||||
const connectedUuids: string[] = [];
|
||||
if (point.connections?.targets) {
|
||||
point.connections.targets.forEach(target => {
|
||||
connectedUuids.push(target.pointUUID);
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
selectedPoint: point,
|
||||
connectedPointUuids: connectedUuids
|
||||
};
|
||||
}, [selectedActionSphere, simulationPaths]);
|
||||
|
||||
const handleAddAction = () => {
|
||||
if (!selectedActionSphere) return;
|
||||
const handleActionUpdate = React.useCallback((updatedAction: Partial<Types.VehicleEventsSchema['point']['actions']>) => {
|
||||
if (!selectedActionSphere?.point?.uuid) return;
|
||||
|
||||
const updatedPaths = simulationPaths.map((path) => {
|
||||
if (path.type === "Conveyor") {
|
||||
if (path.type === "Vehicle" && path.point.uuid === selectedActionSphere.point.uuid) {
|
||||
return {
|
||||
...path,
|
||||
points: path.points.map((point) => {
|
||||
if (point.uuid === selectedActionSphere.point.uuid) {
|
||||
const actionIndex = point.actions.length;
|
||||
const newAction = {
|
||||
uuid: THREE.MathUtils.generateUUID(),
|
||||
name: `Action ${actionIndex + 1}`,
|
||||
type: 'Inherit',
|
||||
material: 'Inherit',
|
||||
delay: 'Inherit',
|
||||
spawnInterval: 'Inherit',
|
||||
isUsed: false
|
||||
};
|
||||
|
||||
return { ...point, actions: [...point.actions, newAction] };
|
||||
point: {
|
||||
...path.point,
|
||||
actions: {
|
||||
...path.point.actions,
|
||||
...updatedAction
|
||||
}
|
||||
return point;
|
||||
}),
|
||||
}
|
||||
};
|
||||
}
|
||||
return path;
|
||||
});
|
||||
|
||||
setSimulationPaths(updatedPaths);
|
||||
};
|
||||
}, [selectedActionSphere?.point?.uuid, simulationPaths, setSimulationPaths]);
|
||||
|
||||
const handleDeleteAction = (uuid: string) => {
|
||||
if (!selectedActionSphere) return;
|
||||
const handleStartPointChange = React.useCallback((uuid: string) => {
|
||||
handleActionUpdate({ start: uuid });
|
||||
}, [handleActionUpdate]);
|
||||
|
||||
const updatedPaths = simulationPaths.map((path) =>
|
||||
path.type === "Conveyor"
|
||||
? {
|
||||
const handleEndPointChange = React.useCallback((uuid: string) => {
|
||||
handleActionUpdate({ end: uuid });
|
||||
}, [handleActionUpdate]);
|
||||
|
||||
const handleHitCountChange = React.useCallback((hitCount: number) => {
|
||||
handleActionUpdate({ hitCount });
|
||||
}, [handleActionUpdate]);
|
||||
|
||||
const handleBufferChange = React.useCallback((buffer: number) => {
|
||||
handleActionUpdate({ buffer });
|
||||
}, [handleActionUpdate]);
|
||||
|
||||
const handleSpeedChange = React.useCallback((speed: number) => {
|
||||
if (!selectedActionSphere?.point?.uuid) return;
|
||||
|
||||
const updatedPaths = simulationPaths.map((path) => {
|
||||
if (path.type === "Vehicle" && path.point.uuid === selectedActionSphere.point.uuid) {
|
||||
return {
|
||||
...path,
|
||||
points: path.points.map((point) =>
|
||||
point.uuid === selectedActionSphere.point.uuid
|
||||
? { ...point, actions: point.actions.filter(action => action.uuid !== uuid) }
|
||||
: point
|
||||
),
|
||||
}
|
||||
: path
|
||||
);
|
||||
|
||||
setSimulationPaths(updatedPaths);
|
||||
};
|
||||
|
||||
const handleActionSelect = (uuid: string, actionType: string) => {
|
||||
if (!selectedActionSphere) return;
|
||||
|
||||
const updatedPaths = simulationPaths.map((path) =>
|
||||
path.type === "Conveyor"
|
||||
? {
|
||||
...path,
|
||||
points: path.points.map((point) =>
|
||||
point.uuid === selectedActionSphere.point.uuid
|
||||
? {
|
||||
...point,
|
||||
actions: point.actions.map((action) =>
|
||||
action.uuid === uuid
|
||||
? {
|
||||
...action,
|
||||
type: actionType,
|
||||
material: actionType === 'Spawn' || actionType === 'Swap' ? 'Inherit' : action.material,
|
||||
delay: actionType === 'Delay' ? 'Inherit' : action.delay,
|
||||
spawnInterval: actionType === 'Spawn' ? 'Inherit' : action.spawnInterval
|
||||
}
|
||||
: action
|
||||
),
|
||||
}
|
||||
: point
|
||||
),
|
||||
}
|
||||
: path
|
||||
);
|
||||
|
||||
setSimulationPaths(updatedPaths);
|
||||
|
||||
// Update the selected item to reflect changes
|
||||
if (selectedItem?.type === "action" && selectedItem.item.uuid === uuid) {
|
||||
const updatedAction = updatedPaths
|
||||
.filter((path): path is Types.ConveyorEventsSchema => path.type === "Conveyor")
|
||||
.flatMap(path => path.points)
|
||||
.find(p => p.uuid === selectedActionSphere.point.uuid)
|
||||
?.actions.find(a => a.uuid === uuid);
|
||||
|
||||
if (updatedAction) {
|
||||
setSelectedItem({
|
||||
type: "action",
|
||||
item: updatedAction
|
||||
});
|
||||
point: {
|
||||
...path.point,
|
||||
speed: speed
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Modified handleMaterialSelect to ensure it only applies to relevant action types
|
||||
const handleMaterialSelect = (uuid: string, material: string) => {
|
||||
if (!selectedActionSphere) return;
|
||||
|
||||
const updatedPaths = simulationPaths.map((path) =>
|
||||
path.type === "Conveyor"
|
||||
? {
|
||||
...path,
|
||||
points: path.points.map((point) =>
|
||||
point.uuid === selectedActionSphere.point.uuid
|
||||
? {
|
||||
...point,
|
||||
actions: point.actions.map((action) =>
|
||||
action.uuid === uuid &&
|
||||
(action.type === 'Spawn' || action.type === 'Swap')
|
||||
? { ...action, material }
|
||||
: action
|
||||
),
|
||||
}
|
||||
: point
|
||||
),
|
||||
}
|
||||
: path
|
||||
);
|
||||
return path;
|
||||
});
|
||||
|
||||
setSimulationPaths(updatedPaths);
|
||||
|
||||
// Update selected item if it's the current action
|
||||
if (selectedItem?.type === "action" && selectedItem.item.uuid === uuid) {
|
||||
setSelectedItem({
|
||||
...selectedItem,
|
||||
item: {
|
||||
...selectedItem.item,
|
||||
material
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleDelayChange = (uuid: string, delay: number | string) => {
|
||||
if (!selectedActionSphere) return;
|
||||
|
||||
const updatedPaths = simulationPaths.map((path) =>
|
||||
path.type === "Conveyor"
|
||||
? {
|
||||
...path,
|
||||
points: path.points.map((point) =>
|
||||
point.uuid === selectedActionSphere.point.uuid
|
||||
? {
|
||||
...point,
|
||||
actions: point.actions.map((action) =>
|
||||
action.uuid === uuid ? { ...action, delay } : action
|
||||
),
|
||||
}
|
||||
: point
|
||||
),
|
||||
}
|
||||
: path
|
||||
);
|
||||
|
||||
setSimulationPaths(updatedPaths);
|
||||
};
|
||||
|
||||
const handleSpawnIntervalChange = (uuid: string, spawnInterval: number | string) => {
|
||||
if (!selectedActionSphere) return;
|
||||
|
||||
const updatedPaths = simulationPaths.map((path) =>
|
||||
path.type === "Conveyor"
|
||||
? {
|
||||
...path,
|
||||
points: path.points.map((point) =>
|
||||
point.uuid === selectedActionSphere.point.uuid
|
||||
? {
|
||||
...point,
|
||||
actions: point.actions.map((action) =>
|
||||
action.uuid === uuid ? { ...action, spawnInterval } : action
|
||||
),
|
||||
}
|
||||
: point
|
||||
),
|
||||
}
|
||||
: path
|
||||
);
|
||||
|
||||
setSimulationPaths(updatedPaths);
|
||||
};
|
||||
|
||||
const handleSpeedChange = (speed: number) => {
|
||||
if (!selectedPath) return;
|
||||
|
||||
const updatedPaths = simulationPaths.map((path) =>
|
||||
path.modeluuid === selectedPath.path.modeluuid ? { ...path, speed } : path
|
||||
);
|
||||
|
||||
setSimulationPaths(updatedPaths);
|
||||
setSelectedPath({ ...selectedPath, path: { ...selectedPath.path, speed } });
|
||||
};
|
||||
|
||||
const handleAddTrigger = () => {
|
||||
if (!selectedActionSphere) return;
|
||||
|
||||
const updatedPaths = simulationPaths.map((path) =>
|
||||
path.type === "Conveyor"
|
||||
? {
|
||||
...path,
|
||||
points: path.points.map((point) => {
|
||||
if (point.uuid === selectedActionSphere.point.uuid) {
|
||||
const triggerIndex = point.triggers.length;
|
||||
const newTrigger = {
|
||||
uuid: THREE.MathUtils.generateUUID(),
|
||||
name: `Trigger ${triggerIndex + 1}`,
|
||||
type: '',
|
||||
isUsed: false
|
||||
};
|
||||
|
||||
return { ...point, triggers: [...point.triggers, newTrigger] };
|
||||
}
|
||||
return point;
|
||||
}),
|
||||
}
|
||||
: path
|
||||
);
|
||||
|
||||
setSimulationPaths(updatedPaths);
|
||||
};
|
||||
|
||||
const handleDeleteTrigger = (uuid: string) => {
|
||||
if (!selectedActionSphere) return;
|
||||
|
||||
const updatedPaths = simulationPaths.map((path) =>
|
||||
path.type === "Conveyor"
|
||||
? {
|
||||
...path,
|
||||
points: path.points.map((point) =>
|
||||
point.uuid === selectedActionSphere.point.uuid
|
||||
? { ...point, triggers: point.triggers.filter(trigger => trigger.uuid !== uuid) }
|
||||
: point
|
||||
),
|
||||
}
|
||||
: path
|
||||
);
|
||||
|
||||
setSimulationPaths(updatedPaths);
|
||||
};
|
||||
|
||||
const handleTriggerSelect = (uuid: string, triggerType: string) => {
|
||||
if (!selectedActionSphere) return;
|
||||
|
||||
const updatedPaths = simulationPaths.map((path) =>
|
||||
path.type === "Conveyor"
|
||||
? {
|
||||
...path,
|
||||
points: path.points.map((point) =>
|
||||
point.uuid === selectedActionSphere.point.uuid
|
||||
? {
|
||||
...point,
|
||||
triggers: point.triggers.map((trigger) =>
|
||||
trigger.uuid === uuid ? { ...trigger, type: triggerType } : trigger
|
||||
),
|
||||
}
|
||||
: point
|
||||
),
|
||||
}
|
||||
: path
|
||||
);
|
||||
|
||||
setSimulationPaths(updatedPaths);
|
||||
};
|
||||
|
||||
// Update the toggle handlers to immediately update the selected item
|
||||
const handleActionToggle = (uuid: string) => {
|
||||
if (!selectedActionSphere) return;
|
||||
const updatedPaths = simulationPaths.map((path) =>
|
||||
path.type === "Conveyor"
|
||||
? {
|
||||
...path,
|
||||
points: path.points.map((point) =>
|
||||
point.uuid === selectedActionSphere.point.uuid
|
||||
? {
|
||||
...point,
|
||||
actions: point.actions.map((action) => ({
|
||||
...action,
|
||||
isUsed: action.uuid === uuid ? !action.isUsed : false,
|
||||
})),
|
||||
}
|
||||
: point
|
||||
),
|
||||
}
|
||||
: path
|
||||
);
|
||||
|
||||
setSimulationPaths(updatedPaths);
|
||||
|
||||
// Immediately update the selected item if it's the one being toggled
|
||||
if (selectedItem?.type === "action" && selectedItem.item.uuid === uuid) {
|
||||
setSelectedItem({
|
||||
...selectedItem,
|
||||
item: {
|
||||
...selectedItem.item,
|
||||
isUsed: !selectedItem.item.isUsed
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Do the same for trigger toggle
|
||||
const handleTriggerToggle = (uuid: string) => {
|
||||
if (!selectedActionSphere) return;
|
||||
|
||||
const updatedPaths = simulationPaths.map((path) =>
|
||||
path.type === "Conveyor"
|
||||
? {
|
||||
...path,
|
||||
points: path.points.map((point) =>
|
||||
point.uuid === selectedActionSphere.point.uuid
|
||||
? {
|
||||
...point,
|
||||
triggers: point.triggers.map((trigger) => ({
|
||||
...trigger,
|
||||
isUsed: trigger.uuid === uuid ? !trigger.isUsed : false,
|
||||
})),
|
||||
}
|
||||
: point
|
||||
),
|
||||
}
|
||||
: path
|
||||
);
|
||||
|
||||
setSimulationPaths(updatedPaths);
|
||||
|
||||
// Immediately update the selected item if it's the one being toggled
|
||||
if (selectedItem?.type === "trigger" && selectedItem.item.uuid === uuid) {
|
||||
setSelectedItem({
|
||||
...selectedItem,
|
||||
item: {
|
||||
...selectedItem.item,
|
||||
isUsed: !selectedItem.item.isUsed
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const [selectedItem, setSelectedItem] = useState<{ type: "action" | "trigger"; item: any; } | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
setSelectedItem(null); // Reset selectedItem when selectedActionSphere changes
|
||||
}, [selectedActionSphere]);
|
||||
}, [selectedActionSphere?.point?.uuid, simulationPaths, setSimulationPaths]);
|
||||
|
||||
return (
|
||||
<div className="machine-mechanics-container">
|
||||
<div className="machine-mechanics-container" key={selectedPoint?.uuid}>
|
||||
<div className="machine-mechanics-header">
|
||||
{selectedActionSphere?.path?.modelName || "point name not found"}
|
||||
{selectedActionSphere?.path?.modelName || "Vehicle point not found"}
|
||||
</div>
|
||||
|
||||
<div className="machine-mechanics-content-container">
|
||||
<div className="actions">
|
||||
<div className="header">
|
||||
<div className="header-value">Actions</div>
|
||||
<div className="add-button" onClick={handleAddAction}>
|
||||
<AddIcon /> Add
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="lists-main-container"
|
||||
ref={actionsContainerRef}
|
||||
style={{ height: "120px" }}
|
||||
>
|
||||
<div className="list-container">
|
||||
<>
|
||||
{console.log(selectedPoint)}
|
||||
</>
|
||||
</div>
|
||||
<div
|
||||
className="resize-icon"
|
||||
id="action-resize"
|
||||
onMouseDown={(e) => handleResize(e, actionsContainerRef)}
|
||||
>
|
||||
<ResizeHeightIcon />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="triggers">
|
||||
<div className="header">
|
||||
<div className="header-value">Triggers</div>
|
||||
<div className="add-button" onClick={handleAddTrigger}>
|
||||
<AddIcon /> Add
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="lists-main-container"
|
||||
ref={triggersContainerRef}
|
||||
style={{ height: "120px" }}
|
||||
>
|
||||
<div className="list-container">
|
||||
{selectedPoint?.triggers.map((trigger) => (
|
||||
<div
|
||||
key={trigger.uuid}
|
||||
className={`list-item ${selectedItem?.type === "trigger" &&
|
||||
selectedItem.item?.uuid === trigger.uuid
|
||||
? "active"
|
||||
: ""
|
||||
}`}
|
||||
>
|
||||
<div
|
||||
className="value"
|
||||
onClick={() => setSelectedItem({ type: "trigger", item: trigger })}
|
||||
>
|
||||
<RenameInput value={trigger.name} />
|
||||
</div>
|
||||
<div
|
||||
className="remove-button"
|
||||
onClick={() => handleDeleteTrigger(trigger.uuid)}
|
||||
>
|
||||
<RemoveIcon />
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div
|
||||
className="resize-icon"
|
||||
id="trigger-resize"
|
||||
onMouseDown={(e) => handleResize(e, triggersContainerRef)}
|
||||
>
|
||||
<ResizeHeightIcon />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="selected-properties-container">
|
||||
{selectedItem && (
|
||||
<div className="selected-properties-container" ref={propertiesContainerRef}>
|
||||
<div className="properties-header">Vehicle Properties</div>
|
||||
|
||||
{selectedPoint && (
|
||||
<>
|
||||
<div className="properties-header">{selectedItem.item.name}</div>
|
||||
<LabledDropdown
|
||||
key={`start-${selectedPoint.uuid}`}
|
||||
label="Start Point"
|
||||
defaultOption={selectedPoint.actions.start || "Select start point"}
|
||||
options={connectedPointUuids}
|
||||
onSelect={handleStartPointChange}
|
||||
/>
|
||||
|
||||
{selectedItem.type === "action" && (
|
||||
<>
|
||||
<InputToggle
|
||||
inputKey="enableTrigger"
|
||||
label="Enable Trigger"
|
||||
value={selectedItem.item.isUsed}
|
||||
onClick={() => handleActionToggle(selectedItem.item.uuid)}
|
||||
/>
|
||||
<LabledDropdown
|
||||
defaultOption={selectedItem.item.type}
|
||||
options={["Inherit", "Spawn", "Swap", "Despawn", "Delay"]}
|
||||
onSelect={(option) => handleActionSelect(selectedItem.item.uuid, option)}
|
||||
/>
|
||||
<LabledDropdown
|
||||
key={`end-${selectedPoint.uuid}`}
|
||||
label="End Point"
|
||||
defaultOption={selectedPoint.actions.end || "Select end point"}
|
||||
options={connectedPointUuids}
|
||||
onSelect={handleEndPointChange}
|
||||
/>
|
||||
|
||||
{/* Only show material dropdown for Spawn/Swap actions */}
|
||||
{(selectedItem.item.type === 'Spawn' || selectedItem.item.type === 'Swap') && (
|
||||
<LabledDropdown
|
||||
label={selectedItem.item.type === 'Spawn' ? 'Spawn Material' : 'Swap Material'}
|
||||
defaultOption={selectedItem.item.material}
|
||||
options={["Inherit", "Crate", "Box"]}
|
||||
onSelect={(option) => handleMaterialSelect(selectedItem.item.uuid, option)}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Only show delay input for Delay actions */}
|
||||
{selectedItem.item.type === 'Delay' && (
|
||||
<InputWithDropDown
|
||||
label="Delay Time"
|
||||
value={selectedItem.item.delay === 'Inherit'
|
||||
? undefined
|
||||
: selectedItem.item.delay}
|
||||
onChange={(value) => {
|
||||
const numValue = parseInt(value);
|
||||
handleDelayChange(
|
||||
selectedItem.item.uuid,
|
||||
!value ? 'Inherit' : numValue
|
||||
);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Only show spawn interval for Spawn actions */}
|
||||
{selectedItem.item.type === 'Spawn' && (
|
||||
<InputWithDropDown
|
||||
label="Spawn Interval"
|
||||
min={0}
|
||||
defaultValue={selectedItem.item.spawnInterval === "Inherit" ? "" : selectedItem.item.spawnInterval.toString()}
|
||||
value={selectedItem.item.spawnInterval === "Inherit" ? "" : selectedItem.item.spawnInterval.toString()}
|
||||
onChange={(value) => {
|
||||
handleSpawnIntervalChange(selectedItem.item.uuid, (value === "") ? "Inherit" : parseInt(value));
|
||||
}}
|
||||
/>
|
||||
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
{selectedItem.type === "trigger" && (
|
||||
<>
|
||||
<InputToggle
|
||||
inputKey="enableTrigger"
|
||||
label="Enable Trigger"
|
||||
value={selectedItem.item.isUsed}
|
||||
onClick={() => handleTriggerToggle(selectedItem.item.uuid)}
|
||||
/>
|
||||
|
||||
<LabledDropdown
|
||||
defaultOption={selectedItem.item.type || "Select Trigger Type"}
|
||||
options={["On-Hit", "Buffer"]}
|
||||
onSelect={(option) => handleTriggerSelect(selectedItem.item.uuid, option)}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
{selectedPath && !selectedItem && (
|
||||
<div className="speed-control">
|
||||
<InputWithDropDown
|
||||
label="ConveyorEventsSchema Speed"
|
||||
value={selectedPath.path.speed.toString()}
|
||||
key={`hitcount-${selectedPoint.uuid}`}
|
||||
label="Hit Count"
|
||||
value={selectedPoint.actions.hitCount.toString()}
|
||||
onChange={(value) => handleHitCountChange(parseInt(value))}
|
||||
/>
|
||||
|
||||
<InputWithDropDown
|
||||
key={`buffer-${selectedPoint.uuid}`}
|
||||
label="Buffer Time"
|
||||
value={selectedPoint.actions.buffer.toString()}
|
||||
onChange={(value) => handleBufferChange(parseInt(value))}
|
||||
/>
|
||||
|
||||
<InputWithDropDown
|
||||
key={`speed-${selectedPoint.uuid}`}
|
||||
label="Vehicle Speed"
|
||||
value={selectedPoint.speed.toString()}
|
||||
onChange={(value) => handleSpeedChange(parseFloat(value))}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="footer">
|
||||
<InfoIcon />
|
||||
By selecting points, you can create events and triggers.
|
||||
Configure vehicle's movement and interaction properties.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default VehicleMechanics;
|
||||
export default React.memo(VehicleMechanics);
|
|
@ -1,9 +1,11 @@
|
|||
import React, { useState } from "react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import InputToggle from "../../../ui/inputs/InputToggle";
|
||||
import InputWithDropDown from "../../../ui/inputs/InputWithDropDown";
|
||||
import { RemoveIcon } from "../../../icons/ExportCommonIcons";
|
||||
import PositionInput from "../customInput/PositionInputs";
|
||||
import RotationInput from "../customInput/RotationInput";
|
||||
import { useselectedFloorItem } from "../../../../store/store";
|
||||
import * as THREE from "three";
|
||||
|
||||
interface UserData {
|
||||
id: number; // Unique identifier for the user data
|
||||
|
@ -14,7 +16,13 @@ interface UserData {
|
|||
const AssetProperties: React.FC = () => {
|
||||
const [userData, setUserData] = useState<UserData[]>([]); // State to track user data
|
||||
const [nextId, setNextId] = useState(1); // Unique ID for new entries
|
||||
const { selectedFloorItem } = useselectedFloorItem();
|
||||
let xValue = selectedFloorItem.position.x;
|
||||
let zValue = selectedFloorItem.position.z;
|
||||
let rotationRad = selectedFloorItem.rotation.y;
|
||||
let rotationDeg = THREE.MathUtils.radToDeg(rotationRad);
|
||||
|
||||
// useEffect(() => {}, [selectedFloorItem]);
|
||||
// Function to handle adding new user data
|
||||
const handleAddUserData = () => {
|
||||
const newUserData: UserData = {
|
||||
|
@ -45,12 +53,16 @@ const AssetProperties: React.FC = () => {
|
|||
return (
|
||||
<div className="asset-properties-container">
|
||||
{/* Name */}
|
||||
<div className="header">Selected Object</div>
|
||||
<div className="header">{selectedFloorItem.userData.name}</div>
|
||||
|
||||
<div className="split"></div>
|
||||
|
||||
<PositionInput onChange={() => { }} />
|
||||
<RotationInput onChange={() => { }} />
|
||||
<PositionInput
|
||||
onChange={() => {}}
|
||||
value1={xValue.toFixed(5)}
|
||||
value2={zValue.toFixed(5)}
|
||||
/>
|
||||
<RotationInput onChange={() => {}} value={rotationDeg} />
|
||||
|
||||
<div className="split"></div>
|
||||
|
||||
|
|
|
@ -3,8 +3,31 @@ import InputRange from "../../../ui/inputs/InputRange";
|
|||
import InputToggle from "../../../ui/inputs/InputToggle";
|
||||
import { AI_Icon } from "../../../icons/ExportCommonIcons";
|
||||
import LabeledButton from "../../../ui/inputs/LabledButton";
|
||||
import {
|
||||
useAzimuth,
|
||||
useElevation,
|
||||
useRenderDistance,
|
||||
useResetCamera,
|
||||
useRoofVisibility,
|
||||
useSelectedWallItem,
|
||||
useShadows,
|
||||
useSocketStore,
|
||||
useToggleView,
|
||||
useWallVisibility,
|
||||
} from "../../../../store/store";
|
||||
import { setEnvironment } from "../../../../services/factoryBuilder/environment/setEnvironment";
|
||||
|
||||
const GlobalProperties: React.FC = () => {
|
||||
const { toggleView, setToggleView } = useToggleView();
|
||||
const { selectedWallItem, setSelectedWallItem } = useSelectedWallItem();
|
||||
const { roofVisibility, setRoofVisibility } = useRoofVisibility();
|
||||
const { wallVisibility, setWallVisibility } = useWallVisibility();
|
||||
const { shadows, setShadows } = useShadows();
|
||||
const { resetCamera, setResetCamera } = useResetCamera();
|
||||
const { elevation, setElevation } = useElevation();
|
||||
const { azimuth, setAzimuth } = useAzimuth();
|
||||
const { renderDistance, setRenderDistance } = useRenderDistance();
|
||||
const { socket } = useSocketStore();
|
||||
const [limitDistance, setLimitDistance] = useState(false);
|
||||
const [distance, setDistance] = useState<number>(5);
|
||||
|
||||
|
@ -23,6 +46,93 @@ const GlobalProperties: React.FC = () => {
|
|||
function updateGridDistance(value: number) {
|
||||
setGridDistance(value);
|
||||
}
|
||||
// Function to toggle roof visibility
|
||||
const changeRoofVisibility = async () => {
|
||||
const email = localStorage.getItem("email");
|
||||
const organization = email!.split("@")[1].split(".")[0];
|
||||
|
||||
//using REST
|
||||
const data = await setEnvironment(
|
||||
organization,
|
||||
localStorage.getItem("userId")!,
|
||||
wallVisibility,
|
||||
!roofVisibility,
|
||||
shadows
|
||||
);
|
||||
// console.log('data: ', data);
|
||||
|
||||
//using Socket
|
||||
// const visData = {
|
||||
// organization: organization,
|
||||
// userId: localStorage.getItem('userId')!,
|
||||
// wallVisibility: wallVisibility,
|
||||
// roofVisibility: !roofVisibility,
|
||||
// shadowVisibility: shadows,
|
||||
// socketId: socket.id
|
||||
// };
|
||||
// socket.emit('v1:Environment:set', visData)
|
||||
|
||||
setRoofVisibility(!roofVisibility); // Toggle roof visibility
|
||||
};
|
||||
// Function to toggle wall visibility
|
||||
const changeWallVisibility = async () => {
|
||||
const email = localStorage.getItem("email");
|
||||
const organization = email!.split("@")[1].split(".")[0];
|
||||
//using REST
|
||||
const data = await setEnvironment(
|
||||
organization,
|
||||
localStorage.getItem("userId")!,
|
||||
!wallVisibility,
|
||||
roofVisibility,
|
||||
shadows
|
||||
);
|
||||
// console.log('data: ', data);
|
||||
|
||||
//using Socket
|
||||
// const visData = {
|
||||
// organization: organization,
|
||||
// userId: localStorage.getItem('userId')!,
|
||||
// wallVisibility: !wallVisibility,
|
||||
// roofVisibility: roofVisibility,
|
||||
// shadowVisibility: shadows,
|
||||
// socketId: socket.id
|
||||
// };
|
||||
// socket.emit('v1:Environment:set', visData)
|
||||
|
||||
setWallVisibility(!wallVisibility); // Toggle wall visibility
|
||||
};
|
||||
|
||||
const shadowVisibility = async () => {
|
||||
const email = localStorage.getItem("email");
|
||||
const organization = email!.split("@")[1].split(".")[0];
|
||||
//using REST
|
||||
const data = await setEnvironment(
|
||||
organization,
|
||||
localStorage.getItem("userId")!,
|
||||
wallVisibility,
|
||||
roofVisibility,
|
||||
!shadows
|
||||
);
|
||||
// console.log('data: ', data);
|
||||
|
||||
//using Socket
|
||||
// const visData = {
|
||||
// organization: organization,
|
||||
// userId: localStorage.getItem('userId')!,
|
||||
// wallVisibility: wallVisibility,
|
||||
// roofVisibility: roofVisibility,
|
||||
// shadowVisibility: !shadows,
|
||||
// socketId: socket.id
|
||||
// };
|
||||
// socket.emit('v1:Environment:set', visData)
|
||||
|
||||
setShadows(!shadows);
|
||||
};
|
||||
const toggleResetCamera = () => {
|
||||
if (!toggleView) {
|
||||
setResetCamera(true); // Trigger reset camera action
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="global-properties-container">
|
||||
|
@ -34,10 +144,29 @@ const GlobalProperties: React.FC = () => {
|
|||
|
||||
<div className="split"></div>
|
||||
|
||||
<InputToggle inputKey="1" label="Roof Visibility" />
|
||||
<InputToggle inputKey="2" label="Wall Visibility" />
|
||||
<InputToggle inputKey="3" label="Shadows Visibility" />
|
||||
<LabeledButton label="Reset Camera" onClick={() => {}} value="Reset"/>
|
||||
<InputToggle
|
||||
value={roofVisibility}
|
||||
inputKey="1"
|
||||
label="Roof Visibility"
|
||||
onClick={changeRoofVisibility}
|
||||
/>
|
||||
<InputToggle
|
||||
value={wallVisibility}
|
||||
inputKey="2"
|
||||
label="Wall Visibility"
|
||||
onClick={changeWallVisibility}
|
||||
/>
|
||||
<InputToggle
|
||||
value={shadows}
|
||||
inputKey="3"
|
||||
label="Shadows Visibility"
|
||||
onClick={shadowVisibility}
|
||||
/>
|
||||
<LabeledButton
|
||||
label="Reset Camera"
|
||||
onClick={toggleResetCamera}
|
||||
value="Reset"
|
||||
/>
|
||||
|
||||
<div className="split"></div>
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ import RenameInput from "../../../ui/inputs/RenameInput";
|
|||
import Vector3Input from "../customInput/Vector3Input";
|
||||
import { useSelectedZoneStore } from "../../../../store/useZoneStore";
|
||||
import { useEditPosition, usezonePosition, usezoneTarget } from "../../../../store/store";
|
||||
import { zoneCameraUpdate } from "../../../../services/realTimeVisulization/zoneData/zoneCameraUpdation";
|
||||
|
||||
const ZoneProperties: React.FC = () => {
|
||||
const { Edit, setEdit } = useEditPosition();
|
||||
|
@ -15,12 +16,25 @@ const ZoneProperties: React.FC = () => {
|
|||
setZoneTarget(selectedZone.zoneViewPortTarget)
|
||||
}, [selectedZone?.zoneViewPortPosition, selectedZone?.zoneViewPortTarget])
|
||||
|
||||
function handleSetView() {
|
||||
console.log("setApi");
|
||||
|
||||
console.log('zoneTarget: ', zoneTarget);
|
||||
console.log('zonePosition: ', zonePosition);
|
||||
setEdit(false);
|
||||
async function handleSetView() {
|
||||
try {
|
||||
const email = localStorage.getItem("email") || "";
|
||||
const organization = email?.split("@")[1]?.split(".")[0];
|
||||
|
||||
let zonesdata = {
|
||||
zoneId: selectedZone.zoneId,
|
||||
viewPortposition: zonePosition,
|
||||
viewPortCenter: zoneTarget
|
||||
};
|
||||
|
||||
let response = await zoneCameraUpdate(zonesdata, organization);
|
||||
console.log('response: ', response);
|
||||
|
||||
|
||||
setEdit(false);
|
||||
} catch (error) {
|
||||
console.error("Error in handleSetView:", error);
|
||||
}
|
||||
}
|
||||
|
||||
function handleEditView() {
|
||||
|
@ -36,7 +50,7 @@ const ZoneProperties: React.FC = () => {
|
|||
}
|
||||
|
||||
useEffect(() => {
|
||||
console.log("Updated selectedZone: ", selectedZone);
|
||||
|
||||
}, [selectedZone]);
|
||||
|
||||
return (
|
||||
|
|
|
@ -0,0 +1,177 @@
|
|||
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 { useSelectedZoneStore } from "../../../../../store/useZoneStore";
|
||||
import { useWidgetStore } from "../../../../../store/useWidgetStore";
|
||||
import axios from "axios";
|
||||
import RenameInput from "../../../../ui/inputs/RenameInput";
|
||||
|
||||
type Props = {};
|
||||
|
||||
const FlotingWidgetInput = (props: Props) => {
|
||||
const [widgetName, setWidgetName] = useState('Widget');
|
||||
const { setMeasurements, updateDuration, updateName } = useChartStore();
|
||||
const [duration, setDuration] = useState('1h')
|
||||
const [dropDowndata, setDropDownData] = useState({});
|
||||
const [selections, setSelections] = useState<Record<string, { name: string; fields: string }>>({});
|
||||
const { selectedZone } = useSelectedZoneStore();
|
||||
const { selectedChartId } = useWidgetStore();
|
||||
const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
|
||||
const email = localStorage.getItem("email") || "";
|
||||
const organization = email?.split("@")[1]?.split(".")[0]
|
||||
|
||||
useEffect(() => {
|
||||
const fetchZoneData = async () => {
|
||||
try {
|
||||
const response = await axios.get(`http://${iotApiUrl}/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(() => {
|
||||
const fetchSavedInputes = async () => {
|
||||
if (selectedChartId.id !== "") {
|
||||
try {
|
||||
const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/WidgetData/${selectedChartId.id}/${organization}`);
|
||||
if (response.status === 200) {
|
||||
setSelections(response.data.Data.measurements)
|
||||
setDuration(response.data.Data.duration)
|
||||
setWidgetName(response.data.widgetName)
|
||||
} else {
|
||||
console.log("Unexpected response:", response);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("There was an error!", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fetchSavedInputes();
|
||||
|
||||
}, [selectedChartId.id]);
|
||||
|
||||
// Sync Zustand state when component mounts
|
||||
useEffect(() => {
|
||||
setMeasurements(selections);
|
||||
updateDuration(duration);
|
||||
updateName(widgetName);
|
||||
}, [selections, duration, widgetName]);
|
||||
|
||||
|
||||
const sendInputes = async (inputMeasurement: any, inputDuration: any, inputName: any) => {
|
||||
try {
|
||||
const response = await axios.post(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/floatwidget/save`, {
|
||||
organization: organization,
|
||||
zoneId: selectedZone.zoneId,
|
||||
widget: {
|
||||
id: selectedChartId.id,
|
||||
panel: selectedChartId.panel,
|
||||
widgetName: inputName,
|
||||
Data: {
|
||||
measurements: inputMeasurement,
|
||||
duration: inputDuration
|
||||
}
|
||||
}
|
||||
} as any);
|
||||
if (response.status === 200) {
|
||||
return true
|
||||
} else {
|
||||
console.log("Unexpected response:", response);
|
||||
return false
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("There was an error!", error);
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
const handleSelect = async (inputKey: string, selectedData: { name: string; fields: string } | null) => {
|
||||
|
||||
// async() => {
|
||||
const newSelections = { ...selections };
|
||||
if (selectedData === null) {
|
||||
delete newSelections[inputKey];
|
||||
} else {
|
||||
newSelections[inputKey] = selectedData;
|
||||
}
|
||||
// setMeasurements(newSelections); // Update Zustand store
|
||||
console.log(newSelections);
|
||||
if (await sendInputes(newSelections, duration, widgetName)) {
|
||||
setSelections(newSelections);
|
||||
}
|
||||
// sendInputes(newSelections, duration); // Send data to server
|
||||
// return newSelections;
|
||||
// };
|
||||
};
|
||||
|
||||
const handleSelectDuration = async (option: string) => {
|
||||
if (await sendInputes(selections, option, widgetName)) {
|
||||
setDuration(option);
|
||||
}
|
||||
// setDuration(option);
|
||||
};
|
||||
|
||||
const handleNameChange = async (name:any) => {
|
||||
console.log('name change requested',name);
|
||||
|
||||
if (await sendInputes(selections, duration, name)) {
|
||||
setWidgetName(name);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="inputs-wrapper">
|
||||
<div className="datas">
|
||||
<div className="datas__label">Title</div>
|
||||
<RenameInput value={selectedChartId?.title || "untited"} onRename={handleNameChange}/>
|
||||
</div>
|
||||
{[...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]} // Load from Zustand
|
||||
/>
|
||||
<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 FlotingWidgetInput;
|
|
@ -162,7 +162,7 @@ const LineGrapInput = (props: Props) => {
|
|||
const fetchSavedInputes = async () => {
|
||||
if (selectedChartId.id !== "") {
|
||||
try {
|
||||
const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_LOCAL_BASE_URL}/api/v2/WidgetData/${selectedChartId.id}/${organization}`);
|
||||
const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/WidgetData/${selectedChartId.id}/${organization}`);
|
||||
if (response.status === 200) {
|
||||
setSelections(response.data.Data.measurements)
|
||||
setDuration(response.data.Data.duration)
|
||||
|
@ -190,7 +190,7 @@ const LineGrapInput = (props: Props) => {
|
|||
|
||||
const sendInputes = async (inputMeasurement: any, inputDuration: any, inputName: any) => {
|
||||
try {
|
||||
const response = await axios.post(`http://${process.env.REACT_APP_SERVER_REST_API_LOCAL_BASE_URL}/api/v2/widget/save`, {
|
||||
const response = await axios.post(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/widget/save`, {
|
||||
organization: organization,
|
||||
zoneId: selectedZone.zoneId,
|
||||
widget: {
|
||||
|
@ -253,7 +253,7 @@ const LineGrapInput = (props: Props) => {
|
|||
<>
|
||||
<div className="inputs-wrapper">
|
||||
<div className="datas">
|
||||
<div className="datas__label">Widget name</div>
|
||||
<div className="datas__label">Title</div>
|
||||
<RenameInput value={selectedChartId?.title || "untited"} onRename={handleNameChange}/>
|
||||
</div>
|
||||
{[...Array(6)].map((_, index) => {
|
||||
|
|
|
@ -131,7 +131,11 @@ const Data = () => {
|
|||
|
||||
{/* Render groups dynamically */}
|
||||
{
|
||||
chartDataGroups[selectedChartId?.id] && <LineGrapInput />
|
||||
chartDataGroups[selectedChartId?.id] &&
|
||||
<>
|
||||
<div className="sideBarHeader">2D Widget Input</div>
|
||||
<LineGrapInput />
|
||||
</>
|
||||
}
|
||||
|
||||
{/* Info Box */}
|
||||
|
|
|
@ -50,7 +50,14 @@ const UserListTemplate: React.FC<UserListTemplateProps> = ({ user }) => {
|
|||
);
|
||||
};
|
||||
|
||||
const CollaborationPopup: React.FC = () => {
|
||||
interface CollaborateProps {
|
||||
setUserManagement: (value: boolean) => void;
|
||||
}
|
||||
|
||||
const CollaborationPopup: React.FC<CollaborateProps> = ({
|
||||
setUserManagement,
|
||||
}) => {
|
||||
const userName = localStorage.getItem("userName") || "Anonymous";
|
||||
const users = [
|
||||
{
|
||||
name: "Alice Johnson",
|
||||
|
@ -81,16 +88,31 @@ const CollaborationPopup: React.FC = () => {
|
|||
access: "Viewer",
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<RenderOverlay>
|
||||
<div className="collaboration-popup-wrapper">
|
||||
<div className="collaboration-popup-container">
|
||||
<div
|
||||
className="collaboration-popup-wrapper"
|
||||
onClick={() => {
|
||||
setUserManagement(false);
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="collaboration-popup-container"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
<div className="header">
|
||||
<div className="content">Share this file</div>
|
||||
<div className="content">
|
||||
<div className="copy-link-button">copy link</div>
|
||||
<div className="close-button">
|
||||
<div
|
||||
className="close-button"
|
||||
onClick={() => {
|
||||
setUserManagement(false);
|
||||
}}
|
||||
>
|
||||
<CloseIcon />
|
||||
</div>
|
||||
</div>
|
||||
|
@ -111,7 +133,10 @@ const CollaborationPopup: React.FC = () => {
|
|||
</div>
|
||||
<div className="users-list-container">
|
||||
<div className="you-container">
|
||||
<div className="your-name">User 1</div>
|
||||
<div className="your-name">
|
||||
<div className="user-profile">{userName[0]}</div>
|
||||
{userName}
|
||||
</div>
|
||||
<div className="indicater">you</div>
|
||||
</div>
|
||||
{users.map((user, index) => (
|
||||
|
|
|
@ -1,12 +1,24 @@
|
|||
import React from "react";
|
||||
import React, { useState } from "react";
|
||||
import RenameInput from "./inputs/RenameInput";
|
||||
import { ArrowIcon } from "../icons/ExportCommonIcons";
|
||||
import MenuBar from "./menu/menu";
|
||||
|
||||
const FileMenu: React.FC = () => {
|
||||
const [openMenu, setOpenMenu] = useState(false);
|
||||
return (
|
||||
<div className="project-dropdowm-container">
|
||||
<div className="project-name">
|
||||
<RenameInput value="untitled" />
|
||||
</div>
|
||||
<div
|
||||
className="more-options-button"
|
||||
onClick={() => {
|
||||
setOpenMenu(!openMenu);
|
||||
}}
|
||||
>
|
||||
<ArrowIcon />
|
||||
{openMenu && <MenuBar setOpenMenu={setOpenMenu} />}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React from "react";
|
||||
import React, { useEffect } from "react";
|
||||
import useModuleStore from "../../store/useModuleStore";
|
||||
import {
|
||||
BuilderIcon,
|
||||
|
@ -7,18 +7,20 @@ import {
|
|||
VisualizationIcon,
|
||||
} from "../icons/ExportModuleIcons";
|
||||
import useToggleStore from "../../store/useUIToggleStore";
|
||||
import { useSelectedZoneStore } from "../../store/useZoneStore";
|
||||
|
||||
const ModuleToggle: React.FC = () => {
|
||||
const { activeModule, setActiveModule } = useModuleStore();
|
||||
const { setToggleUI } = useToggleStore();
|
||||
|
||||
|
||||
return (
|
||||
<div className="module-toggle-container">
|
||||
<div
|
||||
className={`module-list ${activeModule === "builder" && "active"}`}
|
||||
onClick={() => {
|
||||
setActiveModule("builder");
|
||||
setToggleUI(true);
|
||||
setToggleUI(localStorage.getItem('navBarUi') ? localStorage.getItem('navBarUi') === 'true' : true)
|
||||
}}
|
||||
>
|
||||
<div className="icon">
|
||||
|
@ -30,7 +32,7 @@ const ModuleToggle: React.FC = () => {
|
|||
className={`module-list ${activeModule === "simulation" && "active"}`}
|
||||
onClick={() => {
|
||||
setActiveModule("simulation");
|
||||
setToggleUI(true);
|
||||
setToggleUI(localStorage.getItem('navBarUi') ? localStorage.getItem('navBarUi') === 'true' : true)
|
||||
}}
|
||||
>
|
||||
<div className="icon">
|
||||
|
@ -39,12 +41,11 @@ const ModuleToggle: React.FC = () => {
|
|||
<div className="module">Simulation</div>
|
||||
</div>
|
||||
<div
|
||||
className={`module-list ${
|
||||
activeModule === "visualization" && "active"
|
||||
}`}
|
||||
className={`module-list ${activeModule === "visualization" && "active"
|
||||
}`}
|
||||
onClick={() => {
|
||||
setActiveModule("visualization");
|
||||
setToggleUI(true);
|
||||
setToggleUI(localStorage.getItem('navBarUi') ? localStorage.getItem('navBarUi') === 'true' : true)
|
||||
}}
|
||||
>
|
||||
<div className="icon">
|
||||
|
|
|
@ -3,8 +3,10 @@ import {
|
|||
AsileIcon,
|
||||
CommentIcon,
|
||||
CursorIcon,
|
||||
DeleteIcon,
|
||||
FloorIcon,
|
||||
FreeMoveIcon,
|
||||
MeasureToolIcon,
|
||||
PenIcon,
|
||||
PlayIcon,
|
||||
SaveTemplateIcon,
|
||||
|
@ -12,23 +14,30 @@ import {
|
|||
ZoneIcon,
|
||||
} from "../icons/ExportToolsIcons";
|
||||
import { ArrowIcon, TickIcon } from "../icons/ExportCommonIcons";
|
||||
import useModuleStore from "../../store/useModuleStore";
|
||||
import useModuleStore, { useThreeDStore } from "../../store/useModuleStore";
|
||||
import { handleSaveTemplate } from "../../modules/visualization/handleSaveTemplate";
|
||||
import { usePlayButtonStore } from "../../store/usePlayButtonStore";
|
||||
import useTemplateStore from "../../store/useTemplateStore";
|
||||
import { useSelectedZoneStore } from "../../store/useZoneStore";
|
||||
import {
|
||||
useActiveTool,
|
||||
useAddAction,
|
||||
useDeleteModels,
|
||||
useDeletePointOrLine,
|
||||
useMovePoint,
|
||||
useRefTextUpdate,
|
||||
useSelectedWallItem,
|
||||
useToggleView,
|
||||
useToolMode,
|
||||
useTransformMode,
|
||||
} from "../../store/store";
|
||||
import useToggleStore from "../../store/useUIToggleStore";
|
||||
|
||||
const Tools: React.FC = () => {
|
||||
const { templates } = useTemplateStore();
|
||||
const [activeTool, setActiveTool] = useState("cursor");
|
||||
const [activeSubTool, setActiveSubTool] = useState("cursor");
|
||||
const [toggleThreeD, setToggleThreeD] = useState(true);
|
||||
const { toggleThreeD, setToggleThreeD } = useThreeDStore();
|
||||
const { setToggleUI } = useToggleStore();
|
||||
|
||||
const dropdownRef = useRef<HTMLDivElement>(null);
|
||||
const [openDrop, setOpenDrop] = useState(false);
|
||||
|
@ -39,12 +48,27 @@ const Tools: React.FC = () => {
|
|||
const { selectedZone } = useSelectedZoneStore();
|
||||
|
||||
// wall options
|
||||
const { setToggleView } = useToggleView();
|
||||
const { toggleView, setToggleView } = useToggleView();
|
||||
const { setDeleteModels } = useDeleteModels();
|
||||
const { setAddAction } = useAddAction();
|
||||
const { setSelectedWallItem } = useSelectedWallItem();
|
||||
|
||||
const { transformMode, setTransformMode } = useTransformMode();
|
||||
const { deletePointOrLine, setDeletePointOrLine } = useDeletePointOrLine();
|
||||
const { movePoint, setMovePoint } = useMovePoint();
|
||||
const { toolMode, setToolMode } = useToolMode();
|
||||
const { activeTool, setActiveTool } = useActiveTool();
|
||||
const { refTextupdate, setRefTextUpdate } = useRefTextUpdate();
|
||||
|
||||
// Reset activeTool whenever activeModule changes
|
||||
useEffect(() => {
|
||||
setToggleUI(
|
||||
localStorage.getItem("navBarUi")
|
||||
? localStorage.getItem("navBarUi") === "true"
|
||||
: true
|
||||
);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setActiveTool(activeSubTool);
|
||||
setActiveSubTool(activeSubTool);
|
||||
|
@ -56,10 +80,18 @@ const Tools: React.FC = () => {
|
|||
setDeleteModels(false);
|
||||
setAddAction(null);
|
||||
setToggleView(true);
|
||||
// localStorage.setItem("navBarUi", JSON.stringify(!toggleThreeD));
|
||||
} else {
|
||||
setToggleView(false);
|
||||
}
|
||||
setToggleUI(
|
||||
localStorage.getItem("navBarUi")
|
||||
? localStorage.getItem("navBarUi") === "true"
|
||||
: true
|
||||
);
|
||||
setToggleThreeD(!toggleThreeD);
|
||||
setActiveSubTool("cursor");
|
||||
setActiveTool("cursor");
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -84,6 +116,90 @@ const Tools: React.FC = () => {
|
|||
document.removeEventListener("keydown", handleEscKeyPress); // Clean up the event listener
|
||||
};
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
if (!toggleThreeD) {
|
||||
setToggleUI(false);
|
||||
}
|
||||
}, [toggleThreeD]);
|
||||
|
||||
useEffect(() => {
|
||||
setToolMode(null);
|
||||
setDeleteModels(false);
|
||||
setAddAction(null);
|
||||
setTransformMode(null);
|
||||
setMovePoint(false);
|
||||
setDeletePointOrLine(false);
|
||||
setRefTextUpdate((prevUpdate) => prevUpdate - 1);
|
||||
|
||||
switch (activeTool) {
|
||||
case "Move":
|
||||
if (toggleView) {
|
||||
setMovePoint(true);
|
||||
} else {
|
||||
setTransformMode("translate");
|
||||
}
|
||||
break;
|
||||
|
||||
case "Rotate":
|
||||
if (!toggleView) {
|
||||
setTransformMode("rotate");
|
||||
}
|
||||
break;
|
||||
|
||||
case "Scale":
|
||||
if (!toggleView) {
|
||||
setTransformMode("scale");
|
||||
}
|
||||
break;
|
||||
|
||||
case "draw-wall":
|
||||
if (toggleView) {
|
||||
setToolMode("Wall");
|
||||
}
|
||||
break;
|
||||
|
||||
case "draw-aisle":
|
||||
if (toggleView) {
|
||||
setToolMode("Aisle");
|
||||
}
|
||||
break;
|
||||
|
||||
case "draw-zone":
|
||||
if (toggleView) {
|
||||
setToolMode("Zone");
|
||||
}
|
||||
break;
|
||||
|
||||
case "draw-floor":
|
||||
if (toggleView) {
|
||||
setToolMode("Floor");
|
||||
}
|
||||
break;
|
||||
|
||||
case "measure":
|
||||
setToolMode("MeasurementScale");
|
||||
break;
|
||||
|
||||
case "Add pillar":
|
||||
if (!toggleView) {
|
||||
setAddAction("pillar");
|
||||
}
|
||||
break;
|
||||
|
||||
case "delete":
|
||||
if (toggleView) {
|
||||
setDeletePointOrLine(true);
|
||||
} else {
|
||||
setDeleteModels(true);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
setActiveTool(activeTool);
|
||||
}, [activeTool]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -116,13 +232,24 @@ const Tools: React.FC = () => {
|
|||
<FreeMoveIcon isActive={activeTool === "free-hand"} />
|
||||
</div>
|
||||
)}
|
||||
{activeSubTool == "delete" && (
|
||||
<div
|
||||
className={`tool-button ${
|
||||
activeTool === "delete" ? "active" : ""
|
||||
}`}
|
||||
onClick={() => {
|
||||
setActiveTool("delete");
|
||||
}}
|
||||
>
|
||||
<DeleteIcon isActive={activeTool === "delete"} />
|
||||
</div>
|
||||
)}
|
||||
{activeModule !== "visualization" && (
|
||||
<div
|
||||
className="drop-down-option-button"
|
||||
ref={dropdownRef}
|
||||
onClick={() => {
|
||||
setOpenDrop(!openDrop);
|
||||
console.log(openDrop);
|
||||
}}
|
||||
>
|
||||
<ArrowIcon />
|
||||
|
@ -156,6 +283,20 @@ const Tools: React.FC = () => {
|
|||
<FreeMoveIcon isActive={false} />
|
||||
<div className="option">Free Hand</div>
|
||||
</div>
|
||||
<div
|
||||
className="option-list"
|
||||
onClick={() => {
|
||||
setOpenDrop(false);
|
||||
setActiveTool("delete");
|
||||
setActiveSubTool("delete");
|
||||
}}
|
||||
>
|
||||
<div className="active-option">
|
||||
{activeSubTool === "delete" && <TickIcon />}
|
||||
</div>
|
||||
<DeleteIcon isActive={false} />
|
||||
<div className="option">Delete</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
@ -173,6 +314,7 @@ const Tools: React.FC = () => {
|
|||
onClick={() => {
|
||||
setActiveTool("draw-wall");
|
||||
}}
|
||||
title="Wall"
|
||||
>
|
||||
<WallIcon isActive={activeTool === "draw-wall"} />
|
||||
</div>
|
||||
|
@ -183,6 +325,7 @@ const Tools: React.FC = () => {
|
|||
onClick={() => {
|
||||
setActiveTool("draw-zone");
|
||||
}}
|
||||
title="Zone"
|
||||
>
|
||||
<ZoneIcon isActive={activeTool === "draw-zone"} />
|
||||
</div>
|
||||
|
@ -193,6 +336,7 @@ const Tools: React.FC = () => {
|
|||
onClick={() => {
|
||||
setActiveTool("draw-aisle");
|
||||
}}
|
||||
title="Aisle"
|
||||
>
|
||||
<AsileIcon isActive={activeTool === "draw-aisle"} />
|
||||
</div>
|
||||
|
@ -203,12 +347,31 @@ const Tools: React.FC = () => {
|
|||
onClick={() => {
|
||||
setActiveTool("draw-floor");
|
||||
}}
|
||||
title="Floor"
|
||||
>
|
||||
<FloorIcon isActive={activeTool === "draw-floor"} />
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{activeModule === "builder" && (
|
||||
<>
|
||||
<div className="split"></div>
|
||||
<div className="draw-tools">
|
||||
<div
|
||||
className={`tool-button ${
|
||||
activeTool === "measure" ? "active" : ""
|
||||
}`}
|
||||
onClick={() => {
|
||||
setActiveTool("measure");
|
||||
}}
|
||||
title="Measure"
|
||||
>
|
||||
<MeasureToolIcon isActive={activeTool === "measure"} />
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{activeModule === "simulation" && (
|
||||
<>
|
||||
<div className="split"></div>
|
||||
|
@ -257,31 +420,41 @@ const Tools: React.FC = () => {
|
|||
>
|
||||
<CommentIcon isActive={activeTool === "comment"} />
|
||||
</div>
|
||||
<div
|
||||
className={`tool-button ${
|
||||
activeTool === "play" ? "active" : ""
|
||||
}`}
|
||||
onClick={() => {
|
||||
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>
|
||||
{toggleThreeD && (
|
||||
<div
|
||||
className={`tool-button ${
|
||||
activeTool === "play" ? "active" : ""
|
||||
}`}
|
||||
onClick={() => {
|
||||
setIsPlaying(!isPlaying);
|
||||
}}
|
||||
>
|
||||
<PlayIcon isActive={activeTool === "play"} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{activeModule === "builder" && (
|
||||
<>
|
||||
<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>
|
||||
</>
|
||||
) : (
|
||||
|
|
|
@ -119,7 +119,7 @@ const AddButtons: React.FC<ButtonsProps> = ({
|
|||
};
|
||||
|
||||
// Delete the selectedZone state
|
||||
console.log("updatedZone: ", updatedZone);
|
||||
|
||||
setSelectedZone(updatedZone);
|
||||
} else {
|
||||
const updatePanelData = async () => {
|
||||
|
@ -141,13 +141,13 @@ const AddButtons: React.FC<ButtonsProps> = ({
|
|||
|
||||
// API call
|
||||
const response = await panelData(organization, selectedZone.zoneId, newActiveSides);
|
||||
console.log("response: ", response);
|
||||
|
||||
|
||||
// Update state
|
||||
console.log("updatedZone: ", updatedZone);
|
||||
|
||||
setSelectedZone(updatedZone);
|
||||
} catch (error) {
|
||||
console.error("Error updating panel data:", error);
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import { MoveArrowLeft, MoveArrowRight } from "../../icons/SimulationIcons";
|
|||
import { InfoIcon } from "../../icons/ExportCommonIcons";
|
||||
import { useDroppedObjectsStore } from "../../../store/useDroppedObjectsStore";
|
||||
import { getSelect2dZoneData } from "../../../services/realTimeVisulization/zoneData/getSelect2dZoneData";
|
||||
import { getFloatingZoneData } from "../../../services/realTimeVisulization/zoneData/getFloatingData";
|
||||
|
||||
// Define the type for `Side`
|
||||
type Side = "top" | "bottom" | "left" | "right";
|
||||
|
@ -147,27 +148,47 @@ const DisplayZone: React.FC<DisplayZoneProps> = ({
|
|||
};
|
||||
|
||||
async function handleSelect2dZoneData(zoneId: string, zoneName: string) {
|
||||
const email = localStorage.getItem("email") || "";
|
||||
const organization = email?.split("@")[1]?.split(".")[0]
|
||||
let response = await getSelect2dZoneData(zoneId, organization)
|
||||
setSelectedZone({
|
||||
zoneName,
|
||||
activeSides: response.activeSides,
|
||||
panelOrder: response.panelOrder,
|
||||
lockedPanels: response.lockedPanels,
|
||||
widgets: response.widgets,
|
||||
zoneId: zoneId,
|
||||
zoneViewPortTarget:
|
||||
response.viewPortCenter,
|
||||
zoneViewPortPosition:
|
||||
response.viewPortposition,
|
||||
});
|
||||
try {
|
||||
if (selectedZone?.zoneId === zoneId) {
|
||||
console.log("Zone is already selected:", zoneName);
|
||||
return;
|
||||
}
|
||||
const email = localStorage.getItem("email") || "";
|
||||
const organization = email?.split("@")[1]?.split(".")[0];
|
||||
// Fetch data from backend
|
||||
let response = await getSelect2dZoneData(zoneId, organization);
|
||||
let res = await getFloatingZoneData(zoneId, organization);
|
||||
// Set the selected zone in the store
|
||||
useDroppedObjectsStore.getState().setZone(zoneName, zoneId);
|
||||
if (Array.isArray(res)) {
|
||||
res.forEach((val) => {
|
||||
useDroppedObjectsStore.getState().addObject(zoneName, val);
|
||||
});
|
||||
}
|
||||
// Update selected zone state
|
||||
setSelectedZone({
|
||||
zoneName,
|
||||
activeSides: response.activeSides || [],
|
||||
panelOrder: response.panelOrder || [],
|
||||
lockedPanels: response.lockedPanels || [],
|
||||
widgets: response.widgets || [],
|
||||
zoneId: zoneId,
|
||||
zoneViewPortTarget: response.viewPortCenter || {},
|
||||
zoneViewPortPosition: response.viewPortposition || {},
|
||||
});
|
||||
} catch (error) {
|
||||
console.log('error: ', error);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`zone-wrapper ${selectedZone?.activeSides?.includes("bottom") && "bottom"
|
||||
}`}
|
||||
ref={containerRef}
|
||||
className={`zone-wrapper ${
|
||||
selectedZone?.activeSides?.includes("bottom") && "bottom"
|
||||
}`}
|
||||
>
|
||||
{/* Left Arrow */}
|
||||
{showLeftArrow && (
|
||||
|
@ -185,7 +206,6 @@ const DisplayZone: React.FC<DisplayZoneProps> = ({
|
|||
className={`zone ${selectedZone.zoneName === zoneName ? "active" : ""
|
||||
}`}
|
||||
onClick={() => {
|
||||
useDroppedObjectsStore.getState().setZone(zoneName, zonesData[zoneName]?.zoneId);
|
||||
handleSelect2dZoneData(zonesData[zoneName]?.zoneId, zoneName)
|
||||
}}
|
||||
>
|
||||
|
|
|
@ -11,6 +11,8 @@ import {
|
|||
KebabIcon,
|
||||
} from "../../icons/ExportCommonIcons";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { duplicateWidgetApi } from "../../../services/realTimeVisulization/zoneData/duplicateWidget";
|
||||
import { deleteWidgetApi } from "../../../services/realTimeVisulization/zoneData/deleteWidgetApi";
|
||||
|
||||
type Side = "top" | "bottom" | "left" | "right";
|
||||
|
||||
|
@ -34,9 +36,9 @@ export const DraggableWidget = ({
|
|||
}: {
|
||||
selectedZone: {
|
||||
zoneName: string;
|
||||
zoneId: string;
|
||||
activeSides: Side[];
|
||||
panelOrder: Side[];
|
||||
|
||||
lockedPanels: Side[];
|
||||
widgets: Widget[];
|
||||
};
|
||||
|
@ -79,21 +81,28 @@ export const DraggableWidget = ({
|
|||
|
||||
const isPanelHidden = hiddenPanels.includes(widget.panel);
|
||||
|
||||
const deleteSelectedChart = () => {
|
||||
console.log('widget.id: ', widget.id);
|
||||
const updatedWidgets = selectedZone.widgets.filter(
|
||||
(w: Widget) => w.id !== widget.id
|
||||
);
|
||||
console.log('updatedWidgets: ', updatedWidgets);
|
||||
const deleteSelectedChart = async () => {
|
||||
try {
|
||||
const email = localStorage.getItem("email") || "";
|
||||
const organization = email?.split("@")[1]?.split(".")[0];
|
||||
const response = await deleteWidgetApi(widget.id, organization);
|
||||
if (response?.message === "Widget deleted successfully") {
|
||||
const updatedWidgets = selectedZone.widgets.filter(
|
||||
(w: Widget) => w.id !== widget.id
|
||||
);
|
||||
setSelectedZone((prevZone: any) => ({
|
||||
...prevZone,
|
||||
widgets: updatedWidgets,
|
||||
}));
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
setSelectedZone((prevZone: any) => ({
|
||||
...prevZone,
|
||||
widgets: updatedWidgets,
|
||||
}));
|
||||
|
||||
setOpenKebabId(null);
|
||||
} finally {
|
||||
setOpenKebabId(null);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const getCurrentWidgetCount = (panel: Side) =>
|
||||
selectedZone.widgets.filter((w) => w.panel === panel).length;
|
||||
|
||||
|
@ -121,21 +130,32 @@ export const DraggableWidget = ({
|
|||
return currentWidgetCount >= panelCapacity;
|
||||
};
|
||||
|
||||
const duplicateWidget = () => {
|
||||
const duplicatedWidget: Widget = {
|
||||
...widget,
|
||||
id: `${widget.id}-copy-${Date.now()}`,
|
||||
};
|
||||
setSelectedZone((prevZone: any) => ({
|
||||
...prevZone,
|
||||
widgets: [...prevZone.widgets, duplicatedWidget],
|
||||
}));
|
||||
const duplicateWidget = async () => {
|
||||
try {
|
||||
const email = localStorage.getItem("email") || "";
|
||||
const organization = email?.split("@")[1]?.split(".")[0];
|
||||
|
||||
setOpenKebabId(null);
|
||||
const duplicatedWidget: Widget = {
|
||||
...widget,
|
||||
id: `${widget.id}-copy-${Date.now()}`,
|
||||
};
|
||||
|
||||
const response = await duplicateWidgetApi(selectedZone.zoneId, organization, duplicatedWidget);
|
||||
|
||||
if (response?.message === "Widget created successfully") {
|
||||
setSelectedZone((prevZone: any) => ({
|
||||
...prevZone,
|
||||
widgets: [...prevZone.widgets, duplicatedWidget],
|
||||
}));
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
} finally {
|
||||
setOpenKebabId(null);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const handleKebabClick = (event: React.MouseEvent<HTMLDivElement>) => {
|
||||
event.stopPropagation();
|
||||
if (openKebabId === widget.id) {
|
||||
|
@ -176,7 +196,6 @@ export const DraggableWidget = ({
|
|||
};
|
||||
|
||||
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
|
||||
|
@ -296,18 +315,11 @@ export const DraggableWidget = ({
|
|||
)}
|
||||
{widget.type === "polarArea" && (
|
||||
<PolarAreaGraphComponent
|
||||
id={widget.id}
|
||||
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>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
|
||||
import { useThree } from "@react-three/fiber";
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { useAsset3dWidget } from "../../../store/store";
|
||||
import { useAsset3dWidget, useWidgetSubOption } from "../../../store/store";
|
||||
import useModuleStore from "../../../store/useModuleStore";
|
||||
import { ThreeState } from "../../../types/world/worldTypes";
|
||||
import * as THREE from "three";
|
||||
|
@ -9,71 +9,80 @@ import Throughput from "../../layout/3D-cards/cards/Throughput";
|
|||
import ProductionCapacity from "../../layout/3D-cards/cards/ProductionCapacity";
|
||||
import ReturnOfInvestment from "../../layout/3D-cards/cards/ReturnOfInvestment";
|
||||
import StateWorking from "../../layout/3D-cards/cards/StateWorking";
|
||||
import { useSelectedZoneStore } from "../../../store/useZoneStore";
|
||||
|
||||
export default function Dropped3dWidgets() {
|
||||
const { widgetSelect } = useAsset3dWidget();
|
||||
const { widgetSelect, setWidgetSelect } = useAsset3dWidget();
|
||||
const { activeModule } = useModuleStore();
|
||||
const { raycaster, gl, scene }: ThreeState = useThree();
|
||||
const { selectedZone } = useSelectedZoneStore(); // Get currently selected zone
|
||||
const { widgetSubOption, setWidgetSubOption } = useWidgetSubOption()
|
||||
// 🔥 Store widget positions per zone
|
||||
const [zoneWidgets, setZoneWidgets] = useState<Record<
|
||||
string, // Zone ID
|
||||
Record<string, [number, number, number][]> // Widget type -> Positions array
|
||||
>>({});
|
||||
|
||||
// 🔥 Store multiple instances per widget type
|
||||
const [widgetPositions, setWidgetPositions] = useState<Record<string, [number, number, number][]>>({});
|
||||
|
||||
useEffect(() => {
|
||||
if (activeModule !== "visualization") return;
|
||||
if (widgetSubOption === "Floating") return
|
||||
// if (activeModule !== "visualization") return;
|
||||
const canvasElement = gl.domElement;
|
||||
|
||||
const onDrop = (event: DragEvent) => {
|
||||
event.preventDefault(); // Prevent default browser behavior
|
||||
if (widgetSubOption === "3D") {
|
||||
if (selectedZone.zoneName === "") return
|
||||
if (!widgetSelect?.startsWith("ui")) return;
|
||||
const group1 = scene.getObjectByName("itemsGroup");
|
||||
if (!group1) return;
|
||||
const Assets = group1.children
|
||||
.map((val) => scene.getObjectByProperty("uuid", val.uuid))
|
||||
.filter(Boolean) as THREE.Object3D[];
|
||||
const intersects = raycaster.intersectObjects(scene.children, true).filter(
|
||||
(intersect) =>
|
||||
!intersect.object.name.includes("Roof") &&
|
||||
!intersect.object.name.includes("agv-collider") &&
|
||||
!intersect.object.name.includes("MeasurementReference") &&
|
||||
!intersect.object.userData.isPathObject &&
|
||||
!(intersect.object.type === "GridHelper")
|
||||
);
|
||||
if (intersects.length > 0) {
|
||||
const { x, y, z } = intersects[0].point;
|
||||
|
||||
if (!widgetSelect.startsWith("ui")) return;
|
||||
|
||||
|
||||
const group1 = scene.getObjectByName("itemsGroup");
|
||||
if (!group1) return;
|
||||
|
||||
|
||||
|
||||
const Assets = group1.children
|
||||
.map((val) => scene.getObjectByProperty("uuid", val.uuid))
|
||||
.filter(Boolean) as THREE.Object3D[];
|
||||
|
||||
|
||||
const intersects = raycaster.intersectObjects(Assets);
|
||||
|
||||
if (intersects.length > 0) {
|
||||
const { x, y, z } = intersects[0].point;
|
||||
|
||||
|
||||
// ✅ Allow multiple instances by storing positions in an array
|
||||
setWidgetPositions((prev) => ({
|
||||
...prev,
|
||||
[widgetSelect]: [...(prev[widgetSelect] || []), [x, y, z]],
|
||||
}));
|
||||
setZoneWidgets((prev) => ({
|
||||
...prev,
|
||||
[selectedZone.zoneId]: {
|
||||
...(prev[selectedZone.zoneId] || {}),
|
||||
[widgetSelect]: [...(prev[selectedZone.zoneId]?.[widgetSelect] || []), [x, y, z]],
|
||||
},
|
||||
}));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
canvasElement.addEventListener("drop", onDrop);
|
||||
|
||||
return () => {
|
||||
canvasElement.removeEventListener("drop", onDrop);
|
||||
canvasElement.removeEventListener("drop", onDrop)
|
||||
// setWidgetSelect()
|
||||
};
|
||||
}, [widgetSelect, activeModule]);
|
||||
}, [widgetSelect, activeModule, widgetSubOption]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{widgetPositions["ui-Widget 1"]?.map((pos, index) => (
|
||||
{zoneWidgets[selectedZone.zoneId]?.["ui-Widget 1"]?.map((pos, index) => (
|
||||
<ProductionCapacity key={`Widget1-${index}`} position={pos} />
|
||||
))}
|
||||
{widgetPositions["ui-Widget 2"]?.map((pos, index) => (
|
||||
{zoneWidgets[selectedZone.zoneId]?.["ui-Widget 2"]?.map((pos, index) => (
|
||||
<ReturnOfInvestment key={`Widget2-${index}`} position={pos} />
|
||||
))}
|
||||
{widgetPositions["ui-Widget 3"]?.map((pos, index) => (
|
||||
{zoneWidgets[selectedZone.zoneId]?.["ui-Widget 3"]?.map((pos, index) => (
|
||||
<StateWorking key={`Widget3-${index}`} position={pos} />
|
||||
))}
|
||||
{widgetPositions["ui-Widget 4"]?.map((pos, index) => (
|
||||
{zoneWidgets[selectedZone.zoneId]?.["ui-Widget 4"]?.map((pos, index) => (
|
||||
<Throughput key={`Widget4-${index}`} position={pos} />
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,59 +1,88 @@
|
|||
|
||||
|
||||
|
||||
import { WalletIcon } from "../../icons/3dChartIcons";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { Line } from "react-chartjs-2";
|
||||
import { useDroppedObjectsStore, Zones } from "../../../store/useDroppedObjectsStore";
|
||||
import {
|
||||
useDroppedObjectsStore,
|
||||
Zones,
|
||||
} from "../../../store/useDroppedObjectsStore";
|
||||
import useModuleStore from "../../../store/useModuleStore";
|
||||
import { determinePosition } from "./functions/determinePosition";
|
||||
import { getActiveProperties } from "./functions/getActiveProperties";
|
||||
import { addingFloatingWidgets } from "../../../services/realTimeVisulization/zoneData/addFloatingWidgets";
|
||||
|
||||
import TotalCardComponent from "../realTimeVis/floating/TotalCardComponent";
|
||||
import WarehouseThroughputComponent from "../realTimeVis/floating/WarehouseThroughputComponent";
|
||||
import FleetEfficiencyComponent from "../realTimeVis/floating/FleetEfficiencyComponent";
|
||||
import { useWidgetStore } from "../../../store/useWidgetStore";
|
||||
const DroppedObjects: React.FC = () => {
|
||||
const zones = useDroppedObjectsStore((state) => state.zones);
|
||||
const updateObjectPosition = useDroppedObjectsStore((state) => state.updateObjectPosition);
|
||||
const [draggingIndex, setDraggingIndex] = useState<{ zone: string; index: number } | null>(null);
|
||||
|
||||
const updateObjectPosition = useDroppedObjectsStore(
|
||||
(state) => state.updateObjectPosition
|
||||
);
|
||||
const [draggingIndex, setDraggingIndex] = useState<{
|
||||
zone: string;
|
||||
index: number;
|
||||
} | null>(null);
|
||||
const [offset, setOffset] = useState<[number, number] | null>(null);
|
||||
const { setSelectedChartId } = useWidgetStore();
|
||||
const positionRef = useRef<[number, number] | null>(null);
|
||||
const animationRef = useRef<number | null>(null);
|
||||
const { activeModule } = useModuleStore();
|
||||
|
||||
// useEffect(() => {
|
||||
// const initialZones: Record<string, Zones> = {
|
||||
// "Zone 1": {
|
||||
// zoneName: "Zone 1",
|
||||
// zoneId: "2e996073-546c-470c-8323-55bd3700c6aa",
|
||||
// objects: [
|
||||
// {
|
||||
// header: "Today’s Money",
|
||||
// value: 53000, // ✅ Converted to number
|
||||
// per: "+55%",
|
||||
// className: "floating total-card",
|
||||
// position: [146, 214], // ✅ No need for 'as' here
|
||||
// },
|
||||
// {
|
||||
// header: "New Clients",
|
||||
// value: 250, // ✅ Converted to number
|
||||
// per: "+12%",
|
||||
// className: "floating total-card",
|
||||
// position: [344, 295],
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// };
|
||||
|
||||
// useDroppedObjectsStore.setState({ zones: initialZones });
|
||||
// }, []);
|
||||
|
||||
// Get the first zone and its objects
|
||||
const zoneEntries = Object.entries(zones);
|
||||
if (zoneEntries.length === 0) return null; // No zone, nothing to render
|
||||
const [zoneName, zone] = zoneEntries[0]; // Only render the first zone
|
||||
console.log("zone", zone);
|
||||
|
||||
|
||||
|
||||
// Handle pointer down event
|
||||
function handlePointerDown(event: React.PointerEvent, index: number) {
|
||||
|
||||
const obj = zone.objects[index];
|
||||
const offsetX = event.clientX - obj.position[1];
|
||||
const offsetY = event.clientY - obj.position[0];
|
||||
const container = document.getElementById("real-time-vis-canvas");
|
||||
if (!container) return;
|
||||
|
||||
const rect = container.getBoundingClientRect();
|
||||
const relativeX = event.clientX - rect.left;
|
||||
const relativeY = event.clientY - rect.top;
|
||||
|
||||
// Determine which properties are active for this object
|
||||
const [activeProp1, activeProp2] = getActiveProperties(obj.position);
|
||||
|
||||
// Calculate the offset based on the active properties
|
||||
let offsetX = 0;
|
||||
let offsetY = 0;
|
||||
|
||||
if (activeProp1 === "top") {
|
||||
offsetY =
|
||||
relativeY -
|
||||
(typeof obj.position.top === "number" ? obj.position.top : 0);
|
||||
} else if (activeProp1 === "bottom") {
|
||||
offsetY =
|
||||
rect.height -
|
||||
relativeY -
|
||||
(typeof obj.position.bottom === "number" ? obj.position.bottom : 0);
|
||||
}
|
||||
|
||||
if (activeProp2 === "left") {
|
||||
offsetX =
|
||||
relativeX -
|
||||
(typeof obj.position.left === "number" ? obj.position.left : 0);
|
||||
} else if (activeProp2 === "right") {
|
||||
offsetX =
|
||||
rect.width -
|
||||
relativeX -
|
||||
(typeof obj.position.right === "number" ? obj.position.right : 0);
|
||||
}
|
||||
|
||||
setDraggingIndex({ zone: zoneName, index });
|
||||
setOffset([offsetY, offsetX]);
|
||||
}
|
||||
|
||||
// Handle pointer move event
|
||||
function handlePointerMove(event: React.PointerEvent) {
|
||||
if (!draggingIndex || !offset) return;
|
||||
|
||||
|
@ -61,34 +90,92 @@ const DroppedObjects: React.FC = () => {
|
|||
if (!container) return;
|
||||
|
||||
const rect = container.getBoundingClientRect();
|
||||
const relativeX = event.clientX - rect.left;
|
||||
const relativeY = event.clientY - rect.top;
|
||||
|
||||
let newX = event.clientX - offset[1];
|
||||
let newY = event.clientY - offset[0];
|
||||
// Determine which properties are active for the dragged object
|
||||
const obj = zone.objects[draggingIndex.index];
|
||||
const [activeProp1, activeProp2] = getActiveProperties(obj.position);
|
||||
|
||||
// Calculate the new position based on the active properties
|
||||
let newX = 0;
|
||||
let newY = 0;
|
||||
|
||||
if (activeProp2 === "left") {
|
||||
newX = relativeX - offset[1];
|
||||
} else if (activeProp2 === "right") {
|
||||
newX = rect.width - (relativeX + offset[1]);
|
||||
}
|
||||
|
||||
if (activeProp1 === "top") {
|
||||
newY = relativeY - offset[0];
|
||||
} else if (activeProp1 === "bottom") {
|
||||
newY = rect.height - (relativeY + offset[0]);
|
||||
}
|
||||
|
||||
// Ensure the object stays within the canvas boundaries
|
||||
newX = Math.max(0, Math.min(rect.width - 50, newX));
|
||||
newY = Math.max(0, Math.min(rect.height - 50, newY));
|
||||
|
||||
// Update the position reference
|
||||
positionRef.current = [newY, newX];
|
||||
|
||||
// Update the object's position using requestAnimationFrame for smoother animations
|
||||
if (!animationRef.current) {
|
||||
animationRef.current = requestAnimationFrame(() => {
|
||||
if (positionRef.current) {
|
||||
updateObjectPosition(zoneName, draggingIndex.index, positionRef.current);
|
||||
updateObjectPosition(zoneName, draggingIndex.index, {
|
||||
...obj.position,
|
||||
[activeProp1]: positionRef.current[0],
|
||||
[activeProp2]: positionRef.current[1],
|
||||
});
|
||||
}
|
||||
animationRef.current = null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function handlePointerUp() {
|
||||
setDraggingIndex(null);
|
||||
setOffset(null);
|
||||
if (animationRef.current) {
|
||||
cancelAnimationFrame(animationRef.current);
|
||||
animationRef.current = null;
|
||||
// Handle pointer up event
|
||||
async function handlePointerUp(event: React.MouseEvent<HTMLDivElement>) {
|
||||
try {
|
||||
if (!draggingIndex || !offset) return;
|
||||
|
||||
const email = localStorage.getItem("email") || "";
|
||||
const organization = email?.split("@")[1]?.split(".")[0];
|
||||
const container = document.getElementById("real-time-vis-canvas");
|
||||
if (!container) throw new Error("Canvas container not found");
|
||||
|
||||
const rect = container.getBoundingClientRect();
|
||||
const relativeX = event.clientX - rect.left;
|
||||
const relativeY = event.clientY - rect.top;
|
||||
|
||||
// Recalculate the position using determinePosition
|
||||
const newPosition = determinePosition(rect, relativeX, relativeY);
|
||||
|
||||
// Validate the dragging index and get the object
|
||||
if (!zone.objects[draggingIndex.index]) {
|
||||
throw new Error("Dragged object not found in the zone");
|
||||
}
|
||||
const obj = { ...zone.objects[draggingIndex.index], position: newPosition };
|
||||
let response = await addingFloatingWidgets(zone.zoneId, organization, obj);
|
||||
if (response.message === "Widget updated successfully") {
|
||||
updateObjectPosition(zoneName, draggingIndex.index, newPosition);
|
||||
}
|
||||
|
||||
// Reset states
|
||||
setDraggingIndex(null);
|
||||
setOffset(null);
|
||||
|
||||
if (animationRef.current) {
|
||||
cancelAnimationFrame(animationRef.current);
|
||||
animationRef.current = null;
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<div onPointerMove={handlePointerMove} onPointerUp={handlePointerUp}>
|
||||
{zone.objects.map((obj, index) => (
|
||||
|
@ -96,59 +183,42 @@ const DroppedObjects: React.FC = () => {
|
|||
key={`${zoneName}-${index}`}
|
||||
className={obj.className}
|
||||
style={{
|
||||
top: obj.position[0] + "px",
|
||||
left: obj.position[1] + "px",
|
||||
transition: draggingIndex?.index === index ? "none" : "transform 0.1s ease-out",
|
||||
position: "absolute",
|
||||
top:
|
||||
typeof obj.position.top !== "string"
|
||||
? `${obj.position.top}px`
|
||||
: "auto",
|
||||
left:
|
||||
typeof obj.position.left !== "string"
|
||||
? `${obj.position.left}px`
|
||||
: "auto",
|
||||
right:
|
||||
typeof obj.position.right !== "string"
|
||||
? `${obj.position.right}px`
|
||||
: "auto",
|
||||
bottom:
|
||||
typeof obj.position.bottom !== "string"
|
||||
? `${obj.position.bottom}px`
|
||||
: "auto",
|
||||
// transition: draggingIndex?.index === index ? "none" : "transform 0.1s ease-out",
|
||||
}}
|
||||
onPointerDown={(event) => handlePointerDown(event, index)}
|
||||
onClick={() => {
|
||||
setSelectedChartId(obj)
|
||||
}}
|
||||
>
|
||||
{obj.className === "floating total-card" ? (
|
||||
<div>
|
||||
<div className="header-wrapper">
|
||||
<div className="header">{obj.header}</div>
|
||||
<div className="data-values">
|
||||
<div className="value">{obj.value}</div>
|
||||
<div className="per">{obj.per}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="icon">
|
||||
<WalletIcon />
|
||||
</div>
|
||||
</div>
|
||||
<>
|
||||
<TotalCardComponent object={obj} />
|
||||
</>
|
||||
) : obj.className === "warehouseThroughput floating" ? (
|
||||
<div>
|
||||
<div className="header">
|
||||
<h2>Warehouse Throughput</h2>
|
||||
<p>
|
||||
<span>(+5) more</span> in 2025
|
||||
</p>
|
||||
</div>
|
||||
<div className="lineGraph" style={{ height: "100%" }}>
|
||||
{/* <Line data={lineGraphData} options={lineGraphOptions} /> */}
|
||||
</div>
|
||||
</div>
|
||||
<>
|
||||
<WarehouseThroughputComponent />
|
||||
</>
|
||||
) : obj.className === "fleetEfficiency floating" ? (
|
||||
<div>
|
||||
<h2 className="header">Fleet Efficiency</h2>
|
||||
<div className="progressContainer">
|
||||
<div className="progress">
|
||||
<div className="barOverflow">
|
||||
<div
|
||||
className="bar"
|
||||
style={{ transform: `rotate(${obj.value}deg)` }}
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="scaleLabels">
|
||||
<span>0%</span>
|
||||
<div className="centerText">
|
||||
<div className="percentage">{obj.per}%</div>
|
||||
<div className="status">Optimal</div>
|
||||
</div>
|
||||
<span>100%</span>
|
||||
</div>
|
||||
</div>
|
||||
<>
|
||||
<FleetEfficiencyComponent object={obj} />
|
||||
</>
|
||||
) : null}
|
||||
</div>
|
||||
))}
|
||||
|
@ -156,59 +226,4 @@ const DroppedObjects: React.FC = () => {
|
|||
);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
export default DroppedObjects;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// import { Html } from "@react-three/drei";
|
||||
// import { useDroppedObjectsStore } from "../../../store/store";
|
||||
// import { CartIcon, DocumentIcon, GlobeIcon, WalletIcon } from "../../icons/3dChartIcons";
|
||||
// import SimpleCard from "../realTimeVis/floating/SimpleCard";
|
||||
|
||||
// const ICON_MAP: Record<string, React.ComponentType<React.SVGProps<SVGSVGElement>>> = {
|
||||
// WalletIcon,
|
||||
// GlobeIcon,
|
||||
// DocumentIcon,
|
||||
// CartIcon
|
||||
// };
|
||||
// const DroppedObjects: React.FC = () => {
|
||||
// const objects = useDroppedObjectsStore((state) => state.objects); // Get objects from Zustand store
|
||||
//
|
||||
|
||||
// return (
|
||||
// <>
|
||||
// {objects.map((obj, index) => {
|
||||
// const IconComponent = obj.Icon || WalletIcon; // Use obj.Icon directly if it exists
|
||||
// return (
|
||||
// <SimpleCard
|
||||
// key={index}
|
||||
// position={obj.position}
|
||||
// header={obj.header}
|
||||
// icon={IconComponent} // ✅ No need to look it up in ICON_MAP
|
||||
// value={obj.value}
|
||||
// per={obj.per}
|
||||
// />
|
||||
// );
|
||||
// })}
|
||||
// </>
|
||||
// );
|
||||
// };
|
||||
|
||||
// export default DroppedObjects;
|
||||
|
|
|
@ -149,7 +149,7 @@ const Panel: React.FC<PanelProps> = ({
|
|||
};
|
||||
try {
|
||||
let response = await addingWidgets(selectedZone.zoneId, organization, newWidget);
|
||||
console.log("response: ", response);
|
||||
|
||||
if (response.message === "Widget created successfully") {
|
||||
setSelectedZone((prev) => ({
|
||||
...prev,
|
||||
|
|
|
@ -9,10 +9,15 @@ import useModuleStore from "../../../store/useModuleStore";
|
|||
|
||||
import DroppedObjects from "./DroppedFloatingWidgets";
|
||||
import { useDroppedObjectsStore } from "../../../store/useDroppedObjectsStore";
|
||||
import { useAsset3dWidget, useZones } from "../../../store/store";
|
||||
import { getZoneData } from "../../../services/realTimeVisulization/zoneData/getZones";
|
||||
import {
|
||||
useAsset3dWidget,
|
||||
useWidgetSubOption,
|
||||
useZones,
|
||||
} from "../../../store/store";
|
||||
import { getZone2dData } from "../../../services/realTimeVisulization/zoneData/getZoneData";
|
||||
|
||||
import { generateUniqueId } from "../../../functions/generateUniqueId";
|
||||
import { determinePosition } from "./functions/determinePosition";
|
||||
import { addingFloatingWidgets } from "../../../services/realTimeVisulization/zoneData/addFloatingWidgets";
|
||||
|
||||
type Side = "top" | "bottom" | "left" | "right";
|
||||
|
||||
|
@ -25,7 +30,7 @@ type FormattedZoneData = Record<
|
|||
lockedPanels: Side[];
|
||||
zoneId: string;
|
||||
zoneViewPortTarget: number[];
|
||||
zoneViewPortPosition: number[]
|
||||
zoneViewPortPosition: number[];
|
||||
widgets: Widget[];
|
||||
}
|
||||
>;
|
||||
|
@ -45,9 +50,13 @@ const RealTimeVisulization: React.FC = () => {
|
|||
const [droppedObjects, setDroppedObjects] = useState<any[]>([]);
|
||||
const [zonesData, setZonesData] = useState<FormattedZoneData>({});
|
||||
const { selectedZone, setSelectedZone } = useSelectedZoneStore();
|
||||
const { zones } = useZones()
|
||||
const [floatingWidgets, setFloatingWidgets] = useState<Record<string, { zoneName: string; zoneId: string; objects: any[] }>>({});
|
||||
const { zones } = useZones();
|
||||
const [floatingWidgets, setFloatingWidgets] = useState<
|
||||
Record<string, { zoneName: string; zoneId: string; objects: any[] }>
|
||||
>({});
|
||||
const { widgetSelect, setWidgetSelect } = useAsset3dWidget();
|
||||
const { widgetSubOption, setWidgetSubOption } = useWidgetSubOption();
|
||||
|
||||
useEffect(() => {
|
||||
async function GetZoneData() {
|
||||
const email = localStorage.getItem("email") || "";
|
||||
|
@ -57,27 +66,29 @@ const RealTimeVisulization: React.FC = () => {
|
|||
if (!Array.isArray(response)) {
|
||||
return;
|
||||
}
|
||||
const formattedData = response.reduce<FormattedZoneData>((acc, zone) => {
|
||||
acc[zone.zoneName] = {
|
||||
activeSides: [],
|
||||
panelOrder: [],
|
||||
lockedPanels: [],
|
||||
zoneId: zone.zoneId,
|
||||
zoneViewPortTarget: zone.viewPortCenter,
|
||||
zoneViewPortPosition: zone.viewPortposition,
|
||||
widgets: [],
|
||||
};
|
||||
return acc;
|
||||
}, {});
|
||||
const formattedData = response.reduce<FormattedZoneData>(
|
||||
(acc, zone) => {
|
||||
acc[zone.zoneName] = {
|
||||
activeSides: [],
|
||||
panelOrder: [],
|
||||
lockedPanels: [],
|
||||
zoneId: zone.zoneId,
|
||||
zoneViewPortTarget: zone.viewPortCenter,
|
||||
zoneViewPortPosition: zone.viewPortposition,
|
||||
widgets: [],
|
||||
};
|
||||
return acc;
|
||||
},
|
||||
{}
|
||||
);
|
||||
setZonesData(formattedData);
|
||||
} catch (error) {
|
||||
console.log('error: ', error);
|
||||
console.log("error: ", error);
|
||||
}
|
||||
}
|
||||
|
||||
GetZoneData();
|
||||
}, []); // Removed `zones` from dependencies
|
||||
|
||||
}, [activeModule]); // Removed `zones` from dependencies
|
||||
|
||||
useEffect(() => {
|
||||
setZonesData((prev) => {
|
||||
|
@ -97,48 +108,72 @@ const RealTimeVisulization: React.FC = () => {
|
|||
};
|
||||
});
|
||||
}, [selectedZone]);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
}, [floatingWidgets])
|
||||
useEffect(() => {}, [floatingWidgets]);
|
||||
|
||||
const handleDrop = (event: React.DragEvent<HTMLDivElement>) => {
|
||||
event.preventDefault();
|
||||
const data = event.dataTransfer.getData("text/plain");
|
||||
if (widgetSelect !== "") return;
|
||||
if (!data || selectedZone.zoneName === "") return;
|
||||
const handleDrop = async (event: React.DragEvent<HTMLDivElement>) => {
|
||||
try {
|
||||
event.preventDefault();
|
||||
const email = localStorage.getItem("email") || "";
|
||||
const organization = email?.split("@")[1]?.split(".")[0];
|
||||
|
||||
const droppedData = JSON.parse(data);
|
||||
const canvasElement = document.getElementById("real-time-vis-canvas");
|
||||
if (!canvasElement) return;
|
||||
const data = event.dataTransfer.getData("text/plain");
|
||||
// if (widgetSelect !== "") return;
|
||||
if (widgetSubOption === "3D") return;
|
||||
if (!data || selectedZone.zoneName === "") return;
|
||||
|
||||
const canvasRect = canvasElement.getBoundingClientRect();
|
||||
const relativeX = event.clientX - canvasRect.left;
|
||||
const relativeY = event.clientY - canvasRect.top;
|
||||
const droppedData = JSON.parse(data);
|
||||
const canvasElement = document.getElementById("real-time-vis-canvas");
|
||||
if (!canvasElement) throw new Error("Canvas element not found");
|
||||
|
||||
const newObject = {
|
||||
...droppedData,
|
||||
position: [relativeY, relativeX], // Y first because of top/left style
|
||||
};
|
||||
// Only set zone if it’s not already in the store (prevents overwriting objects)
|
||||
const existingZone = useDroppedObjectsStore.getState().zones[selectedZone.zoneName];
|
||||
if (!existingZone) {
|
||||
useDroppedObjectsStore.getState().setZone(selectedZone.zoneName, selectedZone.zoneId);
|
||||
}
|
||||
// Add the dropped object to the zone
|
||||
useDroppedObjectsStore.getState().addObject(selectedZone.zoneName, newObject);
|
||||
setFloatingWidgets((prevWidgets) => ({
|
||||
...prevWidgets,
|
||||
[selectedZone.zoneName]: {
|
||||
...prevWidgets[selectedZone.zoneName],
|
||||
zoneName: selectedZone.zoneName,
|
||||
zoneId: selectedZone.zoneId,
|
||||
objects: [...(prevWidgets[selectedZone.zoneName]?.objects || []), newObject],
|
||||
},
|
||||
}));
|
||||
const canvasRect = canvasElement.getBoundingClientRect();
|
||||
const relativeX = event.clientX - canvasRect.left;
|
||||
const relativeY = event.clientY - canvasRect.top;
|
||||
|
||||
const newObject = {
|
||||
...droppedData,
|
||||
id: generateUniqueId(),
|
||||
position: determinePosition(canvasRect, relativeX, relativeY),
|
||||
};
|
||||
|
||||
let response = await addingFloatingWidgets(
|
||||
selectedZone.zoneId,
|
||||
organization,
|
||||
newObject
|
||||
);
|
||||
|
||||
// Only set zone if it’s not already in the store (prevents overwriting objects)
|
||||
const existingZone =
|
||||
useDroppedObjectsStore.getState().zones[selectedZone.zoneName];
|
||||
if (!existingZone) {
|
||||
useDroppedObjectsStore
|
||||
.getState()
|
||||
.setZone(selectedZone.zoneName, selectedZone.zoneId);
|
||||
}
|
||||
|
||||
// Add the dropped object to the zone if the API call is successful
|
||||
if (response.message === "FloatWidget created successfully") {
|
||||
useDroppedObjectsStore
|
||||
.getState()
|
||||
.addObject(selectedZone.zoneName, newObject);
|
||||
}
|
||||
|
||||
// Update floating widgets state
|
||||
setFloatingWidgets((prevWidgets) => ({
|
||||
...prevWidgets,
|
||||
[selectedZone.zoneName]: {
|
||||
...prevWidgets[selectedZone.zoneName],
|
||||
zoneName: selectedZone.zoneName,
|
||||
zoneId: selectedZone.zoneId,
|
||||
objects: [
|
||||
...(prevWidgets[selectedZone.zoneName]?.objects || []),
|
||||
newObject,
|
||||
],
|
||||
},
|
||||
}));
|
||||
} catch (error) {}
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={containerRef}
|
||||
|
@ -149,7 +184,6 @@ const RealTimeVisulization: React.FC = () => {
|
|||
width: isPlaying || activeModule !== "visualization" ? "100vw" : "",
|
||||
left: isPlaying || activeModule !== "visualization" ? "0%" : "",
|
||||
}}
|
||||
|
||||
>
|
||||
<div
|
||||
className="scene-container"
|
||||
|
@ -162,7 +196,6 @@ const RealTimeVisulization: React.FC = () => {
|
|||
onDrop={(event) => handleDrop(event)}
|
||||
onDragOver={(event) => event.preventDefault()}
|
||||
>
|
||||
|
||||
<Scene />
|
||||
</div>
|
||||
<DroppedObjects />
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
import { getActiveProperties } from "./getActiveProperties";
|
||||
|
||||
export const convertAutoToNumeric = (
|
||||
canvasRect: DOMRect,
|
||||
position: {
|
||||
top: number | "auto";
|
||||
left: number | "auto";
|
||||
right: number | "auto";
|
||||
bottom: number | "auto";
|
||||
}
|
||||
): { top: number; left: number; right: number; bottom: number } => {
|
||||
const { width, height } = canvasRect;
|
||||
|
||||
// Determine which properties are active
|
||||
const [activeProp1, activeProp2] = getActiveProperties(position);
|
||||
|
||||
let top = typeof position.top !== "string" ? position.top : 0;
|
||||
let left = typeof position.left !== "string" ? position.left : 0;
|
||||
let right = typeof position.right !== "string" ? position.right : 0;
|
||||
let bottom = typeof position.bottom !== "string" ? position.bottom : 0;
|
||||
|
||||
// Calculate missing properties based on active properties
|
||||
if (activeProp1 === "top") {
|
||||
bottom = height - top;
|
||||
} else if (activeProp1 === "bottom") {
|
||||
top = height - bottom;
|
||||
}
|
||||
|
||||
if (activeProp2 === "left") {
|
||||
right = width - left;
|
||||
} else if (activeProp2 === "right") {
|
||||
left = width - right;
|
||||
}
|
||||
|
||||
return {
|
||||
top,
|
||||
left,
|
||||
right,
|
||||
bottom,
|
||||
};
|
||||
};
|
|
@ -0,0 +1,64 @@
|
|||
export function determinePosition(
|
||||
canvasRect: DOMRect,
|
||||
relativeX: number,
|
||||
relativeY: number
|
||||
): {
|
||||
top: number | "auto";
|
||||
left: number | "auto";
|
||||
right: number | "auto";
|
||||
bottom: number | "auto";
|
||||
} {
|
||||
// Calculate the midpoints of the canvas
|
||||
const centerX = canvasRect.width / 2;
|
||||
const centerY = canvasRect.height / 2;
|
||||
|
||||
// Initialize position with default values
|
||||
let position: {
|
||||
top: number | "auto";
|
||||
left: number | "auto";
|
||||
right: number | "auto";
|
||||
bottom: number | "auto";
|
||||
};
|
||||
|
||||
if (relativeY < centerY) {
|
||||
// Top half
|
||||
if (relativeX < centerX) {
|
||||
// Left side
|
||||
position = {
|
||||
top: relativeY,
|
||||
left: relativeX,
|
||||
right: "auto",
|
||||
bottom: "auto",
|
||||
};
|
||||
} else {
|
||||
// Right side
|
||||
position = {
|
||||
top: relativeY,
|
||||
right: canvasRect.width - relativeX,
|
||||
left: "auto",
|
||||
bottom: "auto",
|
||||
};
|
||||
}
|
||||
} else {
|
||||
// Bottom half
|
||||
if (relativeX < centerX) {
|
||||
// Left side
|
||||
position = {
|
||||
bottom: canvasRect.height - relativeY,
|
||||
left: relativeX,
|
||||
right: "auto",
|
||||
top: "auto",
|
||||
};
|
||||
} else {
|
||||
// Right side
|
||||
position = {
|
||||
bottom: canvasRect.height - relativeY,
|
||||
right: canvasRect.width - relativeX,
|
||||
left: "auto",
|
||||
top: "auto",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return position;
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
export const getActiveProperties = (position: {
|
||||
top: number | "auto";
|
||||
left: number | "auto";
|
||||
right: number | "auto";
|
||||
bottom: number | "auto";
|
||||
}) => {
|
||||
let activeProps: ["top", "left"] | ["bottom", "right"] = ["top", "left"]; // Default to top-left
|
||||
|
||||
if (
|
||||
typeof position.bottom !== "string" &&
|
||||
typeof position.right !== "string"
|
||||
) {
|
||||
activeProps = ["bottom", "right"];
|
||||
}
|
||||
|
||||
return activeProps;
|
||||
};
|
|
@ -14,6 +14,7 @@ export default function ZoneCentreTarget() {
|
|||
const { Edit, setEdit } = useEditPosition();
|
||||
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
selectedZone.zoneViewPortTarget &&
|
||||
|
@ -39,22 +40,22 @@ export default function ZoneCentreTarget() {
|
|||
|
||||
if (centrePoint.length > 0) {
|
||||
|
||||
let camPosition = new THREE.Vector3(...selectedZone.zoneViewPortPosition);
|
||||
let CamTarget = new THREE.Vector3(...selectedZone.zoneViewPortTarget);
|
||||
// let camPosition = new THREE.Vector3(...selectedZone.zoneViewPortPosition);
|
||||
// let CamTarget = new THREE.Vector3(...selectedZone.zoneViewPortTarget);
|
||||
|
||||
const direction = new THREE.Vector3().subVectors(CamTarget, camPosition).normalize();
|
||||
// const direction = new THREE.Vector3().subVectors(CamTarget, camPosition).normalize();
|
||||
|
||||
const worldUp = new THREE.Vector3(0, 0, 1);
|
||||
const right = new THREE.Vector3().crossVectors(worldUp, direction).normalize();
|
||||
const up = new THREE.Vector3().crossVectors(direction, right).normalize();
|
||||
const offsetPosition = up.clone().multiplyScalar(20);
|
||||
camPosition.add(offsetPosition);
|
||||
// const worldUp = new THREE.Vector3(0, 0, 1);
|
||||
// const right = new THREE.Vector3().crossVectors(worldUp, direction).normalize();
|
||||
// const up = new THREE.Vector3().crossVectors(direction, right).normalize();
|
||||
// const offsetPosition = up.clone().multiplyScalar(20);
|
||||
// camPosition.add(offsetPosition);
|
||||
|
||||
const setCam = async () => {
|
||||
controls.setLookAt(centrePoint[0], 100, centrePoint[2], ...centrePoint, true);
|
||||
setTimeout(() => {
|
||||
controls?.setLookAt(
|
||||
...camPosition.toArray(),
|
||||
...selectedZone.zoneViewPortPosition,
|
||||
selectedZone.zoneViewPortTarget[0],
|
||||
selectedZone.zoneViewPortTarget[1],
|
||||
selectedZone.zoneViewPortTarget[2],
|
||||
|
@ -65,21 +66,9 @@ export default function ZoneCentreTarget() {
|
|||
setCam();
|
||||
} else {
|
||||
|
||||
let camPosition = new THREE.Vector3(...selectedZone.zoneViewPortPosition);
|
||||
let CamTarget = new THREE.Vector3(...selectedZone.zoneViewPortTarget);
|
||||
|
||||
const direction = new THREE.Vector3().subVectors(CamTarget, camPosition).normalize();
|
||||
|
||||
const worldUp = new THREE.Vector3(0, 0, 1);
|
||||
const right = new THREE.Vector3().crossVectors(worldUp, direction).normalize();
|
||||
const up = new THREE.Vector3().crossVectors(direction, right).normalize();
|
||||
|
||||
const offsetPosition = up.clone().multiplyScalar(20);
|
||||
|
||||
camPosition.add(offsetPosition);
|
||||
const setCam = async () => {
|
||||
controls?.setLookAt(
|
||||
...camPosition.toArray(),
|
||||
...selectedZone.zoneViewPortPosition,
|
||||
selectedZone.zoneViewPortTarget[0],
|
||||
selectedZone.zoneViewPortTarget[1],
|
||||
selectedZone.zoneViewPortTarget[2],
|
||||
|
|
|
@ -1,18 +1,36 @@
|
|||
import React from "react";
|
||||
import RegularDropDown from "./RegularDropDown";
|
||||
import { EyeDroperIcon } from "../../icons/ExportCommonIcons";
|
||||
import RegularDropDown from "./RegularDropDown";
|
||||
|
||||
interface EyeDropInputProps {
|
||||
label: string;
|
||||
value: string;
|
||||
onChange: (value: string) => void;
|
||||
options?: string[];
|
||||
}
|
||||
|
||||
const EyeDropInput: React.FC<EyeDropInputProps> = ({
|
||||
label = "Object",
|
||||
onChange,
|
||||
}) => {
|
||||
const handleEyeDropClick = () => {
|
||||
// Here you would typically implement the eye dropper functionality
|
||||
// For now, we'll just simulate selecting a value
|
||||
const simulatedValue = "picked_value"; // Replace with actual eye dropper logic
|
||||
onChange(simulatedValue);
|
||||
};
|
||||
|
||||
const EyeDropInput: React.FC = () => {
|
||||
return (
|
||||
<div className="eye-dropper-input-container">
|
||||
<div className="label">Object</div>
|
||||
<div className="label">{label}</div>
|
||||
<div className="input-container">
|
||||
{/* <input disabled type="text" /> */}
|
||||
<RegularDropDown
|
||||
header="select object"
|
||||
options={[]}
|
||||
onSelect={() => {}}
|
||||
onSelect={() => { }}
|
||||
/>
|
||||
<div className="eye-picker-button">
|
||||
<div className="eye-picker-button" onClick={handleEyeDropClick}>
|
||||
<EyeDroperIcon isActive={false} />
|
||||
</div>
|
||||
</div>
|
||||
|
@ -20,4 +38,4 @@ const EyeDropInput: React.FC = () => {
|
|||
);
|
||||
};
|
||||
|
||||
export default EyeDropInput;
|
||||
export default EyeDropInput;
|
|
@ -58,12 +58,15 @@ const MultiEmailInvite: React.FC = () => {
|
|||
onFocus={() => setInputFocus(true)}
|
||||
onBlur={() => setInputFocus(false)}
|
||||
onKeyDown={handleKeyDown}
|
||||
placeholder="Enter email and press Enter or comma"
|
||||
placeholder="Enter email and press Enter or comma to seperate"
|
||||
/>
|
||||
</div>
|
||||
<div onClick={handleAddEmail} className="invite-button">
|
||||
Invite
|
||||
</div>
|
||||
<div className="users-list-container">
|
||||
{/* list available users */}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -253,7 +253,8 @@ const MultiLevelDropdown = ({
|
|||
className={`dropdown-button ${open ? "open" : ""}`}
|
||||
onClick={() => setOpen(!open)}
|
||||
>
|
||||
{displayLabel} <span className="icon">▾</span>
|
||||
<div className="label">{displayLabel}</div>
|
||||
<span className="icon">▾</span>
|
||||
</button>
|
||||
{open && (
|
||||
<div className="dropdown-menu">
|
||||
|
|
|
@ -32,7 +32,7 @@ const DropDownList: React.FC<DropDownListProps> = ({
|
|||
listType = "default",
|
||||
remove,
|
||||
}) => {
|
||||
|
||||
|
||||
const [isOpen, setIsOpen] = useState<boolean>(defaultOpen);
|
||||
const { zones, setZones } = useZones()
|
||||
|
||||
|
@ -43,6 +43,17 @@ const DropDownList: React.FC<DropDownListProps> = ({
|
|||
const { selectedZone, setSelectedZone } = useSelectedZoneStore();
|
||||
|
||||
useEffect(() => {
|
||||
// console.log(zones);
|
||||
// setZoneDataList([
|
||||
// { id: "2e996073-546c-470c-8323-55bd3700c6aa", name: "zone1" },
|
||||
// { id: "3f473bf0-197c-471c-a71f-943fc9ca2b47", name: "zone2" },
|
||||
// { id: "905e8fb6-9e18-469b-9474-e0478fb9601b", name: "zone3" },
|
||||
// { id: "9d9efcbe-8e96-47eb-bfad-128a9e4c532e", name: "zone4" },
|
||||
// { id: "884f3d29-eb5a-49a5-abe9-d11971c08e85", name: "zone5" },
|
||||
// { id: "70fa55cd-b5c9-4f80-a8c4-6319af3bfb4e", name: "zone6" },
|
||||
// ])
|
||||
|
||||
|
||||
const value = (zones || []).map((val: { zoneId: string; zoneName: string }) => ({
|
||||
id: val.zoneId,
|
||||
name: val.zoneName
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import React from "react";
|
||||
import React, { useEffect } from "react";
|
||||
import RenameInput from "../inputs/RenameInput";
|
||||
import { EyeIcon, LockIcon, RmoveIcon } from "../../icons/ExportCommonIcons";
|
||||
import { useSelectedZoneStore } from "../../../store/useZoneStore";
|
||||
import { getZoneData } from "../../../services/realTimeVisulization/zoneData/getZones";
|
||||
import { useSubModuleStore } from "../../../store/useModuleStore";
|
||||
import useModuleStore, { useSubModuleStore } from "../../../store/useModuleStore";
|
||||
|
||||
interface ListProps {
|
||||
items?: { id: string; name: string }[]; // Optional array of items to render
|
||||
|
@ -12,27 +12,55 @@ interface ListProps {
|
|||
}
|
||||
|
||||
const List: React.FC<ListProps> = ({ items = [], remove }) => {
|
||||
const { setSelectedZone } = useSelectedZoneStore();
|
||||
const { activeModule, setActiveModule } = useModuleStore();
|
||||
const { selectedZone, setSelectedZone } = useSelectedZoneStore();
|
||||
const { setSubModule } = useSubModuleStore();
|
||||
useEffect(() => {
|
||||
useSelectedZoneStore.getState().setSelectedZone({
|
||||
zoneName: "",
|
||||
activeSides: [],
|
||||
panelOrder: [],
|
||||
lockedPanels: [],
|
||||
zoneId: "",
|
||||
zoneViewPortTarget: [],
|
||||
zoneViewPortPosition: [],
|
||||
widgets: [],
|
||||
});
|
||||
}, [activeModule]);
|
||||
|
||||
|
||||
async function handleSelectZone(id: string) {
|
||||
setSubModule("zoneProperties")
|
||||
const email = localStorage.getItem('email')
|
||||
const organization = (email!.split("@")[1]).split(".")[0];
|
||||
let response = await getZoneData(id, organization)
|
||||
setSelectedZone({
|
||||
zoneName: response?.zoneName,
|
||||
activeSides: response?.activeSides || [],
|
||||
panelOrder: response?.panelOrder || [],
|
||||
lockedPanels: response?.lockedPanels || [],
|
||||
widgets: response?.widgets || [],
|
||||
zoneId: response?.zoneId,
|
||||
zoneViewPortTarget: response?.viewPortCenter || [],
|
||||
zoneViewPortPosition:
|
||||
response?.viewPortposition || [],
|
||||
});
|
||||
try {
|
||||
// Avoid re-fetching if the same zone is already selected
|
||||
if (selectedZone?.zoneId === id) {
|
||||
console.log("Zone is already selected:", selectedZone.zoneName);
|
||||
return;
|
||||
}
|
||||
|
||||
setSubModule("zoneProperties");
|
||||
|
||||
const email = localStorage.getItem("email");
|
||||
const organization = email?.split("@")[1]?.split(".")[0] || "";
|
||||
|
||||
let response = await getZoneData(id, organization);
|
||||
|
||||
setSelectedZone({
|
||||
zoneName: response?.zoneName,
|
||||
activeSides: response?.activeSides || [],
|
||||
panelOrder: response?.panelOrder || [],
|
||||
lockedPanels: response?.lockedPanels || [],
|
||||
widgets: response?.widgets || [],
|
||||
zoneId: response?.zoneId,
|
||||
zoneViewPortTarget: response?.viewPortCenter || [],
|
||||
zoneViewPortPosition: response?.viewPortposition || [],
|
||||
});
|
||||
|
||||
console.log("Zone selected:", response?.zoneName);
|
||||
} catch (error) {
|
||||
console.error("Error selecting zone:", error);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{items.length > 0 ? (
|
||||
|
|
|
@ -0,0 +1,523 @@
|
|||
import React, { useState } from "react";
|
||||
import { ArrowIcon } from "../../icons/ExportCommonIcons";
|
||||
|
||||
interface MenuBarProps {
|
||||
setOpenMenu: (isOpen: boolean) => void; // Function to update menu state
|
||||
}
|
||||
|
||||
const MenuBar: React.FC<MenuBarProps> = ({ setOpenMenu }) => {
|
||||
const [activeMenu, setActiveMenu] = useState<string | null>(null);
|
||||
const [activeSubMenu, setActiveSubMenu] = useState<string | null>(null);
|
||||
|
||||
// State to track selection for all menu items
|
||||
const [selectedItems, setSelectedItems] = useState<Record<string, boolean>>(
|
||||
{}
|
||||
);
|
||||
|
||||
// Function to toggle selection for a specific item
|
||||
const toggleSelection = (itemName: string) => {
|
||||
setSelectedItems((prev) => ({
|
||||
...prev,
|
||||
[itemName]: !prev[itemName], // Toggle the selection state
|
||||
}));
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className="menu-bar"
|
||||
onPointerLeave={() => {
|
||||
setOpenMenu(false);
|
||||
}}
|
||||
>
|
||||
{/* Top-level menu buttons */}
|
||||
<div className="menu-buttons">
|
||||
{/* File Menu */}
|
||||
<div
|
||||
className="menu-button-container"
|
||||
onMouseEnter={() => setActiveMenu("File")}
|
||||
onMouseLeave={() => {
|
||||
setActiveMenu(null);
|
||||
setActiveSubMenu(null);
|
||||
}}
|
||||
>
|
||||
<div className="menu-button">
|
||||
File
|
||||
<span className="dropdown-icon">
|
||||
<ArrowIcon />
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* File Dropdown */}
|
||||
{activeMenu === "File" && (
|
||||
<div className="dropdown-menu">
|
||||
{/* New File */}
|
||||
<div
|
||||
className="menu-item-container"
|
||||
onClick={() => toggleSelection("New File")}
|
||||
>
|
||||
<div className="menu-item">
|
||||
<span>New File</span>
|
||||
<div className="menu-item-right">
|
||||
<span className="shortcut">Ctrl + N</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Open Local File */}
|
||||
<div
|
||||
className="menu-item-container"
|
||||
onClick={() => toggleSelection("Open Local File")}
|
||||
>
|
||||
<div className="menu-item">
|
||||
<span>Open Local File</span>
|
||||
<div className="menu-item-right">
|
||||
<span className="shortcut">Ctrl + O</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Save Version */}
|
||||
<div
|
||||
className="menu-item-container"
|
||||
onClick={() => toggleSelection("Save Version")}
|
||||
>
|
||||
<div className="menu-item">
|
||||
<span>Save Version</span>
|
||||
</div>
|
||||
<div className="split"></div>
|
||||
</div>
|
||||
|
||||
{/* Make a Copy */}
|
||||
<div
|
||||
className="menu-item-container"
|
||||
onClick={() => toggleSelection("Make a Copy")}
|
||||
>
|
||||
<div className="menu-item">
|
||||
<span>Make a Copy</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Share */}
|
||||
<div
|
||||
className="menu-item-container"
|
||||
onClick={() => toggleSelection("Share")}
|
||||
>
|
||||
<div className="menu-item">
|
||||
<span>Share</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Rename */}
|
||||
<div
|
||||
className="menu-item-container"
|
||||
onClick={() => toggleSelection("Rename")}
|
||||
>
|
||||
<div className="menu-item">
|
||||
<span>Rename</span>
|
||||
</div>
|
||||
<div className="split"></div>
|
||||
</div>
|
||||
|
||||
{/* Import */}
|
||||
<div
|
||||
className="menu-item-container"
|
||||
onClick={() => toggleSelection("Import")}
|
||||
>
|
||||
<div className="menu-item">
|
||||
<span>Import</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Close File */}
|
||||
<div
|
||||
className="menu-item-container"
|
||||
onClick={() => toggleSelection("Close File")}
|
||||
>
|
||||
<div className="menu-item">
|
||||
<span>Close File</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Edit Menu */}
|
||||
<div
|
||||
className="menu-button-container"
|
||||
onMouseEnter={() => setActiveMenu("Edit")}
|
||||
onMouseLeave={() => {
|
||||
setActiveMenu(null);
|
||||
setActiveSubMenu(null);
|
||||
}}
|
||||
>
|
||||
<div className="menu-button">
|
||||
Edit
|
||||
<span className="dropdown-icon">
|
||||
<ArrowIcon />
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Edit Dropdown */}
|
||||
{activeMenu === "Edit" && (
|
||||
<div className="dropdown-menu">
|
||||
{/* Undo */}
|
||||
<div
|
||||
className="menu-item-container"
|
||||
onClick={() => toggleSelection("Undo")}
|
||||
>
|
||||
<div className="menu-item">
|
||||
<span>Undo</span>
|
||||
<div className="menu-item-right">
|
||||
<span className="shortcut">Ctrl + Z</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Redo */}
|
||||
<div
|
||||
className="menu-item-container"
|
||||
onClick={() => toggleSelection("Redo")}
|
||||
>
|
||||
<div className="menu-item">
|
||||
<span>Redo</span>
|
||||
<div className="menu-item-right">
|
||||
<span className="shortcut">Ctrl + Shift + Z</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="split"></div>
|
||||
</div>
|
||||
|
||||
{/* Undo History */}
|
||||
<div
|
||||
className="menu-item-container"
|
||||
onClick={() => toggleSelection("Undo History")}
|
||||
>
|
||||
<div className="menu-item">
|
||||
<span>Undo History</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Redo History */}
|
||||
<div
|
||||
className="menu-item-container"
|
||||
onClick={() => toggleSelection("Redo History")}
|
||||
>
|
||||
<div className="menu-item">
|
||||
<span>Redo History</span>
|
||||
</div>
|
||||
<div className="split"></div>
|
||||
</div>
|
||||
|
||||
{/* Find */}
|
||||
<div
|
||||
className="menu-item-container"
|
||||
onClick={() => toggleSelection("Find")}
|
||||
>
|
||||
<div className="menu-item">
|
||||
<span>Find</span>
|
||||
<div className="menu-item-right">
|
||||
<span className="shortcut">Ctrl + F</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Delete */}
|
||||
<div
|
||||
className="menu-item-container"
|
||||
onClick={() => toggleSelection("Delete")}
|
||||
>
|
||||
<div className="menu-item">
|
||||
<span>Delete</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Select by... */}
|
||||
<div
|
||||
className="menu-item-container"
|
||||
onClick={() => toggleSelection("Select by...")}
|
||||
>
|
||||
<div className="menu-item">
|
||||
<span>Select by...</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Keymap */}
|
||||
<div
|
||||
className="menu-item-container"
|
||||
onClick={() => toggleSelection("Keymap")}
|
||||
>
|
||||
<div className="menu-item">
|
||||
<span>Keymap</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* View Menu */}
|
||||
<div
|
||||
className="menu-button-container"
|
||||
onMouseEnter={() => setActiveMenu("View")}
|
||||
onMouseLeave={() => {
|
||||
setActiveMenu(null);
|
||||
setActiveSubMenu(null);
|
||||
}}
|
||||
>
|
||||
<div className="menu-button">
|
||||
View
|
||||
<span className="dropdown-icon">
|
||||
<ArrowIcon />
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* View Dropdown */}
|
||||
{activeMenu === "View" && (
|
||||
<div className="dropdown-menu">
|
||||
{/* Grid */}
|
||||
<div
|
||||
className={"menu-item-container"}
|
||||
onClick={() => toggleSelection("Grid")}
|
||||
>
|
||||
<div className="menu-item">
|
||||
<span>Grid</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Gizmo */}
|
||||
<div
|
||||
className="menu-item-container"
|
||||
onMouseEnter={() => setActiveSubMenu("View-Gizmo")}
|
||||
onMouseLeave={() => setActiveSubMenu(null)}
|
||||
>
|
||||
<div className="menu-item">
|
||||
<span>Gizmo</span>
|
||||
<span className="dropdown-icon">
|
||||
<ArrowIcon />
|
||||
</span>
|
||||
</div>
|
||||
<div className="split"></div>
|
||||
|
||||
{/* Gizmo Submenu */}
|
||||
{activeSubMenu === "View-Gizmo" && (
|
||||
<div className="submenu">
|
||||
{/* Visibility */}
|
||||
<div
|
||||
className="submenu-item"
|
||||
onClick={() => toggleSelection("Visibility")}
|
||||
>
|
||||
<span>
|
||||
{selectedItems["Visibility"] && "✓ "}
|
||||
Visibility
|
||||
</span>
|
||||
</div>
|
||||
<div className="split"></div>
|
||||
|
||||
{/* Cube view */}
|
||||
<div
|
||||
className="submenu-item"
|
||||
onClick={() => toggleSelection("Cube view")}
|
||||
>
|
||||
<span>Cube view</span>
|
||||
</div>
|
||||
|
||||
{/* Sphere view */}
|
||||
<div
|
||||
className="submenu-item"
|
||||
onClick={() => toggleSelection("Sphere view")}
|
||||
>
|
||||
<span>Sphere view</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Zoom */}
|
||||
<div
|
||||
className="menu-item-container"
|
||||
onClick={() => toggleSelection("Zoom")}
|
||||
>
|
||||
<div className="menu-item">
|
||||
<span>Zoom</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Full Screen */}
|
||||
<div
|
||||
className="menu-item-container"
|
||||
onClick={() => toggleSelection("Full Screen")}
|
||||
>
|
||||
<div className="menu-item">
|
||||
<span>Full Screen</span>
|
||||
<div className="menu-item-right">
|
||||
<span className="shortcut">F11</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Version History Menu */}
|
||||
<div
|
||||
className="menu-button-container"
|
||||
onMouseEnter={() => setActiveMenu("Version history")}
|
||||
onMouseLeave={() => {
|
||||
setActiveMenu(null);
|
||||
setActiveSubMenu(null);
|
||||
}}
|
||||
>
|
||||
<div className="menu-button">Version history</div>
|
||||
</div>
|
||||
|
||||
{/* Export As Menu */}
|
||||
<div
|
||||
className="menu-button-container"
|
||||
onMouseEnter={() => setActiveMenu("Export as...")}
|
||||
onMouseLeave={() => {
|
||||
setActiveMenu(null);
|
||||
setActiveSubMenu(null);
|
||||
}}
|
||||
>
|
||||
<div className="menu-button">Export as...</div>
|
||||
</div>
|
||||
|
||||
{/* Apps Menu */}
|
||||
{/* <div
|
||||
className="menu-button-container"
|
||||
onMouseEnter={() => setActiveMenu("Apps")}
|
||||
onMouseLeave={() => {
|
||||
setActiveMenu(null);
|
||||
setActiveSubMenu(null);
|
||||
}}
|
||||
>
|
||||
<div className="menu-button">
|
||||
Apps
|
||||
<span className="dropdown-icon">
|
||||
<ArrowIcon />
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{activeMenu === "Apps" && (
|
||||
<div className="dropdown-menu">
|
||||
<div
|
||||
className="menu-item-container"
|
||||
onClick={() => toggleSelection("New App")}
|
||||
>
|
||||
<div className="menu-item">
|
||||
<span>
|
||||
|
||||
New App
|
||||
</span>
|
||||
</div>
|
||||
<div className="split"></div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className="menu-item-container"
|
||||
onClick={() => toggleSelection("Work-flow Monitor")}
|
||||
>
|
||||
<div className="menu-item">
|
||||
<span>
|
||||
|
||||
Work-flow Monitor
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className="menu-item-container"
|
||||
onClick={() => toggleSelection("Temperature Visualizer")}
|
||||
>
|
||||
<div className="menu-item">
|
||||
<span>
|
||||
|
||||
Temperature Visualizer
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className="menu-item-container"
|
||||
onClick={() => toggleSelection("View all")}
|
||||
>
|
||||
<div className="menu-item">
|
||||
<span>
|
||||
|
||||
View all
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div> */}
|
||||
|
||||
{/* Help Menu */}
|
||||
<div
|
||||
className="menu-button-container"
|
||||
onMouseEnter={() => setActiveMenu("Help")}
|
||||
onMouseLeave={() => {
|
||||
setActiveMenu(null);
|
||||
setActiveSubMenu(null);
|
||||
}}
|
||||
>
|
||||
<div className="menu-button">
|
||||
Help
|
||||
<span className="dropdown-icon">
|
||||
<ArrowIcon />
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Help Dropdown */}
|
||||
{activeMenu === "Help" && (
|
||||
<div className="dropdown-menu">
|
||||
{/* Shortcuts */}
|
||||
<div
|
||||
className="menu-item-container"
|
||||
onClick={() => toggleSelection("Shortcuts")}
|
||||
>
|
||||
<div className="menu-item">
|
||||
<span>Shortcuts</span>
|
||||
<div className="menu-item-right">
|
||||
<span className="shortcut">Ctrl + Shift + ?</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Manual */}
|
||||
<div
|
||||
className="menu-item-container"
|
||||
onClick={() => toggleSelection("Manual")}
|
||||
>
|
||||
<div className="menu-item">
|
||||
<span>Manual</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Video Tutorials */}
|
||||
<div
|
||||
className="menu-item-container"
|
||||
onClick={() => toggleSelection("Video Tutorials")}
|
||||
>
|
||||
<div className="menu-item">
|
||||
<span>Video Tutorials</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Report a bug */}
|
||||
<div
|
||||
className="menu-item-container"
|
||||
onClick={() => toggleSelection("Report a bug")}
|
||||
>
|
||||
<div className="menu-item">
|
||||
<span>Report a bug</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default MenuBar;
|
|
@ -345,7 +345,7 @@ const BarGraphComponent = ({
|
|||
|
||||
if (id !== "") {
|
||||
try {
|
||||
const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_LOCAL_BASE_URL}/api/v2/WidgetData/${id}/${organization}`);
|
||||
const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/WidgetData/${id}/${organization}`);
|
||||
if (response.status === 200) {
|
||||
setmeasurements(response.data.Data.measurements)
|
||||
setDuration(response.data.Data.duration)
|
||||
|
|
|
@ -158,7 +158,7 @@ const DoughnutGraphComponent = ({
|
|||
|
||||
if (id !== "") {
|
||||
try {
|
||||
const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_LOCAL_BASE_URL}/api/v2/WidgetData/${id}/${organization}`);
|
||||
const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/WidgetData/${id}/${organization}`);
|
||||
if (response.status === 200) {
|
||||
setmeasurements(response.data.Data.measurements)
|
||||
setDuration(response.data.Data.duration)
|
||||
|
|
|
@ -158,7 +158,7 @@ const LineGraphComponent = ({
|
|||
|
||||
if (id !== "") {
|
||||
try {
|
||||
const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_LOCAL_BASE_URL}/api/v2/WidgetData/${id}/${organization}`);
|
||||
const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/WidgetData/${id}/${organization}`);
|
||||
if (response.status === 200) {
|
||||
setmeasurements(response.data.Data.measurements)
|
||||
setDuration(response.data.Data.duration)
|
||||
|
|
|
@ -344,7 +344,7 @@ const PieChartComponent = ({
|
|||
|
||||
if (id !== "") {
|
||||
try {
|
||||
const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_LOCAL_BASE_URL}/api/v2/WidgetData/${id}/${organization}`);
|
||||
const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/WidgetData/${id}/${organization}`);
|
||||
if (response.status === 200) {
|
||||
setmeasurements(response.data.Data.measurements)
|
||||
setDuration(response.data.Data.duration)
|
||||
|
|
|
@ -1,22 +1,64 @@
|
|||
import { useMemo } from "react";
|
||||
import { Line, PolarArea } from "react-chartjs-2";
|
||||
import React, { useEffect, useMemo, useState } from "react";
|
||||
import { PolarArea } from "react-chartjs-2";
|
||||
import io from "socket.io-client";
|
||||
import { useThemeStore } from "../../../../store/useThemeStore";
|
||||
import useChartStore from "../../../../store/useChartStore";
|
||||
import { useWidgetStore } from "../../../../store/useWidgetStore";
|
||||
import axios from "axios";
|
||||
|
||||
interface ChartComponentProps {
|
||||
id: string;
|
||||
type: any;
|
||||
title: string;
|
||||
fontFamily?: string;
|
||||
fontSize?: string;
|
||||
fontWeight?: "Light" | "Regular" | "Bold";
|
||||
data: any;
|
||||
}
|
||||
|
||||
const PolarAreaGraphComponent = ({
|
||||
id,
|
||||
type,
|
||||
title,
|
||||
fontFamily,
|
||||
fontSize,
|
||||
fontWeight = "Regular",
|
||||
}: ChartComponentProps) => {
|
||||
// Memoize Font Weight Mapping
|
||||
const { themeColor } = useThemeStore();
|
||||
const { measurements: chartMeasurements, duration: chartDuration, name: widgetName } = useChartStore();
|
||||
const [measurements, setmeasurements] = useState<any>({});
|
||||
const [duration, setDuration] = useState("1h")
|
||||
const [name, setName] = useState("Widget")
|
||||
const [chartData, setChartData] = useState<{ labels: string[]; datasets: any[] }>({
|
||||
labels: [],
|
||||
datasets: [],
|
||||
});
|
||||
const { selectedChartId } = useWidgetStore();
|
||||
|
||||
const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
|
||||
const email = localStorage.getItem("email") || "";
|
||||
const organization = email?.split("@")[1]?.split(".")[0]
|
||||
const defaultData = {
|
||||
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
|
||||
datasets: [
|
||||
{
|
||||
label: "Dataset",
|
||||
data: [12, 19, 3, 5, 2, 3],
|
||||
backgroundColor: ["#6f42c1"],
|
||||
borderColor: "#b392f0",
|
||||
borderWidth: 1,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
console.log("titleeeeeeeeeeeeeeeeeee",title);
|
||||
},[])
|
||||
|
||||
// Memoize Theme Colors
|
||||
const buttonActionColor = useMemo(() => themeColor[0] || "#5c87df", [themeColor]);
|
||||
const buttonAbortColor = useMemo(() => themeColor[1] || "#ffffff", [themeColor]);
|
||||
|
||||
// Memoize Font Styling
|
||||
const chartFontWeightMap = useMemo(
|
||||
() => ({
|
||||
Light: "lighter" as const,
|
||||
|
@ -26,19 +68,9 @@ const PolarAreaGraphComponent = ({
|
|||
[]
|
||||
);
|
||||
|
||||
// Parse and Memoize Font Size
|
||||
const fontSizeValue = useMemo(
|
||||
() => (fontSize ? parseInt(fontSize) : 12),
|
||||
[fontSize]
|
||||
);
|
||||
const fontSizeValue = useMemo(() => (fontSize ? parseInt(fontSize) : 12), [fontSize]);
|
||||
const fontWeightValue = useMemo(() => chartFontWeightMap[fontWeight], [fontWeight, chartFontWeightMap]);
|
||||
|
||||
// Determine and Memoize Font Weight
|
||||
const fontWeightValue = useMemo(
|
||||
() => chartFontWeightMap[fontWeight],
|
||||
[fontWeight, chartFontWeightMap]
|
||||
);
|
||||
|
||||
// Memoize Chart Font Style
|
||||
const chartFontStyle = useMemo(
|
||||
() => ({
|
||||
family: fontFamily || "Arial",
|
||||
|
@ -48,46 +80,110 @@ const PolarAreaGraphComponent = ({
|
|||
[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: false, // This hides the x-axis labels
|
||||
// Memoize Chart Options
|
||||
const options = useMemo(
|
||||
() => ({
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
title: {
|
||||
display: true,
|
||||
text: name,
|
||||
font: chartFontStyle,
|
||||
},
|
||||
legend: {
|
||||
display: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
[title, chartFontStyle]
|
||||
);
|
||||
scales: {
|
||||
// x: {
|
||||
// ticks: {
|
||||
// display: true, // This hides the x-axis labels
|
||||
// },
|
||||
// },
|
||||
},
|
||||
}),
|
||||
[title, chartFontStyle, name]
|
||||
);
|
||||
|
||||
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,
|
||||
},
|
||||
],
|
||||
};
|
||||
// useEffect(() => {console.log(measurements);
|
||||
// },[measurements])
|
||||
|
||||
return <PolarArea data={chartData} options={options} />;
|
||||
useEffect(() => {
|
||||
if (!iotApiUrl || !measurements || Object.keys(measurements).length === 0) return;
|
||||
|
||||
const socket = io(`http://${iotApiUrl}`);
|
||||
|
||||
const inputData = {
|
||||
measurements,
|
||||
duration,
|
||||
interval: 1000,
|
||||
};
|
||||
|
||||
|
||||
const startStream = () => {
|
||||
socket.emit("lineInput", inputData);
|
||||
};
|
||||
|
||||
socket.on("connect", startStream);
|
||||
|
||||
socket.on("lineOutput", (response) => {
|
||||
const responseData = response.data;
|
||||
|
||||
// Extract timestamps and values
|
||||
const labels = responseData.time;
|
||||
const datasets = Object.keys(measurements).map((key) => {
|
||||
const measurement = measurements[key];
|
||||
const datasetKey = `${measurement.name}.${measurement.fields}`;
|
||||
return {
|
||||
label: datasetKey,
|
||||
data: responseData[datasetKey]?.values ?? [],
|
||||
backgroundColor: "#6f42c1",
|
||||
borderColor: "#b392f0",
|
||||
borderWidth: 1,
|
||||
};
|
||||
});
|
||||
|
||||
setChartData({ labels, datasets });
|
||||
});
|
||||
|
||||
return () => {
|
||||
socket.off("lineOutput");
|
||||
socket.emit("stop_stream"); // Stop streaming when component unmounts
|
||||
socket.disconnect();
|
||||
};
|
||||
}, [measurements, duration, iotApiUrl]);
|
||||
|
||||
const fetchSavedInputes = async() => {
|
||||
|
||||
if (id !== "") {
|
||||
try {
|
||||
const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/WidgetData/${id}/${organization}`);
|
||||
if (response.status === 200) {
|
||||
setmeasurements(response.data.Data.measurements)
|
||||
setDuration(response.data.Data.duration)
|
||||
setName(response.data.widgetName)
|
||||
} else {
|
||||
console.log("Unexpected response:", response);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("There was an error!", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
fetchSavedInputes();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedChartId?.id === id) {
|
||||
fetchSavedInputes();
|
||||
}
|
||||
}
|
||||
,[chartMeasurements, chartDuration, widgetName])
|
||||
|
||||
return <PolarArea data={Object.keys(measurements).length > 0 ? chartData : defaultData} options={options} />;
|
||||
};
|
||||
|
||||
export default PolarAreaGraphComponent;
|
||||
export default PolarAreaGraphComponent;
|
|
@ -14,8 +14,6 @@ const FleetEfficiency = () => {
|
|||
per: progress,
|
||||
|
||||
});
|
||||
|
||||
console.log("Dragged Data:", cardData);
|
||||
event.dataTransfer.setData("text/plain", cardData);
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
import React from 'react'
|
||||
|
||||
type Props = {}
|
||||
|
||||
const FleetEfficiencyComponent = ({
|
||||
object
|
||||
}: any) => {
|
||||
return (
|
||||
<>
|
||||
<h2 className="header">Fleet Efficiency</h2>
|
||||
<div className="progressContainer">
|
||||
<div className="progress">
|
||||
<div className="barOverflow">
|
||||
<div
|
||||
className="bar"
|
||||
style={{ transform: `rotate(${object.value}deg)` }}
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="scaleLabels">
|
||||
<span>0%</span>
|
||||
<div className="centerText">
|
||||
<div className="percentage">{object.per}%</div>
|
||||
<div className="status">Optimal</div>
|
||||
</div>
|
||||
<span>100%</span>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default FleetEfficiencyComponent
|
|
@ -23,6 +23,7 @@ const SimpleCard: React.FC<SimpleCardProps> = ({
|
|||
value,
|
||||
per,
|
||||
icon: Icon,
|
||||
|
||||
className: event.currentTarget.className,
|
||||
position: [rect.top, rect.left], // ✅ Store position
|
||||
});
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
import React from 'react'
|
||||
import { WalletIcon } from '../../../icons/3dChartIcons'
|
||||
import { useWidgetStore } from '../../../../store/useWidgetStore';
|
||||
|
||||
const TotalCardComponent = ({
|
||||
object
|
||||
}: any) => {
|
||||
|
||||
const { setSelectedChartId } =
|
||||
useWidgetStore();
|
||||
return (
|
||||
<>
|
||||
<div className="header-wrapper" onClick={() => {
|
||||
setSelectedChartId(object.id)
|
||||
}}>
|
||||
<div className="header">{object.header}</div>
|
||||
<div className="data-values">
|
||||
<div className="value">{object.value}</div>
|
||||
<div className="per">{object.per}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="icon">
|
||||
<WalletIcon />
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default TotalCardComponent
|
|
@ -0,0 +1,132 @@
|
|||
import React, { useState } from 'react'
|
||||
import { Line } from 'react-chartjs-2'
|
||||
import axios from 'axios';
|
||||
|
||||
const WarehouseThroughputComponent = ({
|
||||
object
|
||||
}: any) => {
|
||||
|
||||
const [measurements, setmeasurements] = useState<any>({});
|
||||
const [duration, setDuration] = useState("1h")
|
||||
|
||||
const lineGraphData = {
|
||||
labels: [
|
||||
"Jan",
|
||||
"Feb",
|
||||
"Mar",
|
||||
"Apr",
|
||||
"May",
|
||||
"Jun",
|
||||
"Jul",
|
||||
"Aug",
|
||||
"Sep",
|
||||
"Oct",
|
||||
"Nov",
|
||||
"Dec",
|
||||
], // Months of the year
|
||||
datasets: [
|
||||
{
|
||||
label: "Throughput (units/month)",
|
||||
data: [500, 400, 300, 450, 350, 250, 200, 300, 250, 150, 100, 150], // Example monthly data
|
||||
borderColor: "#6f42c1", // Use the desired color for the line (purple)
|
||||
backgroundColor: "rgba(111, 66, 193, 0.2)", // Use a semi-transparent purple for the fill
|
||||
borderWidth: 2, // Line thickness
|
||||
fill: true, // Enable fill for this dataset
|
||||
pointRadius: 0, // Remove dots at each data point
|
||||
tension: 0.5, // Smooth interpolation for the line
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
// Line graph options
|
||||
const lineGraphOptions = {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false, // Allow custom height/width adjustments
|
||||
plugins: {
|
||||
legend: {
|
||||
display: false, // Hide legend
|
||||
},
|
||||
title: {
|
||||
display: false, // No chart title needed
|
||||
},
|
||||
tooltip: {
|
||||
callbacks: {
|
||||
label: (context: any) => {
|
||||
const value = context.parsed.y;
|
||||
return `${value} units`; // Customize tooltip to display "units"
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
grid: {
|
||||
display: false, // Hide x-axis grid lines
|
||||
},
|
||||
ticks: {
|
||||
maxRotation: 0, // Prevent label rotation
|
||||
autoSkip: false, // Display all months
|
||||
font: {
|
||||
size: 8, // Adjust font size for readability
|
||||
color: "#ffffff", // Light text color for labels
|
||||
},
|
||||
},
|
||||
},
|
||||
y: {
|
||||
display: true, // Show y-axis
|
||||
grid: {
|
||||
drawBorder: false, // Remove border line
|
||||
color: "rgba(255, 255, 255, 0.2)", // Light gray color for grid lines
|
||||
borderDash: [5, 5], // Dotted line style (array defines dash and gap lengths)
|
||||
},
|
||||
ticks: {
|
||||
font: {
|
||||
size: 8, // Adjust font size for readability
|
||||
color: "#ffffff", // Light text color for ticks
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
elements: {
|
||||
line: {
|
||||
tension: 0.5, // Smooth interpolation for the line
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
// const fetchSavedInputes = async() => {
|
||||
|
||||
// if (object.id !== "") {
|
||||
// try {
|
||||
// const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_LOCAL_BASE_URL}/api/v2/WidgetData/${id}/${organization}`);
|
||||
// if (response.status === 200) {
|
||||
// setmeasurements(response.data.Data.measurements)
|
||||
// setDuration(response.data.Data.duration)
|
||||
// setName(response.data.widgetName)
|
||||
// } else {
|
||||
// console.log("Unexpected response:", response);
|
||||
// }
|
||||
// } catch (error) {
|
||||
// console.error("There was an error!", error);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="header">
|
||||
<h2>Warehouse Throughput</h2>
|
||||
<p>
|
||||
<span>(+5) more</span> in 2025
|
||||
</p>
|
||||
</div>
|
||||
<div className="lineGraph" style={{ height: "100%" }}>
|
||||
<Line data={lineGraphData} options={lineGraphOptions} />
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default WarehouseThroughputComponent
|
|
@ -1,24 +1,17 @@
|
|||
import PolygonGenerator from "./polygonGenerator";
|
||||
import { useThree } from "@react-three/fiber";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { useEffect, useMemo, useRef, useState } from "react";
|
||||
import * as THREE from "three";
|
||||
import * as Types from "../../../types/world/worldTypes";
|
||||
import PathNavigator from "./pathNavigator";
|
||||
import NavMeshDetails from "./navMeshDetails";
|
||||
|
||||
const Agv = ({
|
||||
lines,
|
||||
plane,
|
||||
}: {
|
||||
lines: Types.RefLines;
|
||||
plane: Types.RefMesh;
|
||||
}) => {
|
||||
let pathPoints = [
|
||||
const Agv = ({ lines, plane }: { lines: Types.RefLines; plane: Types.RefMesh; }) => {
|
||||
const pathPoints = useMemo(() => [
|
||||
[
|
||||
{ x: 8.477161935339709, y: 0, z: 17.41343083550102 },
|
||||
{ x: 9.175416491482693, y: 0, z: -12.361001232663693 },
|
||||
],
|
||||
,
|
||||
// [
|
||||
// { x: 13.508213355232144, y: 0, z: -15.456970649652018 },
|
||||
// { x: -30.464866520869617, y: 0, z: 9.779806557688929 },
|
||||
|
@ -27,7 +20,8 @@ const Agv = ({
|
|||
{ x: 16.792040856420844, y: 0, z: 15.86281907549489 },
|
||||
{ x: -42.77173264503395, y: 0, z: -15.821322764400804 },
|
||||
],
|
||||
];
|
||||
], []);
|
||||
|
||||
let groupRef = useRef() as Types.RefGroup;
|
||||
const [navMesh, setNavMesh] = useState();
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ async function Draw(
|
|||
floorPlanGroup: Types.RefGroup,
|
||||
ReferenceLineMesh: Types.RefMesh,
|
||||
LineCreated: Types.RefBoolean,
|
||||
setRefTextUpdate: Types.NumberIncrementState,
|
||||
setRefTextUpdate: any,
|
||||
Tube: Types.RefTubeGeometry,
|
||||
anglesnappedPoint: Types.RefVector3,
|
||||
isAngleSnapped: Types.RefBoolean,
|
||||
|
|
|
@ -17,7 +17,7 @@ function addRoofToScene(
|
|||
};
|
||||
|
||||
const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings);
|
||||
const material = new THREE.MeshStandardMaterial({ color: CONSTANTS.roofConfig.defaultColor, side: THREE.DoubleSide, transparent: true, depthWrite: false });
|
||||
const material = new THREE.MeshStandardMaterial({ color: CONSTANTS.roofConfig.defaultColor, side: THREE.DoubleSide });
|
||||
const mesh = new THREE.Mesh(geometry, material);
|
||||
mesh.position.y = CONSTANTS.wallConfig.height + floor;
|
||||
mesh.castShadow = true;
|
||||
|
|
|
@ -193,7 +193,7 @@ const FloorItemsGroup = ({ itemsGroup, hoveredDeletableFloorItem, AttachedObject
|
|||
}
|
||||
const Mode = transformMode;
|
||||
|
||||
if (Mode !== null || activeTool === "Cursor") {
|
||||
if (Mode !== null || activeTool === "cursor") {
|
||||
if (!itemsGroup.current) return;
|
||||
let intersects = raycaster.intersectObjects(itemsGroup.current.children, true);
|
||||
if (intersects.length > 0 && intersects[0]?.object?.parent?.parent?.position && intersects[0]?.object?.parent?.parent?.scale && intersects[0]?.object?.parent?.parent?.rotation) {
|
||||
|
@ -225,7 +225,7 @@ const FloorItemsGroup = ({ itemsGroup, hoveredDeletableFloorItem, AttachedObject
|
|||
|
||||
const Mode = transformMode;
|
||||
|
||||
if (Mode !== null || activeTool === "Cursor") {
|
||||
if (Mode !== null || activeTool === "cursor") {
|
||||
if (!itemsGroup.current) return;
|
||||
let intersects = raycaster.intersectObjects(itemsGroup.current.children, true);
|
||||
if (intersects.length > 0 && intersects[0]?.object?.parent?.parent?.position && intersects[0]?.object?.parent?.parent?.scale && intersects[0]?.object?.parent?.parent?.rotation) {
|
||||
|
|
|
@ -1,21 +1,20 @@
|
|||
import * as THREE from 'three';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { useFrame } from '@react-three/fiber';
|
||||
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
|
||||
import camModel from '../../assets/gltf-glb/camera face 2.gltf';
|
||||
import getActiveUsersData from '../../services/factoryBuilder/collab/getActiveUsers';
|
||||
import { useActiveUsers, useSocketStore } from '../../store/store';
|
||||
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { Text, Html } from '@react-three/drei';
|
||||
import CollabUserIcon from './collabUserIcon';
|
||||
import image from '../../assets/image/userImage.png';
|
||||
|
||||
import * as THREE from "three";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { useFrame } from "@react-three/fiber";
|
||||
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
|
||||
import camModel from "../../assets/gltf-glb/camera face 2.gltf";
|
||||
import getActiveUsersData from "../../services/factoryBuilder/collab/getActiveUsers";
|
||||
import { useActiveUsers, useSocketStore } from "../../store/store";
|
||||
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { Html } from "@react-three/drei";
|
||||
import CollabUserIcon from "./collabUserIcon";
|
||||
import { getAvatarColor } from "./users/functions/getAvatarColor";
|
||||
|
||||
const CamModelsGroup = () => {
|
||||
let navigate = useNavigate();
|
||||
const groupRef = useRef<THREE.Group>(null);
|
||||
const email = localStorage.getItem('email');
|
||||
const email = localStorage.getItem("email");
|
||||
const { activeUsers, setActiveUsers } = useActiveUsers();
|
||||
const { socket } = useSocketStore();
|
||||
const loader = new GLTFLoader();
|
||||
|
@ -23,93 +22,147 @@ const CamModelsGroup = () => {
|
|||
const [cams, setCams] = useState<any[]>([]);
|
||||
const [models, setModels] = useState<Record<string, { targetPosition: THREE.Vector3; targetRotation: THREE.Euler }>>({});
|
||||
|
||||
dracoLoader.setDecoderPath('three/examples/jsm/libs/draco/gltf/');
|
||||
dracoLoader.setDecoderPath("three/examples/jsm/libs/draco/gltf/");
|
||||
loader.setDRACOLoader(dracoLoader);
|
||||
|
||||
useEffect(() => {
|
||||
if (!email) {
|
||||
navigate('/');
|
||||
navigate("/");
|
||||
}
|
||||
if (!socket) return;
|
||||
const organization = email!.split('@')[1].split('.')[0];
|
||||
const organization = email!.split("@")[1].split(".")[0];
|
||||
|
||||
socket.on('userConnectRespones', (data: any) => {
|
||||
socket.on("userConnectRespones", (data: any) => {
|
||||
if (!groupRef.current) return;
|
||||
if (data.data.userData.email === email) return
|
||||
if (socket.id === data.socketId || organization !== data.organization) return;
|
||||
if (data.data.userData.email === email) return;
|
||||
if (socket.id === data.socketId || organization !== data.organization)
|
||||
return;
|
||||
|
||||
const model = groupRef.current.getObjectByProperty('uuid', data.data.userData._id);
|
||||
const model = groupRef.current.getObjectByProperty(
|
||||
"uuid",
|
||||
data.data.userData._id
|
||||
);
|
||||
if (model) {
|
||||
groupRef.current.remove(model);
|
||||
}
|
||||
loader.load(camModel, (gltf) => {
|
||||
const newModel = gltf.scene.clone();
|
||||
newModel.uuid = data.data.userData._id;
|
||||
newModel.position.set(data.data.position.x, data.data.position.y, data.data.position.z);
|
||||
newModel.rotation.set(data.data.rotation.x, data.data.rotation.y, data.data.rotation.z);
|
||||
newModel.position.set(
|
||||
data.data.position.x,
|
||||
data.data.position.y,
|
||||
data.data.position.z
|
||||
);
|
||||
newModel.rotation.set(
|
||||
data.data.rotation.x,
|
||||
data.data.rotation.y,
|
||||
data.data.rotation.z
|
||||
);
|
||||
newModel.userData = data.data.userData;
|
||||
setCams((prev) => [...prev, newModel]);
|
||||
setActiveUsers([...activeUsers, data.data.userData]);
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('userDisConnectRespones', (data: any) => {
|
||||
socket.on("userDisConnectRespones", (data: any) => {
|
||||
if (!groupRef.current) return;
|
||||
if (socket.id === data.socketId || organization !== data.organization) return;
|
||||
if (socket.id === data.socketId || organization !== data.organization)
|
||||
return;
|
||||
|
||||
setCams((prev) => prev.filter((cam) => cam.uuid !== data.data.userData._id));
|
||||
setActiveUsers(activeUsers.filter((user: any) => user._id !== data.data.userData._id));
|
||||
setCams((prev) =>
|
||||
prev.filter((cam) => cam.uuid !== data.data.userData._id)
|
||||
);
|
||||
setActiveUsers(
|
||||
activeUsers.filter((user: any) => user._id !== data.data.userData._id)
|
||||
);
|
||||
});
|
||||
|
||||
socket.on('cameraUpdateResponse', (data: any) => {
|
||||
if (!groupRef.current || socket.id === data.socketId || organization !== data.organization) return;
|
||||
socket.on("cameraUpdateResponse", (data: any) => {
|
||||
if (
|
||||
!groupRef.current ||
|
||||
socket.id === data.socketId ||
|
||||
organization !== data.organization
|
||||
)
|
||||
return;
|
||||
|
||||
setModels((prev) => ({
|
||||
...prev,
|
||||
[data.data.userId]: {
|
||||
targetPosition: new THREE.Vector3(data.data.position.x, data.data.position.y, data.data.position.z),
|
||||
targetRotation: new THREE.Euler(data.data.rotation.x, data.data.rotation.y, data.data.rotation.z),
|
||||
targetPosition: new THREE.Vector3(
|
||||
data.data.position.x,
|
||||
data.data.position.y,
|
||||
data.data.position.z
|
||||
),
|
||||
targetRotation: new THREE.Euler(
|
||||
data.data.rotation.x,
|
||||
data.data.rotation.y,
|
||||
data.data.rotation.z
|
||||
),
|
||||
},
|
||||
}));
|
||||
});
|
||||
|
||||
return () => {
|
||||
socket.off('userConnectRespones');
|
||||
socket.off('userDisConnectRespones');
|
||||
socket.off('cameraUpdateResponse');
|
||||
socket.off("userConnectRespones");
|
||||
socket.off("userDisConnectRespones");
|
||||
socket.off("cameraUpdateResponse");
|
||||
};
|
||||
}, [socket, activeUsers]);
|
||||
|
||||
useFrame(() => {
|
||||
if (!groupRef.current) return;
|
||||
Object.keys(models).forEach((uuid) => {
|
||||
const model = groupRef.current!.getObjectByProperty('uuid', uuid);
|
||||
const model = groupRef.current!.getObjectByProperty("uuid", uuid);
|
||||
if (!model) return;
|
||||
|
||||
const { targetPosition, targetRotation } = models[uuid];
|
||||
model.position.lerp(targetPosition, 0.1);
|
||||
model.rotation.x = THREE.MathUtils.lerp(model.rotation.x, targetRotation.x, 0.1);
|
||||
model.rotation.y = THREE.MathUtils.lerp(model.rotation.y, targetRotation.y, 0.1);
|
||||
model.rotation.z = THREE.MathUtils.lerp(model.rotation.z, targetRotation.z, 0.1);
|
||||
model.rotation.x = THREE.MathUtils.lerp(
|
||||
model.rotation.x,
|
||||
targetRotation.x,
|
||||
0.1
|
||||
);
|
||||
model.rotation.y = THREE.MathUtils.lerp(
|
||||
model.rotation.y,
|
||||
targetRotation.y,
|
||||
0.1
|
||||
);
|
||||
model.rotation.z = THREE.MathUtils.lerp(
|
||||
model.rotation.z,
|
||||
targetRotation.z,
|
||||
0.1
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!groupRef.current) return;
|
||||
const organization = email!.split('@')[1].split('.')[0];
|
||||
const organization = email!.split("@")[1].split(".")[0];
|
||||
getActiveUsersData(organization).then((data) => {
|
||||
const filteredData = data.cameraDatas.filter((camera: any) => camera.userData.email !== email);
|
||||
const filteredData = data.cameraDatas.filter(
|
||||
(camera: any) => camera.userData.email !== email
|
||||
);
|
||||
let a:any = [];
|
||||
if (filteredData.length > 0) {
|
||||
loader.load(camModel, (gltf) => {
|
||||
const newCams = filteredData.map((cam: any) => {
|
||||
const newModel = gltf.scene.clone();
|
||||
newModel.uuid = cam.userData._id;
|
||||
newModel.position.set(cam.position.x, cam.position.y, cam.position.z);
|
||||
newModel.rotation.set(cam.rotation.x, cam.rotation.y, cam.rotation.z);
|
||||
newModel.position.set(
|
||||
cam.position.x,
|
||||
cam.position.y,
|
||||
cam.position.z
|
||||
);
|
||||
newModel.rotation.set(
|
||||
cam.rotation.x,
|
||||
cam.rotation.y,
|
||||
cam.rotation.z
|
||||
);
|
||||
newModel.userData = cam.userData;
|
||||
setActiveUsers([...activeUsers, cam.userData]);
|
||||
a.push(cam.userData);
|
||||
return newModel;
|
||||
});
|
||||
setActiveUsers(a);
|
||||
setCams((prev) => [...prev, ...newCams]);
|
||||
});
|
||||
}
|
||||
|
@ -119,7 +172,7 @@ const CamModelsGroup = () => {
|
|||
return (
|
||||
<group ref={groupRef} name="Cam-Model-Group">
|
||||
{cams.map((cam, index) => (
|
||||
<primitive key={index} object={cam} >
|
||||
<primitive key={index} object={cam}>
|
||||
<Html
|
||||
as="div"
|
||||
center
|
||||
|
@ -130,8 +183,14 @@ const CamModelsGroup = () => {
|
|||
textAlign: "center",
|
||||
fontFamily: "Arial, sans-serif",
|
||||
}}
|
||||
position={[-0.015, 0, 0.7]}>
|
||||
<CollabUserIcon color={"#ff0000"} userImage={image} userName={cam.userData.userName} />
|
||||
position={[-0.015, 0, 0.7]}
|
||||
>
|
||||
<CollabUserIcon
|
||||
userImage={""}
|
||||
userName={cam.userData.userName}
|
||||
index={index}
|
||||
color={getAvatarColor(index)}
|
||||
/>
|
||||
</Html>
|
||||
</primitive>
|
||||
))}
|
||||
|
|
|
@ -1,53 +1,33 @@
|
|||
import React from "react";
|
||||
import CustomAvatar from "./users/Avatar";
|
||||
|
||||
interface CollabUserIconProps {
|
||||
color: string;
|
||||
userImage: string;
|
||||
userName: string;
|
||||
userName: string;
|
||||
userImage?: string;
|
||||
index?: number;
|
||||
color: string;
|
||||
}
|
||||
|
||||
const CollabUserIcon: React.FC<CollabUserIconProps> = ({
|
||||
color,
|
||||
userImage,
|
||||
userName,
|
||||
userImage,
|
||||
userName,
|
||||
index = 0,
|
||||
color,
|
||||
}) => {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
flexDirection: "column",
|
||||
gap: "6px",
|
||||
// transform:"translate(-20%, 0%)",
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src={userImage}
|
||||
alt={userName}
|
||||
style={{
|
||||
width: "30px",
|
||||
height: "30px",
|
||||
outline: `2px solid ${color}`,
|
||||
borderRadius: "50%",
|
||||
objectFit: 'cover'
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
padding: "3px 5px",
|
||||
backgroundColor: color,
|
||||
borderRadius: "6px",
|
||||
color: "white",
|
||||
fontSize: "14px",
|
||||
fontWeight: 400
|
||||
}}
|
||||
>
|
||||
{userName}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<div className="collab-user-live-container">
|
||||
<div className="user-image-container">
|
||||
{userImage ? (
|
||||
<img className="user-image" src={userImage} alt={userName} />
|
||||
) : (
|
||||
<CustomAvatar name={userName} index={index} />
|
||||
)}
|
||||
</div>
|
||||
<div className="user-name" style={{ backgroundColor: color }}>
|
||||
{userName}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CollabUserIcon;
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
import React, { useEffect, useState } from "react";
|
||||
import { getInitials } from "./functions/getInitials";
|
||||
import { getAvatarColor } from "./functions/getAvatarColor";
|
||||
|
||||
interface AvatarProps {
|
||||
name: string; // Name can be a full name or initials
|
||||
size?: number;
|
||||
index?: number;
|
||||
textColor?: string;
|
||||
}
|
||||
|
||||
const CustomAvatar: React.FC<AvatarProps> = ({
|
||||
name,
|
||||
size = 100,
|
||||
index = 0,
|
||||
textColor = "#ffffff",
|
||||
}) => {
|
||||
const [imageSrc, setImageSrc] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const canvas = document.createElement("canvas"); // Create an offscreen canvas
|
||||
canvas.width = size;
|
||||
canvas.height = size;
|
||||
const ctx = canvas.getContext("2d");
|
||||
if (ctx) {
|
||||
const initials = getInitials(name); // Convert name to initials if needed
|
||||
|
||||
// Draw background
|
||||
ctx.fillStyle = getAvatarColor(index);
|
||||
ctx.fillRect(0, 0, size, size);
|
||||
|
||||
// Draw initials
|
||||
ctx.fillStyle = textColor;
|
||||
ctx.font = `bold ${size / 2}px Arial`;
|
||||
ctx.textAlign = "center";
|
||||
ctx.textBaseline = "middle";
|
||||
ctx.fillText(initials, size / 2, size / 2);
|
||||
|
||||
// Generate image source
|
||||
const dataURL = canvas.toDataURL("image/png");
|
||||
setImageSrc(dataURL);
|
||||
}
|
||||
}, [name, size, textColor]);
|
||||
|
||||
if (!imageSrc) {
|
||||
return null; // Return null while the image is being generated
|
||||
}
|
||||
|
||||
return (
|
||||
<img
|
||||
className="user-image"
|
||||
src={imageSrc}
|
||||
alt="User Avatar"
|
||||
style={{ width: "100%", height: "100%" }}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default CustomAvatar;
|
|
@ -0,0 +1,26 @@
|
|||
const avatarColors: string[] = [
|
||||
"#FF5733", // Red Orange
|
||||
"#48ac2a", // Leaf Green
|
||||
"#0050eb", // Royal Blue
|
||||
"#FF33A1", // Hot Pink
|
||||
"#FF8C33", // Deep Orange
|
||||
"#8C33FF", // Violet
|
||||
"#FF3333", // Bright Red
|
||||
"#43c06d", // Emerald Green
|
||||
"#A133FF", // Amethyst Purple
|
||||
"#C70039", // Crimson
|
||||
"#900C3F", // Maroon
|
||||
"#581845", // Plum
|
||||
"#3498DB", // Sky Blue
|
||||
"#2ECC71", // Green Mint
|
||||
"#E74C3C", // Tomato Red
|
||||
"#00adff", // Azure
|
||||
"#DBAD05", // Amber Yellow
|
||||
"#FF5733", // Red Orange
|
||||
"#FF33A1", // Hot Pink
|
||||
"#900C3F", // Maroon
|
||||
];
|
||||
|
||||
export function getAvatarColor(index: number): string {
|
||||
return avatarColors[index % avatarColors.length];
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
export const getInitials = (fullName: string): string => {
|
||||
// Extract initials from the name
|
||||
const words = fullName.split(" ");
|
||||
const initials = words
|
||||
.map((word) => word[0])
|
||||
.slice(0, 2)
|
||||
.join("")
|
||||
.toUpperCase();
|
||||
return initials;
|
||||
};
|
|
@ -9,10 +9,11 @@ import * as THREE from "three";
|
|||
// Define the shape of the selected card
|
||||
interface SelectedCard {
|
||||
assetName: string;
|
||||
uploadedOn: string;
|
||||
uploadedOn: number;
|
||||
price: number;
|
||||
rating: number;
|
||||
views: number;
|
||||
description: string;
|
||||
}
|
||||
|
||||
// Define the props type for AssetPreview
|
||||
|
@ -24,7 +25,7 @@ interface AssetPreviewProps {
|
|||
function Ui() {
|
||||
return (
|
||||
<Text color="#6f42c1" anchorX="center" anchorY="middle" scale={0.3}>
|
||||
Loading your model...
|
||||
Loading preview...
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
|
@ -93,19 +94,7 @@ const AssetPreview: React.FC<AssetPreviewProps> = ({
|
|||
<div className="asset-details">
|
||||
<div className="asset-name">{selectedCard.assetName}</div>
|
||||
<div className="asset-description">
|
||||
Lorem ipsum dolor sit amet consectetur adipisicing elit.
|
||||
Doloremque nisi beatae facilis architecto quaerat delectus velit
|
||||
aliquid assumenda cumque vitae! Tempore quibusdam ab natus in
|
||||
minima voluptates, aliquid corrupti excepturi consectetur
|
||||
distinctio sequi beatae odit autem? Distinctio ab, voluptatem
|
||||
omnis quibusdam, incidunt eum ipsa aliquid enim eaque eveniet nisi
|
||||
autem, accusantium vel! Laborum in iste voluptates ad! Harum eum
|
||||
amet pariatur fugit laudantium dolorem maxime voluptates atque
|
||||
molestiae modi inventore quidem maiores dolore numquam, natus
|
||||
quisquam optio distinctio eveniet aliquam, aut eligendi laboriosam
|
||||
eaque! Porro cumque cum distinctio ullam debitis, dolorum
|
||||
similique! Harum cupiditate perferendis voluptatum molestiae,
|
||||
fugiat quisquam assumenda!
|
||||
{`${selectedCard.assetName} is used in factories to improve efficiency and production speed It is designed to handle heavy workloads and perform repetitive tasks with precision. Many industries rely on this machine to manufacture products quickly and accurately. It reduces human effort and minimizes errors in the production process. Regular maintenance is required to keep the machine in good working condition.With advanced technology, this machine continues to enhance industrial operations and increase productivity.`}
|
||||
</div>
|
||||
<div className="asset-review">
|
||||
<div className="asset-rating">
|
||||
|
|
|
@ -12,17 +12,19 @@ import { getAssetDownload } from "../../services/marketplace/getAssetDownload";
|
|||
|
||||
interface CardProps {
|
||||
assetName: string;
|
||||
uploadedOn: string;
|
||||
uploadedOn: number;
|
||||
price: number;
|
||||
rating: number;
|
||||
views: number;
|
||||
image: string;
|
||||
description: string;
|
||||
onSelectCard: (cardData: {
|
||||
assetName: string;
|
||||
uploadedOn: string;
|
||||
uploadedOn: number;
|
||||
price: number;
|
||||
rating: number;
|
||||
views: number;
|
||||
description: string;
|
||||
}) => void;
|
||||
}
|
||||
|
||||
|
@ -33,10 +35,11 @@ const Card: React.FC<CardProps> = ({
|
|||
rating,
|
||||
views,
|
||||
image,
|
||||
description,
|
||||
onSelectCard,
|
||||
}) => {
|
||||
const handleCardSelect = () => {
|
||||
onSelectCard({ assetName, uploadedOn, price, rating, views });
|
||||
onSelectCard({ assetName, uploadedOn, price, rating, views, description });
|
||||
};
|
||||
|
||||
return (
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import React, { useEffect, useState } from "react";
|
||||
import Card from "./Card";
|
||||
import AssetPreview from "./AssetPreview";
|
||||
import RenderOverlay from "../../components/templates/Overlay";
|
||||
import { fetchAssets } from "../../services/marketplace/fetchAssets";
|
||||
|
||||
interface ModelData {
|
||||
CreatedBy: string;
|
||||
animated: string | null;
|
||||
|
@ -23,18 +22,20 @@ interface ModelsProps {
|
|||
const CardsContainer: React.FC<ModelsProps> = ({ models }) => {
|
||||
const [selectedCard, setSelectedCard] = useState<{
|
||||
assetName: string;
|
||||
uploadedOn: string;
|
||||
uploadedOn: number;
|
||||
price: number;
|
||||
rating: number;
|
||||
views: number;
|
||||
description: string;
|
||||
} | null>(null);
|
||||
|
||||
const handleCardSelect = (cardData: {
|
||||
assetName: string;
|
||||
uploadedOn: string;
|
||||
uploadedOn: number;
|
||||
price: number;
|
||||
rating: number;
|
||||
views: number;
|
||||
description: string;
|
||||
}) => {
|
||||
setSelectedCard(cardData);
|
||||
};
|
||||
|
@ -48,12 +49,13 @@ const CardsContainer: React.FC<ModelsProps> = ({ models }) => {
|
|||
<Card
|
||||
key={assetDetail._id}
|
||||
assetName={assetDetail?.filename}
|
||||
uploadedOn={assetDetail.uploadDate.toString()}
|
||||
uploadedOn={assetDetail.uploadDate}
|
||||
price={36500}
|
||||
rating={4.5}
|
||||
views={800}
|
||||
onSelectCard={handleCardSelect}
|
||||
image={assetDetail.thumbnail}
|
||||
description={assetDetail.description}
|
||||
/>
|
||||
))}
|
||||
{/* <RenderOverlay> */}
|
||||
|
|
|
@ -29,11 +29,10 @@ const FilterSearch: React.FC<ModelsProps> = ({
|
|||
filteredModels,
|
||||
}) => {
|
||||
const [activeOption, setActiveOption] = useState("Sort by"); // State for active option
|
||||
console.log("filteredModels: ", filteredModels);
|
||||
|
||||
const handleSelect = (option: string) => {
|
||||
setActiveOption(option);
|
||||
console.log("option: ", option);
|
||||
|
||||
// Alphabet ascending
|
||||
// Alphabet descending
|
||||
// All
|
||||
|
|
|
@ -21,6 +21,7 @@ import DroppedObjects from "../../components/ui/componets/DroppedFloatingWidgets
|
|||
import ZoneCentreTarget from "../../components/ui/componets/zoneCameraTarget";
|
||||
import ProductionCapacity from "../../components/layout/3D-cards/cards/ProductionCapacity";
|
||||
import Dropped3dWidgets from "../../components/ui/componets/Dropped3dWidget";
|
||||
import { useWidgetSubOption } from "../../store/store";
|
||||
|
||||
export default function Scene() {
|
||||
|
||||
|
@ -31,9 +32,6 @@ export default function Scene() {
|
|||
{ name: "right", keys: ["ArrowRight", "d", "D"] },
|
||||
], [])
|
||||
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<KeyboardControls map={map}>
|
||||
<Canvas
|
||||
|
@ -45,7 +43,7 @@ export default function Scene() {
|
|||
}}
|
||||
|
||||
>
|
||||
<Dropped3dWidgets/>
|
||||
<Dropped3dWidgets />
|
||||
<Controls />
|
||||
<TransformControl />
|
||||
<SelectionControls />
|
||||
|
|
|
@ -36,7 +36,7 @@ const MeasurementTool = () => {
|
|||
isLeftMouseDown = false;
|
||||
if (evt.button === 0 && !drag) {
|
||||
raycaster.setFromCamera(pointer, camera);
|
||||
const intersects = raycaster.intersectObjects(scene.children, true).filter(intersect => !intersect.object.name.includes("Roof") && !intersect.object.name.includes("MeasurementReference") && !(intersect.object.type === "GridHelper"));
|
||||
const intersects = raycaster.intersectObjects(scene.children, true).filter(intersect => !intersect.object.name.includes("Roof") && !intersect.object.name.includes("MeasurementReference") && !intersect.object.name.includes("agv-collider") && !(intersect.object.type === "GridHelper"));
|
||||
|
||||
if (intersects.length > 0) {
|
||||
const intersectionPoint = intersects[0].point.clone();
|
||||
|
@ -83,7 +83,7 @@ const MeasurementTool = () => {
|
|||
useFrame(() => {
|
||||
if (points.length === 1) {
|
||||
raycaster.setFromCamera(pointer, camera);
|
||||
const intersects = raycaster.intersectObjects(scene.children, true).filter(intersect => !intersect.object.name.includes("Roof") && !intersect.object.name.includes("MeasurementReference") && !(intersect.object.type === "GridHelper"));
|
||||
const intersects = raycaster.intersectObjects(scene.children, true).filter(intersect => !intersect.object.name.includes("Roof") && !intersect.object.name.includes("MeasurementReference") && !intersect.object.name.includes("agv-collider") && !(intersect.object.type === "GridHelper"));
|
||||
|
||||
if (intersects.length > 0) {
|
||||
updateMeasurement(points[0], intersects[0].point);
|
||||
|
|
|
@ -29,6 +29,7 @@ import {
|
|||
useUpdateScene,
|
||||
useWalls,
|
||||
useToolMode,
|
||||
useRefTextUpdate,
|
||||
} from "../../../store/store";
|
||||
|
||||
////////// 3D Function Imports //////////
|
||||
|
@ -118,7 +119,7 @@ export default function World() {
|
|||
const { shadows, setShadows } = useShadows();
|
||||
const { updateScene, setUpdateScene } = useUpdateScene();
|
||||
const { walls, setWalls } = useWalls();
|
||||
const [RefTextupdate, setRefTextUpdate] = useState(-1000);
|
||||
const { refTextupdate, setRefTextUpdate } = useRefTextUpdate();
|
||||
|
||||
// const loader = new GLTFLoader();
|
||||
// const dracoLoader = new DRACOLoader();
|
||||
|
@ -158,7 +159,7 @@ export default function World() {
|
|||
////////// All Toggle's //////////
|
||||
|
||||
useEffect(() => {
|
||||
setRefTextUpdate((prevUpdate) => prevUpdate - 1);
|
||||
setRefTextUpdate((prevUpdate: number) => prevUpdate - 1);
|
||||
if (dragPointControls.current) {
|
||||
dragPointControls.current.enabled = false;
|
||||
}
|
||||
|
@ -241,7 +242,7 @@ export default function World() {
|
|||
<DistanceText key={toggleView} />
|
||||
|
||||
<ReferenceDistanceText
|
||||
key={RefTextupdate}
|
||||
key={refTextupdate}
|
||||
line={ReferenceLineMesh.current}
|
||||
/>
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@ function Behaviour() {
|
|||
],
|
||||
assetPosition: [...item.position],
|
||||
assetRotation: [item.rotation.x, item.rotation.y, item.rotation.z],
|
||||
speed: 1,
|
||||
speed: 'Inherit',
|
||||
};
|
||||
|
||||
newPaths.push(newPath);
|
||||
|
@ -67,12 +67,11 @@ function Behaviour() {
|
|||
point: {
|
||||
uuid: pointUUID,
|
||||
position: [pointPosition.x, pointPosition.y, pointPosition.z],
|
||||
actions: [{ uuid: THREE.MathUtils.generateUUID(), name: 'Action 1', type: 'Start', start: THREE.MathUtils.generateUUID(), hitCount: 1, end: THREE.MathUtils.generateUUID(), buffer: 0, isUsed: false }],
|
||||
triggers: [],
|
||||
actions: { uuid: THREE.MathUtils.generateUUID(), name: 'Action 1', type: 'Start', start: '', hitCount: 1, end: '', buffer: 0 },
|
||||
connections: { source: { pathUUID: item.modeluuid, pointUUID: pointUUID }, targets: [] },
|
||||
speed: 2,
|
||||
},
|
||||
assetPosition: [...item.position],
|
||||
speed: 2,
|
||||
};
|
||||
|
||||
newPaths.push(newVehiclePath);
|
||||
|
|
|
@ -86,6 +86,7 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
|
|||
};
|
||||
}
|
||||
}
|
||||
// In the updatePathConnections function, modify the Vehicle handling section:
|
||||
else if (path.type === 'Vehicle') {
|
||||
// Handle outgoing connections from Vehicle
|
||||
if (path.modeluuid === fromPathUUID && path.point.uuid === fromPointUUID) {
|
||||
|
@ -95,6 +96,27 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
|
|||
};
|
||||
const existingTargets = path.point.connections.targets || [];
|
||||
|
||||
// Check if we're trying to add a connection to a Conveyor
|
||||
const toPath = simulationPaths.find(p => p.modeluuid === toPathUUID);
|
||||
const isConnectingToConveyor = toPath?.type === 'Conveyor';
|
||||
|
||||
// Count existing connections
|
||||
if (existingTargets.length >= 2) {
|
||||
console.log("Vehicle can have maximum 2 connections");
|
||||
return path;
|
||||
}
|
||||
|
||||
// Check if we already have a Conveyor connection and trying to add another
|
||||
const hasConveyorConnection = existingTargets.some(target => {
|
||||
const targetPath = simulationPaths.find(p => p.modeluuid === target.pathUUID);
|
||||
return targetPath?.type === 'Conveyor';
|
||||
});
|
||||
|
||||
if (hasConveyorConnection && isConnectingToConveyor) {
|
||||
console.log("Vehicle can only have one connection to a Conveyor");
|
||||
return path;
|
||||
}
|
||||
|
||||
if (!existingTargets.some(target =>
|
||||
target.pathUUID === newTarget.pathUUID &&
|
||||
target.pointUUID === newTarget.pointUUID
|
||||
|
@ -119,6 +141,27 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
|
|||
};
|
||||
const existingTargets = path.point.connections.targets || [];
|
||||
|
||||
// Check if we're receiving a connection from a Conveyor
|
||||
const fromPath = simulationPaths.find(p => p.modeluuid === fromPathUUID);
|
||||
const isConnectingFromConveyor = fromPath?.type === 'Conveyor';
|
||||
|
||||
// Count existing connections
|
||||
if (existingTargets.length >= 2) {
|
||||
console.log("Vehicle can have maximum 2 connections");
|
||||
return path;
|
||||
}
|
||||
|
||||
// Check if we already have a Conveyor connection and trying to add another
|
||||
const hasConveyorConnection = existingTargets.some(target => {
|
||||
const targetPath = simulationPaths.find(p => p.modeluuid === target.pathUUID);
|
||||
return targetPath?.type === 'Conveyor';
|
||||
});
|
||||
|
||||
if (hasConveyorConnection && isConnectingFromConveyor) {
|
||||
console.log("Vehicle can only have one connection to a Conveyor");
|
||||
return path;
|
||||
}
|
||||
|
||||
if (!existingTargets.some(target =>
|
||||
target.pathUUID === reverseTarget.pathUUID &&
|
||||
target.pointUUID === reverseTarget.pointUUID
|
||||
|
@ -135,6 +178,7 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
|
|||
};
|
||||
}
|
||||
}
|
||||
return path;
|
||||
}
|
||||
return path;
|
||||
});
|
||||
|
@ -168,7 +212,6 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
|
|||
drag = true;
|
||||
}
|
||||
};
|
||||
|
||||
const onContextMenu = (evt: MouseEvent) => {
|
||||
evt.preventDefault();
|
||||
if (drag || evt.button === 0) return;
|
||||
|
@ -200,29 +243,126 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
|
|||
const firstPath = simulationPaths.find(p => p.modeluuid === firstSelected?.pathUUID);
|
||||
const secondPath = simulationPaths.find(p => p.modeluuid === pathUUID);
|
||||
|
||||
// Prevent vehicle-to-vehicle connections
|
||||
if (firstPath && secondPath && firstPath.type === 'Vehicle' && secondPath.type === 'Vehicle') {
|
||||
console.log("Cannot connect two vehicle paths together");
|
||||
return;
|
||||
}
|
||||
const isAlreadyConnected = simulationPaths.some(path => {
|
||||
if (path.type === 'Conveyor') {
|
||||
return path.points.some(point =>
|
||||
point.uuid === sphereUUID &&
|
||||
point.connections.targets.length > 0
|
||||
);
|
||||
} else if (path.type === 'Vehicle') {
|
||||
return path.point.uuid === sphereUUID &&
|
||||
path.point.connections.targets.length > 0;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
if (isAlreadyConnected) {
|
||||
console.log("Sphere is already connected. Ignoring.");
|
||||
// Prevent conveyor middle point to conveyor connections
|
||||
if (firstPath && secondPath &&
|
||||
firstPath.type === 'Conveyor' &&
|
||||
secondPath.type === 'Conveyor' &&
|
||||
!firstSelected?.isCorner) {
|
||||
console.log("Conveyor middle points can only connect to non-conveyor paths");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!firstSelected) {
|
||||
// Check if this specific connection already exists
|
||||
const isDuplicateConnection = firstSelected
|
||||
? simulationPaths.some(path => {
|
||||
if (path.modeluuid === firstSelected.pathUUID) {
|
||||
if (path.type === 'Conveyor') {
|
||||
const point = path.points.find(p => p.uuid === firstSelected.sphereUUID);
|
||||
return point?.connections.targets.some(t =>
|
||||
t.pathUUID === pathUUID && t.pointUUID === sphereUUID
|
||||
);
|
||||
} else if (path.type === 'Vehicle') {
|
||||
return path.point.connections.targets.some(t =>
|
||||
t.pathUUID === pathUUID && t.pointUUID === sphereUUID
|
||||
);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
})
|
||||
: false;
|
||||
|
||||
if (isDuplicateConnection) {
|
||||
console.log("These points are already connected. Ignoring.");
|
||||
return;
|
||||
}
|
||||
|
||||
// For Vehicles, skip the "already connected" check since they can have multiple connections
|
||||
if (intersected.userData.path.type !== 'Vehicle') {
|
||||
const isAlreadyConnected = simulationPaths.some(path => {
|
||||
if (path.type === 'Conveyor') {
|
||||
return path.points.some(point =>
|
||||
point.uuid === sphereUUID &&
|
||||
point.connections.targets.length > 0
|
||||
);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
if (isAlreadyConnected) {
|
||||
console.log("Conveyor point is already connected. Ignoring.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Check vehicle connection limits
|
||||
const checkVehicleConnections = (pathUUID: string) => {
|
||||
const path = simulationPaths.find(p => p.modeluuid === pathUUID);
|
||||
if (path?.type === 'Vehicle') {
|
||||
return path.point.connections.targets.length >= 2;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
if (firstSelected) {
|
||||
// Check if either selected point is from a Vehicle with max connections
|
||||
if (checkVehicleConnections(firstSelected.pathUUID) ||
|
||||
checkVehicleConnections(pathUUID)) {
|
||||
console.log("Vehicle already has maximum connections");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if we're trying to add a second Conveyor connection to a Vehicle
|
||||
if (firstPath?.type === 'Vehicle' && secondPath?.type === 'Conveyor') {
|
||||
const hasConveyorConnection = firstPath.point.connections.targets.some(target => {
|
||||
const targetPath = simulationPaths.find(p => p.modeluuid === target.pathUUID);
|
||||
return targetPath?.type === 'Conveyor';
|
||||
});
|
||||
|
||||
if (hasConveyorConnection) {
|
||||
console.log("Vehicle can only have one connection to a Conveyor");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (secondPath?.type === 'Vehicle' && firstPath?.type === 'Conveyor') {
|
||||
const hasConveyorConnection = secondPath.point.connections.targets.some(target => {
|
||||
const targetPath = simulationPaths.find(p => p.modeluuid === target.pathUUID);
|
||||
return targetPath?.type === 'Conveyor';
|
||||
});
|
||||
|
||||
if (hasConveyorConnection) {
|
||||
console.log("Vehicle can only have one connection to a Conveyor");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Prevent same-path connections
|
||||
if (firstSelected.pathUUID === pathUUID) {
|
||||
console.log("Cannot connect spheres on the same path.");
|
||||
return;
|
||||
}
|
||||
|
||||
// At least one must be start/end point
|
||||
if (!firstSelected.isCorner && !isStartOrEnd) {
|
||||
console.log("At least one of the selected spheres must be a start or end point.");
|
||||
return;
|
||||
}
|
||||
|
||||
// All checks passed - make the connection
|
||||
handleAddConnection(
|
||||
firstSelected.pathUUID,
|
||||
firstSelected.sphereUUID,
|
||||
pathUUID,
|
||||
sphereUUID
|
||||
);
|
||||
} else {
|
||||
// First selection - just store it
|
||||
setFirstSelected({
|
||||
pathUUID,
|
||||
sphereUUID,
|
||||
|
@ -230,28 +370,11 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
|
|||
isCorner: isStartOrEnd
|
||||
});
|
||||
setIsConnecting(true);
|
||||
} else {
|
||||
if (firstSelected.sphereUUID === sphereUUID) return;
|
||||
|
||||
if (firstSelected.pathUUID === pathUUID) {
|
||||
console.log("Cannot connect spheres on the same path.");
|
||||
return;
|
||||
}
|
||||
if (!firstSelected.isCorner && !isStartOrEnd) {
|
||||
console.log("At least one of the selected spheres must be a start or end point.");
|
||||
return;
|
||||
}
|
||||
|
||||
handleAddConnection(
|
||||
firstSelected.pathUUID,
|
||||
firstSelected.sphereUUID,
|
||||
pathUUID,
|
||||
sphereUUID
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Clicked outside - cancel connection
|
||||
setFirstSelected(null);
|
||||
setCurrentLine(null);
|
||||
setIsConnecting(false);
|
||||
|
@ -294,7 +417,6 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
|
|||
|
||||
if (intersects.length > 0) {
|
||||
point = intersects[0].point;
|
||||
|
||||
if (point.y < 0.05) {
|
||||
point = new THREE.Vector3(point.x, 0.05, point.z);
|
||||
}
|
||||
|
@ -316,28 +438,68 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
|
|||
const secondPath = simulationPaths.find(p => p.modeluuid === pathUUID);
|
||||
const isVehicleToVehicle = firstPath?.type === 'Vehicle' && secondPath?.type === 'Vehicle';
|
||||
|
||||
// Inside the useFrame hook, where we check for snapped spheres:
|
||||
const isConnectable = (pathData.type === 'Vehicle' ||
|
||||
(pathData.points.length > 0 && (
|
||||
sphereUUID === pathData.points[0].uuid ||
|
||||
sphereUUID === pathData.points[pathData.points.length - 1].uuid
|
||||
))) && !isVehicleToVehicle;
|
||||
))) &&
|
||||
!isVehicleToVehicle &&
|
||||
!(firstPath?.type === 'Conveyor' &&
|
||||
pathData.type === 'Conveyor' &&
|
||||
!firstSelected.isCorner);
|
||||
|
||||
const isAlreadyConnected = simulationPaths.some(path => {
|
||||
if (path.type === 'Conveyor') {
|
||||
return path.points.some(point =>
|
||||
point.uuid === sphereUUID &&
|
||||
point.connections.targets.length > 0
|
||||
);
|
||||
} else if (path.type === 'Vehicle') {
|
||||
return path.point.uuid === sphereUUID &&
|
||||
path.point.connections.targets.length > 0;
|
||||
// Check for duplicate connection (regardless of path type)
|
||||
const isDuplicateConnection = simulationPaths.some(path => {
|
||||
if (path.modeluuid === firstSelected.pathUUID) {
|
||||
if (path.type === 'Conveyor') {
|
||||
const point = path.points.find(p => p.uuid === firstSelected.sphereUUID);
|
||||
return point?.connections.targets.some(t =>
|
||||
t.pathUUID === pathUUID && t.pointUUID === sphereUUID
|
||||
);
|
||||
} else if (path.type === 'Vehicle') {
|
||||
return path.point.connections.targets.some(t =>
|
||||
t.pathUUID === pathUUID && t.pointUUID === sphereUUID
|
||||
);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
// For non-Vehicle paths, check if already connected
|
||||
const isNonVehicleAlreadyConnected = pathData.type !== 'Vehicle' &&
|
||||
simulationPaths.some(path => {
|
||||
if (path.type === 'Conveyor') {
|
||||
return path.points.some(point =>
|
||||
point.uuid === sphereUUID &&
|
||||
point.connections.targets.length > 0
|
||||
);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
// Check vehicle connection limits
|
||||
const isVehicleAtMaxConnections = pathData.type === 'Vehicle' &&
|
||||
pathData.point.connections.targets.length >= 2;
|
||||
|
||||
const isVehicleConveyorConflict =
|
||||
(firstPath?.type === 'Vehicle' && secondPath?.type === 'Conveyor' &&
|
||||
firstPath.point.connections.targets.some(t => {
|
||||
const targetPath = simulationPaths.find(p => p.modeluuid === t.pathUUID);
|
||||
return targetPath?.type === 'Conveyor';
|
||||
})) ||
|
||||
(secondPath?.type === 'Vehicle' && firstPath?.type === 'Conveyor' &&
|
||||
secondPath.point.connections.targets.some(t => {
|
||||
const targetPath = simulationPaths.find(p => p.modeluuid === t.pathUUID);
|
||||
return targetPath?.type === 'Conveyor';
|
||||
}));
|
||||
|
||||
if (
|
||||
!isAlreadyConnected &&
|
||||
!isDuplicateConnection &&
|
||||
!isVehicleToVehicle &&
|
||||
!isNonVehicleAlreadyConnected &&
|
||||
!isVehicleAtMaxConnections &&
|
||||
!isVehicleConveyorConflict &&
|
||||
firstSelected.sphereUUID !== sphereUUID &&
|
||||
firstSelected.pathUUID !== pathUUID &&
|
||||
(firstSelected.isCorner || isConnectable)
|
||||
|
@ -371,13 +533,6 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
|
|||
end: point,
|
||||
mid: midPoint,
|
||||
});
|
||||
console.log({
|
||||
start: firstSelected.position,
|
||||
end: point,
|
||||
mid: midPoint,
|
||||
});
|
||||
|
||||
// setIsConnecting(true);
|
||||
|
||||
if (sphereIntersects.length > 0) {
|
||||
setHelperLineColor(isInvalidConnection ? 'red' : '#6cf542');
|
||||
|
|
|
@ -1,27 +1,10 @@
|
|||
import * as THREE from 'three';
|
||||
import * as Types from '../../../types/world/worldTypes';
|
||||
import { useRef, useState, useEffect } from 'react';
|
||||
import { Sphere, TransformControls } from '@react-three/drei';
|
||||
import { useIsConnecting, useRenderDistance, useSelectedActionSphere, useSelectedPath, useSimulationPaths } from '../../../store/store';
|
||||
import { useFrame, useThree } from '@react-three/fiber';
|
||||
import { useSubModuleStore } from '../../../store/useModuleStore';
|
||||
import { point } from '@turf/helpers';
|
||||
|
||||
interface ConveyorEventsSchema {
|
||||
modeluuid: string;
|
||||
modelName: string;
|
||||
type: 'Conveyor';
|
||||
points: {
|
||||
uuid: string;
|
||||
position: [number, number, number];
|
||||
rotation: [number, number, number];
|
||||
actions: { uuid: string; name: string; type: string; material: string; delay: number | string; spawnInterval: number | string; isUsed: boolean }[] | [];
|
||||
triggers: { uuid: string; name: string; type: string; isUsed: boolean }[] | [];
|
||||
connections: { source: { pathUUID: string; pointUUID: string }; targets: { pathUUID: string; pointUUID: string }[] };
|
||||
}[];
|
||||
assetPosition: [number, number, number];
|
||||
assetRotation: [number, number, number];
|
||||
speed: number;
|
||||
}
|
||||
|
||||
function PathCreation({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObject<THREE.Group> }) {
|
||||
const { renderDistance } = useRenderDistance();
|
||||
|
@ -89,7 +72,7 @@ function PathCreation({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObject
|
|||
};
|
||||
}
|
||||
return path;
|
||||
}) as ConveyorEventsSchema[];
|
||||
}) as Types.ConveyorEventsSchema[];
|
||||
|
||||
setSimulationPaths(updatedPaths);
|
||||
};
|
||||
|
|
|
@ -14,7 +14,7 @@ function Simulation() {
|
|||
const [processes, setProcesses] = useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
console.log('simulationPaths: ', simulationPaths);
|
||||
}, [simulationPaths]);
|
||||
|
||||
// useEffect(() => {
|
||||
|
|
|
@ -93,6 +93,7 @@ const PathCreator = ({ simulationPaths, setSimulationPaths, connections, setConn
|
|||
intersects = intersects.filter(
|
||||
(intersect) =>
|
||||
!intersect.object.name.includes("Roof") &&
|
||||
!intersect.object.name.includes("agv-collider") &&
|
||||
!intersect.object.name.includes("MeasurementReference") &&
|
||||
!intersect.object.userData.isPathObject &&
|
||||
!(intersect.object.type === "GridHelper")
|
||||
|
@ -146,6 +147,7 @@ const PathCreator = ({ simulationPaths, setSimulationPaths, connections, setConn
|
|||
const intersects = raycaster.intersectObjects(scene.children, true).filter(
|
||||
(intersect) =>
|
||||
!intersect.object.name.includes("Roof") &&
|
||||
!intersect.object.name.includes("agv-collider") &&
|
||||
!intersect.object.name.includes("MeasurementReference") &&
|
||||
!intersect.object.userData.isPathObject &&
|
||||
!(intersect.object.type === "GridHelper")
|
||||
|
@ -262,6 +264,7 @@ const PathCreator = ({ simulationPaths, setSimulationPaths, connections, setConn
|
|||
const intersects = raycaster.intersectObjects(scene.children, true).filter(
|
||||
(intersect) =>
|
||||
!intersect.object.name.includes("Roof") &&
|
||||
!intersect.object.name.includes("agv-collider") &&
|
||||
!intersect.object.name.includes("MeasurementReference") &&
|
||||
!intersect.object.userData.isPathObject &&
|
||||
!(intersect.object.type === "GridHelper")
|
||||
|
|
|
@ -1,8 +1,13 @@
|
|||
import React from 'react';
|
||||
import SidePannel from '../components/layout/Dashboard/SidePannel';
|
||||
import DashboardHome from '../components/layout/Dashboard/DashboardHome';
|
||||
|
||||
const Dashboard: React.FC = () => {
|
||||
return (
|
||||
<div>Dashboard</div>
|
||||
<div className="dashboard-main">
|
||||
<SidePannel />
|
||||
<DashboardHome />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ import React, { useEffect } from "react";
|
|||
import ModuleToggle from "../components/ui/ModuleToggle";
|
||||
import SideBarLeft from "../components/layout/sidebarLeft/SideBarLeft";
|
||||
import SideBarRight from "../components/layout/sidebarRight/SideBarRight";
|
||||
import useModuleStore from "../store/useModuleStore";
|
||||
import useModuleStore, { useThreeDStore } from "../store/useModuleStore";
|
||||
import RealTimeVisulization from "../components/ui/componets/RealTimeVisulization";
|
||||
import Tools from "../components/ui/Tools";
|
||||
// import Scene from "../modules/scene/scene";
|
||||
|
@ -49,14 +49,14 @@ const Project: React.FC = () => {
|
|||
}
|
||||
}, []);
|
||||
const { isPlaying } = usePlayButtonStore();
|
||||
const { toggleThreeD } = useThreeDStore();
|
||||
|
||||
return (
|
||||
<div className="project-main">
|
||||
{loadingProgress && <LoadingPage progress={loadingProgress} />}
|
||||
{!isPlaying && (
|
||||
<>
|
||||
<ModuleToggle />
|
||||
<ModuleToggle />
|
||||
{toggleThreeD && <ModuleToggle />}
|
||||
<SideBarLeft />
|
||||
<SideBarRight />
|
||||
</>
|
||||
|
|
|
@ -6,37 +6,37 @@ const MqttEvents = () => {
|
|||
const { setTouch, setTemperature, setHumidity } = useDrieUIValue();
|
||||
useEffect(() => {
|
||||
|
||||
const client = mqtt.connect(`ws://${process.env.REACT_APP_SERVER_MQTT_URL}`);
|
||||
// const client = mqtt.connect(`ws://${process.env.REACT_APP_SERVER_MQTT_URL}`);
|
||||
|
||||
client.subscribe("touch");
|
||||
client.subscribe("temperature");
|
||||
client.subscribe("humidity");
|
||||
// client.subscribe("touch");
|
||||
// client.subscribe("temperature");
|
||||
// client.subscribe("humidity");
|
||||
|
||||
const handleMessage = (topic: string, message: any) => {
|
||||
const value = message.toString();
|
||||
// const handleMessage = (topic: string, message: any) => {
|
||||
// const value = message.toString();
|
||||
|
||||
if (topic === "touch") {
|
||||
setTouch(value);
|
||||
} else if (topic === "temperature") {
|
||||
setTemperature(parseFloat(value));
|
||||
} else if (topic === "humidity") {
|
||||
setHumidity(parseFloat(value));
|
||||
}
|
||||
};
|
||||
// if (topic === "touch") {
|
||||
// setTouch(value);
|
||||
// } else if (topic === "temperature") {
|
||||
// setTemperature(parseFloat(value));
|
||||
// } else if (topic === "humidity") {
|
||||
// setHumidity(parseFloat(value));
|
||||
// }
|
||||
// };
|
||||
|
||||
client.on("message", handleMessage);
|
||||
// client.on("message", handleMessage);
|
||||
|
||||
client.on("error", (err) => {
|
||||
console.error("MQTT Connection Error:", err);
|
||||
});
|
||||
// client.on("error", (err) => {
|
||||
// console.error("MQTT Connection Error:", err);
|
||||
// });
|
||||
|
||||
client.on("close", () => {
|
||||
console.log("MQTT Connection Closed");
|
||||
});
|
||||
// client.on("close", () => {
|
||||
// console.log("MQTT Connection Closed");
|
||||
// });
|
||||
|
||||
return () => {
|
||||
client.end();
|
||||
};
|
||||
// return () => {
|
||||
// client.end();
|
||||
// };
|
||||
}, [setTouch, setTemperature, setHumidity]);
|
||||
|
||||
return null;
|
||||
|
|
|
@ -15,6 +15,7 @@ export const deleteZonesApi = async (userId: string, organization: string, zoneI
|
|||
}
|
||||
|
||||
const result = await response.json();
|
||||
console.log('result: ', result);
|
||||
return result;
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
|
||||
// let url_Backend_dwinzo = `http://192.168.0.102:5000`;
|
||||
export const addingFloatingWidgets = async (
|
||||
zoneId: string,
|
||||
organization: string,
|
||||
widget: {}
|
||||
) => {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${url_Backend_dwinzo}/api/v2/floatwidget/save`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({ organization, zoneId, widget }),
|
||||
}
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error("Failed to add Floatingwidget in the zone");
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
};
|
|
@ -1,5 +1,5 @@
|
|||
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_LOCAL_BASE_URL}`;
|
||||
|
||||
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
|
||||
// let url_Backend_dwinzo = `http://192.168.0.102:5000`;
|
||||
export const addingWidgets = async (
|
||||
zoneId: string,
|
||||
organization: string,
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
|
||||
// let url_Backend_dwinzo = `http://192.168.0.102:5000`;
|
||||
|
||||
export const deleteWidgetApi = async (
|
||||
widgetID: string,
|
||||
organization: string
|
||||
) => {
|
||||
try {
|
||||
const response = await fetch(`${url_Backend_dwinzo}/api/v2/delete/widget`, {
|
||||
method: "PATCH",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({ organization, widgetID }),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error("Failed to delete widget in the zone");
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
};
|
|
@ -0,0 +1,30 @@
|
|||
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
|
||||
// let url_Backend_dwinzo = `http://192.168.0.102:5000`;
|
||||
export const duplicateWidgetApi = async (
|
||||
zoneId: string,
|
||||
organization: string,
|
||||
widget: {}
|
||||
) => {
|
||||
try {
|
||||
const response = await fetch(`${url_Backend_dwinzo}/api/v2/widget/save`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({ organization, zoneId, widget }),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error("Failed to duplicate widget in the zone");
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
};
|
|
@ -0,0 +1,26 @@
|
|||
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
|
||||
// let url_Backend_dwinzo = `http://192.168.0.102:5000`;
|
||||
export const getFloatingZoneData = async (
|
||||
ZoneId?: string,
|
||||
organization?: string
|
||||
) => {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${url_Backend_dwinzo}/api/v2/floadData/${ZoneId}/${organization}`,
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error("Failed to fetch ZoneFloatingData");
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
} catch (error: any) {
|
||||
throw new Error(error.message);
|
||||
}
|
||||
};
|
|
@ -1,4 +1,4 @@
|
|||
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_LOCAL_BASE_URL}`;
|
||||
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
|
||||
console.log("url_Backend_dwinzo: ", url_Backend_dwinzo);
|
||||
|
||||
export const getSelect2dZoneData = async (
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_LOCAL_BASE_URL}`;
|
||||
|
||||
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
|
||||
// let url_Backend_dwinzo = `http://192.168.0.102:5000`;
|
||||
export const getZone2dData = async (organization?: string) => {
|
||||
try {
|
||||
const response = await fetch(
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_LOCAL_BASE_URL}`;
|
||||
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
|
||||
|
||||
export const getZoneData = async (zoneId: string, organization: string) => {
|
||||
console.log("organization: ", organization);
|
||||
console.log("zoneId: ", zoneId);
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${url_Backend_dwinzo}/api/v2/A_zone/${zoneId}/${organization}`,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
|
||||
|
||||
// let url_Backend_dwinzo = `http://192.168.0.102:5000`;
|
||||
type Side = "top" | "bottom" | "left" | "right";
|
||||
|
||||
export const panelData = async (
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
|
||||
// let url_Backend_dwinzo = `http://192.168.0.102:5000`;
|
||||
|
||||
export const zoneCameraUpdate = async (
|
||||
zonesdata:{},organization:string
|
||||
) => {
|
||||
try {
|
||||
const response = await fetch(`${url_Backend_dwinzo}/api/v2/zone/save`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({ zonesdata, organization }),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error("Failed to update camera position in the zone");
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
};
|