v2-ui #88
|
@ -10,7 +10,6 @@ import {
|
||||||
import ShortcutHelper from "./shortcutHelper";
|
import ShortcutHelper from "./shortcutHelper";
|
||||||
import { useShortcutStore } from "../../store/builder/store";
|
import { useShortcutStore } from "../../store/builder/store";
|
||||||
import { usePlayButtonStore } from "../../store/usePlayButtonStore";
|
import { usePlayButtonStore } from "../../store/usePlayButtonStore";
|
||||||
import OuterClick from "../../utils/outerClick";
|
|
||||||
|
|
||||||
const Footer: React.FC = () => {
|
const Footer: React.FC = () => {
|
||||||
const { logs, setIsLogListVisible } = useLogger();
|
const { logs, setIsLogListVisible } = useLogger();
|
||||||
|
@ -19,11 +18,6 @@ const Footer: React.FC = () => {
|
||||||
const { isPlaying } = usePlayButtonStore();
|
const { isPlaying } = usePlayButtonStore();
|
||||||
const { showShortcuts, setShowShortcuts } = useShortcutStore();
|
const { showShortcuts, setShowShortcuts } = useShortcutStore();
|
||||||
|
|
||||||
OuterClick({
|
|
||||||
contextClassName: ["shortcut-helper-overlay"],
|
|
||||||
setMenuVisible: () => setShowShortcuts(false),
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="footer-container">
|
<div className="footer-container">
|
||||||
<div className="footer-wrapper">
|
<div className="footer-wrapper">
|
||||||
|
@ -53,6 +47,7 @@ const Footer: React.FC = () => {
|
||||||
<div className="bg-dummy right-bottom"></div>
|
<div className="bg-dummy right-bottom"></div>
|
||||||
<div className="log-container">
|
<div className="log-container">
|
||||||
<button
|
<button
|
||||||
|
id="log-details-buttton"
|
||||||
className={`logs-detail ${lastLog ? lastLog.type : ""}`}
|
className={`logs-detail ${lastLog ? lastLog.type : ""}`}
|
||||||
onClick={() => setIsLogListVisible(true)}
|
onClick={() => setIsLogListVisible(true)}
|
||||||
>
|
>
|
||||||
|
@ -81,7 +76,7 @@ const Footer: React.FC = () => {
|
||||||
showShortcuts ? "visible" : ""
|
showShortcuts ? "visible" : ""
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<ShortcutHelper />
|
<ShortcutHelper setShowShortcuts={setShowShortcuts}/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -29,8 +29,8 @@ import {
|
||||||
DublicateIcon,
|
DublicateIcon,
|
||||||
DuplicateInstanceIcon,
|
DuplicateInstanceIcon,
|
||||||
PlayIcon,
|
PlayIcon,
|
||||||
BrowserIcon,
|
|
||||||
} from "../icons/ShortcutIcons";
|
} from "../icons/ShortcutIcons";
|
||||||
|
import { CloseIcon } from "../icons/ExportCommonIcons";
|
||||||
|
|
||||||
interface ShortcutItem {
|
interface ShortcutItem {
|
||||||
keys: string[];
|
keys: string[];
|
||||||
|
@ -44,7 +44,13 @@ interface ShortcutGroup {
|
||||||
items: ShortcutItem[];
|
items: ShortcutItem[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const ShortcutHelper = () => {
|
interface ShortcutHelperProps {
|
||||||
|
setShowShortcuts: (value: boolean) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ShortcutHelper: React.FC<ShortcutHelperProps> = ({
|
||||||
|
setShowShortcuts,
|
||||||
|
}) => {
|
||||||
const shortcuts: ShortcutGroup[] = [
|
const shortcuts: ShortcutGroup[] = [
|
||||||
{
|
{
|
||||||
category: "Essential",
|
category: "Essential",
|
||||||
|
@ -256,31 +262,31 @@ const ShortcutHelper = () => {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
|
||||||
category: "Miscellaneous",
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
keys: ["F5", "F11", "F12", "CTRL", "+", "R"],
|
|
||||||
name: "Browser Defaults",
|
|
||||||
description: "Reserved for browser defaults",
|
|
||||||
icon: <BrowserIcon />,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
|
|
||||||
const [activeCategory, setActiveCategory] =
|
const [activeCategory, setActiveCategory] =
|
||||||
React.useState<string>("Essential");
|
React.useState<string>("Essential");
|
||||||
|
|
||||||
const activeShortcuts =
|
const activeShortcuts =
|
||||||
shortcuts.find((group) => group.category === activeCategory)?.items || [];
|
shortcuts.find((group) => group.category === activeCategory)?.items ?? [];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="shortcut-helper-container">
|
<div className="shortcut-helper-container">
|
||||||
|
<button
|
||||||
|
id="close-shortcuts-helper"
|
||||||
|
className="close-button"
|
||||||
|
title="close-btn"
|
||||||
|
onClick={() => {
|
||||||
|
setShowShortcuts(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CloseIcon />
|
||||||
|
</button>
|
||||||
<div className="header">
|
<div className="header">
|
||||||
<div className="header-wrapper">
|
<div className="header-wrapper">
|
||||||
{shortcuts.map((group) => (
|
{shortcuts.map((group) => (
|
||||||
<button
|
<button
|
||||||
|
id={group.category}
|
||||||
key={group.category}
|
key={group.category}
|
||||||
onClick={() => setActiveCategory(group.category)}
|
onClick={() => setActiveCategory(group.category)}
|
||||||
className={activeCategory === group.category ? "active" : ""}
|
className={activeCategory === group.category ? "active" : ""}
|
||||||
|
@ -312,7 +318,7 @@ const ShortcutHelper = () => {
|
||||||
{item.keys.map((key, i) => (
|
{item.keys.map((key, i) => (
|
||||||
<span
|
<span
|
||||||
key={`${key}-${i}`}
|
key={`${key}-${i}`}
|
||||||
className={`key ${key === "+" || key === "OR" ? "add" : ""}`}
|
className={`key${key === "+" || key === "OR" ? " add" : ""}`}
|
||||||
>
|
>
|
||||||
{key}
|
{key}
|
||||||
</span>
|
</span>
|
||||||
|
|
|
@ -105,7 +105,7 @@ export function EyeIcon({ isClosed }: { isClosed: boolean }) {
|
||||||
</g>
|
</g>
|
||||||
<defs>
|
<defs>
|
||||||
<clipPath id="clip0_889_1582">
|
<clipPath id="clip0_889_1582">
|
||||||
<rect width="12" height="12" fill="white" />
|
<rect width="12" height="12" fill="var(--text-color)" />
|
||||||
</clipPath>
|
</clipPath>
|
||||||
</defs>
|
</defs>
|
||||||
</svg>
|
</svg>
|
||||||
|
@ -212,7 +212,7 @@ export function SettingsIcon() {
|
||||||
</g>
|
</g>
|
||||||
<defs>
|
<defs>
|
||||||
<clipPath id="clip0_111_481">
|
<clipPath id="clip0_111_481">
|
||||||
<rect width="12" height="12" fill="white" />
|
<rect width="12" height="12" fill="var(--text-color)" />
|
||||||
</clipPath>
|
</clipPath>
|
||||||
</defs>
|
</defs>
|
||||||
</svg>
|
</svg>
|
||||||
|
@ -236,7 +236,7 @@ export function HelpIcon() {
|
||||||
</g>
|
</g>
|
||||||
<defs>
|
<defs>
|
||||||
<clipPath id="clip0_111_484">
|
<clipPath id="clip0_111_484">
|
||||||
<rect width="12" height="12" fill="white" />
|
<rect width="12" height="12" fill="var(--text-color)" />
|
||||||
</clipPath>
|
</clipPath>
|
||||||
</defs>
|
</defs>
|
||||||
</svg>
|
</svg>
|
||||||
|
@ -262,7 +262,7 @@ export function TrashIcon() {
|
||||||
</g>
|
</g>
|
||||||
<defs>
|
<defs>
|
||||||
<clipPath id="clip0_111_486">
|
<clipPath id="clip0_111_486">
|
||||||
<rect width="12" height="12" fill="white" />
|
<rect width="12" height="12" fill="var(--text-color)" />
|
||||||
</clipPath>
|
</clipPath>
|
||||||
</defs>
|
</defs>
|
||||||
</svg>
|
</svg>
|
||||||
|
@ -852,7 +852,7 @@ export const LogTickIcon = () => {
|
||||||
</g>
|
</g>
|
||||||
<defs>
|
<defs>
|
||||||
<clipPath id="clip0_3962_4223">
|
<clipPath id="clip0_3962_4223">
|
||||||
<rect width="10" height="10" fill="white" />
|
<rect width="10" height="10" fill="var(--text-color)" />
|
||||||
</clipPath>
|
</clipPath>
|
||||||
</defs>
|
</defs>
|
||||||
</svg>
|
</svg>
|
||||||
|
@ -874,19 +874,19 @@ export const LogInfoIcon = () => {
|
||||||
/>
|
/>
|
||||||
<path
|
<path
|
||||||
d="M5.04492 6.94531H6.95492"
|
d="M5.04492 6.94531H6.95492"
|
||||||
stroke="white"
|
stroke="var(--text-color)"
|
||||||
strokeWidth="0.955"
|
strokeWidth="0.955"
|
||||||
strokeMiterlimit="10"
|
strokeMiterlimit="10"
|
||||||
/>
|
/>
|
||||||
<path
|
<path
|
||||||
d="M5.04492 4.07812H5.99992V6.94313"
|
d="M5.04492 4.07812H5.99992V6.94313"
|
||||||
stroke="white"
|
stroke="var(--text-color)"
|
||||||
strokeWidth="0.955"
|
strokeWidth="0.955"
|
||||||
strokeMiterlimit="10"
|
strokeMiterlimit="10"
|
||||||
/>
|
/>
|
||||||
<path
|
<path
|
||||||
d="M5.52539 2.65625H6.47539"
|
d="M5.52539 2.65625H6.47539"
|
||||||
stroke="white"
|
stroke="var(--text-color)"
|
||||||
strokeWidth="0.955"
|
strokeWidth="0.955"
|
||||||
strokeMiterlimit="10"
|
strokeMiterlimit="10"
|
||||||
/>
|
/>
|
||||||
|
@ -909,19 +909,19 @@ export const WarningIcon = () => {
|
||||||
/>
|
/>
|
||||||
<path
|
<path
|
||||||
d="M5.04492 6.94531H6.95492"
|
d="M5.04492 6.94531H6.95492"
|
||||||
stroke="white"
|
stroke="var(--text-color)"
|
||||||
strokeWidth="0.955"
|
strokeWidth="0.955"
|
||||||
strokeMiterlimit="10"
|
strokeMiterlimit="10"
|
||||||
/>
|
/>
|
||||||
<path
|
<path
|
||||||
d="M5.04492 4.07812H5.99992V6.94313"
|
d="M5.04492 4.07812H5.99992V6.94313"
|
||||||
stroke="white"
|
stroke="var(--text-color)"
|
||||||
strokeWidth="0.955"
|
strokeWidth="0.955"
|
||||||
strokeMiterlimit="10"
|
strokeMiterlimit="10"
|
||||||
/>
|
/>
|
||||||
<path
|
<path
|
||||||
d="M5.52539 2.65625H6.47539"
|
d="M5.52539 2.65625H6.47539"
|
||||||
stroke="white"
|
stroke="var(--text-color)"
|
||||||
strokeWidth="0.955"
|
strokeWidth="0.955"
|
||||||
strokeMiterlimit="10"
|
strokeMiterlimit="10"
|
||||||
/>
|
/>
|
||||||
|
@ -961,7 +961,81 @@ export const LocationIcon = () => {
|
||||||
fillRule="evenodd"
|
fillRule="evenodd"
|
||||||
clipRule="evenodd"
|
clipRule="evenodd"
|
||||||
d="M3 9.45984L10.4205 9.98901L11.085 17.5L18 2.5L3 9.45984Z"
|
d="M3 9.45984L10.4205 9.98901L11.085 17.5L18 2.5L3 9.45984Z"
|
||||||
fill="white"
|
fill="var(--text-color)"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export const SaveDiskIcon = () => {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
width="51"
|
||||||
|
height="51"
|
||||||
|
viewBox="0 0 51 51"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
clipRule="evenodd"
|
||||||
|
d="M4.2832 15.0331C4.2832 9.2892 8.93959 4.63281 14.6835 4.63281H31.681C33.3359 4.63281 34.9231 5.29026 36.0934 6.46052L44.0567 14.4239C45.2269 15.5942 45.8845 17.1814 45.8845 18.8364V35.8337C45.8845 41.5776 41.228 46.2341 35.4841 46.2341H14.6835C8.93959 46.2341 4.2832 41.5776 4.2832 35.8337V15.0331ZM16.7636 12.9531V8.79294H29.244V12.9531H16.7636ZM33.4041 15.0331V9.65452L41.1151 17.3655C41.5051 17.7556 41.7243 18.2847 41.7243 18.8364V35.8337C41.7243 39.2802 38.9306 42.0739 35.4841 42.0739H14.6835C11.2372 42.0739 8.44333 39.2802 8.44333 35.8337V15.0331C8.44333 12.3161 10.1798 10.0047 12.6035 9.14803V15.0331C12.6035 16.1819 13.5347 17.1132 14.6835 17.1132H31.324C32.4728 17.1132 33.4041 16.1819 33.4041 15.0331Z"
|
||||||
|
fill="#E6E6E6"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
clipRule="evenodd"
|
||||||
|
d="M25.0859 21.2656C20.4907 21.2656 16.7656 24.9908 16.7656 29.5859C16.7656 34.1809 20.4907 37.9061 25.0859 37.9061C29.6809 37.9061 33.4061 34.1809 33.4061 29.5859C33.4061 24.9908 29.6809 21.2656 25.0859 21.2656ZM20.9257 29.5859C20.9257 27.2882 22.7882 25.4257 25.0859 25.4257C27.3835 25.4257 29.246 27.2882 29.246 29.5859C29.246 31.8835 27.3835 33.746 25.0859 33.746C22.7882 33.746 20.9257 31.8835 20.9257 29.5859Z"
|
||||||
|
fill="#B6B6B6"
|
||||||
|
/>
|
||||||
|
<circle cx="25.084" cy="29.5898" r="4.16012" fill="#D7D7D7" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export const WalkIcon = () => {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
width="18"
|
||||||
|
height="18"
|
||||||
|
viewBox="0 0 18 18"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M10.5 3.5C11.0523 3.5 11.5 3.94772 11.5 4.5V8.5H6.5V4.5C6.5 3.94772 6.94772 3.5 7.5 3.5H10.5ZM10.4746 4.71094C10.4331 4.71095 10.3949 4.73196 10.3594 4.77344C10.3238 4.81197 10.2971 4.86257 10.2793 4.9248C10.2289 5.09668 10.163 5.3394 10.083 5.65332L10.0166 5.94727C9.93366 6.33229 9.86296 6.61232 9.80371 6.78711C9.64369 6.44039 9.50522 6.09665 9.38965 5.75586C9.27406 5.41503 9.21488 5.23842 9.21191 5.22656C9.20004 5.18808 9.17329 5.15269 9.13184 5.12012C9.09035 5.08755 9.03685 5.07129 8.97168 5.07129C8.92148 5.07135 8.87149 5.08479 8.82129 5.11133C8.77404 5.13495 8.74436 5.16867 8.73242 5.21289L8.6875 5.34668C8.60156 5.59562 8.51085 5.85245 8.41602 6.11621C8.32421 6.37975 8.24009 6.60348 8.16309 6.78711C7.96155 6.05508 7.80881 5.41163 7.70508 4.85742C7.69322 4.80425 7.66829 4.76683 7.62988 4.74609C7.59143 4.72243 7.55406 4.71098 7.51855 4.71094C7.45644 4.71094 7.39415 4.73018 7.33203 4.76855C7.26992 4.80404 7.23841 4.85299 7.23828 4.91504C7.23828 4.96542 7.26596 5.12009 7.32227 5.37793C7.37857 5.63575 7.44541 5.88782 7.52246 6.13379L7.63379 6.51172C7.70488 6.75758 7.76126 6.94868 7.80273 7.08496C7.84715 7.22116 7.88924 7.32152 7.92773 7.38672C7.94547 7.41628 7.97913 7.44313 8.0293 7.4668C8.07968 7.48754 8.1322 7.49805 8.18555 7.49805C8.23282 7.49799 8.27602 7.48654 8.31445 7.46289C8.35284 7.43627 8.38261 7.40074 8.40332 7.35645C8.41518 7.32384 8.42568 7.29233 8.43457 7.2627C8.44638 7.23022 8.45598 7.20042 8.46484 7.17383C8.61004 6.7412 8.77527 6.29237 8.95898 5.82715C9.03307 6.06119 9.13625 6.33545 9.26953 6.64941C9.40577 6.9604 9.52046 7.20597 9.6123 7.38672C9.63305 7.41636 9.66742 7.44309 9.71484 7.4668C9.76504 7.48744 9.81504 7.498 9.86523 7.49805C9.91558 7.49805 9.96339 7.48656 10.0078 7.46289C10.0522 7.43626 10.0838 7.40079 10.1016 7.35645C10.1549 7.20233 10.217 6.97225 10.2881 6.66699C10.2999 6.61959 10.3133 6.56035 10.3281 6.48926C10.3459 6.41814 10.3679 6.33505 10.3945 6.24023C10.5042 5.82532 10.6054 5.47686 10.6973 5.19531C10.7387 5.071 10.7588 4.9954 10.7588 4.96875C10.7588 4.87393 10.7368 4.80708 10.6924 4.76855C10.6479 4.73011 10.5753 4.71094 10.4746 4.71094Z"
|
||||||
|
fill="#B392F0"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M11.5 14.5H6.5V9.5H11.5V14.5ZM8.92773 10.1016C8.84362 10.1016 8.70774 10.1241 8.52051 10.1699C8.33706 10.212 8.2073 10.2528 8.13086 10.291C7.98945 10.3598 7.86687 10.4722 7.76367 10.6289C7.66048 10.7818 7.6094 10.933 7.60938 11.082C7.60938 11.2426 7.65843 11.3958 7.75781 11.541C7.85721 11.6863 8.00832 11.8088 8.21094 11.9082C8.40585 12.0037 8.62961 12.0855 8.88184 12.1543C9.05769 12.1963 9.23023 12.2709 9.39844 12.3779C9.56653 12.4849 9.65039 12.6075 9.65039 12.7451C9.65039 12.8866 9.57178 12.9989 9.41504 13.083C9.26212 13.1671 9.07117 13.21 8.8418 13.21C8.64303 13.21 8.47656 13.1691 8.34277 13.0889C8.20911 13.0048 8.11501 12.8842 8.06152 12.7275C8.04994 12.6857 8.01608 12.6434 7.95898 12.6016C7.90164 12.5557 7.84551 12.5332 7.79199 12.5332C7.68901 12.5333 7.61417 12.5578 7.56836 12.6074C7.52262 12.6533 7.50001 12.728 7.5 12.8311C7.5 12.9801 7.55318 13.118 7.66016 13.2441C7.77099 13.3703 7.92018 13.4796 8.10742 13.5713C8.36738 13.686 8.58586 13.7432 8.76172 13.7432C8.93355 13.7432 9.10346 13.7237 9.27148 13.6855C9.43969 13.6473 9.58771 13.5901 9.71387 13.5137C9.90103 13.4067 10.035 13.2901 10.1152 13.1641C10.1993 13.0341 10.2412 12.8789 10.2412 12.6992C10.2412 12.493 10.1992 12.3324 10.1152 12.2178C10.0311 12.0993 9.89533 11.9839 9.70801 11.873C9.60097 11.8081 9.48969 11.7549 9.375 11.7129C9.26418 11.667 9.12104 11.617 8.94531 11.5635C8.81151 11.5291 8.68695 11.4892 8.57227 11.4434C8.47295 11.409 8.38705 11.365 8.31445 11.3115C8.24564 11.2542 8.21094 11.1868 8.21094 11.1104C8.21105 11.0188 8.24375 10.9388 8.30859 10.8701C8.37731 10.7976 8.46133 10.7423 8.56055 10.7041C8.65994 10.6659 8.75591 10.6465 8.84766 10.6465C8.93174 10.6465 9.02776 10.6577 9.13477 10.6807C9.24542 10.7036 9.32945 10.7251 9.38672 10.7441C9.44399 10.7709 9.50337 10.8241 9.56445 10.9043C9.61013 10.9576 9.64819 10.9977 9.67871 11.0244C9.70919 11.0511 9.74003 11.0644 9.77051 11.0645C9.89662 11.0645 9.98447 11.0379 10.0342 10.9844C10.0877 10.9309 10.1152 10.8696 10.1152 10.8008C10.1151 10.6863 10.0743 10.591 9.99414 10.5146C9.91393 10.4383 9.79001 10.3523 9.62207 10.2568C9.53797 10.2071 9.42871 10.1684 9.29492 10.1416C9.16112 10.1148 9.0386 10.1016 8.92773 10.1016Z"
|
||||||
|
fill="#B392F0"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M5.5 14.5H1.5C0.947715 14.5 0.5 14.0523 0.5 13.5V10.5C0.5 9.94772 0.947715 9.5 1.5 9.5H5.5V14.5ZM3.2207 10.1016C3.15581 10.1016 3.09643 10.119 3.04297 10.1533C2.98952 10.1839 2.95068 10.2218 2.92773 10.2676C2.7557 10.5811 2.61583 10.8527 2.50879 11.082C2.40558 11.3114 2.29118 11.5871 2.16504 11.9082L1.93555 12.4697C1.80939 12.7412 1.71237 12.9596 1.64355 13.124C1.57867 13.2844 1.53057 13.4369 1.5 13.582C1.5 13.647 1.5347 13.6991 1.60352 13.7373C1.67594 13.7716 1.75407 13.789 1.83789 13.7891C1.93714 13.7891 2.00461 13.7603 2.03906 13.7031C2.08876 13.619 2.14798 13.4771 2.2168 13.2783C2.26645 13.1408 2.30634 13.0375 2.33691 12.9688C2.37132 12.8999 2.41213 12.8346 2.45801 12.7734C2.57258 12.7735 2.72524 12.7816 2.91602 12.7969C2.96566 12.7969 3.04043 12.801 3.13965 12.8086C3.24282 12.8124 3.33682 12.8135 3.4209 12.8135C3.57382 12.8135 3.77088 12.7971 4.01172 12.7627C4.05749 12.8314 4.09749 12.9134 4.13184 13.0088C4.16998 13.1042 4.19348 13.1614 4.20117 13.1807C4.2394 13.2915 4.27516 13.3875 4.30957 13.4678C4.34392 13.5441 4.38595 13.6148 4.43555 13.6797C4.49289 13.7561 4.57761 13.7949 4.68848 13.7949C4.77633 13.7949 4.8469 13.7714 4.90039 13.7256C4.9577 13.6759 4.9863 13.6053 4.98633 13.5137C4.98633 13.4754 4.9557 13.406 4.89453 13.3066C4.79909 13.1539 4.71105 12.986 4.63086 12.8027C4.5544 12.6154 4.46251 12.3836 4.35547 12.1084L4.23535 11.8047C4.09011 11.4263 3.96959 11.1301 3.87402 10.916C3.78227 10.7019 3.67099 10.4936 3.54102 10.291C3.50281 10.2261 3.47419 10.1842 3.45508 10.165C3.43596 10.1421 3.40941 10.1268 3.375 10.1191C3.34447 10.1077 3.29313 10.1016 3.2207 10.1016ZM3.22559 10.7959C3.34406 10.9985 3.46356 11.2713 3.58203 11.6152C3.69672 11.9364 3.78454 12.162 3.8457 12.292C3.73102 12.3188 3.57176 12.332 3.36914 12.332C3.08636 12.332 2.82271 12.3167 2.57812 12.2861C2.7004 11.9461 2.80974 11.6865 2.90527 11.5068C3.04658 11.2166 3.15295 10.9793 3.22559 10.7959Z"
|
||||||
|
fill="#B392F0"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M16.5 9.5C17.0523 9.5 17.5 9.94772 17.5 10.5V13.5C17.5 14.0523 17.0523 14.5 16.5 14.5H12.5V9.5H16.5ZM13.8779 10.1016C13.7938 10.1016 13.7111 10.1302 13.6309 10.1875C13.5507 10.241 13.5107 10.3085 13.5107 10.3887C13.5108 10.5146 13.5169 10.6386 13.5283 10.7607C13.5436 10.8827 13.5528 10.9574 13.5566 10.9844C13.5834 11.1907 13.6048 11.3992 13.6201 11.6094C13.6354 11.8158 13.6426 12.0782 13.6426 12.3955C13.6426 12.6783 13.6293 12.946 13.6025 13.1982C13.5911 13.1944 13.5676 13.1924 13.5332 13.1924C13.4722 13.1925 13.4147 13.2098 13.3613 13.2441C13.3118 13.2747 13.2871 13.3187 13.2871 13.376C13.2872 13.5174 13.3923 13.6185 13.6025 13.6797C13.8127 13.737 14.0477 13.7656 14.3076 13.7656C14.6515 13.7656 14.9346 13.739 15.1562 13.6855C15.3816 13.6282 15.5595 13.5556 15.6895 13.4678C15.8232 13.3799 15.9652 13.2654 16.1143 13.124C16.2557 12.9941 16.3701 12.8235 16.458 12.6133C16.5459 12.3993 16.5898 12.1949 16.5898 12C16.5898 11.7363 16.5499 11.5105 16.4697 11.3232C16.3895 11.1321 16.2863 10.9636 16.1602 10.8184C16.0034 10.6425 15.7717 10.4842 15.4658 10.3428C15.1638 10.1976 14.7532 10.124 14.2334 10.124C14.199 10.124 14.1469 10.1209 14.0781 10.1133C13.9943 10.1057 13.9275 10.1016 13.8779 10.1016ZM14.1592 10.623C14.4495 10.6231 14.698 10.6445 14.9043 10.6865C15.1107 10.7247 15.2844 10.7881 15.4258 10.876C15.6169 10.9945 15.7588 11.1456 15.8506 11.3291C15.9422 11.5125 15.9883 11.7344 15.9883 11.9941C15.9882 12.2425 15.9188 12.4622 15.7812 12.6533C15.6436 12.8406 15.4618 12.9877 15.2363 13.0947C15.0109 13.1978 14.7717 13.25 14.5195 13.25C14.3709 13.25 14.2508 13.2418 14.1592 13.2266C14.2012 12.9896 14.2217 12.6739 14.2217 12.2803C14.2217 12.0089 14.2104 11.6513 14.1875 11.208C14.1799 11.0937 14.1758 11.0137 14.1758 10.9678C14.1643 10.8301 14.1592 10.7148 14.1592 10.623Z"
|
||||||
|
fill="#B392F0"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export const EyeCloseIcon = () => {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
width="16"
|
||||||
|
height="16"
|
||||||
|
viewBox="0 0 16 16"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M2.80346 6.01162C2.71239 5.79913 2.4663 5.7007 2.25381 5.79177C2.04132 5.88284 1.94288 6.12892 2.03395 6.34141L2.80346 6.01162ZM13.9661 6.34141C14.0571 6.12892 13.9587 5.88284 13.7462 5.79177C13.5337 5.7007 13.2876 5.79913 13.1965 6.01162L13.9661 6.34141ZM8 9.66482C6.20065 9.66482 4.9087 8.7688 4.05101 7.84513C3.62228 7.38343 3.30794 6.92041 3.10107 6.57287C2.99787 6.39949 2.92207 6.25595 2.87269 6.15719C2.84801 6.10785 2.82999 6.06978 2.81848 6.04486C2.81273 6.03241 2.80861 6.02326 2.80611 6.01764C2.80486 6.01483 2.8039 6.01263 2.80346 6.01162C2.80345 6.0116 2.80347 6.01167 2.80346 6.01162C2.80351 6.01175 2.80346 6.01162 2.41871 6.17652C2.03395 6.34141 2.03387 6.34122 2.03395 6.34141C2.03399 6.3415 2.03423 6.34206 2.03431 6.34223C2.03444 6.34256 2.03462 6.34295 2.03481 6.3434C2.0352 6.3443 2.03569 6.34544 2.03629 6.34681C2.03749 6.34955 2.03911 6.35323 2.04115 6.35782C2.04522 6.36698 2.05098 6.37976 2.05845 6.39592C2.07337 6.42825 2.09514 6.47411 2.12388 6.53159C2.18133 6.6465 2.26686 6.80818 2.38168 7.00108C2.61084 7.38607 2.95928 7.8998 3.43751 8.41478C4.39374 9.44459 5.89244 10.502 8 10.502V9.66482ZM9.8488 9.31923C9.30719 9.53244 8.69303 9.66482 8 9.66482V10.502C8.8027 10.502 9.52068 10.3481 10.1554 10.0983L9.8488 9.31923ZM13.5813 6.17652C13.1965 6.01162 13.1965 6.0117 13.1965 6.01162C13.1966 6.01153 13.1968 6.01103 13.1968 6.01109C13.1967 6.0112 13.1966 6.0116 13.1962 6.01229C13.1956 6.01367 13.1945 6.0162 13.1929 6.01982C13.1897 6.02707 13.1844 6.03871 13.1771 6.05443C13.1624 6.08586 13.1396 6.13351 13.1083 6.19475C13.0458 6.31732 12.95 6.49371 12.8197 6.70295C12.5582 7.12264 12.1616 7.66782 11.6204 8.17574L12.1934 8.78616C12.8012 8.21564 13.2418 7.6086 13.5302 7.14569C13.6748 6.91363 13.7822 6.71612 13.8542 6.57511C13.8901 6.50456 13.9173 6.44801 13.9359 6.40818C13.9451 6.38827 13.9523 6.37252 13.9573 6.36128C13.9598 6.35566 13.9618 6.35117 13.9633 6.34784C13.964 6.34618 13.9646 6.34481 13.9651 6.34373C13.9653 6.3432 13.9655 6.34273 13.9656 6.34235C13.9657 6.34215 13.966 6.34157 13.9661 6.34141C13.9661 6.34145 13.9661 6.34133 13.9661 6.34141C13.9661 6.34132 13.9661 6.34141 13.5813 6.17652ZM11.6204 8.17574C11.141 8.6257 10.5528 9.04212 9.8488 9.31923L10.1554 10.0983C10.9779 9.77455 11.6538 9.29255 12.1934 8.78616L11.6204 8.17574Z"
|
||||||
|
fill="var(--text-color)"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
opacity="0.5"
|
||||||
|
d="M8.41869 10.0914C8.41869 9.86022 8.23127 9.6728 8.0001 9.6728C7.76892 9.6728 7.5815 9.86022 7.5815 10.0914H8.41869ZM10.3531 9.48839C10.227 9.29461 9.96767 9.23975 9.77389 9.36583C9.58016 9.49191 9.52524 9.75122 9.65138 9.945L10.3531 9.48839ZM6.34884 9.945C6.47493 9.75122 6.42005 9.49191 6.22628 9.36583C6.0325 9.23975 5.77321 9.29461 5.64712 9.48839L6.34884 9.945ZM4.85859 10.7003C4.7325 10.894 4.78738 11.1533 4.98115 11.2794C5.17493 11.4055 5.43423 11.3506 5.56031 11.1569L4.85859 10.7003ZM12.203 8.19292C12.0395 8.02944 11.7745 8.02944 11.611 8.19292C11.4475 8.3564 11.4475 8.62139 11.611 8.78487L12.203 8.19292ZM12.4482 9.62206C12.6117 9.78554 12.8767 9.78554 13.0402 9.62206C13.2036 9.45859 13.2036 9.19359 13.0402 9.03011L12.4482 9.62206ZM7.5815 11.4867C7.5815 11.7179 7.76892 11.9053 8.0001 11.9053C8.23127 11.9053 8.41869 11.7179 8.41869 11.4867H7.5815ZM10.4399 11.1569C10.566 11.3506 10.8253 11.4055 11.019 11.2794C11.2128 11.1533 11.2677 10.894 11.1416 10.7003L10.4399 11.1569ZM4.38918 8.78487C4.55265 8.62139 4.55265 8.3564 4.38918 8.19292C4.22571 8.02944 3.96067 8.02944 3.7972 8.19292L4.38918 8.78487ZM2.96001 9.03011C2.79653 9.19359 2.79653 9.45859 2.96001 9.62206C3.12348 9.78554 3.38852 9.78554 3.55199 9.62206L2.96001 9.03011ZM5.64712 9.48839L4.85859 10.7003L5.56031 11.1569L6.34884 9.945L5.64712 9.48839ZM11.611 8.78487L12.4482 9.62206L13.0402 9.03011L12.203 8.19292L11.611 8.78487ZM7.5815 10.0914V11.4867H8.41869V10.0914H7.5815ZM9.65138 9.945L10.4399 11.1569L11.1416 10.7003L10.3531 9.48839L9.65138 9.945ZM3.7972 8.19292L2.96001 9.03011L3.55199 9.62206L4.38918 8.78487L3.7972 8.19292Z"
|
||||||
|
fill="var(--text-color)"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
|
|
|
@ -17,10 +17,18 @@ const ConfirmationPopup: React.FC<ConfirmationPopupProps> = ({
|
||||||
<div className="confirmation-modal">
|
<div className="confirmation-modal">
|
||||||
<p className="message">{message}</p>
|
<p className="message">{message}</p>
|
||||||
<div className="buttton-wrapper">
|
<div className="buttton-wrapper">
|
||||||
<button className="confirmation-button" onClick={onConfirm}>
|
<button
|
||||||
|
className="confirmation-button"
|
||||||
|
id="confirmationPopup-confirm-button"
|
||||||
|
onClick={onConfirm}
|
||||||
|
>
|
||||||
OK
|
OK
|
||||||
</button>
|
</button>
|
||||||
<button className="confirmation-button" onClick={onCancel}>
|
<button
|
||||||
|
className="confirmation-button"
|
||||||
|
id="confirmationPopup-cancel-button"
|
||||||
|
onClick={onCancel}
|
||||||
|
>
|
||||||
Cancel
|
Cancel
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -0,0 +1,88 @@
|
||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import useCameraModeStore, {
|
||||||
|
usePlayButtonStore,
|
||||||
|
} from "../../../store/usePlayButtonStore";
|
||||||
|
import useModuleStore from "../../../store/useModuleStore";
|
||||||
|
import { PlayIcon } from "../../icons/ShortcutIcons";
|
||||||
|
import InputToggle from "../../ui/inputs/InputToggle";
|
||||||
|
import { EyeCloseIcon, WalkIcon } from "../../icons/ExportCommonIcons";
|
||||||
|
import { ExitIcon } from "../../icons/SimulationIcons";
|
||||||
|
import { useCamMode } from "../../../store/builder/store";
|
||||||
|
|
||||||
|
const ControlsPlayer: React.FC = () => {
|
||||||
|
const { setIsPlaying } = usePlayButtonStore();
|
||||||
|
const { activeModule } = useModuleStore();
|
||||||
|
const { walkMode, toggleWalkMode } = useCameraModeStore();
|
||||||
|
const [hidePlayer, setHidePlayer] = useState(false);
|
||||||
|
const { camMode } = useCamMode();
|
||||||
|
|
||||||
|
const changeCamMode = () => {
|
||||||
|
toggleWalkMode();
|
||||||
|
echo.log("switch camera mode to first person");
|
||||||
|
// Simulate "/" keypress
|
||||||
|
const slashKeyEvent = new KeyboardEvent("keydown", {
|
||||||
|
key: "/",
|
||||||
|
code: "Slash",
|
||||||
|
keyCode: 191, // for compatibility
|
||||||
|
which: 191,
|
||||||
|
bubbles: true,
|
||||||
|
cancelable: true,
|
||||||
|
});
|
||||||
|
document.dispatchEvent(slashKeyEvent);
|
||||||
|
};
|
||||||
|
useEffect(() => {
|
||||||
|
if (camMode === "ThirdPerson") {
|
||||||
|
toggleWalkMode();
|
||||||
|
} else return;
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [camMode]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`controls-player-container${hidePlayer ? " hide" : ""}`}>
|
||||||
|
{!hidePlayer && (
|
||||||
|
<div className="controls-left">
|
||||||
|
<PlayIcon />
|
||||||
|
<div className="label">Running {activeModule}...</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="controls-right">
|
||||||
|
{!hidePlayer && activeModule === "builder" && (
|
||||||
|
<div className="walkMode-wrapper">
|
||||||
|
<WalkIcon />
|
||||||
|
<InputToggle
|
||||||
|
value={walkMode}
|
||||||
|
inputKey="1"
|
||||||
|
label="Walk Mode"
|
||||||
|
onClick={changeCamMode}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<button
|
||||||
|
id="controls-player-play-button"
|
||||||
|
className={`btn-wrapper${hidePlayer ? " hide" : ""}`}
|
||||||
|
onClick={() => setIsPlaying(false)}
|
||||||
|
>
|
||||||
|
<div className="icon">
|
||||||
|
<ExitIcon />
|
||||||
|
</div>
|
||||||
|
{!hidePlayer && "Exit"}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className={`btn-wrapper${hidePlayer ? " hide" : ""}`}
|
||||||
|
id="hide-btn"
|
||||||
|
onClick={() => {
|
||||||
|
setHidePlayer(!hidePlayer);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="icon">
|
||||||
|
<EyeCloseIcon />
|
||||||
|
</div>
|
||||||
|
{!hidePlayer && "Hide"}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ControlsPlayer;
|
|
@ -20,6 +20,7 @@ const Header: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
|
id="toggle-leftSidebar-ui-button"
|
||||||
className={`toggle-sidebar-ui-button ${!toggleUILeft ? "active" : ""}`}
|
className={`toggle-sidebar-ui-button ${!toggleUILeft ? "active" : ""}`}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (activeModule !== "market") {
|
if (activeModule !== "market") {
|
||||||
|
|
|
@ -5,13 +5,7 @@ import ChartComponent from "./ChartComponent";
|
||||||
import { StockIncreseIcon } from "../../../../icons/RealTimeVisulationIcons";
|
import { StockIncreseIcon } from "../../../../icons/RealTimeVisulationIcons";
|
||||||
import { generateUniqueId } from "../../../../../functions/generateUniqueId";
|
import { generateUniqueId } from "../../../../../functions/generateUniqueId";
|
||||||
|
|
||||||
const chartTypes: ChartType[] = [
|
const chartTypes: ChartType[] = ["bar", "line", "pie", "doughnut", "polarArea"];
|
||||||
"bar",
|
|
||||||
"line",
|
|
||||||
"pie",
|
|
||||||
"doughnut",
|
|
||||||
"polarArea",
|
|
||||||
];
|
|
||||||
|
|
||||||
const sampleData = {
|
const sampleData = {
|
||||||
labels: ["January", "February", "March", "April", "May", "June", "July"],
|
labels: ["January", "February", "March", "April", "May", "June", "July"],
|
||||||
|
@ -37,17 +31,17 @@ const ChartWidget: React.FC<WidgetProps> = ({ type, index, title }) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`chart chart-${index + 1}`}
|
className={`chart chart-${index + 1}`}
|
||||||
|
id={`${type}-widget`}
|
||||||
draggable
|
draggable
|
||||||
onDragStart={() => {
|
onDragStart={() => {
|
||||||
setDraggedAsset({
|
setDraggedAsset({
|
||||||
type,
|
type,
|
||||||
id: generateUniqueId(
|
id: generateUniqueId(),
|
||||||
),
|
|
||||||
title,
|
title,
|
||||||
panel: "top",
|
panel: "top",
|
||||||
Data: {
|
Data: {
|
||||||
measurements:{},
|
measurements: {},
|
||||||
duration:'1h'
|
duration: "1h",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
|
@ -62,7 +56,7 @@ const ProgressBarWidget = ({
|
||||||
id,
|
id,
|
||||||
title,
|
title,
|
||||||
data,
|
data,
|
||||||
type
|
type,
|
||||||
}: {
|
}: {
|
||||||
id: string;
|
id: string;
|
||||||
title: string;
|
title: string;
|
||||||
|
@ -75,6 +69,7 @@ const ProgressBarWidget = ({
|
||||||
<div
|
<div
|
||||||
className="chart progressBar"
|
className="chart progressBar"
|
||||||
draggable
|
draggable
|
||||||
|
id={`${type}-widget`}
|
||||||
onDragStart={() => {
|
onDragStart={() => {
|
||||||
setDraggedAsset({
|
setDraggedAsset({
|
||||||
type: type,
|
type: type,
|
||||||
|
|
|
@ -61,6 +61,7 @@ const Header: React.FC = () => {
|
||||||
<div className="header-container">
|
<div className="header-container">
|
||||||
<div className="options-container">
|
<div className="options-container">
|
||||||
<button
|
<button
|
||||||
|
id="toggle-rightSidebar-ui-button"
|
||||||
className={`toggle-sidebar-ui-button ${
|
className={`toggle-sidebar-ui-button ${
|
||||||
!toggleUIRight ? "active" : ""
|
!toggleUIRight ? "active" : ""
|
||||||
}`}
|
}`}
|
||||||
|
@ -80,6 +81,7 @@ const Header: React.FC = () => {
|
||||||
<ToggleSidebarIcon />
|
<ToggleSidebarIcon />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
|
id="share-button"
|
||||||
className="share-button"
|
className="share-button"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setUserManagement(true);
|
setUserManagement(true);
|
||||||
|
@ -99,6 +101,7 @@ const Header: React.FC = () => {
|
||||||
)}
|
)}
|
||||||
{guestUsers.slice(0, 3).map((user, index) => (
|
{guestUsers.slice(0, 3).map((user, index) => (
|
||||||
<button
|
<button
|
||||||
|
id="user-profile-button"
|
||||||
key={`${index}-${user.userName}`}
|
key={`${index}-${user.userName}`}
|
||||||
className="user-profile"
|
className="user-profile"
|
||||||
style={{ background: getAvatarColor(index, user.userName) }}
|
style={{ background: getAvatarColor(index, user.userName) }}
|
||||||
|
|
|
@ -59,12 +59,15 @@ const SideBarRight: React.FC = () => {
|
||||||
}, [activeModule, selectedEventData, selectedEventSphere, setSubModule]);
|
}, [activeModule, selectedEventData, selectedEventSphere, setSubModule]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`sidebar-right-wrapper ${toggleUIRight ? "open" : "closed"}`}>
|
<div
|
||||||
|
className={`sidebar-right-wrapper ${toggleUIRight ? "open" : "closed"}`}
|
||||||
|
>
|
||||||
<Header />
|
<Header />
|
||||||
{toggleUIRight && (
|
{toggleUIRight && (
|
||||||
<div className="sidebar-actions-container">
|
<div className="sidebar-actions-container">
|
||||||
{activeModule !== "simulation" && (
|
{activeModule !== "simulation" && (
|
||||||
<button
|
<button
|
||||||
|
id="sidebar-action-list-properties"
|
||||||
className={`sidebar-action-list ${
|
className={`sidebar-action-list ${
|
||||||
subModule === "properties" ? "active" : ""
|
subModule === "properties" ? "active" : ""
|
||||||
}`}
|
}`}
|
||||||
|
@ -80,6 +83,7 @@ const SideBarRight: React.FC = () => {
|
||||||
{activeModule === "simulation" && (
|
{activeModule === "simulation" && (
|
||||||
<>
|
<>
|
||||||
<button
|
<button
|
||||||
|
id="sidebar-action-list-simulation"
|
||||||
className={`sidebar-action-list ${
|
className={`sidebar-action-list ${
|
||||||
subModule === "simulations" ? "active" : ""
|
subModule === "simulations" ? "active" : ""
|
||||||
}`}
|
}`}
|
||||||
|
@ -92,6 +96,7 @@ const SideBarRight: React.FC = () => {
|
||||||
<SimulationIcon isActive={subModule === "simulations"} />
|
<SimulationIcon isActive={subModule === "simulations"} />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
|
id="sidebar-action-list-mechanics"
|
||||||
className={`sidebar-action-list ${
|
className={`sidebar-action-list ${
|
||||||
subModule === "mechanics" ? "active" : ""
|
subModule === "mechanics" ? "active" : ""
|
||||||
}`}
|
}`}
|
||||||
|
@ -104,6 +109,7 @@ const SideBarRight: React.FC = () => {
|
||||||
<MechanicsIcon isActive={subModule === "mechanics"} />
|
<MechanicsIcon isActive={subModule === "mechanics"} />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
|
id="sidebar-action-list-analysis"
|
||||||
className={`sidebar-action-list ${
|
className={`sidebar-action-list ${
|
||||||
subModule === "analysis" ? "active" : ""
|
subModule === "analysis" ? "active" : ""
|
||||||
}`}
|
}`}
|
||||||
|
@ -163,7 +169,9 @@ const SideBarRight: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{/* simulation */}
|
{/* simulation */}
|
||||||
{toggleUIRight && !viewVersionHistory && activeModule === "simulation" && (
|
{toggleUIRight &&
|
||||||
|
!viewVersionHistory &&
|
||||||
|
activeModule === "simulation" && (
|
||||||
<>
|
<>
|
||||||
{subModule === "simulations" && (
|
{subModule === "simulations" && (
|
||||||
<div className="sidebar-right-container">
|
<div className="sidebar-right-container">
|
||||||
|
|
|
@ -94,6 +94,7 @@ const EventProperties: React.FC = () => {
|
||||||
<div className="product-item">
|
<div className="product-item">
|
||||||
{products.map((product) => (
|
{products.map((product) => (
|
||||||
<button
|
<button
|
||||||
|
id="add-event-to-product-button"
|
||||||
key={product.productId}
|
key={product.productId}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (selectedEventData) {
|
if (selectedEventData) {
|
||||||
|
|
|
@ -12,6 +12,7 @@ const PickAndPlaceAction: React.FC<PickAndPlaceActionProps> = ({
|
||||||
<div className="value-field-container">
|
<div className="value-field-container">
|
||||||
<div className="label">Reset</div>
|
<div className="label">Reset</div>
|
||||||
<button
|
<button
|
||||||
|
id="pick-and-place-action-clear-button"
|
||||||
type="button"
|
type="button"
|
||||||
className="regularDropdown-container"
|
className="regularDropdown-container"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
|
|
@ -63,6 +63,7 @@ const TravelAction: React.FC<TravelActionProps> = ({
|
||||||
<div className="value-field-container">
|
<div className="value-field-container">
|
||||||
<div className="label">Reset</div>
|
<div className="label">Reset</div>
|
||||||
<button
|
<button
|
||||||
|
id="rest-button"
|
||||||
type="button"
|
type="button"
|
||||||
className="regularDropdown-container"
|
className="regularDropdown-container"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
|
|
@ -6,7 +6,10 @@ import {
|
||||||
} from "../../../../../icons/ExportCommonIcons";
|
} from "../../../../../icons/ExportCommonIcons";
|
||||||
import RenameInput from "../../../../../ui/inputs/RenameInput";
|
import RenameInput from "../../../../../ui/inputs/RenameInput";
|
||||||
import { handleResize } from "../../../../../../functions/handleResizePannel";
|
import { handleResize } from "../../../../../../functions/handleResizePannel";
|
||||||
import { useSelectedAction, useSelectedProduct } from "../../../../../../store/simulation/useSimulationStore";
|
import {
|
||||||
|
useSelectedAction,
|
||||||
|
useSelectedProduct,
|
||||||
|
} from "../../../../../../store/simulation/useSimulationStore";
|
||||||
import { useProductStore } from "../../../../../../store/simulation/useProductStore";
|
import { useProductStore } from "../../../../../../store/simulation/useProductStore";
|
||||||
import { upsertProductOrEventApi } from "../../../../../../services/simulation/UpsertProductOrEventApi";
|
import { upsertProductOrEventApi } from "../../../../../../services/simulation/UpsertProductOrEventApi";
|
||||||
|
|
||||||
|
@ -25,8 +28,8 @@ const ActionsList: React.FC<ActionsListProps> = ({
|
||||||
}) => {
|
}) => {
|
||||||
const actionsContainerRef = useRef<HTMLDivElement>(null);
|
const actionsContainerRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
const email = localStorage.getItem('email')
|
const email = localStorage.getItem("email");
|
||||||
const organization = (email!.split("@")[1]).split(".")[0];
|
const organization = email!.split("@")[1].split(".")[0];
|
||||||
|
|
||||||
// store
|
// store
|
||||||
const { renameAction } = useProductStore();
|
const { renameAction } = useProductStore();
|
||||||
|
@ -35,15 +38,19 @@ const ActionsList: React.FC<ActionsListProps> = ({
|
||||||
|
|
||||||
const handleRenameAction = (newName: string) => {
|
const handleRenameAction = (newName: string) => {
|
||||||
if (!selectedAction.actionId) return;
|
if (!selectedAction.actionId) return;
|
||||||
const event = renameAction(selectedProduct.productId, selectedAction.actionId, newName);
|
const event = renameAction(
|
||||||
|
selectedProduct.productId,
|
||||||
|
selectedAction.actionId,
|
||||||
|
newName
|
||||||
|
);
|
||||||
setSelectedAction(selectedAction.actionId, newName);
|
setSelectedAction(selectedAction.actionId, newName);
|
||||||
if (event) {
|
if (event) {
|
||||||
upsertProductOrEventApi({
|
upsertProductOrEventApi({
|
||||||
productName: selectedProduct.productName,
|
productName: selectedProduct.productName,
|
||||||
productId: selectedProduct.productId,
|
productId: selectedProduct.productId,
|
||||||
organization: organization,
|
organization: organization,
|
||||||
eventDatas: event
|
eventDatas: event,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -58,6 +65,7 @@ const ActionsList: React.FC<ActionsListProps> = ({
|
||||||
<div className="header-value">Actions</div>
|
<div className="header-value">Actions</div>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
|
id="add-action-button"
|
||||||
className="add-button"
|
className="add-button"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (handleAddAction) {
|
if (handleAddAction) {
|
||||||
|
@ -75,16 +83,18 @@ const ActionsList: React.FC<ActionsListProps> = ({
|
||||||
style={{ height: "120px" }}
|
style={{ height: "120px" }}
|
||||||
>
|
>
|
||||||
<div className="list-container">
|
<div className="list-container">
|
||||||
{multipleAction && selectedPointData &&
|
{multipleAction &&
|
||||||
selectedPointData.actions.map((action: any) => (
|
selectedPointData?.actions?.map((action: any) => (
|
||||||
<div
|
<div
|
||||||
key={action.actionUuid}
|
key={action.actionUuid}
|
||||||
className={`list-item ${selectedAction.actionId === action.actionUuid
|
className={`list-item ${
|
||||||
|
selectedAction.actionId === action.actionUuid
|
||||||
? "active"
|
? "active"
|
||||||
: ""
|
: ""
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
|
id="action-button"
|
||||||
className="value"
|
className="value"
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
handleActionSelect(action.actionUuid, action.actionName)
|
handleActionSelect(action.actionUuid, action.actionName)
|
||||||
|
@ -95,8 +105,9 @@ const ActionsList: React.FC<ActionsListProps> = ({
|
||||||
onRename={(value) => handleRenameAction(value)}
|
onRename={(value) => handleRenameAction(value)}
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
{selectedPointData.actions.length > 1 && (
|
{selectedPointData?.actions?.length > 1 && (
|
||||||
<button
|
<button
|
||||||
|
id="remove-action-button"
|
||||||
className="remove-button"
|
className="remove-button"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (handleDeleteAction) {
|
if (handleDeleteAction) {
|
||||||
|
@ -109,12 +120,13 @@ const ActionsList: React.FC<ActionsListProps> = ({
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
{!multipleAction && selectedPointData && (
|
{!multipleAction && selectedPointData?.action && (
|
||||||
<div
|
<div
|
||||||
key={selectedPointData.action.actionUuid}
|
key={selectedPointData.action.actionUuid}
|
||||||
className={`list-item active`}
|
className={`list-item active`}
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
|
id="action-button"
|
||||||
className="value"
|
className="value"
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
handleActionSelect(
|
handleActionSelect(
|
||||||
|
|
|
@ -9,35 +9,67 @@ import LabledDropdown from "../../../../../ui/inputs/LabledDropdown";
|
||||||
import RenameInput from "../../../../../ui/inputs/RenameInput";
|
import RenameInput from "../../../../../ui/inputs/RenameInput";
|
||||||
import { handleResize } from "../../../../../../functions/handleResizePannel";
|
import { handleResize } from "../../../../../../functions/handleResizePannel";
|
||||||
import { useProductStore } from "../../../../../../store/simulation/useProductStore";
|
import { useProductStore } from "../../../../../../store/simulation/useProductStore";
|
||||||
import { useSelectedAction, useSelectedProduct } from "../../../../../../store/simulation/useSimulationStore";
|
import {
|
||||||
|
useSelectedAction,
|
||||||
|
useSelectedProduct,
|
||||||
|
} from "../../../../../../store/simulation/useSimulationStore";
|
||||||
import { upsertProductOrEventApi } from "../../../../../../services/simulation/UpsertProductOrEventApi";
|
import { upsertProductOrEventApi } from "../../../../../../services/simulation/UpsertProductOrEventApi";
|
||||||
|
|
||||||
type TriggerProps = {
|
type TriggerProps = {
|
||||||
selectedPointData?: PointsScheme | undefined;
|
selectedPointData?: PointsScheme | undefined;
|
||||||
type?: 'Conveyor' | 'Vehicle' | 'RoboticArm' | 'Machine' | 'StorageUnit';
|
type?: "Conveyor" | "Vehicle" | "RoboticArm" | "Machine" | "StorageUnit";
|
||||||
};
|
};
|
||||||
|
|
||||||
const Trigger = ({ selectedPointData, type }: TriggerProps) => {
|
const Trigger = ({ selectedPointData, type }: TriggerProps) => {
|
||||||
const [currentAction, setCurrentAction] = useState<string | undefined>();
|
const [currentAction, setCurrentAction] = useState<string | undefined>();
|
||||||
const { selectedProduct } = useSelectedProduct();
|
const { selectedProduct } = useSelectedProduct();
|
||||||
const { getActionByUuid, getEventByModelUuid, getPointByUuid, getTriggerByUuid, addTrigger, removeTrigger, updateTrigger, renameTrigger, getProductById } = useProductStore();
|
const {
|
||||||
|
getActionByUuid,
|
||||||
|
getEventByModelUuid,
|
||||||
|
getPointByUuid,
|
||||||
|
getTriggerByUuid,
|
||||||
|
addTrigger,
|
||||||
|
removeTrigger,
|
||||||
|
updateTrigger,
|
||||||
|
renameTrigger,
|
||||||
|
getProductById,
|
||||||
|
} = useProductStore();
|
||||||
const [triggers, setTriggers] = useState<TriggerSchema[]>([]);
|
const [triggers, setTriggers] = useState<TriggerSchema[]>([]);
|
||||||
const [selectedTrigger, setSelectedTrigger] = useState<TriggerSchema | undefined>();
|
const [selectedTrigger, setSelectedTrigger] = useState<
|
||||||
const [activeOption, setActiveOption] = useState<"onComplete" | "onStart" | "onStop" | "delay" | "onError">("onComplete");
|
TriggerSchema | undefined
|
||||||
|
>();
|
||||||
|
const [activeOption, setActiveOption] = useState<
|
||||||
|
"onComplete" | "onStart" | "onStop" | "delay" | "onError"
|
||||||
|
>("onComplete");
|
||||||
const triggersContainerRef = useRef<HTMLDivElement>(null);
|
const triggersContainerRef = useRef<HTMLDivElement>(null);
|
||||||
const { selectedAction } = useSelectedAction();
|
const { selectedAction } = useSelectedAction();
|
||||||
|
|
||||||
const email = localStorage.getItem('email')
|
const email = localStorage.getItem("email");
|
||||||
const organization = (email!.split("@")[1]).split(".")[0];
|
const organization = email!.split("@")[1].split(".")[0];
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!selectedPointData || !selectedProduct) return;
|
if (!selectedPointData || !selectedProduct) return;
|
||||||
|
|
||||||
let actionUuid: string | undefined;
|
let actionUuid: string | undefined;
|
||||||
|
|
||||||
if (type === 'Conveyor' || type === 'Vehicle' || type === 'Machine' || type === 'StorageUnit') {
|
if (
|
||||||
actionUuid = (selectedPointData as ConveyorPointSchema | VehiclePointSchema | MachinePointSchema | StoragePointSchema).action?.actionUuid;
|
type === "Conveyor" ||
|
||||||
} else if (type === 'RoboticArm' && selectedAction && selectedAction.actionId) {
|
type === "Vehicle" ||
|
||||||
|
type === "Machine" ||
|
||||||
|
type === "StorageUnit"
|
||||||
|
) {
|
||||||
|
actionUuid = (
|
||||||
|
selectedPointData as
|
||||||
|
| ConveyorPointSchema
|
||||||
|
| VehiclePointSchema
|
||||||
|
| MachinePointSchema
|
||||||
|
| StoragePointSchema
|
||||||
|
).action?.actionUuid;
|
||||||
|
} else if (
|
||||||
|
type === "RoboticArm" &&
|
||||||
|
selectedAction &&
|
||||||
|
selectedAction.actionId
|
||||||
|
) {
|
||||||
actionUuid = selectedAction.actionId;
|
actionUuid = selectedAction.actionId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,9 +86,9 @@ const Trigger = ({ selectedPointData, type }: TriggerProps) => {
|
||||||
productName: productName,
|
productName: productName,
|
||||||
productId: productId,
|
productId: productId,
|
||||||
organization: organization,
|
organization: organization,
|
||||||
eventDatas: eventData
|
eventDatas: eventData,
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!currentAction || !selectedProduct) return;
|
if (!currentAction || !selectedProduct) return;
|
||||||
|
@ -67,12 +99,24 @@ const Trigger = ({ selectedPointData, type }: TriggerProps) => {
|
||||||
}, [currentAction, selectedProduct]);
|
}, [currentAction, selectedProduct]);
|
||||||
|
|
||||||
const triggeredModel = useMemo(() => {
|
const triggeredModel = useMemo(() => {
|
||||||
if (!selectedProduct || !selectedTrigger?.triggeredAsset?.triggeredModel?.modelUuid) return undefined;
|
if (
|
||||||
return getEventByModelUuid(selectedProduct.productId, selectedTrigger.triggeredAsset.triggeredModel.modelUuid);
|
!selectedProduct ||
|
||||||
|
!selectedTrigger?.triggeredAsset?.triggeredModel?.modelUuid
|
||||||
|
)
|
||||||
|
return undefined;
|
||||||
|
return getEventByModelUuid(
|
||||||
|
selectedProduct.productId,
|
||||||
|
selectedTrigger.triggeredAsset.triggeredModel.modelUuid
|
||||||
|
);
|
||||||
}, [selectedProduct, selectedTrigger]);
|
}, [selectedProduct, selectedTrigger]);
|
||||||
|
|
||||||
const triggeredPoint = useMemo(() => {
|
const triggeredPoint = useMemo(() => {
|
||||||
if (!selectedProduct || !triggeredModel || !selectedTrigger?.triggeredAsset?.triggeredPoint?.pointUuid) return undefined;
|
if (
|
||||||
|
!selectedProduct ||
|
||||||
|
!triggeredModel ||
|
||||||
|
!selectedTrigger?.triggeredAsset?.triggeredPoint?.pointUuid
|
||||||
|
)
|
||||||
|
return undefined;
|
||||||
return getPointByUuid(
|
return getPointByUuid(
|
||||||
selectedProduct.productId,
|
selectedProduct.productId,
|
||||||
triggeredModel.modelUuid,
|
triggeredModel.modelUuid,
|
||||||
|
@ -81,25 +125,40 @@ const Trigger = ({ selectedPointData, type }: TriggerProps) => {
|
||||||
}, [selectedProduct, triggeredModel, selectedTrigger]);
|
}, [selectedProduct, triggeredModel, selectedTrigger]);
|
||||||
|
|
||||||
const triggeredAction = useMemo(() => {
|
const triggeredAction = useMemo(() => {
|
||||||
if (!selectedProduct || !selectedTrigger?.triggeredAsset?.triggeredAction?.actionUuid) return undefined;
|
if (
|
||||||
|
!selectedProduct ||
|
||||||
|
!selectedTrigger?.triggeredAsset?.triggeredAction?.actionUuid
|
||||||
|
)
|
||||||
|
return undefined;
|
||||||
return getActionByUuid(
|
return getActionByUuid(
|
||||||
selectedProduct.productId,
|
selectedProduct.productId,
|
||||||
selectedTrigger.triggeredAsset.triggeredAction.actionUuid
|
selectedTrigger.triggeredAsset.triggeredAction.actionUuid
|
||||||
);
|
);
|
||||||
}, [selectedProduct, selectedTrigger]);
|
}, [selectedProduct, selectedTrigger]);
|
||||||
|
|
||||||
const modelOptions = getProductById(selectedProduct.productId)?.eventDatas || [];
|
const modelOptions =
|
||||||
|
getProductById(selectedProduct.productId)?.eventDatas || [];
|
||||||
|
|
||||||
const pointOptions: PointsScheme[] = useMemo(() => {
|
const pointOptions: PointsScheme[] = useMemo(() => {
|
||||||
if (!triggeredModel) return [];
|
if (!triggeredModel) return [];
|
||||||
|
|
||||||
const model = modelOptions.find(m => m.modelUuid === triggeredModel.modelUuid);
|
const model = modelOptions.find(
|
||||||
|
(m) => m.modelUuid === triggeredModel.modelUuid
|
||||||
|
);
|
||||||
if (!model) return [];
|
if (!model) return [];
|
||||||
|
|
||||||
if ('points' in model) {
|
if ("points" in model) {
|
||||||
return (model as ConveyorEventSchema).points;
|
return (model as ConveyorEventSchema).points;
|
||||||
} else if ('point' in model) {
|
} else if ("point" in model) {
|
||||||
return [(model as VehicleEventSchema | RoboticArmEventSchema | MachineEventSchema | StorageEventSchema).point];
|
return [
|
||||||
|
(
|
||||||
|
model as
|
||||||
|
| VehicleEventSchema
|
||||||
|
| RoboticArmEventSchema
|
||||||
|
| MachineEventSchema
|
||||||
|
| StorageEventSchema
|
||||||
|
).point,
|
||||||
|
];
|
||||||
}
|
}
|
||||||
return [];
|
return [];
|
||||||
}, [triggeredModel, modelOptions]);
|
}, [triggeredModel, modelOptions]);
|
||||||
|
@ -109,10 +168,14 @@ const Trigger = ({ selectedPointData, type }: TriggerProps) => {
|
||||||
const point = pointOptions.find((p) => p.uuid === triggeredPoint.uuid);
|
const point = pointOptions.find((p) => p.uuid === triggeredPoint.uuid);
|
||||||
if (!point) return [];
|
if (!point) return [];
|
||||||
|
|
||||||
if ('action' in point) {
|
if ("action" in point) {
|
||||||
const typedPoint = point as ConveyorPointSchema | VehiclePointSchema | MachinePointSchema | StoragePointSchema;
|
const typedPoint = point as
|
||||||
|
| ConveyorPointSchema
|
||||||
|
| VehiclePointSchema
|
||||||
|
| MachinePointSchema
|
||||||
|
| StoragePointSchema;
|
||||||
return typedPoint.action ? [typedPoint.action] : [];
|
return typedPoint.action ? [typedPoint.action] : [];
|
||||||
} else if ('actions' in point) {
|
} else if ("actions" in point) {
|
||||||
const typedPoint = point as RoboticArmPointSchema;
|
const typedPoint = point as RoboticArmPointSchema;
|
||||||
return typedPoint.actions;
|
return typedPoint.actions;
|
||||||
}
|
}
|
||||||
|
@ -122,22 +185,25 @@ const Trigger = ({ selectedPointData, type }: TriggerProps) => {
|
||||||
const handleModelSelect = (option: string, triggerUuid: string) => {
|
const handleModelSelect = (option: string, triggerUuid: string) => {
|
||||||
if (!selectedProduct) return;
|
if (!selectedProduct) return;
|
||||||
|
|
||||||
const selectedModel = modelOptions.find(m => m.modelName === option);
|
const selectedModel = modelOptions.find((m) => m.modelName === option);
|
||||||
if (!selectedModel) return;
|
if (!selectedModel) return;
|
||||||
|
|
||||||
const event = updateTrigger(selectedProduct.productId, triggerUuid, {
|
const event = updateTrigger(selectedProduct.productId, triggerUuid, {
|
||||||
triggeredAsset: {
|
triggeredAsset: {
|
||||||
triggeredModel: {
|
triggeredModel: {
|
||||||
modelName: selectedModel.modelName,
|
modelName: selectedModel.modelName,
|
||||||
modelUuid: selectedModel.modelUuid
|
modelUuid: selectedModel.modelUuid,
|
||||||
},
|
},
|
||||||
triggeredPoint: null,
|
triggeredPoint: null,
|
||||||
triggeredAction: null
|
triggeredAction: null,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (event) {
|
if (event) {
|
||||||
const updatedTrigger = getTriggerByUuid(selectedProduct.productId, triggerUuid);
|
const updatedTrigger = getTriggerByUuid(
|
||||||
|
selectedProduct.productId,
|
||||||
|
triggerUuid
|
||||||
|
);
|
||||||
setSelectedTrigger(updatedTrigger);
|
setSelectedTrigger(updatedTrigger);
|
||||||
|
|
||||||
updateBackend(
|
updateBackend(
|
||||||
|
@ -152,7 +218,9 @@ const Trigger = ({ selectedPointData, type }: TriggerProps) => {
|
||||||
const handlePointSelect = (option: string, triggerUuid: string) => {
|
const handlePointSelect = (option: string, triggerUuid: string) => {
|
||||||
if (!selectedProduct || !selectedTrigger) return;
|
if (!selectedProduct || !selectedTrigger) return;
|
||||||
|
|
||||||
const pointUuid = pointOptions.find(p => `Point ${p.uuid.slice(0, 4)}` === option)?.uuid;
|
const pointUuid = pointOptions.find(
|
||||||
|
(p) => `Point ${p.uuid.slice(0, 4)}` === option
|
||||||
|
)?.uuid;
|
||||||
if (!pointUuid) return;
|
if (!pointUuid) return;
|
||||||
|
|
||||||
if (selectedTrigger.triggeredAsset?.triggeredModel) {
|
if (selectedTrigger.triggeredAsset?.triggeredModel) {
|
||||||
|
@ -161,14 +229,17 @@ const Trigger = ({ selectedPointData, type }: TriggerProps) => {
|
||||||
...selectedTrigger.triggeredAsset,
|
...selectedTrigger.triggeredAsset,
|
||||||
triggeredPoint: {
|
triggeredPoint: {
|
||||||
pointName: option,
|
pointName: option,
|
||||||
pointUuid: pointUuid
|
pointUuid: pointUuid,
|
||||||
|
},
|
||||||
|
triggeredAction: null,
|
||||||
},
|
},
|
||||||
triggeredAction: null
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (event) {
|
if (event) {
|
||||||
const updatedTrigger = getTriggerByUuid(selectedProduct.productId, triggerUuid);
|
const updatedTrigger = getTriggerByUuid(
|
||||||
|
selectedProduct.productId,
|
||||||
|
triggerUuid
|
||||||
|
);
|
||||||
setSelectedTrigger(updatedTrigger);
|
setSelectedTrigger(updatedTrigger);
|
||||||
|
|
||||||
updateBackend(
|
updateBackend(
|
||||||
|
@ -181,11 +252,12 @@ const Trigger = ({ selectedPointData, type }: TriggerProps) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const handleActionSelect = (option: string, triggerUuid: string) => {
|
const handleActionSelect = (option: string, triggerUuid: string) => {
|
||||||
if (!selectedProduct || !selectedTrigger) return;
|
if (!selectedProduct || !selectedTrigger) return;
|
||||||
|
|
||||||
const selectedAction = actionOptions.find((a: any) => a.actionName === option);
|
const selectedAction = actionOptions.find(
|
||||||
|
(a: any) => a.actionName === option
|
||||||
|
);
|
||||||
|
|
||||||
if (!selectedAction) return;
|
if (!selectedAction) return;
|
||||||
|
|
||||||
|
@ -195,9 +267,9 @@ const Trigger = ({ selectedPointData, type }: TriggerProps) => {
|
||||||
...selectedTrigger.triggeredAsset,
|
...selectedTrigger.triggeredAsset,
|
||||||
triggeredAction: {
|
triggeredAction: {
|
||||||
actionName: option,
|
actionName: option,
|
||||||
actionUuid: selectedAction.actionUuid
|
actionUuid: selectedAction.actionUuid,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (event) {
|
if (event) {
|
||||||
|
@ -219,10 +291,14 @@ const Trigger = ({ selectedPointData, type }: TriggerProps) => {
|
||||||
triggerName: `New Trigger ${triggers.length + 1}`,
|
triggerName: `New Trigger ${triggers.length + 1}`,
|
||||||
triggerType: activeOption,
|
triggerType: activeOption,
|
||||||
delay: 0,
|
delay: 0,
|
||||||
triggeredAsset: null
|
triggeredAsset: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
const event = addTrigger(selectedProduct.productId, currentAction, newTrigger);
|
const event = addTrigger(
|
||||||
|
selectedProduct.productId,
|
||||||
|
currentAction,
|
||||||
|
newTrigger
|
||||||
|
);
|
||||||
|
|
||||||
if (event) {
|
if (event) {
|
||||||
updateBackend(
|
updateBackend(
|
||||||
|
@ -233,7 +309,10 @@ const Trigger = ({ selectedPointData, type }: TriggerProps) => {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const updatedAction = getActionByUuid(selectedProduct.productId, currentAction);
|
const updatedAction = getActionByUuid(
|
||||||
|
selectedProduct.productId,
|
||||||
|
currentAction
|
||||||
|
);
|
||||||
const updatedTriggers = updatedAction?.triggers || [];
|
const updatedTriggers = updatedAction?.triggers || [];
|
||||||
|
|
||||||
setTriggers(updatedTriggers);
|
setTriggers(updatedTriggers);
|
||||||
|
@ -254,8 +333,8 @@ const Trigger = ({ selectedPointData, type }: TriggerProps) => {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const index = triggers.findIndex(t => t.triggerUuid === triggerUuid);
|
const index = triggers.findIndex((t) => t.triggerUuid === triggerUuid);
|
||||||
const newTriggers = triggers.filter(t => t.triggerUuid !== triggerUuid);
|
const newTriggers = triggers.filter((t) => t.triggerUuid !== triggerUuid);
|
||||||
setTriggers(newTriggers);
|
setTriggers(newTriggers);
|
||||||
|
|
||||||
if (selectedTrigger?.triggerUuid === triggerUuid) {
|
if (selectedTrigger?.triggerUuid === triggerUuid) {
|
||||||
|
@ -266,7 +345,11 @@ const Trigger = ({ selectedPointData, type }: TriggerProps) => {
|
||||||
|
|
||||||
const handleTriggerRename = (triggerUuid: string, newName: string) => {
|
const handleTriggerRename = (triggerUuid: string, newName: string) => {
|
||||||
if (!selectedProduct) return;
|
if (!selectedProduct) return;
|
||||||
const event = renameTrigger(selectedProduct.productId, triggerUuid, newName);
|
const event = renameTrigger(
|
||||||
|
selectedProduct.productId,
|
||||||
|
triggerUuid,
|
||||||
|
newName
|
||||||
|
);
|
||||||
|
|
||||||
if (event) {
|
if (event) {
|
||||||
updateBackend(
|
updateBackend(
|
||||||
|
@ -281,13 +364,23 @@ const Trigger = ({ selectedPointData, type }: TriggerProps) => {
|
||||||
const handleTriggerTypeChange = (option: string) => {
|
const handleTriggerTypeChange = (option: string) => {
|
||||||
if (!selectedTrigger || !selectedProduct) return;
|
if (!selectedTrigger || !selectedProduct) return;
|
||||||
|
|
||||||
const validTypes: Array<TriggerSchema['triggerType']> = ["onComplete", "onStart", "onStop", "delay", "onError"];
|
const validTypes: Array<TriggerSchema["triggerType"]> = [
|
||||||
if (!validTypes.includes(option as TriggerSchema['triggerType'])) return;
|
"onComplete",
|
||||||
|
"onStart",
|
||||||
|
"onStop",
|
||||||
|
"delay",
|
||||||
|
"onError",
|
||||||
|
];
|
||||||
|
if (!validTypes.includes(option as TriggerSchema["triggerType"])) return;
|
||||||
|
|
||||||
setActiveOption(option as TriggerSchema['triggerType']);
|
setActiveOption(option as TriggerSchema["triggerType"]);
|
||||||
const event = updateTrigger(selectedProduct.productId, selectedTrigger.triggerUuid, {
|
const event = updateTrigger(
|
||||||
triggerType: option as TriggerSchema['triggerType']
|
selectedProduct.productId,
|
||||||
});
|
selectedTrigger.triggerUuid,
|
||||||
|
{
|
||||||
|
triggerType: option as TriggerSchema["triggerType"],
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
if (event) {
|
if (event) {
|
||||||
updateBackend(
|
updateBackend(
|
||||||
|
@ -304,6 +397,7 @@ const Trigger = ({ selectedPointData, type }: TriggerProps) => {
|
||||||
<div className="header">
|
<div className="header">
|
||||||
<div className="title">Trigger</div>
|
<div className="title">Trigger</div>
|
||||||
<button
|
<button
|
||||||
|
id="add-trigger-button"
|
||||||
className="add-button"
|
className="add-button"
|
||||||
onClick={handleAddTrigger}
|
onClick={handleAddTrigger}
|
||||||
style={{ cursor: "pointer" }}
|
style={{ cursor: "pointer" }}
|
||||||
|
@ -322,17 +416,24 @@ const Trigger = ({ selectedPointData, type }: TriggerProps) => {
|
||||||
{triggers.map((trigger) => (
|
{triggers.map((trigger) => (
|
||||||
<div
|
<div
|
||||||
key={trigger.triggerUuid}
|
key={trigger.triggerUuid}
|
||||||
className={`list-item ${selectedTrigger?.triggerUuid === trigger.triggerUuid ? "active" : ""}`}
|
className={`list-item ${
|
||||||
|
selectedTrigger?.triggerUuid === trigger.triggerUuid
|
||||||
|
? "active"
|
||||||
|
: ""
|
||||||
|
}`}
|
||||||
onClick={() => setSelectedTrigger(trigger)}
|
onClick={() => setSelectedTrigger(trigger)}
|
||||||
>
|
>
|
||||||
<button className="value">
|
<button id="trigger" className="value">
|
||||||
<RenameInput
|
<RenameInput
|
||||||
value={trigger.triggerName}
|
value={trigger.triggerName}
|
||||||
onRename={(newName) => handleTriggerRename(trigger.triggerUuid, newName)}
|
onRename={(newName) =>
|
||||||
|
handleTriggerRename(trigger.triggerUuid, newName)
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
{triggers.length > 1 && (
|
{triggers.length > 1 && (
|
||||||
<button
|
<button
|
||||||
|
id="remove-trigger-button"
|
||||||
className="remove-button"
|
className="remove-button"
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
@ -369,20 +470,36 @@ const Trigger = ({ selectedPointData, type }: TriggerProps) => {
|
||||||
<LabledDropdown
|
<LabledDropdown
|
||||||
label="Triggered Object"
|
label="Triggered Object"
|
||||||
defaultOption={triggeredModel?.modelName || ""}
|
defaultOption={triggeredModel?.modelName || ""}
|
||||||
options={[...modelOptions.map((option) => (option.modelName))]}
|
options={[...modelOptions.map((option) => option.modelName)]}
|
||||||
onSelect={(option) => { handleModelSelect(option, selectedTrigger.triggerUuid) }}
|
onSelect={(option) => {
|
||||||
|
handleModelSelect(option, selectedTrigger.triggerUuid);
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<LabledDropdown
|
<LabledDropdown
|
||||||
label="Triggered Point"
|
label="Triggered Point"
|
||||||
defaultOption={triggeredPoint?.uuid ? `Point ${triggeredPoint?.uuid.slice(0, 4)}` : ''}
|
defaultOption={
|
||||||
options={[...pointOptions.map((option) => (`Point ${option.uuid.slice(0, 4)}`))]}
|
triggeredPoint?.uuid
|
||||||
onSelect={(option) => { handlePointSelect(option, selectedTrigger.triggerUuid) }}
|
? `Point ${triggeredPoint?.uuid.slice(0, 4)}`
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
options={[
|
||||||
|
...pointOptions.map(
|
||||||
|
(option) => `Point ${option.uuid.slice(0, 4)}`
|
||||||
|
),
|
||||||
|
]}
|
||||||
|
onSelect={(option) => {
|
||||||
|
handlePointSelect(option, selectedTrigger.triggerUuid);
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<LabledDropdown
|
<LabledDropdown
|
||||||
label="Triggered Action"
|
label="Triggered Action"
|
||||||
defaultOption={triggeredAction?.actionName || ''}
|
defaultOption={triggeredAction?.actionName || ""}
|
||||||
options={[...actionOptions.map((option: any) => (option.actionName))]}
|
options={[
|
||||||
onSelect={(option) => { handleActionSelect(option, selectedTrigger.triggerUuid) }}
|
...actionOptions.map((option: any) => option.actionName),
|
||||||
|
]}
|
||||||
|
onSelect={(option) => {
|
||||||
|
handleActionSelect(option, selectedTrigger.triggerUuid);
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -22,6 +22,8 @@ import { upsertProductOrEventApi } from "../../../../services/simulation/UpsertP
|
||||||
import { deleteProductApi } from "../../../../services/simulation/deleteProductApi";
|
import { deleteProductApi } from "../../../../services/simulation/deleteProductApi";
|
||||||
import { renameProductApi } from "../../../../services/simulation/renameProductApi";
|
import { renameProductApi } from "../../../../services/simulation/renameProductApi";
|
||||||
import { determineExecutionMachineSequences } from "../../../../modules/simulation/simulator/functions/determineExecutionMachineSequences";
|
import { determineExecutionMachineSequences } from "../../../../modules/simulation/simulator/functions/determineExecutionMachineSequences";
|
||||||
|
import ComparePopUp from "../../../../modules/simulation/compare/compare";
|
||||||
|
import { useCompareStore } from "../../../../store/builder/store";
|
||||||
|
|
||||||
interface Event {
|
interface Event {
|
||||||
modelName: string;
|
modelName: string;
|
||||||
|
@ -49,7 +51,7 @@ const Simulations: React.FC = () => {
|
||||||
renameProduct,
|
renameProduct,
|
||||||
addEvent,
|
addEvent,
|
||||||
removeEvent,
|
removeEvent,
|
||||||
getProductById
|
getProductById,
|
||||||
} = useProductStore();
|
} = useProductStore();
|
||||||
const { selectedProduct, setSelectedProduct } = useSelectedProduct();
|
const { selectedProduct, setSelectedProduct } = useSelectedProduct();
|
||||||
const { getEventByModelUuid } = useEventsStore();
|
const { getEventByModelUuid } = useEventsStore();
|
||||||
|
@ -58,7 +60,7 @@ const Simulations: React.FC = () => {
|
||||||
const organization = email!.split("@")[1].split(".")[0];
|
const organization = email!.split("@")[1].split(".")[0];
|
||||||
const [openObjects, setOpenObjects] = useState(true);
|
const [openObjects, setOpenObjects] = useState(true);
|
||||||
const [processes, setProcesses] = useState<Event[][]>();
|
const [processes, setProcesses] = useState<Event[][]>();
|
||||||
|
const { comparePopUp, setComparePopUp } = useCompareStore();
|
||||||
const handleAddProduct = () => {
|
const handleAddProduct = () => {
|
||||||
const id = generateUUID();
|
const id = generateUUID();
|
||||||
const name = `Product ${products.length + 1}`;
|
const name = `Product ${products.length + 1}`;
|
||||||
|
@ -122,21 +124,21 @@ const Simulations: React.FC = () => {
|
||||||
const selectedProductData = getProductById(selectedProduct.productId);
|
const selectedProductData = getProductById(selectedProduct.productId);
|
||||||
|
|
||||||
if (selectedProductData) {
|
if (selectedProductData) {
|
||||||
determineExecutionMachineSequences([selectedProductData])
|
determineExecutionMachineSequences([selectedProductData]).then(
|
||||||
.then((sequences) => {
|
(sequences) => {
|
||||||
sequences.forEach((sequence) => {
|
sequences.forEach((sequence) => {
|
||||||
const events: Event[] =
|
const events: Event[] =
|
||||||
sequence.map((event) => ({
|
sequence.map((event) => ({
|
||||||
modelName: event.modelName,
|
modelName: event.modelName,
|
||||||
modelId: event.modelUuid
|
modelId: event.modelUuid,
|
||||||
})) || [];
|
})) || [];
|
||||||
processes.push(events);
|
processes.push(events);
|
||||||
})
|
});
|
||||||
setProcesses(processes)
|
setProcesses(processes);
|
||||||
})
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}, [selectedProduct.productId, products])
|
);
|
||||||
|
}
|
||||||
|
}, [selectedProduct.productId, products]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="simulations-container">
|
<div className="simulations-container">
|
||||||
|
@ -145,7 +147,11 @@ const Simulations: React.FC = () => {
|
||||||
<div className="actions section">
|
<div className="actions section">
|
||||||
<div className="header">
|
<div className="header">
|
||||||
<div className="header-value">Products</div>
|
<div className="header-value">Products</div>
|
||||||
<button className="add-button" onClick={handleAddProduct}>
|
<button
|
||||||
|
id="add-simulation"
|
||||||
|
className="add-button"
|
||||||
|
onClick={handleAddProduct}
|
||||||
|
>
|
||||||
<AddIcon /> Add
|
<AddIcon /> Add
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -158,7 +164,8 @@ const Simulations: React.FC = () => {
|
||||||
{products.map((product, index) => (
|
{products.map((product, index) => (
|
||||||
<div
|
<div
|
||||||
key={product.productId}
|
key={product.productId}
|
||||||
className={`list-item ${selectedProduct.productId === product.productId
|
className={`list-item ${
|
||||||
|
selectedProduct.productId === product.productId
|
||||||
? "active"
|
? "active"
|
||||||
: ""
|
: ""
|
||||||
}`}
|
}`}
|
||||||
|
@ -185,6 +192,7 @@ const Simulations: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
{products.length > 1 && (
|
{products.length > 1 && (
|
||||||
<button
|
<button
|
||||||
|
id="remove-product-button"
|
||||||
className="remove-button"
|
className="remove-button"
|
||||||
onClick={() => handleRemoveProduct(product.productId)}
|
onClick={() => handleRemoveProduct(product.productId)}
|
||||||
>
|
>
|
||||||
|
@ -206,6 +214,7 @@ const Simulations: React.FC = () => {
|
||||||
|
|
||||||
<div className="simulation-process section">
|
<div className="simulation-process section">
|
||||||
<button
|
<button
|
||||||
|
id="collapse-header"
|
||||||
className="collapse-header-container"
|
className="collapse-header-container"
|
||||||
onClick={() => setOpenObjects(!openObjects)}
|
onClick={() => setOpenObjects(!openObjects)}
|
||||||
>
|
>
|
||||||
|
@ -215,14 +224,13 @@ const Simulations: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
{openObjects &&
|
{openObjects &&
|
||||||
processes?.map((process, index) =>
|
processes?.map((process, index) => (
|
||||||
<section key={index}>
|
<section key={index}>
|
||||||
{process.map((event, index) => (
|
{process.map((event, index) => (
|
||||||
<List key={`${index}-${event.modelName}`} val={event} />
|
<List key={`${index}-${event.modelName}`} val={event} />
|
||||||
))}
|
))}
|
||||||
</section>
|
</section>
|
||||||
)
|
))}
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="compare-simulations-container">
|
<div className="compare-simulations-container">
|
||||||
|
@ -233,7 +241,7 @@ const Simulations: React.FC = () => {
|
||||||
Click '<span>Compare</span>' to review and analyze the layout
|
Click '<span>Compare</span>' to review and analyze the layout
|
||||||
differences between them.
|
differences between them.
|
||||||
</div>
|
</div>
|
||||||
<div className="input">
|
<div className="input" onClick={() => setComparePopUp(true)}>
|
||||||
<input type="button" value={"Compare"} className="submit" />
|
<input type="button" value={"Compare"} className="submit" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -258,6 +266,12 @@ const Simulations: React.FC = () => {
|
||||||
/>
|
/>
|
||||||
</RenderOverlay>
|
</RenderOverlay>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{comparePopUp && (
|
||||||
|
<RenderOverlay>
|
||||||
|
<ComparePopUp />
|
||||||
|
</RenderOverlay>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -57,13 +57,17 @@ const VersionHistory = () => {
|
||||||
<div className="version-history-header">
|
<div className="version-history-header">
|
||||||
<div className="version-history-title">Version History</div>
|
<div className="version-history-title">Version History</div>
|
||||||
<div className="version-history-icons">
|
<div className="version-history-icons">
|
||||||
<button className="icon add-icon" onClick={addNewVersion}>
|
<button
|
||||||
|
id="add-version"
|
||||||
|
className="icon add-icon"
|
||||||
|
onClick={addNewVersion}
|
||||||
|
>
|
||||||
<AddIcon />
|
<AddIcon />
|
||||||
</button>
|
</button>
|
||||||
<div className="icon kebab-icon">
|
<div id="version-kebab" className="icon kebab-icon">
|
||||||
<KebabIcon />
|
<KebabIcon />
|
||||||
</div>
|
</div>
|
||||||
<div className="icon close-icon">
|
<div id="version-close" className="icon close-icon">
|
||||||
<CloseIcon />
|
<CloseIcon />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -96,8 +100,9 @@ const VersionHistory = () => {
|
||||||
<div className="saved-versions-list">
|
<div className="saved-versions-list">
|
||||||
{versions.map((version, index) => (
|
{versions.map((version, index) => (
|
||||||
<button
|
<button
|
||||||
key={index}
|
key={version.versionName}
|
||||||
className="saved-version"
|
className="saved-version"
|
||||||
|
id={`${version.versionName}-${index}`}
|
||||||
onClick={() => handleSelectVersion(version)}
|
onClick={() => handleSelectVersion(version)}
|
||||||
>
|
>
|
||||||
<div className="version-name">{version.versionName}</div>
|
<div className="version-name">{version.versionName}</div>
|
||||||
|
|
|
@ -49,6 +49,7 @@ const FileMenu: React.FC = () => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
|
id="project-dropdown-button"
|
||||||
className="project-dropdowm-container"
|
className="project-dropdowm-container"
|
||||||
ref={containerRef}
|
ref={containerRef}
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
|
|
|
@ -17,6 +17,7 @@ const ModuleToggle: React.FC = () => {
|
||||||
return (
|
return (
|
||||||
<div className="module-toggle-container">
|
<div className="module-toggle-container">
|
||||||
<button
|
<button
|
||||||
|
id={"builder"}
|
||||||
className={`module-list ${activeModule === "builder" ? "active" : ""}`}
|
className={`module-list ${activeModule === "builder" ? "active" : ""}`}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setActiveModule("builder");
|
setActiveModule("builder");
|
||||||
|
@ -37,6 +38,7 @@ const ModuleToggle: React.FC = () => {
|
||||||
<div className="module">Builder</div>
|
<div className="module">Builder</div>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
|
id={"simulation"}
|
||||||
className={`module-list ${
|
className={`module-list ${
|
||||||
activeModule === "simulation" ? "active" : ""
|
activeModule === "simulation" ? "active" : ""
|
||||||
}`}
|
}`}
|
||||||
|
@ -59,6 +61,7 @@ const ModuleToggle: React.FC = () => {
|
||||||
<div className="module">Simulation</div>
|
<div className="module">Simulation</div>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
|
id={"visualization"}
|
||||||
className={`module-list ${
|
className={`module-list ${
|
||||||
activeModule === "visualization" ? "active" : ""
|
activeModule === "visualization" ? "active" : ""
|
||||||
}`}
|
}`}
|
||||||
|
@ -81,6 +84,7 @@ const ModuleToggle: React.FC = () => {
|
||||||
<div className="module">Visualization</div>
|
<div className="module">Visualization</div>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
|
id={"market"}
|
||||||
className={`module-list ${activeModule === "market" ? "active" : ""}`}
|
className={`module-list ${activeModule === "market" ? "active" : ""}`}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setActiveModule("market");
|
setActiveModule("market");
|
||||||
|
|
|
@ -39,10 +39,24 @@ import {
|
||||||
} from "../../store/visualization/useDroppedObjectsStore";
|
} from "../../store/visualization/useDroppedObjectsStore";
|
||||||
|
|
||||||
// Utility component
|
// Utility component
|
||||||
const ToolButton = ({ icon: Icon, active, onClick, tooltip }: any) => (
|
const ToolButton = ({
|
||||||
<button className={`tool-button ${active ? "active" : ""}`} onClick={onClick}>
|
toolKey,
|
||||||
|
toolId,
|
||||||
|
icon: Icon,
|
||||||
|
active,
|
||||||
|
onClick,
|
||||||
|
tooltip,
|
||||||
|
}: any) => (
|
||||||
|
<button
|
||||||
|
key={toolKey} // used in rendering list
|
||||||
|
id={toolId}
|
||||||
|
className={`tool-button ${active ? "active" : ""}`}
|
||||||
|
onClick={onClick}
|
||||||
|
>
|
||||||
<div className="tooltip">{tooltip}</div>
|
<div className="tooltip">{tooltip}</div>
|
||||||
|
<div className="tool" id={toolId}>
|
||||||
<Icon isActive={active} />
|
<Icon isActive={active} />
|
||||||
|
</div>
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -125,7 +139,7 @@ const Tools: React.FC = () => {
|
||||||
switch (tool) {
|
switch (tool) {
|
||||||
case "cursor":
|
case "cursor":
|
||||||
if (toggleView) {
|
if (toggleView) {
|
||||||
setToolMode('move');
|
setToolMode("move");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "draw-wall":
|
case "draw-wall":
|
||||||
|
@ -170,37 +184,33 @@ const Tools: React.FC = () => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (isPlaying && activeModule !== "simulation") {
|
|
||||||
return (
|
|
||||||
<button className="exitPlay" onClick={() => setIsPlaying(false)}>
|
|
||||||
X
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const renderBuilderTools = () => (
|
const renderBuilderTools = () => (
|
||||||
<>
|
<>
|
||||||
{!toggleThreeD && (
|
{!toggleThreeD && (
|
||||||
<div className="draw-tools">
|
<div className="draw-tools">
|
||||||
<ToolButton
|
<ToolButton
|
||||||
|
toolId="drawWall"
|
||||||
icon={WallIcon}
|
icon={WallIcon}
|
||||||
tooltip="draw wall (q)"
|
tooltip="draw wall (q)"
|
||||||
active={activeTool === "draw-wall"}
|
active={activeTool === "draw-wall"}
|
||||||
onClick={() => setActiveTool("draw-wall")}
|
onClick={() => setActiveTool("draw-wall")}
|
||||||
/>
|
/>
|
||||||
<ToolButton
|
<ToolButton
|
||||||
|
toolId="drawZone"
|
||||||
icon={ZoneIcon}
|
icon={ZoneIcon}
|
||||||
tooltip="draw zone (e)"
|
tooltip="draw zone (e)"
|
||||||
active={activeTool === "draw-zone"}
|
active={activeTool === "draw-zone"}
|
||||||
onClick={() => setActiveTool("draw-zone")}
|
onClick={() => setActiveTool("draw-zone")}
|
||||||
/>
|
/>
|
||||||
<ToolButton
|
<ToolButton
|
||||||
|
toolId="drawAisle"
|
||||||
icon={AsileIcon}
|
icon={AsileIcon}
|
||||||
tooltip="draw aisle (r)"
|
tooltip="draw aisle (r)"
|
||||||
active={activeTool === "draw-aisle"}
|
active={activeTool === "draw-aisle"}
|
||||||
onClick={() => setActiveTool("draw-aisle")}
|
onClick={() => setActiveTool("draw-aisle")}
|
||||||
/>
|
/>
|
||||||
<ToolButton
|
<ToolButton
|
||||||
|
toolId="drawFloor"
|
||||||
icon={FloorIcon}
|
icon={FloorIcon}
|
||||||
tooltip="draw floor (t)"
|
tooltip="draw floor (t)"
|
||||||
active={activeTool === "draw-floor"}
|
active={activeTool === "draw-floor"}
|
||||||
|
@ -210,6 +220,7 @@ const Tools: React.FC = () => {
|
||||||
)}
|
)}
|
||||||
<div className="draw-tools">
|
<div className="draw-tools">
|
||||||
<ToolButton
|
<ToolButton
|
||||||
|
toolId="measureScale"
|
||||||
icon={MeasureToolIcon}
|
icon={MeasureToolIcon}
|
||||||
tooltip="measure scale (m)"
|
tooltip="measure scale (m)"
|
||||||
active={activeTool === "measure"}
|
active={activeTool === "measure"}
|
||||||
|
@ -222,6 +233,7 @@ const Tools: React.FC = () => {
|
||||||
const renderSimulationTools = () => (
|
const renderSimulationTools = () => (
|
||||||
<div className="draw-tools">
|
<div className="draw-tools">
|
||||||
<ToolButton
|
<ToolButton
|
||||||
|
toolId="pen"
|
||||||
icon={PenIcon}
|
icon={PenIcon}
|
||||||
tooltip="pen"
|
tooltip="pen"
|
||||||
active={activeTool === "pen"}
|
active={activeTool === "pen"}
|
||||||
|
@ -233,6 +245,7 @@ const Tools: React.FC = () => {
|
||||||
const renderVisualizationTools = () => (
|
const renderVisualizationTools = () => (
|
||||||
<div className="draw-tools">
|
<div className="draw-tools">
|
||||||
<ToolButton
|
<ToolButton
|
||||||
|
toolId="saveTemplate"
|
||||||
icon={SaveTemplateIcon}
|
icon={SaveTemplateIcon}
|
||||||
tooltip="save template"
|
tooltip="save template"
|
||||||
active={false}
|
active={false}
|
||||||
|
@ -252,6 +265,7 @@ const Tools: React.FC = () => {
|
||||||
|
|
||||||
const renderModeSwitcher = () => (
|
const renderModeSwitcher = () => (
|
||||||
<button
|
<button
|
||||||
|
id="toggle-threed-button"
|
||||||
className={`toggle-threed-button${toggleThreeD ? " toggled" : ""}`}
|
className={`toggle-threed-button${toggleThreeD ? " toggled" : ""}`}
|
||||||
onClick={toggle2D3D}
|
onClick={toggle2D3D}
|
||||||
>
|
>
|
||||||
|
@ -309,6 +323,7 @@ const Tools: React.FC = () => {
|
||||||
activeSubTool === tool && (
|
activeSubTool === tool && (
|
||||||
<ToolButton
|
<ToolButton
|
||||||
key={tool}
|
key={tool}
|
||||||
|
toolId={tool}
|
||||||
icon={getIconByTool(tool)}
|
icon={getIconByTool(tool)}
|
||||||
tooltip={`${tool} (${getTooltipShortcut(tool)})`}
|
tooltip={`${tool} (${getTooltipShortcut(tool)})`}
|
||||||
active={activeTool === tool}
|
active={activeTool === tool}
|
||||||
|
@ -319,6 +334,8 @@ const Tools: React.FC = () => {
|
||||||
{/* Dropdown Menu */}
|
{/* Dropdown Menu */}
|
||||||
{activeModule !== "visualization" && (
|
{activeModule !== "visualization" && (
|
||||||
<button
|
<button
|
||||||
|
id="drop-down-button"
|
||||||
|
title="drop-down"
|
||||||
className="drop-down-option-button"
|
className="drop-down-option-button"
|
||||||
ref={dropdownRef}
|
ref={dropdownRef}
|
||||||
onClick={() => setOpenDrop(!openDrop)}
|
onClick={() => setOpenDrop(!openDrop)}
|
||||||
|
@ -329,6 +346,7 @@ const Tools: React.FC = () => {
|
||||||
{["cursor", "free-hand", "delete"].map((option) => (
|
{["cursor", "free-hand", "delete"].map((option) => (
|
||||||
<button
|
<button
|
||||||
key={option}
|
key={option}
|
||||||
|
id={`${option}-tool`}
|
||||||
className="option-list"
|
className="option-list"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setActiveTool(option);
|
setActiveTool(option);
|
||||||
|
@ -357,6 +375,7 @@ const Tools: React.FC = () => {
|
||||||
<div className="split"></div>
|
<div className="split"></div>
|
||||||
<div className="general-options">
|
<div className="general-options">
|
||||||
<ToolButton
|
<ToolButton
|
||||||
|
toolId="comment"
|
||||||
icon={CommentIcon}
|
icon={CommentIcon}
|
||||||
tooltip="comment"
|
tooltip="comment"
|
||||||
active={activeTool === "comment"}
|
active={activeTool === "comment"}
|
||||||
|
@ -364,6 +383,7 @@ const Tools: React.FC = () => {
|
||||||
/>
|
/>
|
||||||
{toggleThreeD && (
|
{toggleThreeD && (
|
||||||
<ToolButton
|
<ToolButton
|
||||||
|
toolId="play"
|
||||||
icon={PlayIcon}
|
icon={PlayIcon}
|
||||||
tooltip="play (ctrl + p)"
|
tooltip="play (ctrl + p)"
|
||||||
active={activeTool === "play"}
|
active={activeTool === "play"}
|
||||||
|
|
|
@ -24,7 +24,7 @@ const EyeDropInput: React.FC<EyeDropInputProps> = ({
|
||||||
<input disabled type="text" value={value}/>
|
<input disabled type="text" value={value}/>
|
||||||
{/* <input type="text" value={activeValue ?? "null"} disabled /> */}
|
{/* <input type="text" value={activeValue ?? "null"} disabled /> */}
|
||||||
{/* <input type="button" value="Clear" onClick={handleEyeDropClick}/> */}
|
{/* <input type="button" value="Clear" onClick={handleEyeDropClick}/> */}
|
||||||
<button className="eye-picker-button" onClick={handleEyeDropClick}>
|
<button id="eye-picker" className="eye-picker-button" onClick={handleEyeDropClick}>
|
||||||
<EyeDroperIcon isActive={false} />
|
<EyeDroperIcon isActive={false} />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -24,7 +24,10 @@ const InputToggle: React.FC<InputToggleProps> = ({
|
||||||
<label htmlFor={`toogle-input-${inputKey}`} className="label">
|
<label htmlFor={`toogle-input-${inputKey}`} className="label">
|
||||||
{label}
|
{label}
|
||||||
</label>
|
</label>
|
||||||
<div className={"check-box"} onClick={handleOnClick}
|
<button
|
||||||
|
id="check-box"
|
||||||
|
className={"check-box"}
|
||||||
|
onClick={handleOnClick}
|
||||||
style={{
|
style={{
|
||||||
background: value ? "var(--background-color-accent)" : "",
|
background: value ? "var(--background-color-accent)" : "",
|
||||||
outline: value ? "" : "1px solid var(--border-color)",
|
outline: value ? "" : "1px solid var(--border-color)",
|
||||||
|
@ -44,7 +47,7 @@ const InputToggle: React.FC<InputToggleProps> = ({
|
||||||
checked={value}
|
checked={value}
|
||||||
readOnly
|
readOnly
|
||||||
/>
|
/>
|
||||||
</div>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -16,7 +16,12 @@ const LabeledButton: React.FC<LabeledButtonProps> = ({
|
||||||
return (
|
return (
|
||||||
<div className="labeled-button-container">
|
<div className="labeled-button-container">
|
||||||
<div className="label">{label}</div>
|
<div className="label">{label}</div>
|
||||||
<button className="button" onClick={onClick} disabled={disabled}>
|
<button
|
||||||
|
id="label-button"
|
||||||
|
className="button"
|
||||||
|
onClick={onClick}
|
||||||
|
disabled={disabled}
|
||||||
|
>
|
||||||
{value}
|
{value}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -16,7 +16,10 @@ const DropdownItem = ({
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (!disabled) onClick();
|
if (!disabled) onClick();
|
||||||
}}
|
}}
|
||||||
style={{ cursor: disabled ? "not-allowed": "default", opacity: disabled ? 0.5 : 1 }}
|
style={{
|
||||||
|
cursor: disabled ? "not-allowed" : "default",
|
||||||
|
opacity: disabled ? 0.5 : 1,
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{label}
|
{label}
|
||||||
</div>
|
</div>
|
||||||
|
@ -127,13 +130,17 @@ const MultiLevelDropdown = ({
|
||||||
const disabledFieldsList = Object.values(allSelections)
|
const disabledFieldsList = Object.values(allSelections)
|
||||||
.filter(
|
.filter(
|
||||||
(sel) =>
|
(sel) =>
|
||||||
!(sel.name === selectedValue?.name && sel.fields === selectedValue?.fields)
|
!(
|
||||||
|
sel.name === selectedValue?.name &&
|
||||||
|
sel.fields === selectedValue?.fields
|
||||||
|
)
|
||||||
)
|
)
|
||||||
.map((sel) => `${sel.name}-${sel.fields}`);
|
.map((sel) => `${sel.name}-${sel.fields}`);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="multi-level-dropdown" ref={dropdownRef}>
|
<div className="multi-level-dropdown" ref={dropdownRef}>
|
||||||
<button
|
<button
|
||||||
|
id="multi-level-drop"
|
||||||
className={`dropdown-button ${open ? "open" : ""}`}
|
className={`dropdown-button ${open ? "open" : ""}`}
|
||||||
onClick={() => setOpen(!open)}
|
onClick={() => setOpen(!open)}
|
||||||
>
|
>
|
||||||
|
|
|
@ -25,6 +25,7 @@ const PreviewSelectionWithUpload: React.FC<PreviewSelectionWithUploadProps> = ({
|
||||||
{preview && (
|
{preview && (
|
||||||
<>
|
<>
|
||||||
<button
|
<button
|
||||||
|
id="preview-selection-button"
|
||||||
className="input-header-container"
|
className="input-header-container"
|
||||||
onClick={() => setShowPreview(!showPreview)}
|
onClick={() => setShowPreview(!showPreview)}
|
||||||
>
|
>
|
||||||
|
|
|
@ -55,7 +55,11 @@ const Search: React.FC<SearchProps> = ({
|
||||||
onBlur={handleBlur}
|
onBlur={handleBlur}
|
||||||
/>
|
/>
|
||||||
{inputValue && (
|
{inputValue && (
|
||||||
<button className="clear-button" onClick={handleClear}>
|
<button
|
||||||
|
id="clear-button"
|
||||||
|
className="clear-button"
|
||||||
|
onClick={handleClear}
|
||||||
|
>
|
||||||
<CloseIcon />
|
<CloseIcon />
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -123,6 +123,7 @@ const DropDownList: React.FC<DropDownListProps> = ({
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<button
|
<button
|
||||||
|
id="collapse-btn"
|
||||||
title="collapse-btn"
|
title="collapse-btn"
|
||||||
className="collapse-icon option"
|
className="collapse-icon option"
|
||||||
style={{ transform: isOpen ? "rotate(0deg)" : "rotate(-90deg)" }}
|
style={{ transform: isOpen ? "rotate(0deg)" : "rotate(-90deg)" }}
|
||||||
|
|
|
@ -12,7 +12,11 @@ import {
|
||||||
LockIcon,
|
LockIcon,
|
||||||
RemoveIcon,
|
RemoveIcon,
|
||||||
} from "../../icons/ExportCommonIcons";
|
} from "../../icons/ExportCommonIcons";
|
||||||
import { useFloorItems, useZoneAssetId, useZones } from "../../../store/builder/store";
|
import {
|
||||||
|
useFloorItems,
|
||||||
|
useZoneAssetId,
|
||||||
|
useZones,
|
||||||
|
} from "../../../store/builder/store";
|
||||||
import { zoneCameraUpdate } from "../../../services/visulization/zone/zoneCameraUpdation";
|
import { zoneCameraUpdate } from "../../../services/visulization/zone/zoneCameraUpdation";
|
||||||
import { setFloorItemApi } from "../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi";
|
import { setFloorItemApi } from "../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi";
|
||||||
|
|
||||||
|
@ -160,11 +164,14 @@ const List: React.FC<ListProps> = ({ items = [], remove }) => {
|
||||||
<React.Fragment key={`zone-${item.id}`}>
|
<React.Fragment key={`zone-${item.id}`}>
|
||||||
<li
|
<li
|
||||||
className="list-container"
|
className="list-container"
|
||||||
onClick={() => handleSelectZone(item.id)}
|
onClick={() => {
|
||||||
|
handleSelectZone(item.id);
|
||||||
|
toggleZoneExpansion(item.id);
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<div className={`list-item ${item.active ? "active" : ""}`}>
|
<div className={`list-item ${item.active ? "active" : ""}`}>
|
||||||
<div className="zone-header">
|
<div className="zone-header">
|
||||||
<button className="value">
|
<button className="value" id="zone-name">
|
||||||
<RenameInput
|
<RenameInput
|
||||||
value={item.name}
|
value={item.name}
|
||||||
onRename={handleZoneNameChange}
|
onRename={handleZoneNameChange}
|
||||||
|
@ -186,6 +193,7 @@ const List: React.FC<ListProps> = ({ items = [], remove }) => {
|
||||||
)}
|
)}
|
||||||
{item.assets && item.assets.length > 0 && (
|
{item.assets && item.assets.length > 0 && (
|
||||||
<button
|
<button
|
||||||
|
id="expand-btn"
|
||||||
className="expand-icon option"
|
className="expand-icon option"
|
||||||
onClick={() => toggleZoneExpansion(item.id)}
|
onClick={() => toggleZoneExpansion(item.id)}
|
||||||
>
|
>
|
||||||
|
@ -207,6 +215,7 @@ const List: React.FC<ListProps> = ({ items = [], remove }) => {
|
||||||
>
|
>
|
||||||
<div className="list-item">
|
<div className="list-item">
|
||||||
<button
|
<button
|
||||||
|
id={`${asset.name}-${asset.id}`}
|
||||||
className="value"
|
className="value"
|
||||||
onClick={() => handleAssetClick(asset)}
|
onClick={() => handleAssetClick(asset)}
|
||||||
>
|
>
|
||||||
|
@ -216,14 +225,14 @@ const List: React.FC<ListProps> = ({ items = [], remove }) => {
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
<div className="options-container">
|
<div className="options-container">
|
||||||
<button className="lock option">
|
<button className="lock option" id="lock-btn">
|
||||||
<LockIcon isLocked />
|
<LockIcon isLocked />
|
||||||
</button>
|
</button>
|
||||||
<button className="visibe option">
|
<button className="visibe option" id="visible-btn">
|
||||||
<EyeIcon isClosed />
|
<EyeIcon isClosed />
|
||||||
</button>
|
</button>
|
||||||
{remove && (
|
{remove && (
|
||||||
<button className="remove option">
|
<button className="remove option" id="remove-btn">
|
||||||
<RemoveIcon />
|
<RemoveIcon />
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -38,6 +38,7 @@ const LogList: React.FC = () => {
|
||||||
<div className="head">Log List</div>
|
<div className="head">Log List</div>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
|
id="close-btn"
|
||||||
title="close-btn"
|
title="close-btn"
|
||||||
className="close"
|
className="close"
|
||||||
onClick={() => setIsLogListVisible(false)}
|
onClick={() => setIsLogListVisible(false)}
|
||||||
|
@ -51,6 +52,7 @@ const LogList: React.FC = () => {
|
||||||
<div className="log-nav-wrapper">
|
<div className="log-nav-wrapper">
|
||||||
{["all", "info", "warning", "error"].map((type) => (
|
{["all", "info", "warning", "error"].map((type) => (
|
||||||
<button
|
<button
|
||||||
|
id="log-type"
|
||||||
title="log-type"
|
title="log-type"
|
||||||
key={type}
|
key={type}
|
||||||
className={`log-nav ${selectedTab === type ? "active" : ""}`}
|
className={`log-nav ${selectedTab === type ? "active" : ""}`}
|
||||||
|
@ -60,7 +62,12 @@ const LogList: React.FC = () => {
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<button title="clear-btn" className="clear-button" onClick={clear}>
|
<button
|
||||||
|
id="clean-btn"
|
||||||
|
title="clear-btn"
|
||||||
|
className="clear-button"
|
||||||
|
onClick={clear}
|
||||||
|
>
|
||||||
clear
|
clear
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -80,7 +87,9 @@ const LogList: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
))
|
))
|
||||||
) : (
|
) : (
|
||||||
<div className="no-log">There are no logs to display at the moment.</div>
|
<div className="no-log">
|
||||||
|
There are no logs to display at the moment.
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -11,7 +11,7 @@ interface EditWidgetOptionProps {
|
||||||
|
|
||||||
const EditWidgetOption: React.FC<EditWidgetOptionProps> = ({
|
const EditWidgetOption: React.FC<EditWidgetOptionProps> = ({
|
||||||
options,
|
options,
|
||||||
onClick
|
onClick,
|
||||||
}) => {
|
}) => {
|
||||||
const { top } = useTopData();
|
const { top } = useTopData();
|
||||||
const { left } = useLeftData();
|
const { left } = useLeftData();
|
||||||
|
@ -29,6 +29,7 @@ const EditWidgetOption: React.FC<EditWidgetOptionProps> = ({
|
||||||
<div className="context-menu-options">
|
<div className="context-menu-options">
|
||||||
{options.map((option, index) => (
|
{options.map((option, index) => (
|
||||||
<button
|
<button
|
||||||
|
id="content-menu-option-button"
|
||||||
className="option"
|
className="option"
|
||||||
key={`${index}-${option}`}
|
key={`${index}-${option}`}
|
||||||
onClick={() => onClick(option)}
|
onClick={() => onClick(option)}
|
||||||
|
|
|
@ -2,7 +2,9 @@ import React, { useState } from "react";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { ArrowIcon } from "../../icons/ExportCommonIcons";
|
import { ArrowIcon } from "../../icons/ExportCommonIcons";
|
||||||
import { toggleTheme } from "../../../utils/theme";
|
import { toggleTheme } from "../../../utils/theme";
|
||||||
import useVersionHistoryStore, { useShortcutStore } from "../../../store/builder/store";
|
import useVersionHistoryStore, {
|
||||||
|
useShortcutStore,
|
||||||
|
} from "../../../store/builder/store";
|
||||||
import { useSubModuleStore } from "../../../store/useModuleStore";
|
import { useSubModuleStore } from "../../../store/useModuleStore";
|
||||||
|
|
||||||
interface MenuBarProps {
|
interface MenuBarProps {
|
||||||
|
@ -102,6 +104,7 @@ const MenuBar: React.FC<MenuBarProps> = ({ setOpenMenu }) => {
|
||||||
// render menu item and sub menu item component
|
// render menu item and sub menu item component
|
||||||
const renderMenuItem = ({ label, shortcut, action }: MenuItem) => (
|
const renderMenuItem = ({ label, shortcut, action }: MenuItem) => (
|
||||||
<button
|
<button
|
||||||
|
id={label}
|
||||||
className="menu-item-container"
|
className="menu-item-container"
|
||||||
key={label}
|
key={label}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
@ -127,6 +130,7 @@ const MenuBar: React.FC<MenuBarProps> = ({ setOpenMenu }) => {
|
||||||
<div className="submenu">
|
<div className="submenu">
|
||||||
{submenu.map((item) => (
|
{submenu.map((item) => (
|
||||||
<button
|
<button
|
||||||
|
id={item.label}
|
||||||
key={item.label}
|
key={item.label}
|
||||||
className="submenu-item"
|
className="submenu-item"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
@ -148,6 +152,7 @@ const MenuBar: React.FC<MenuBarProps> = ({ setOpenMenu }) => {
|
||||||
<div className="menu-buttons">
|
<div className="menu-buttons">
|
||||||
{Object.entries(menus).map(([menu, items]) => (
|
{Object.entries(menus).map(([menu, items]) => (
|
||||||
<button
|
<button
|
||||||
|
id={menu}
|
||||||
key={menu}
|
key={menu}
|
||||||
className="menu-button-container"
|
className="menu-button-container"
|
||||||
onMouseEnter={() => setActiveMenu(menu)}
|
onMouseEnter={() => setActiveMenu(menu)}
|
||||||
|
@ -168,6 +173,7 @@ const MenuBar: React.FC<MenuBarProps> = ({ setOpenMenu }) => {
|
||||||
{items.map((item) =>
|
{items.map((item) =>
|
||||||
item.submenu ? (
|
item.submenu ? (
|
||||||
<button
|
<button
|
||||||
|
id={item.label}
|
||||||
key={item.label}
|
key={item.label}
|
||||||
className="menu-item-container"
|
className="menu-item-container"
|
||||||
onMouseEnter={() => setActiveSubMenu(item.label)}
|
onMouseEnter={() => setActiveSubMenu(item.label)}
|
||||||
|
@ -193,6 +199,7 @@ const MenuBar: React.FC<MenuBarProps> = ({ setOpenMenu }) => {
|
||||||
|
|
||||||
{/* Version History */}
|
{/* Version History */}
|
||||||
<button
|
<button
|
||||||
|
id="version-history"
|
||||||
className="menu-button-container"
|
className="menu-button-container"
|
||||||
onMouseEnter={() => setActiveMenu("Version history")}
|
onMouseEnter={() => setActiveMenu("Version history")}
|
||||||
onMouseLeave={() => {
|
onMouseLeave={() => {
|
||||||
|
@ -209,6 +216,7 @@ const MenuBar: React.FC<MenuBarProps> = ({ setOpenMenu }) => {
|
||||||
|
|
||||||
{/* Theme */}
|
{/* Theme */}
|
||||||
<button
|
<button
|
||||||
|
id="theme-btn"
|
||||||
className="menu-button-container"
|
className="menu-button-container"
|
||||||
onMouseEnter={() => setActiveMenu("Theme")}
|
onMouseEnter={() => setActiveMenu("Theme")}
|
||||||
onMouseLeave={() => {
|
onMouseLeave={() => {
|
||||||
|
@ -223,7 +231,11 @@ const MenuBar: React.FC<MenuBarProps> = ({ setOpenMenu }) => {
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{/* Log out */}
|
{/* Log out */}
|
||||||
<button className="menu-button-container" onClick={handleLogout}>
|
<button
|
||||||
|
id="logout"
|
||||||
|
className="menu-button-container"
|
||||||
|
onClick={handleLogout}
|
||||||
|
>
|
||||||
<div className="menu-button">Log out</div>
|
<div className="menu-button">Log out</div>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -82,6 +82,7 @@ const AssetDetailsCard: React.FC<AssetDetailsCardInterface> = ({
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
|
id="expand-buttton"
|
||||||
className="expand-button"
|
className="expand-button"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setMoreDetails(!moreDetails);
|
setMoreDetails(!moreDetails);
|
||||||
|
|
|
@ -11,6 +11,7 @@ import {
|
||||||
DailyProductionIcon,
|
DailyProductionIcon,
|
||||||
EndIcon,
|
EndIcon,
|
||||||
ExpandIcon,
|
ExpandIcon,
|
||||||
|
EyeCloseIcon,
|
||||||
HourlySimulationIcon,
|
HourlySimulationIcon,
|
||||||
InfoIcon,
|
InfoIcon,
|
||||||
MonthlyROI,
|
MonthlyROI,
|
||||||
|
@ -30,6 +31,7 @@ const SimulationPlayer: React.FC = () => {
|
||||||
const sliderRef = useRef<HTMLDivElement>(null);
|
const sliderRef = useRef<HTMLDivElement>(null);
|
||||||
const [expand, setExpand] = useState(true);
|
const [expand, setExpand] = useState(true);
|
||||||
const [playSimulation, setPlaySimulation] = useState(false);
|
const [playSimulation, setPlaySimulation] = useState(false);
|
||||||
|
const [hidePlayer, setHidePlayer] = useState(false);
|
||||||
|
|
||||||
const { speed, setSpeed } = useAnimationPlaySpeed();
|
const { speed, setSpeed } = useAnimationPlaySpeed();
|
||||||
const { setIsPlaying } = usePlayButtonStore();
|
const { setIsPlaying } = usePlayButtonStore();
|
||||||
|
@ -161,10 +163,10 @@ const SimulationPlayer: React.FC = () => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="simulation-player-wrapper">
|
<div className={`simulation-player-wrapper${hidePlayer ? " hide" : ""}`}>
|
||||||
<div className={`simulation-player-container ${expand ? "open" : ""}`}>
|
<div className={`simulation-player-container ${expand ? "open" : ""}`}>
|
||||||
<div className="controls-container">
|
<div className="controls-container">
|
||||||
{subModule === "analysis" && (
|
{!hidePlayer && subModule === "analysis" && (
|
||||||
<div className="production-details">
|
<div className="production-details">
|
||||||
{/* hourlySimulation */}
|
{/* hourlySimulation */}
|
||||||
<div className="hourly-wrapper production-wrapper">
|
<div className="hourly-wrapper production-wrapper">
|
||||||
|
@ -213,7 +215,7 @@ const SimulationPlayer: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{subModule !== "analysis" && (
|
{!hidePlayer && subModule !== "analysis" && (
|
||||||
<div className="header">
|
<div className="header">
|
||||||
<InfoIcon />
|
<InfoIcon />
|
||||||
{playSimulation
|
{playSimulation
|
||||||
|
@ -222,7 +224,9 @@ const SimulationPlayer: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="controls-wrapper">
|
<div className="controls-wrapper">
|
||||||
|
{!hidePlayer && (
|
||||||
<button
|
<button
|
||||||
|
id="simulation-reset-button"
|
||||||
className="simulation-button-container"
|
className="simulation-button-container"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
handleReset();
|
handleReset();
|
||||||
|
@ -231,7 +235,10 @@ const SimulationPlayer: React.FC = () => {
|
||||||
<ResetIcon />
|
<ResetIcon />
|
||||||
Reset
|
Reset
|
||||||
</button>
|
</button>
|
||||||
|
)}
|
||||||
|
{!hidePlayer && (
|
||||||
<button
|
<button
|
||||||
|
id="simulation-play-button"
|
||||||
className="simulation-button-container"
|
className="simulation-button-container"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
handlePlayStop();
|
handlePlayStop();
|
||||||
|
@ -240,17 +247,30 @@ const SimulationPlayer: React.FC = () => {
|
||||||
<PlayStopIcon />
|
<PlayStopIcon />
|
||||||
{playSimulation ? "Play" : "Stop"}
|
{playSimulation ? "Play" : "Stop"}
|
||||||
</button>
|
</button>
|
||||||
|
)}
|
||||||
<button
|
<button
|
||||||
|
id="simulation-reset-button"
|
||||||
className="simulation-button-container"
|
className="simulation-button-container"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
handleExit();
|
handleExit();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ExitIcon />
|
<ExitIcon />
|
||||||
Exit
|
{!hidePlayer && "Exit"}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
id="simulation-reset-button"
|
||||||
|
className="simulation-button-container"
|
||||||
|
onClick={() => {
|
||||||
|
setHidePlayer(!hidePlayer);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<EyeCloseIcon />
|
||||||
|
{!hidePlayer && "Hide"}
|
||||||
</button>
|
</button>
|
||||||
{subModule === "analysis" && (
|
{subModule === "analysis" && (
|
||||||
<button
|
<button
|
||||||
|
id="simulation-expand-button"
|
||||||
className="expand-icon-container"
|
className="expand-icon-container"
|
||||||
onClick={() => setExpand(!expand)}
|
onClick={() => setExpand(!expand)}
|
||||||
>
|
>
|
||||||
|
@ -259,6 +279,7 @@ const SimulationPlayer: React.FC = () => {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{!hidePlayer && (
|
||||||
<div className="progresser-wrapper">
|
<div className="progresser-wrapper">
|
||||||
<div className="time-displayer">
|
<div className="time-displayer">
|
||||||
<div className="start-time-wrappper">
|
<div className="start-time-wrappper">
|
||||||
|
@ -328,6 +349,7 @@ const SimulationPlayer: React.FC = () => {
|
||||||
<div className="custom-slider-wrapper">
|
<div className="custom-slider-wrapper">
|
||||||
<div className="custom-slider">
|
<div className="custom-slider">
|
||||||
<button
|
<button
|
||||||
|
id="slider-handle"
|
||||||
className={`slider-handle ${
|
className={`slider-handle ${
|
||||||
isDragging ? "dragging" : ""
|
isDragging ? "dragging" : ""
|
||||||
}`}
|
}`}
|
||||||
|
@ -351,7 +373,8 @@ const SimulationPlayer: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{subModule === "analysis" && (
|
)}
|
||||||
|
{!hidePlayer && subModule === "analysis" && (
|
||||||
<div className="processDisplayer">
|
<div className="processDisplayer">
|
||||||
<div className="start-displayer timmer">00:00</div>
|
<div className="start-displayer timmer">00:00</div>
|
||||||
<div className="end-displayer timmer">24:00</div>
|
<div className="end-displayer timmer">24:00</div>
|
||||||
|
|
|
@ -3,7 +3,6 @@ import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader';
|
||||||
import gsap from 'gsap';
|
import gsap from 'gsap';
|
||||||
import * as THREE from 'three';
|
import * as THREE from 'three';
|
||||||
import * as CONSTANTS from '../../../types/world/worldConstants';
|
import * as CONSTANTS from '../../../types/world/worldConstants';
|
||||||
import { toast } from 'react-toastify';
|
|
||||||
import * as Types from "../../../types/world/worldTypes";
|
import * as Types from "../../../types/world/worldTypes";
|
||||||
import { initializeDB, retrieveGLTF, storeGLTF } from '../../../utils/indexDB/idbUtils';
|
import { initializeDB, retrieveGLTF, storeGLTF } from '../../../utils/indexDB/idbUtils';
|
||||||
import { getCamera } from '../../../services/factoryBuilder/camera/getCameraApi';
|
import { getCamera } from '../../../services/factoryBuilder/camera/getCameraApi';
|
||||||
|
@ -91,7 +90,7 @@ async function loadInitialFloorItems(
|
||||||
},
|
},
|
||||||
undefined,
|
undefined,
|
||||||
(error) => {
|
(error) => {
|
||||||
toast.error(`[IndexedDB] Error loading ${item.modelName}:`);
|
echo.error(`[IndexedDB] Error loading ${item.modelName}:`);
|
||||||
URL.revokeObjectURL(blobUrl);
|
URL.revokeObjectURL(blobUrl);
|
||||||
resolve();
|
resolve();
|
||||||
}
|
}
|
||||||
|
@ -111,7 +110,7 @@ async function loadInitialFloorItems(
|
||||||
},
|
},
|
||||||
undefined,
|
undefined,
|
||||||
(error) => {
|
(error) => {
|
||||||
toast.error(`[Backend] Error loading ${item.modelName}:`);
|
echo.error(`[Backend] Error loading ${item.modelName}:`);
|
||||||
resolve();
|
resolve();
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -33,7 +33,7 @@ async function loadInitialWallItems(
|
||||||
|
|
||||||
const loadedWallItems = await Promise.all(items.map(async (item: Types.WallItem) => {
|
const loadedWallItems = await Promise.all(items.map(async (item: Types.WallItem) => {
|
||||||
// Check THREE.js cache first
|
// Check THREE.js cache first
|
||||||
const cachedModel = THREE.Cache.get(item.modelName!);
|
const cachedModel = THREE.Cache.get(item.modelfileID!);
|
||||||
if (cachedModel) {
|
if (cachedModel) {
|
||||||
return processModel(cachedModel, item);
|
return processModel(cachedModel, item);
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,7 @@ async function loadInitialWallItems(
|
||||||
return new Promise<Types.WallItem>((resolve) => {
|
return new Promise<Types.WallItem>((resolve) => {
|
||||||
loader.load(blobUrl, (gltf) => {
|
loader.load(blobUrl, (gltf) => {
|
||||||
URL.revokeObjectURL(blobUrl);
|
URL.revokeObjectURL(blobUrl);
|
||||||
THREE.Cache.add(item.modelName!, gltf);
|
THREE.Cache.add(item.modelfileID!, gltf);
|
||||||
resolve(processModel(gltf, item));
|
resolve(processModel(gltf, item));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -58,8 +58,8 @@ async function loadInitialWallItems(
|
||||||
try {
|
try {
|
||||||
// Cache the model
|
// Cache the model
|
||||||
const modelBlob = await fetch(modelUrl).then((res) => res.blob());
|
const modelBlob = await fetch(modelUrl).then((res) => res.blob());
|
||||||
await storeGLTF(item.modelName!, modelBlob);
|
await storeGLTF(item.modelfileID!, modelBlob);
|
||||||
THREE.Cache.add(item.modelName!, gltf);
|
THREE.Cache.add(item.modelfileID!, gltf);
|
||||||
resolve(processModel(gltf, item));
|
resolve(processModel(gltf, item));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to cache model:', error);
|
console.error('Failed to cache model:', error);
|
||||||
|
|
|
@ -0,0 +1,125 @@
|
||||||
|
import * as THREE from "three"
|
||||||
|
import { useEffect } from 'react'
|
||||||
|
import { getFloorAssets } from '../../../services/factoryBuilder/assest/floorAsset/getFloorItemsApi';
|
||||||
|
import { useLoadingProgress } from '../../../store/builder/store';
|
||||||
|
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
|
||||||
|
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";
|
||||||
|
import { FloorItems } from "../../../types/world/worldTypes";
|
||||||
|
import { useAssetsStore } from "../../../store/builder/useAssetStore";
|
||||||
|
import Models from "./models/models";
|
||||||
|
import { useGLTF } from "@react-three/drei";
|
||||||
|
|
||||||
|
const gltfLoaderWorker = new Worker(
|
||||||
|
new URL(
|
||||||
|
"../../../services/factoryBuilder/webWorkers/gltfLoaderWorker.js",
|
||||||
|
import.meta.url
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
function AssetsGroup() {
|
||||||
|
const { setLoadingProgress } = useLoadingProgress();
|
||||||
|
const { setAssets } = useAssetsStore();
|
||||||
|
|
||||||
|
const loader = new GLTFLoader();
|
||||||
|
const dracoLoader = new DRACOLoader();
|
||||||
|
|
||||||
|
dracoLoader.setDecoderPath(
|
||||||
|
"https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/"
|
||||||
|
);
|
||||||
|
loader.setDRACOLoader(dracoLoader);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const email = localStorage.getItem("email");
|
||||||
|
const organization = email!.split("@")[1].split(".")[0];
|
||||||
|
|
||||||
|
let totalAssets = 0;
|
||||||
|
let loadedAssets = 0;
|
||||||
|
|
||||||
|
const updateLoadingProgress = (progress: number) => {
|
||||||
|
if (progress < 100) {
|
||||||
|
setLoadingProgress(progress);
|
||||||
|
} else if (progress === 100) {
|
||||||
|
setTimeout(() => {
|
||||||
|
setLoadingProgress(100);
|
||||||
|
setTimeout(() => {
|
||||||
|
setLoadingProgress(0);
|
||||||
|
}, 1500);
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
getFloorAssets(organization).then((data) => {
|
||||||
|
if (data.length > 0) {
|
||||||
|
const uniqueItems = (data as FloorItems).filter((item, index, self) => index === self.findIndex((t) => t.modelfileID === item.modelfileID));
|
||||||
|
totalAssets = uniqueItems.length;
|
||||||
|
if (totalAssets === 0) {
|
||||||
|
updateLoadingProgress(100);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
gltfLoaderWorker.postMessage({ floorItems: uniqueItems });
|
||||||
|
} else {
|
||||||
|
gltfLoaderWorker.postMessage({ floorItems: [] });
|
||||||
|
updateLoadingProgress(100);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
gltfLoaderWorker.onmessage = async (event) => {
|
||||||
|
if (event.data.message === "gltfLoaded" && event.data.modelBlob) {
|
||||||
|
const blobUrl = URL.createObjectURL(event.data.modelBlob);
|
||||||
|
loader.load(blobUrl, (gltf) => {
|
||||||
|
URL.revokeObjectURL(blobUrl);
|
||||||
|
THREE.Cache.remove(blobUrl);
|
||||||
|
THREE.Cache.add(event.data.modelID, gltf);
|
||||||
|
|
||||||
|
loadedAssets++;
|
||||||
|
const progress = Math.round((loadedAssets / totalAssets) * 100);
|
||||||
|
updateLoadingProgress(progress);
|
||||||
|
|
||||||
|
if (loadedAssets === totalAssets) {
|
||||||
|
const assets: Asset[] = [];
|
||||||
|
getFloorAssets(organization).then((data: FloorItems) => {
|
||||||
|
data.forEach((item) => {
|
||||||
|
if (item.eventData) {
|
||||||
|
assets.push({
|
||||||
|
modelUuid: item.modelUuid,
|
||||||
|
modelName: item.modelName,
|
||||||
|
assetId: item.modelfileID,
|
||||||
|
position: item.position,
|
||||||
|
rotation: [item.rotation.x, item.rotation.y, item.rotation.z],
|
||||||
|
isLocked: item.isLocked,
|
||||||
|
isCollidable: false,
|
||||||
|
isVisible: item.isVisible,
|
||||||
|
opacity: 1,
|
||||||
|
eventData: item.eventData
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
assets.push({
|
||||||
|
modelUuid: item.modelUuid,
|
||||||
|
modelName: item.modelName,
|
||||||
|
assetId: item.modelfileID,
|
||||||
|
position: item.position,
|
||||||
|
rotation: [item.rotation.x, item.rotation.y, item.rotation.z],
|
||||||
|
isLocked: item.isLocked,
|
||||||
|
isCollidable: false,
|
||||||
|
isVisible: item.isVisible,
|
||||||
|
opacity: 1,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
setAssets(assets);
|
||||||
|
})
|
||||||
|
updateLoadingProgress(100);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Models />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AssetsGroup;
|
|
@ -0,0 +1,82 @@
|
||||||
|
import { Outlines } from '@react-three/drei';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import { retrieveGLTF, storeGLTF } from '../../../../../utils/indexDB/idbUtils';
|
||||||
|
import { GLTF, GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
|
||||||
|
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader';
|
||||||
|
import * as THREE from 'three';
|
||||||
|
|
||||||
|
function Model({ asset }: { asset: Asset }) {
|
||||||
|
const url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`;
|
||||||
|
const [gltfScene, setGltfScene] = useState<GLTF | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const loader = new GLTFLoader();
|
||||||
|
const dracoLoader = new DRACOLoader();
|
||||||
|
|
||||||
|
dracoLoader.setDecoderPath('https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/');
|
||||||
|
loader.setDRACOLoader(dracoLoader);
|
||||||
|
const loadModel = async () => {
|
||||||
|
try {
|
||||||
|
const cachedModel = THREE.Cache.get(asset.assetId!);
|
||||||
|
if (cachedModel) {
|
||||||
|
setGltfScene(cachedModel);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check IndexedDB
|
||||||
|
const indexedDBModel = await retrieveGLTF(asset.assetId!);
|
||||||
|
if (indexedDBModel) {
|
||||||
|
const blobUrl = URL.createObjectURL(indexedDBModel);
|
||||||
|
loader.load(blobUrl, (gltf) => {
|
||||||
|
URL.revokeObjectURL(blobUrl);
|
||||||
|
THREE.Cache.remove(blobUrl);
|
||||||
|
THREE.Cache.add(asset.assetId!, gltf);
|
||||||
|
setGltfScene(gltf);
|
||||||
|
},
|
||||||
|
undefined,
|
||||||
|
(error) => {
|
||||||
|
echo.error(`[IndexedDB] Error loading ${asset.modelName}:`);
|
||||||
|
URL.revokeObjectURL(blobUrl);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch from Backend
|
||||||
|
const modelUrl = `${url_Backend_dwinzo}/api/v2/AssetFile/${asset.assetId!}`;
|
||||||
|
loader.load(modelUrl, async (gltf) => {
|
||||||
|
const modelBlob = await fetch(modelUrl).then((res) => res.blob());
|
||||||
|
await storeGLTF(asset.assetId!, modelBlob);
|
||||||
|
THREE.Cache.add(asset.assetId!, gltf);
|
||||||
|
setGltfScene(gltf);
|
||||||
|
},
|
||||||
|
undefined,
|
||||||
|
(error) => {
|
||||||
|
echo.error(`[Backend] Error loading ${asset.modelName}:`);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Failed to load model:", asset.assetId, err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
loadModel();
|
||||||
|
|
||||||
|
}, [asset.assetId]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{gltfScene &&
|
||||||
|
<group
|
||||||
|
position={asset.position}
|
||||||
|
rotation={asset.rotation}
|
||||||
|
visible={asset.isVisible}
|
||||||
|
>
|
||||||
|
<primitive object={gltfScene.scene.clone()} />
|
||||||
|
</group>
|
||||||
|
}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Model;
|
|
@ -0,0 +1,17 @@
|
||||||
|
import React from 'react'
|
||||||
|
import { useAssetsStore } from '../../../../store/builder/useAssetStore';
|
||||||
|
import Model from './model/model';
|
||||||
|
|
||||||
|
function Models() {
|
||||||
|
const { assets } = useAssetsStore();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{assets.map((asset) =>
|
||||||
|
<Model key={asset.modelUuid} asset={asset} />
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Models
|
|
@ -47,6 +47,7 @@ import MeasurementTool from "../scene/tools/measurementTool";
|
||||||
import NavMesh from "../simulation/vehicle/navMesh/navMesh";
|
import NavMesh from "../simulation/vehicle/navMesh/navMesh";
|
||||||
import CalculateAreaGroup from "./groups/calculateAreaGroup";
|
import CalculateAreaGroup from "./groups/calculateAreaGroup";
|
||||||
import LayoutImage from "./layout/layoutImage";
|
import LayoutImage from "./layout/layoutImage";
|
||||||
|
import AssetsGroup from "./assetGroup/assetsGroup";
|
||||||
|
|
||||||
export default function Builder() {
|
export default function Builder() {
|
||||||
const state = useThree<Types.ThreeState>(); // Importing the state from the useThree hook, which contains the scene, camera, and other Three.js elements.
|
const state = useThree<Types.ThreeState>(); // Importing the state from the useThree hook, which contains the scene, camera, and other Three.js elements.
|
||||||
|
@ -299,6 +300,8 @@ export default function Builder() {
|
||||||
anglesnappedPoint={anglesnappedPoint}
|
anglesnappedPoint={anglesnappedPoint}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{/* <AssetsGroup /> */}
|
||||||
|
|
||||||
<MeasurementTool />
|
<MeasurementTool />
|
||||||
|
|
||||||
<CalculateAreaGroup />
|
<CalculateAreaGroup />
|
||||||
|
|
|
@ -78,7 +78,7 @@ export default async function assetManager(
|
||||||
},
|
},
|
||||||
undefined,
|
undefined,
|
||||||
(error) => {
|
(error) => {
|
||||||
toast.error(`[IndexedDB] Error loading ${item.modelName}:`);
|
echo.error(`[IndexedDB] Error loading ${item.modelName}:`);
|
||||||
resolve();
|
resolve();
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -97,7 +97,7 @@ export default async function assetManager(
|
||||||
},
|
},
|
||||||
undefined,
|
undefined,
|
||||||
(error) => {
|
(error) => {
|
||||||
toast.error(`[Backend] Error loading ${item.modelName}:`);
|
echo.error(`[Backend] Error loading ${item.modelName}:`);
|
||||||
resolve();
|
resolve();
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -28,7 +28,7 @@ function updateDistanceText(
|
||||||
const textMesh = text as THREE.Mesh;
|
const textMesh = text as THREE.Mesh;
|
||||||
if (textMesh.userData[0][1] === linePoints[0][1] && textMesh.userData[1][1] === linePoints[1][1]) {
|
if (textMesh.userData[0][1] === linePoints[0][1] && textMesh.userData[1][1] === linePoints[1][1]) {
|
||||||
textMesh.position.set(position.x, 1, position.z);
|
textMesh.position.set(position.x, 1, position.z);
|
||||||
const className = `Distance line-${textMesh.userData[0][1]}_${textMesh.userData[1][1]}_${linePoints[0][2]}`;
|
const className = `distance line-${textMesh.userData[0][1]}_${textMesh.userData[1][1]}_${linePoints[0][2]}`;
|
||||||
const element = document.getElementsByClassName(className)[0] as HTMLElement;
|
const element = document.getElementsByClassName(className)[0] as HTMLElement;
|
||||||
if (element) {
|
if (element) {
|
||||||
element.innerHTML = `${distance} m`;
|
element.innerHTML = `${distance} m`;
|
||||||
|
|
|
@ -104,7 +104,7 @@ const FloorItemsGroup = ({
|
||||||
updateLoadingProgress(100);
|
updateLoadingProgress(100);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
gltfLoaderWorker.postMessage({ floorItems: data });
|
gltfLoaderWorker.postMessage({ floorItems: uniqueItems });
|
||||||
} else {
|
} else {
|
||||||
gltfLoaderWorker.postMessage({ floorItems: [] });
|
gltfLoaderWorker.postMessage({ floorItems: [] });
|
||||||
loadInitialFloorItems(
|
loadInitialFloorItems(
|
||||||
|
|
|
@ -7,7 +7,7 @@ interface CollabUserIconProps {
|
||||||
userName: string;
|
userName: string;
|
||||||
userImage?: string;
|
userImage?: string;
|
||||||
color: string;
|
color: string;
|
||||||
id: string,
|
id: string;
|
||||||
position?: {
|
position?: {
|
||||||
x: number;
|
x: number;
|
||||||
y: number;
|
y: number;
|
||||||
|
@ -39,6 +39,7 @@ const CollabUserIcon: React.FC<CollabUserIconProps> = ({
|
||||||
return (
|
return (
|
||||||
<div className="collab-user-live-container">
|
<div className="collab-user-live-container">
|
||||||
<button
|
<button
|
||||||
|
id="live-user-button"
|
||||||
className="user-image-container"
|
className="user-image-container"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (!position || !rotation || !target) return;
|
if (!position || !rotation || !target) return;
|
||||||
|
|
|
@ -65,7 +65,10 @@ const AssetPreview: React.FC<AssetPreviewProps> = ({
|
||||||
>
|
>
|
||||||
<Suspense fallback={<Ui />}>
|
<Suspense fallback={<Ui />}>
|
||||||
{selectedCard.assetName && modelUrl && (
|
{selectedCard.assetName && modelUrl && (
|
||||||
<GltfLoader fromServer={modelUrl} assetId={selectedCard?.AssetID} />
|
<GltfLoader
|
||||||
|
fromServer={modelUrl}
|
||||||
|
assetId={selectedCard?.AssetID}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
<OrbitControls minPolarAngle={0} maxPolarAngle={Math.PI / 2} />
|
<OrbitControls minPolarAngle={0} maxPolarAngle={Math.PI / 2} />
|
||||||
<ContactShadows
|
<ContactShadows
|
||||||
|
@ -114,7 +117,11 @@ const AssetPreview: React.FC<AssetPreviewProps> = ({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* close button */}
|
{/* close button */}
|
||||||
<button className="closeButton" onClick={() => setSelectedCard(null)}>
|
<button
|
||||||
|
id="asset-back-buttton"
|
||||||
|
className="closeButton"
|
||||||
|
onClick={() => setSelectedCard(null)}
|
||||||
|
>
|
||||||
{`<-back`}
|
{`<-back`}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -7,7 +7,6 @@ import {
|
||||||
VerifiedIcon,
|
VerifiedIcon,
|
||||||
} from "../../components/icons/marketPlaceIcons";
|
} from "../../components/icons/marketPlaceIcons";
|
||||||
|
|
||||||
|
|
||||||
interface CardProps {
|
interface CardProps {
|
||||||
assetName: string;
|
assetName: string;
|
||||||
uploadedOn: number;
|
uploadedOn: number;
|
||||||
|
@ -16,8 +15,8 @@ interface CardProps {
|
||||||
views: number;
|
views: number;
|
||||||
image: string;
|
image: string;
|
||||||
description: string;
|
description: string;
|
||||||
AssetID: string
|
AssetID: string;
|
||||||
modelUrl: string
|
modelUrl: string;
|
||||||
onSelectCard: (cardData: {
|
onSelectCard: (cardData: {
|
||||||
assetName: string;
|
assetName: string;
|
||||||
uploadedOn: number;
|
uploadedOn: number;
|
||||||
|
@ -40,14 +39,19 @@ const Card: React.FC<CardProps> = ({
|
||||||
AssetID,
|
AssetID,
|
||||||
modelUrl,
|
modelUrl,
|
||||||
onSelectCard,
|
onSelectCard,
|
||||||
|
|
||||||
}) => {
|
}) => {
|
||||||
const handleCardSelect = () => {
|
const handleCardSelect = () => {
|
||||||
console.log('assetName: ', assetName);
|
console.log("assetName: ", assetName);
|
||||||
console.log('AssetID: ', AssetID);
|
console.log("AssetID: ", AssetID);
|
||||||
|
|
||||||
onSelectCard({
|
onSelectCard({
|
||||||
assetName, uploadedOn, price, rating, views, description, AssetID
|
assetName,
|
||||||
|
uploadedOn,
|
||||||
|
price,
|
||||||
|
rating,
|
||||||
|
views,
|
||||||
|
description,
|
||||||
|
AssetID,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -89,12 +93,8 @@ const Card: React.FC<CardProps> = ({
|
||||||
<div className="stars-container">
|
<div className="stars-container">
|
||||||
<div className="stars-wrapper">
|
<div className="stars-wrapper">
|
||||||
{[...Array(5)].map((_, index) => (
|
{[...Array(5)].map((_, index) => (
|
||||||
<React.Fragment key={index} >
|
<React.Fragment key={index}>
|
||||||
{index < 3 ? (
|
{index < 3 ? <FilledStarsIconSmall /> : <StarsIconSmall />}
|
||||||
<FilledStarsIconSmall />
|
|
||||||
) : (
|
|
||||||
<StarsIconSmall />
|
|
||||||
)}
|
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
@ -102,7 +102,11 @@ const Card: React.FC<CardProps> = ({
|
||||||
₹ {price}/<span>unit</span>
|
₹ {price}/<span>unit</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button className="buy-now-button" onClick={handleCardSelect}>
|
<button
|
||||||
|
id={`${AssetID}-asset-buy`}
|
||||||
|
className="buy-now-button"
|
||||||
|
onClick={handleCardSelect}
|
||||||
|
>
|
||||||
Buy now
|
Buy now
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -39,10 +39,14 @@ const FilterSearch: React.FC<ModelsProps> = ({
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (activeOption === "Alphabet ascending") {
|
if (activeOption === "Alphabet ascending") {
|
||||||
const ascending = [...models].sort((a, b) => a.filename.localeCompare(b.filename));
|
const ascending = [...models].sort((a, b) =>
|
||||||
|
a.filename.localeCompare(b.filename)
|
||||||
|
);
|
||||||
setModels(ascending);
|
setModels(ascending);
|
||||||
} else if (activeOption === "Alphabet descending") {
|
} else if (activeOption === "Alphabet descending") {
|
||||||
const descending = [...models].sort((a, b) => b.filename.localeCompare(a.filename));
|
const descending = [...models].sort((a, b) =>
|
||||||
|
b.filename.localeCompare(a.filename)
|
||||||
|
);
|
||||||
setModels(descending);
|
setModels(descending);
|
||||||
}
|
}
|
||||||
}, [activeOption, models, setModels]);
|
}, [activeOption, models, setModels]);
|
||||||
|
@ -75,6 +79,7 @@ const FilterSearch: React.FC<ModelsProps> = ({
|
||||||
<div className="stars">
|
<div className="stars">
|
||||||
{[0, 1, 2, 3, 4].map((i) => (
|
{[0, 1, 2, 3, 4].map((i) => (
|
||||||
<button
|
<button
|
||||||
|
id={`${i + 1}-star-button`}
|
||||||
key={i}
|
key={i}
|
||||||
onClick={() => handleStarClick(i)}
|
onClick={() => handleStarClick(i)}
|
||||||
className={`star-wrapper ${i < rating ? "filled" : "empty"}`}
|
className={`star-wrapper ${i < rating ? "filled" : "empty"}`}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { useKeyboardControls } from "@react-three/drei";
|
||||||
import switchToThirdPerson from "./switchToThirdPerson";
|
import switchToThirdPerson from "./switchToThirdPerson";
|
||||||
import switchToFirstPerson from "./switchToFirstPerson";
|
import switchToFirstPerson from "./switchToFirstPerson";
|
||||||
import { detectModifierKeys } from "../../../utils/shortcutkeys/detectModifierKeys";
|
import { detectModifierKeys } from "../../../utils/shortcutkeys/detectModifierKeys";
|
||||||
|
import { firstPersonCamera } from "./firstPersonCamera";
|
||||||
|
|
||||||
const CamMode: React.FC = () => {
|
const CamMode: React.FC = () => {
|
||||||
const { camMode, setCamMode } = useCamMode();
|
const { camMode, setCamMode } = useCamMode();
|
||||||
|
@ -65,26 +66,14 @@ const CamMode: React.FC = () => {
|
||||||
const keyCombination = detectModifierKeys(event);
|
const keyCombination = detectModifierKeys(event);
|
||||||
|
|
||||||
if (keyCombination === "/" && !isTransitioning && !toggleView) {
|
if (keyCombination === "/" && !isTransitioning && !toggleView) {
|
||||||
setIsTransitioning(true);
|
firstPersonCamera({
|
||||||
|
setIsTransitioning,
|
||||||
state.controls.mouseButtons.left =
|
state,
|
||||||
CONSTANTS.controlsTransition.leftMouse;
|
camMode,
|
||||||
state.controls.mouseButtons.right =
|
setCamMode,
|
||||||
CONSTANTS.controlsTransition.rightMouse;
|
switchToFirstPerson,
|
||||||
state.controls.mouseButtons.wheel =
|
switchToThirdPerson,
|
||||||
CONSTANTS.controlsTransition.wheelMouse;
|
});
|
||||||
state.controls.mouseButtons.middle =
|
|
||||||
CONSTANTS.controlsTransition.middleMouse;
|
|
||||||
|
|
||||||
if (camMode === "ThirdPerson") {
|
|
||||||
setCamMode("FirstPerson");
|
|
||||||
await switchToFirstPerson(state.controls, state.camera);
|
|
||||||
} else if (camMode === "FirstPerson") {
|
|
||||||
setCamMode("ThirdPerson");
|
|
||||||
await switchToThirdPerson(state.controls, state.camera);
|
|
||||||
}
|
|
||||||
|
|
||||||
setIsTransitioning(false);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -92,6 +81,7 @@ const CamMode: React.FC = () => {
|
||||||
return () => {
|
return () => {
|
||||||
window.removeEventListener("keydown", handleKeyPress);
|
window.removeEventListener("keydown", handleKeyPress);
|
||||||
};
|
};
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [
|
}, [
|
||||||
camMode,
|
camMode,
|
||||||
isTransitioning,
|
isTransitioning,
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
import * as CONSTANTS from "../../../types/world/worldConstants";
|
||||||
|
|
||||||
|
interface FirstPersonCameraProps {
|
||||||
|
setIsTransitioning?: (value: boolean) => void;
|
||||||
|
state: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FirstPersonCameraParams extends FirstPersonCameraProps {
|
||||||
|
camMode: string;
|
||||||
|
setCamMode: (mode: string) => void;
|
||||||
|
switchToFirstPerson: (controls: any, camera: any) => Promise<void>;
|
||||||
|
switchToThirdPerson: (controls: any, camera: any) => Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function firstPersonCamera({
|
||||||
|
setIsTransitioning,
|
||||||
|
state,
|
||||||
|
camMode,
|
||||||
|
setCamMode,
|
||||||
|
switchToFirstPerson,
|
||||||
|
switchToThirdPerson
|
||||||
|
}: FirstPersonCameraParams): Promise<void> {
|
||||||
|
setIsTransitioning && setIsTransitioning(true);
|
||||||
|
|
||||||
|
state.controls.mouseButtons.left = CONSTANTS.controlsTransition.leftMouse;
|
||||||
|
state.controls.mouseButtons.right = CONSTANTS.controlsTransition.rightMouse;
|
||||||
|
state.controls.mouseButtons.wheel = CONSTANTS.controlsTransition.wheelMouse;
|
||||||
|
state.controls.mouseButtons.middle = CONSTANTS.controlsTransition.middleMouse;
|
||||||
|
|
||||||
|
if (camMode === "ThirdPerson") {
|
||||||
|
setCamMode("FirstPerson");
|
||||||
|
await switchToFirstPerson(state.controls, state.camera);
|
||||||
|
} else if (camMode === "FirstPerson") {
|
||||||
|
setCamMode("ThirdPerson");
|
||||||
|
await switchToThirdPerson(state.controls, state.camera);
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsTransitioning && setIsTransitioning(false);
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
import React from "react";
|
||||||
|
import { InfoIcon } from "../../../components/icons/ShortcutIcons";
|
||||||
|
import { SaveDiskIcon } from "../../../components/icons/ExportCommonIcons";
|
||||||
|
import { useCompareStore } from "../../../store/builder/store";
|
||||||
|
import OuterClick from "../../../utils/outerClick";
|
||||||
|
|
||||||
|
const ComparePopUp = () => {
|
||||||
|
const { setComparePopUp } = useCompareStore();
|
||||||
|
|
||||||
|
OuterClick({
|
||||||
|
contextClassName: ["compare-wrapper", "input"],
|
||||||
|
setMenuVisible: () => setComparePopUp(false),
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="compare-container">
|
||||||
|
<div className="compare-wrapper">
|
||||||
|
<div className="grid-wrapper">
|
||||||
|
<div className="header">
|
||||||
|
Do you want to save this version before comparing?
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="cards-container">
|
||||||
|
<div className="card"></div>
|
||||||
|
<div className="card"></div>
|
||||||
|
|
||||||
|
<div className="card-layout-wrapper">
|
||||||
|
<div className="card-layout-container">
|
||||||
|
<div className="tab-header">
|
||||||
|
<div className="label-tab">Layout !</div>
|
||||||
|
<div className="status"></div>
|
||||||
|
</div>
|
||||||
|
<div className="icon">
|
||||||
|
<SaveDiskIcon />
|
||||||
|
</div>
|
||||||
|
<div className="skeleton-wrapper">
|
||||||
|
<div className="skeleton"></div>
|
||||||
|
<div className="skeleton"></div>
|
||||||
|
<div className="skeleton"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="card"></div>
|
||||||
|
<div className="card"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="button-wrapper">
|
||||||
|
<div className="button-group">
|
||||||
|
<div className="save btn">Save & Continue</div>
|
||||||
|
<div className="replace btn">Replace Existing Version</div>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
className="cancel btn"
|
||||||
|
id="compare-cancel-btn"
|
||||||
|
onClick={() => setComparePopUp(false)}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="footer">
|
||||||
|
<InfoIcon /> Save this version and proceed.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ComparePopUp;
|
|
@ -14,7 +14,7 @@ const Templates = () => {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function templateData() {
|
async function templateData() {
|
||||||
try {
|
try {
|
||||||
const email = localStorage.getItem("email") || "";
|
const email = localStorage.getItem("email") ?? "";
|
||||||
const organization = email?.split("@")[1]?.split(".")[0];
|
const organization = email?.split("@")[1]?.split(".")[0];
|
||||||
let response = await getTemplateData(organization);
|
let response = await getTemplateData(organization);
|
||||||
setTemplates(response);
|
setTemplates(response);
|
||||||
|
@ -33,8 +33,7 @@ const Templates = () => {
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
const email = localStorage.getItem("email") || "";
|
const email = localStorage.getItem("email") ?? "";
|
||||||
|
|
||||||
const organization = email?.split("@")[1]?.split(".")[0];
|
const organization = email?.split("@")[1]?.split(".")[0];
|
||||||
let deleteTemplate = {
|
let deleteTemplate = {
|
||||||
organization: organization,
|
organization: organization,
|
||||||
|
@ -56,7 +55,7 @@ const Templates = () => {
|
||||||
const handleLoadTemplate = async (template: any) => {
|
const handleLoadTemplate = async (template: any) => {
|
||||||
try {
|
try {
|
||||||
if (selectedZone.zoneName === "") return;
|
if (selectedZone.zoneName === "") return;
|
||||||
const email = localStorage.getItem("email") || "";
|
const email = localStorage.getItem("email") ?? "";
|
||||||
const organization = email?.split("@")[1]?.split(".")[0];
|
const organization = email?.split("@")[1]?.split(".")[0];
|
||||||
|
|
||||||
let loadingTemplate = {
|
let loadingTemplate = {
|
||||||
|
@ -110,10 +109,10 @@ const Templates = () => {
|
||||||
)}
|
)}
|
||||||
<div className="template-details">
|
<div className="template-details">
|
||||||
<div className="template-name">
|
<div className="template-name">
|
||||||
{/* {`Template ${index + 1}`} */}
|
|
||||||
<RenameInput value={`Template ${index + 1}`} />
|
<RenameInput value={`Template ${index + 1}`} />
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
|
id="template-delete-button"
|
||||||
onClick={(e) => handleDeleteTemplate(e, template.id)}
|
onClick={(e) => handleDeleteTemplate(e, template.id)}
|
||||||
className="delete-button"
|
className="delete-button"
|
||||||
aria-label="Delete template"
|
aria-label="Delete template"
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useState } from "react";
|
import React from "react";
|
||||||
import {
|
import {
|
||||||
CleanPannel,
|
CleanPannel,
|
||||||
EyeIcon,
|
EyeIcon,
|
||||||
|
@ -95,8 +95,7 @@ const AddButtons: React.FC<ButtonsProps> = ({
|
||||||
|
|
||||||
// Function to toggle lock/unlock a panel
|
// Function to toggle lock/unlock a panel
|
||||||
const toggleLockPanel = async (side: Side) => {
|
const toggleLockPanel = async (side: Side) => {
|
||||||
// console.log('side: ', side);
|
const email = localStorage.getItem("email") ?? "";
|
||||||
const email = localStorage.getItem("email") || "";
|
|
||||||
const organization = email?.split("@")[1]?.split(".")[0]; // Fallback value
|
const organization = email?.split("@")[1]?.split(".")[0]; // Fallback value
|
||||||
//add api
|
//add api
|
||||||
const newLockedPanels = selectedZone.lockedPanels.includes(side)
|
const newLockedPanels = selectedZone.lockedPanels.includes(side)
|
||||||
|
@ -118,12 +117,6 @@ const AddButtons: React.FC<ButtonsProps> = ({
|
||||||
}
|
}
|
||||||
|
|
||||||
setSelectedZone(updatedZone);
|
setSelectedZone(updatedZone);
|
||||||
// let response = await lockPanel(selectedZone.zoneId, organization, newLockedPanels)
|
|
||||||
// console.log('response: ', response);
|
|
||||||
// if (response.message === 'locked panel updated successfully') {
|
|
||||||
// // Update the selectedZone state
|
|
||||||
// setSelectedZone(updatedZone);
|
|
||||||
// }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Function to clean all widgets from a panel
|
// Function to clean all widgets from a panel
|
||||||
|
@ -136,7 +129,7 @@ const AddButtons: React.FC<ButtonsProps> = ({
|
||||||
)
|
)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const email = localStorage.getItem("email") || "";
|
const email = localStorage.getItem("email") ?? "";
|
||||||
const organization = email?.split("@")[1]?.split(".")[0]; // Fallback value
|
const organization = email?.split("@")[1]?.split(".")[0]; // Fallback value
|
||||||
|
|
||||||
let clearPanel = {
|
let clearPanel = {
|
||||||
|
@ -155,23 +148,7 @@ const AddButtons: React.FC<ButtonsProps> = ({
|
||||||
widgets: cleanedWidgets,
|
widgets: cleanedWidgets,
|
||||||
};
|
};
|
||||||
// Update the selectedZone state
|
// Update the selectedZone state
|
||||||
// console.log('updatedZone: ', updatedZone);
|
|
||||||
setSelectedZone(updatedZone);
|
setSelectedZone(updatedZone);
|
||||||
|
|
||||||
// let response = await clearPanel(selectedZone.zoneId, organization, side)
|
|
||||||
// console.log('response: ', response);
|
|
||||||
// if (response.message === 'PanelWidgets cleared successfully') {
|
|
||||||
|
|
||||||
// const cleanedWidgets = selectedZone.widgets.filter(
|
|
||||||
// (widget) => widget.panel !== side
|
|
||||||
// );
|
|
||||||
// const updatedZone = {
|
|
||||||
// ...selectedZone,
|
|
||||||
// widgets: cleanedWidgets,
|
|
||||||
// };
|
|
||||||
// // Update the selectedZone state
|
|
||||||
// setSelectedZone(updatedZone);
|
|
||||||
// }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Function to handle "+" button click
|
// Function to handle "+" button click
|
||||||
|
@ -186,8 +163,8 @@ const AddButtons: React.FC<ButtonsProps> = ({
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
console.log("Removing after wait...");
|
console.log("Removing after wait...");
|
||||||
|
|
||||||
const email = localStorage.getItem("email") || "";
|
const email = localStorage.getItem("email") ?? "";
|
||||||
const organization = email?.split("@")[1]?.split(".")[0] || "";
|
const organization = email?.split("@")[1]?.split(".")[0] ?? "";
|
||||||
|
|
||||||
// Remove widgets for that side
|
// Remove widgets for that side
|
||||||
const cleanedWidgets = selectedZone.widgets.filter(
|
const cleanedWidgets = selectedZone.widgets.filter(
|
||||||
|
@ -229,8 +206,8 @@ const AddButtons: React.FC<ButtonsProps> = ({
|
||||||
} else {
|
} else {
|
||||||
// Panel does not exist: Add it
|
// Panel does not exist: Add it
|
||||||
try {
|
try {
|
||||||
const email = localStorage.getItem("email") || "";
|
const email = localStorage.getItem("email") ?? "";
|
||||||
const organization = email?.split("@")[1]?.split(".")[0] || "";
|
const organization = email?.split("@")[1]?.split(".")[0] ?? "";
|
||||||
|
|
||||||
const newActiveSides = selectedZone.activeSides.includes(side)
|
const newActiveSides = selectedZone.activeSides.includes(side)
|
||||||
? [...selectedZone.activeSides]
|
? [...selectedZone.activeSides]
|
||||||
|
@ -261,13 +238,13 @@ const AddButtons: React.FC<ButtonsProps> = ({
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
|
||||||
<div>
|
<div>
|
||||||
{(["top", "right", "bottom", "left"] as Side[]).map((side) => (
|
{(["top", "right", "bottom", "left"] as Side[]).map((side) => (
|
||||||
<div key={side} className={`side-button-container ${side}`}>
|
<div key={side} className={`side-button-container ${side}`}>
|
||||||
{/* "+" Button */}
|
{/* "+" Button */}
|
||||||
|
|
||||||
<button
|
<button
|
||||||
|
id="panel-add-button"
|
||||||
className={`side-button ${side}${
|
className={`side-button ${side}${
|
||||||
selectedZone.activeSides.includes(side) ? " active" : ""
|
selectedZone.activeSides.includes(side) ? " active" : ""
|
||||||
}`}
|
}`}
|
||||||
|
@ -297,12 +274,13 @@ const AddButtons: React.FC<ButtonsProps> = ({
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
{/* Hide Panel */}
|
{/* Hide Panel */}
|
||||||
<div
|
<button
|
||||||
className={`icon ${
|
className={`icon ${
|
||||||
hiddenPanels[selectedZone.zoneId]?.includes(side)
|
hiddenPanels[selectedZone.zoneId]?.includes(side)
|
||||||
? "active"
|
? "active"
|
||||||
: ""
|
: ""
|
||||||
}`}
|
}`}
|
||||||
|
id="hide-panel-visulization"
|
||||||
title={
|
title={
|
||||||
hiddenPanels[selectedZone.zoneId]?.includes(side)
|
hiddenPanels[selectedZone.zoneId]?.includes(side)
|
||||||
? "Show Panel"
|
? "Show Panel"
|
||||||
|
@ -317,12 +295,13 @@ const AddButtons: React.FC<ButtonsProps> = ({
|
||||||
: "var(--text-color)"
|
: "var(--text-color)"
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</button>
|
||||||
|
|
||||||
{/* Clean Panel */}
|
{/* Clean Panel */}
|
||||||
<div
|
<button
|
||||||
className="icon"
|
className="icon"
|
||||||
title="Clean Panel"
|
title="Clean Panel"
|
||||||
|
id="clean-panel-visulization"
|
||||||
onClick={() => cleanPanel(side)}
|
onClick={() => cleanPanel(side)}
|
||||||
style={{
|
style={{
|
||||||
cursor:
|
cursor:
|
||||||
|
@ -333,13 +312,14 @@ const AddButtons: React.FC<ButtonsProps> = ({
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<CleanPannel />
|
<CleanPannel />
|
||||||
</div>
|
</button>
|
||||||
|
|
||||||
{/* Lock/Unlock Panel */}
|
{/* Lock/Unlock Panel */}
|
||||||
<div
|
<button
|
||||||
className={`icon ${
|
className={`icon ${
|
||||||
selectedZone.lockedPanels.includes(side) ? "active" : ""
|
selectedZone.lockedPanels.includes(side) ? "active" : ""
|
||||||
}`}
|
}`}
|
||||||
|
id="lock-panel-visulization"
|
||||||
title={
|
title={
|
||||||
selectedZone.lockedPanels.includes(side)
|
selectedZone.lockedPanels.includes(side)
|
||||||
? "Unlock Panel"
|
? "Unlock Panel"
|
||||||
|
@ -354,13 +334,12 @@ const AddButtons: React.FC<ButtonsProps> = ({
|
||||||
: "var(--text-color)"
|
: "var(--text-color)"
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React, { useEffect, useMemo, useRef, useState } from "react";
|
import React, { useEffect, useMemo, useRef, useState } from "react";
|
||||||
|
|
||||||
import { arrayMove } from "@dnd-kit/sortable";
|
import { arrayMove } from "@dnd-kit/sortable";
|
||||||
import { useAsset3dWidget, useSocketStore } from "../../../../store/builder/store";
|
import { useSocketStore } from "../../../../store/builder/store";
|
||||||
import { usePlayButtonStore } from "../../../../store/usePlayButtonStore";
|
import { usePlayButtonStore } from "../../../../store/usePlayButtonStore";
|
||||||
import { useWidgetStore } from "../../../../store/useWidgetStore";
|
import { useWidgetStore } from "../../../../store/useWidgetStore";
|
||||||
import { DraggableWidget } from "../2d/DraggableWidget";
|
import { DraggableWidget } from "../2d/DraggableWidget";
|
||||||
|
@ -47,7 +47,7 @@ interface PanelProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
const generateUniqueId = () =>
|
const generateUniqueId = () =>
|
||||||
`${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
`${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
|
||||||
|
|
||||||
const Panel: React.FC<PanelProps> = ({
|
const Panel: React.FC<PanelProps> = ({
|
||||||
selectedZone,
|
selectedZone,
|
||||||
|
@ -56,7 +56,6 @@ const Panel: React.FC<PanelProps> = ({
|
||||||
setZonesData,
|
setZonesData,
|
||||||
waitingPanels,
|
waitingPanels,
|
||||||
}) => {
|
}) => {
|
||||||
const { widgetSelect, setWidgetSelect } = useAsset3dWidget();
|
|
||||||
const panelRefs = useRef<{ [side in Side]?: HTMLDivElement }>({});
|
const panelRefs = useRef<{ [side in Side]?: HTMLDivElement }>({});
|
||||||
const [panelDimensions, setPanelDimensions] = useState<{
|
const [panelDimensions, setPanelDimensions] = useState<{
|
||||||
[side in Side]?: { width: number; height: number };
|
[side in Side]?: { width: number; height: number };
|
||||||
|
@ -183,7 +182,7 @@ const Panel: React.FC<PanelProps> = ({
|
||||||
|
|
||||||
// Add widget to panel
|
// Add widget to panel
|
||||||
const addWidgetToPanel = async (asset: any, panel: Side) => {
|
const addWidgetToPanel = async (asset: any, panel: Side) => {
|
||||||
const email = localStorage.getItem("email") || "";
|
const email = localStorage.getItem("email") ?? "";
|
||||||
const organization = email?.split("@")[1]?.split(".")[0];
|
const organization = email?.split("@")[1]?.split(".")[0];
|
||||||
|
|
||||||
const newWidget = {
|
const newWidget = {
|
||||||
|
@ -285,7 +284,7 @@ const Panel: React.FC<PanelProps> = ({
|
||||||
{selectedZone.activeSides.map((side) => (
|
{selectedZone.activeSides.map((side) => (
|
||||||
<div
|
<div
|
||||||
key={side}
|
key={side}
|
||||||
id="panel-wrapper"
|
id={`panel-wrapper-${side}`}
|
||||||
className={`panel ${side}-panel absolute ${
|
className={`panel ${side}-panel absolute ${
|
||||||
hiddenPanels[selectedZone.zoneId]?.includes(side) ? "hidePanel" : ""
|
hiddenPanels[selectedZone.zoneId]?.includes(side) ? "hidePanel" : ""
|
||||||
}`}
|
}`}
|
||||||
|
@ -301,14 +300,14 @@ const Panel: React.FC<PanelProps> = ({
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={`panel-content
|
className={`panel-content ${
|
||||||
${waitingPanels === side ? `${side}-closing` : ""}
|
waitingPanels === side ? `${side}-closing` : ""
|
||||||
${
|
}${
|
||||||
!hiddenPanels[selectedZone.zoneId]?.includes(side) && waitingPanels !== side
|
!hiddenPanels[selectedZone.zoneId]?.includes(side) &&
|
||||||
|
waitingPanels !== side
|
||||||
? `${side}-opening`
|
? `${side}-opening`
|
||||||
: ""
|
: ""
|
||||||
}
|
} ${isPlaying ? "fullScreen" : ""}`}
|
||||||
${isPlaying ? "fullScreen" : ""}`}
|
|
||||||
style={{
|
style={{
|
||||||
pointerEvents:
|
pointerEvents:
|
||||||
selectedZone.lockedPanels.includes(side) ||
|
selectedZone.lockedPanels.includes(side) ||
|
||||||
|
|
|
@ -13,6 +13,7 @@ import {
|
||||||
MoveArrowRight,
|
MoveArrowRight,
|
||||||
} from "../../../components/icons/SimulationIcons";
|
} from "../../../components/icons/SimulationIcons";
|
||||||
import { InfoIcon } from "../../../components/icons/ExportCommonIcons";
|
import { InfoIcon } from "../../../components/icons/ExportCommonIcons";
|
||||||
|
import { usePlayButtonStore } from "../../../store/usePlayButtonStore";
|
||||||
|
|
||||||
// Define the type for `Side`
|
// Define the type for `Side`
|
||||||
type Side = "top" | "bottom" | "left" | "right";
|
type Side = "top" | "bottom" | "left" | "right";
|
||||||
|
@ -88,6 +89,7 @@ const DisplayZone: React.FC<DisplayZoneProps> = ({
|
||||||
const [showLeftArrow, setShowLeftArrow] = useState(false);
|
const [showLeftArrow, setShowLeftArrow] = useState(false);
|
||||||
const [showRightArrow, setShowRightArrow] = useState(false);
|
const [showRightArrow, setShowRightArrow] = useState(false);
|
||||||
const { floatingWidget, setFloatingWidget } = useFloatingWidget();
|
const { floatingWidget, setFloatingWidget } = useFloatingWidget();
|
||||||
|
const { isPlaying } = usePlayButtonStore();
|
||||||
|
|
||||||
const { setSelectedChartId } = useWidgetStore();
|
const { setSelectedChartId } = useWidgetStore();
|
||||||
|
|
||||||
|
@ -206,16 +208,21 @@ const DisplayZone: React.FC<DisplayZoneProps> = ({
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
ref={containerRef}
|
ref={containerRef}
|
||||||
className={`zone-wrapper ${
|
id="zone-container"
|
||||||
|
className={`zone-container ${
|
||||||
selectedZone?.activeSides?.includes("bottom") &&
|
selectedZone?.activeSides?.includes("bottom") &&
|
||||||
!hiddenPanels[selectedZone.zoneId]?.includes("bottom")
|
!hiddenPanels[selectedZone.zoneId]?.includes("bottom")
|
||||||
? "bottom"
|
? "bottom"
|
||||||
: ""
|
: ""
|
||||||
}`}
|
} ${isPlaying ? "visualization-playing" : ""}`}
|
||||||
>
|
>
|
||||||
{/* Left Arrow */}
|
{/* Left Arrow */}
|
||||||
{showLeftArrow && (
|
{showLeftArrow && (
|
||||||
<button className="arrow left-arrow" onClick={handleScrollLeft}>
|
<button
|
||||||
|
id="zone-preview-left-arrow"
|
||||||
|
className="arrow left-arrow"
|
||||||
|
onClick={handleScrollLeft}
|
||||||
|
>
|
||||||
<MoveArrowLeft />
|
<MoveArrowLeft />
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
|
@ -252,7 +259,11 @@ const DisplayZone: React.FC<DisplayZoneProps> = ({
|
||||||
|
|
||||||
{/* Right Arrow */}
|
{/* Right Arrow */}
|
||||||
{showRightArrow && (
|
{showRightArrow && (
|
||||||
<button className="arrow right-arrow" onClick={handleScrollRight}>
|
<button
|
||||||
|
id="zone-preview-right-arrow"
|
||||||
|
className="arrow right-arrow"
|
||||||
|
onClick={handleScrollRight}
|
||||||
|
>
|
||||||
<MoveArrowRight />
|
<MoveArrowRight />
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -32,6 +32,7 @@ import RenderOverlay from "../components/templates/Overlay";
|
||||||
import LogList from "../components/ui/log/LogList";
|
import LogList from "../components/ui/log/LogList";
|
||||||
import Footer from "../components/footer/Footer";
|
import Footer from "../components/footer/Footer";
|
||||||
import SelectFloorPlan from "../components/temporary/SelectFloorPlan";
|
import SelectFloorPlan from "../components/temporary/SelectFloorPlan";
|
||||||
|
import ControlsPlayer from "../components/layout/controls/ControlsPlayer";
|
||||||
|
|
||||||
const Project: React.FC = () => {
|
const Project: React.FC = () => {
|
||||||
let navigate = useNavigate();
|
let navigate = useNavigate();
|
||||||
|
@ -97,8 +98,10 @@ const Project: React.FC = () => {
|
||||||
)}
|
)}
|
||||||
<RealTimeVisulization />
|
<RealTimeVisulization />
|
||||||
{activeModule === "market" && <MarketPlace />}
|
{activeModule === "market" && <MarketPlace />}
|
||||||
{activeModule !== "market" && <Tools />}
|
{activeModule !== "market" && !isPlaying && <Tools />}
|
||||||
{isPlaying && activeModule === "simulation" && <SimulationPlayer />}
|
{isPlaying && activeModule === "simulation" && <SimulationPlayer />}
|
||||||
|
{isPlaying && activeModule !== "simulation" && <ControlsPlayer />}
|
||||||
|
|
||||||
{/* remove this later */}
|
{/* remove this later */}
|
||||||
{activeModule === "builder" && !toggleThreeD && <SelectFloorPlan />}
|
{activeModule === "builder" && !toggleThreeD && <SelectFloorPlan />}
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -102,7 +102,7 @@ const UserAuth: React.FC = () => {
|
||||||
)}
|
)}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<button className="google-login">
|
<button id="google-login" className="google-login">
|
||||||
<span className="google-icon">G</span> Continue with Google
|
<span className="google-icon">G</span> Continue with Google
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
@ -139,6 +139,7 @@ const UserAuth: React.FC = () => {
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
|
id="toogle-password"
|
||||||
type="button"
|
type="button"
|
||||||
className="toggle-password"
|
className="toggle-password"
|
||||||
onClick={() => setShowPassword(!showPassword)}
|
onClick={() => setShowPassword(!showPassword)}
|
||||||
|
@ -154,7 +155,7 @@ const UserAuth: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<button type="submit" className="continue-button">
|
<button id="form-submit" type="submit" className="continue-button">
|
||||||
{isSignIn ? "Continue" : "Register"}
|
{isSignIn ? "Continue" : "Register"}
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_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`;
|
|
||||||
type Side = "top" | "bottom" | "left" | "right";
|
type Side = "top" | "bottom" | "left" | "right";
|
||||||
|
|
||||||
export const panelData = async (
|
export const panelData = async (
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import * as THREE from "three";
|
import * as THREE from "three";
|
||||||
import { create } from "zustand";
|
import { create } from "zustand";
|
||||||
import { io } from "socket.io-client";
|
import { io } from "socket.io-client";
|
||||||
import * as CONSTANTS from '../../types/world/worldConstants';
|
import * as CONSTANTS from "../../types/world/worldConstants";
|
||||||
|
|
||||||
export const useSocketStore = create<any>((set: any, get: any) => ({
|
export const useSocketStore = create<any>((set: any, get: any) => ({
|
||||||
socket: null,
|
socket: null,
|
||||||
|
@ -90,7 +90,13 @@ export const useZonePoints = create<ZonePointsState>((set) => ({
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const useSelectedItem = create<any>((set: any) => ({
|
export const useSelectedItem = create<any>((set: any) => ({
|
||||||
selectedItem: { name: "", id: "", type: undefined, category: '', subCatergory: '' },
|
selectedItem: {
|
||||||
|
name: "",
|
||||||
|
id: "",
|
||||||
|
type: undefined,
|
||||||
|
category: "",
|
||||||
|
subCatergory: "",
|
||||||
|
},
|
||||||
setSelectedItem: (x: any) => set(() => ({ selectedItem: x })),
|
setSelectedItem: (x: any) => set(() => ({ selectedItem: x })),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@ -387,8 +393,14 @@ export const useLimitDistance = create<any>((set: any) => ({
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const useTileDistance = create<any>((set: any) => ({
|
export const useTileDistance = create<any>((set: any) => ({
|
||||||
gridValue: { size: CONSTANTS.gridConfig.size, divisions: CONSTANTS.gridConfig.divisions },
|
gridValue: {
|
||||||
planeValue: { height: CONSTANTS.planeConfig.height, width: CONSTANTS.planeConfig.width },
|
size: CONSTANTS.gridConfig.size,
|
||||||
|
divisions: CONSTANTS.gridConfig.divisions,
|
||||||
|
},
|
||||||
|
planeValue: {
|
||||||
|
height: CONSTANTS.planeConfig.height,
|
||||||
|
width: CONSTANTS.planeConfig.width,
|
||||||
|
},
|
||||||
|
|
||||||
setGridValue: (value: any) =>
|
setGridValue: (value: any) =>
|
||||||
set((state: any) => ({
|
set((state: any) => ({
|
||||||
|
@ -452,3 +464,16 @@ export const useShortcutStore = create<ShortcutStore>((set) => ({
|
||||||
toggleShortcuts: () =>
|
toggleShortcuts: () =>
|
||||||
set((state) => ({ showShortcuts: !state.showShortcuts })),
|
set((state) => ({ showShortcuts: !state.showShortcuts })),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
interface CompareStore {
|
||||||
|
comparePopUp: boolean;
|
||||||
|
setComparePopUp: (value: boolean) => void;
|
||||||
|
toggleComparePopUp: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useCompareStore = create<CompareStore>((set) => ({
|
||||||
|
comparePopUp: false,
|
||||||
|
setComparePopUp: (value) => set({ comparePopUp: value }),
|
||||||
|
toggleComparePopUp: () =>
|
||||||
|
set((state) => ({ comparePopUp: !state.comparePopUp })),
|
||||||
|
}));
|
||||||
|
|
|
@ -0,0 +1,221 @@
|
||||||
|
import { create } from 'zustand';
|
||||||
|
import { immer } from 'zustand/middleware/immer';
|
||||||
|
|
||||||
|
interface AssetsStore {
|
||||||
|
assets: Assets;
|
||||||
|
|
||||||
|
// Asset CRUD operations
|
||||||
|
addAsset: (asset: Asset) => void;
|
||||||
|
removeAsset: (modelUuid: string) => void;
|
||||||
|
updateAsset: (modelUuid: string, updates: Partial<Asset>) => void;
|
||||||
|
setAssets: (assets: Assets) => void;
|
||||||
|
|
||||||
|
// Asset properties
|
||||||
|
setPosition: (modelUuid: string, position: [number, number, number]) => void;
|
||||||
|
setRotation: (modelUuid: string, rotation: [number, number, number]) => void;
|
||||||
|
setLock: (modelUuid: string, isLocked: boolean) => void;
|
||||||
|
setCollision: (modelUuid: string, isCollidable: boolean) => void;
|
||||||
|
setVisibility: (modelUuid: string, isVisible: boolean) => void;
|
||||||
|
setOpacity: (modelUuid: string, opacity: number) => void;
|
||||||
|
|
||||||
|
// Animation controls
|
||||||
|
setAnimation: (modelUuid: string, animation: string) => void;
|
||||||
|
setCurrentAnimation: (modelUuid: string, current: string, isPlaying: boolean) => void;
|
||||||
|
addAnimation: (modelUuid: string, animation: string) => void;
|
||||||
|
removeAnimation: (modelUuid: string, animation: string) => void;
|
||||||
|
|
||||||
|
// Event data operations
|
||||||
|
addEventData: (modelUuid: string, eventData: Asset['eventData']) => void;
|
||||||
|
updateEventData: (modelUuid: string, updates: Partial<Asset['eventData']>) => void;
|
||||||
|
removeEventData: (modelUuid: string) => void;
|
||||||
|
|
||||||
|
// Helper functions
|
||||||
|
getAssetById: (modelUuid: string) => Asset | undefined;
|
||||||
|
getAssetByPointUuid: (pointUuid: string) => Asset | undefined;
|
||||||
|
hasAsset: (modelUuid: string) => boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useAssetsStore = create<AssetsStore>()(
|
||||||
|
immer((set, get) => ({
|
||||||
|
assets: [],
|
||||||
|
|
||||||
|
// Asset CRUD operations
|
||||||
|
addAsset: (asset) => {
|
||||||
|
set((state) => {
|
||||||
|
if (!state.assets.some(a => a.modelUuid === asset.modelUuid)) {
|
||||||
|
state.assets.push(asset);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
removeAsset: (modelUuid) => {
|
||||||
|
set((state) => {
|
||||||
|
state.assets = state.assets.filter(a => a.modelUuid !== modelUuid);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
updateAsset: (modelUuid, updates) => {
|
||||||
|
set((state) => {
|
||||||
|
const asset = state.assets.find(a => a.modelUuid === modelUuid);
|
||||||
|
if (asset) {
|
||||||
|
Object.assign(asset, updates);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
setAssets: (assets) => {
|
||||||
|
set((state) => {
|
||||||
|
state.assets = assets;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// Asset properties
|
||||||
|
setPosition: (modelUuid, position) => {
|
||||||
|
set((state) => {
|
||||||
|
const asset = state.assets.find(a => a.modelUuid === modelUuid);
|
||||||
|
if (asset) {
|
||||||
|
asset.position = position;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
setRotation: (modelUuid, rotation) => {
|
||||||
|
set((state) => {
|
||||||
|
const asset = state.assets.find(a => a.modelUuid === modelUuid);
|
||||||
|
if (asset) {
|
||||||
|
asset.rotation = rotation;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
setLock: (modelUuid, isLocked) => {
|
||||||
|
set((state) => {
|
||||||
|
const asset = state.assets.find(a => a.modelUuid === modelUuid);
|
||||||
|
if (asset) {
|
||||||
|
asset.isLocked = isLocked;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
setCollision: (modelUuid, isCollidable) => {
|
||||||
|
set((state) => {
|
||||||
|
const asset = state.assets.find(a => a.modelUuid === modelUuid);
|
||||||
|
if (asset) {
|
||||||
|
asset.isCollidable = isCollidable;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
setVisibility: (modelUuid, isVisible) => {
|
||||||
|
set((state) => {
|
||||||
|
const asset = state.assets.find(a => a.modelUuid === modelUuid);
|
||||||
|
if (asset) {
|
||||||
|
asset.isVisible = isVisible;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
setOpacity: (modelUuid, opacity) => {
|
||||||
|
set((state) => {
|
||||||
|
const asset = state.assets.find(a => a.modelUuid === modelUuid);
|
||||||
|
if (asset) {
|
||||||
|
asset.opacity = opacity;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// Animation controls
|
||||||
|
setAnimation: (modelUuid, animation) => {
|
||||||
|
set((state) => {
|
||||||
|
const asset = state.assets.find(a => a.modelUuid === modelUuid);
|
||||||
|
if (asset) {
|
||||||
|
if (!asset.animationState) {
|
||||||
|
asset.animationState = { current: animation, playing: false };
|
||||||
|
} else {
|
||||||
|
asset.animationState.current = animation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
setCurrentAnimation: (modelUuid, current, isPlaying) => {
|
||||||
|
set((state) => {
|
||||||
|
const asset = state.assets.find(a => a.modelUuid === modelUuid);
|
||||||
|
if (asset?.animationState) {
|
||||||
|
asset.animationState.current = current;
|
||||||
|
asset.animationState.playing = isPlaying;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
addAnimation: (modelUuid, animation) => {
|
||||||
|
set((state) => {
|
||||||
|
const asset = state.assets.find(a => a.modelUuid === modelUuid);
|
||||||
|
if (asset) {
|
||||||
|
if (!asset.animations) {
|
||||||
|
asset.animations = [animation];
|
||||||
|
} else if (!asset.animations.includes(animation)) {
|
||||||
|
asset.animations.push(animation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
removeAnimation: (modelUuid, animation) => {
|
||||||
|
set((state) => {
|
||||||
|
const asset = state.assets.find(a => a.modelUuid === modelUuid);
|
||||||
|
if (asset?.animations) {
|
||||||
|
asset.animations = asset.animations.filter(a => a !== animation);
|
||||||
|
if (asset.animationState?.current === animation) {
|
||||||
|
asset.animationState.playing = false;
|
||||||
|
asset.animationState.current = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// Event data operations
|
||||||
|
addEventData: (modelUuid, eventData) => {
|
||||||
|
set((state) => {
|
||||||
|
const asset = state.assets.find(a => a.modelUuid === modelUuid);
|
||||||
|
if (asset) {
|
||||||
|
asset.eventData = eventData;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
updateEventData: (modelUuid, updates) => {
|
||||||
|
set((state) => {
|
||||||
|
const asset = state.assets.find(a => a.modelUuid === modelUuid);
|
||||||
|
if (asset?.eventData) {
|
||||||
|
asset.eventData = { ...asset.eventData, ...updates };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
removeEventData: (modelUuid) => {
|
||||||
|
set((state) => {
|
||||||
|
const asset = state.assets.find(a => a.modelUuid === modelUuid);
|
||||||
|
if (asset) {
|
||||||
|
delete asset.eventData;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// Helper functions
|
||||||
|
getAssetById: (modelUuid) => {
|
||||||
|
return get().assets.find(a => a.modelUuid === modelUuid);
|
||||||
|
},
|
||||||
|
|
||||||
|
getAssetByPointUuid: (pointUuid) => {
|
||||||
|
return get().assets.find(asset =>
|
||||||
|
asset.eventData?.point?.uuid === pointUuid ||
|
||||||
|
asset.eventData?.points?.some(p => p.uuid === pointUuid)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
hasAsset: (modelUuid) => {
|
||||||
|
return get().assets.some(a => a.modelUuid === modelUuid);
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
);
|
|
@ -33,3 +33,17 @@ export const useAnimationPlaySpeed = create<AnimationSpeedState>((set) => ({
|
||||||
speed: 1,
|
speed: 1,
|
||||||
setSpeed: (value) => set({ speed: value }),
|
setSpeed: (value) => set({ speed: value }),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
interface CameraModeState {
|
||||||
|
walkMode: boolean;
|
||||||
|
setWalkMode: (enabled: boolean) => void;
|
||||||
|
toggleWalkMode: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const useCameraModeStore = create<CameraModeState>((set) => ({
|
||||||
|
walkMode: false,
|
||||||
|
setWalkMode: (enabled) => set({ walkMode: enabled }),
|
||||||
|
toggleWalkMode: () => set((state) => ({ walkMode: !state.walkMode })),
|
||||||
|
}));
|
||||||
|
|
||||||
|
export default useCameraModeStore;
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
@use "../../abstracts/variables" as *;
|
@use "../../abstracts/variables" as *;
|
||||||
@use "../../abstracts/mixins" as *;
|
@use "../../abstracts/mixins" as *;
|
||||||
|
|
||||||
|
|
||||||
.footer-container {
|
.footer-container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
position: absolute;
|
position: fixed;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
z-index: 3;
|
z-index: 3;
|
||||||
|
@ -12,6 +11,7 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
|
pointer-events: none;
|
||||||
|
|
||||||
.footer-wrapper {
|
.footer-wrapper {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -33,7 +33,7 @@
|
||||||
|
|
||||||
.selector {
|
.selector {
|
||||||
color: var(--text-color);
|
color: var(--text-color);
|
||||||
font-size: var(--font-size-small)
|
font-size: var(--font-size-small);
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
|
@ -46,7 +46,7 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
pointer-events: all;
|
||||||
// dummy
|
// dummy
|
||||||
.bg-dummy {
|
.bg-dummy {
|
||||||
background: var(--background-color-solid);
|
background: var(--background-color-solid);
|
||||||
|
@ -165,11 +165,23 @@
|
||||||
min-height: 320px;
|
min-height: 320px;
|
||||||
height: 320px;
|
height: 320px;
|
||||||
border-radius: 18px;
|
border-radius: 18px;
|
||||||
|
pointer-events: all;
|
||||||
|
|
||||||
background: var(--background-color);
|
background: var(--background-color);
|
||||||
backdrop-filter: blur(20px);
|
backdrop-filter: blur(20px);
|
||||||
|
.close-button {
|
||||||
|
position: absolute;
|
||||||
|
@include flex-center;
|
||||||
|
height: 26px;
|
||||||
|
width: 26px;
|
||||||
|
right: 12px;
|
||||||
|
top: 10px;
|
||||||
|
background: var(--background-color);
|
||||||
|
border-radius: #{$border-radius-medium};
|
||||||
|
outline: 1px solid var(--border-color);
|
||||||
|
&:hover{
|
||||||
|
background: var(--background-color-solid);
|
||||||
|
}
|
||||||
|
}
|
||||||
.header {
|
.header {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
@ -191,7 +203,7 @@
|
||||||
padding-left: 10px;
|
padding-left: 10px;
|
||||||
|
|
||||||
&::before {
|
&::before {
|
||||||
content: '';
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
|
@ -243,7 +255,6 @@
|
||||||
|
|
||||||
.shortcut-intro {
|
.shortcut-intro {
|
||||||
display: flex;
|
display: flex;
|
||||||
// align-items: center;
|
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
|
|
||||||
.value-wrapper {
|
.value-wrapper {
|
||||||
|
@ -253,7 +264,6 @@
|
||||||
|
|
||||||
.description {
|
.description {
|
||||||
font-size: var(--font-size-tiny);
|
font-size: var(--font-size-tiny);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -264,14 +274,22 @@
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
|
||||||
.key {
|
.key {
|
||||||
background: linear-gradient(135.11deg, #656DC2 3.48%, #9526E5 91.33%);
|
background: linear-gradient(
|
||||||
|
135.11deg,
|
||||||
|
#656dc2 3.48%,
|
||||||
|
#9526e5 91.33%
|
||||||
|
);
|
||||||
|
@include flex-center;
|
||||||
padding: 4px 10px;
|
padding: 4px 10px;
|
||||||
|
height: 25px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
font-size: var(--font-size-tiny);
|
font-size: var(--font-size-tiny);
|
||||||
color: var(--icon-default-color-active);
|
color: var(--icon-default-color-active);
|
||||||
|
&:last-child{
|
||||||
|
background: var(--background-color-button);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.key.add {
|
.key.add {
|
||||||
|
@ -294,19 +312,12 @@
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
/* or center if vertical centering is desired */
|
/* or center if vertical centering is desired */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.shortcut-helper-overlay {
|
.shortcut-helper-overlay {
|
||||||
max-height: 0;
|
max-height: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
// opacity: 0;
|
|
||||||
transform: translateY(20px);
|
transform: translateY(20px);
|
||||||
transition: all 0.3s ease-in-out;
|
transition: all 0.3s ease-in-out;
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,16 @@
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
transform: translate(-50%, 0);
|
transform: translate(-50%, 0);
|
||||||
width: 70vw;
|
width: 70vw;
|
||||||
|
transition: all 0.3s;
|
||||||
|
&.hide {
|
||||||
|
width: fit-content;
|
||||||
|
.simulation-player-container
|
||||||
|
.controls-container
|
||||||
|
.simulation-button-container {
|
||||||
|
width: 32px;
|
||||||
|
height: 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
.simulation-player-container {
|
.simulation-player-container {
|
||||||
background: var(--background-color);
|
background: var(--background-color);
|
||||||
padding: 7px;
|
padding: 7px;
|
||||||
|
@ -90,11 +99,12 @@
|
||||||
@include flex-center;
|
@include flex-center;
|
||||||
gap: 2px;
|
gap: 2px;
|
||||||
padding: 4px 8px;
|
padding: 4px 8px;
|
||||||
min-width: 64px;
|
width: 64px;
|
||||||
background: var(--background-color);
|
background: var(--background-color);
|
||||||
border-radius: #{$border-radius-extra-large};
|
border-radius: #{$border-radius-extra-large};
|
||||||
height: fit-content;
|
height: fit-content;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
transition: all 0.2s;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
outline: 1px solid var(--border-color);
|
outline: 1px solid var(--border-color);
|
||||||
|
@ -347,6 +357,86 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.controls-player-container {
|
||||||
|
min-width: 26vw;
|
||||||
|
max-width: 80vw;
|
||||||
|
border-radius: 15px;
|
||||||
|
gap: 40px;
|
||||||
|
background: var(--background-color);
|
||||||
|
backdrop-filter: blur(20px);
|
||||||
|
cursor: pointer;
|
||||||
|
@include flex-center;
|
||||||
|
justify-content: space-between;
|
||||||
|
position: fixed;
|
||||||
|
bottom: 32px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, 0);
|
||||||
|
color: var(--accent-color);
|
||||||
|
z-index: 100;
|
||||||
|
isolation: isolate;
|
||||||
|
font-weight: 700;
|
||||||
|
padding: 8px;
|
||||||
|
transition: all 0.2s;
|
||||||
|
|
||||||
|
&.hide {
|
||||||
|
min-width: auto;
|
||||||
|
width: 92px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.controls-left,
|
||||||
|
.controls-right {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
font-size: var(--font-size-small);
|
||||||
|
|
||||||
|
.label {
|
||||||
|
text-transform: capitalize;
|
||||||
|
font-size: var(--font-size-small);
|
||||||
|
}
|
||||||
|
|
||||||
|
.walkMode-wrapper {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
|
||||||
|
.input-toggle-container {
|
||||||
|
padding: 0;
|
||||||
|
gap: 4px;
|
||||||
|
|
||||||
|
.label {
|
||||||
|
font-size: var(--font-size-small);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-wrapper {
|
||||||
|
@include flex-center;
|
||||||
|
gap: 2px;
|
||||||
|
padding: 4px 8px;
|
||||||
|
width: 64px;
|
||||||
|
background: var(--background-color);
|
||||||
|
border-radius: 20px;
|
||||||
|
height: fit-content;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s;
|
||||||
|
outline: 1px solid transparent;
|
||||||
|
&:hover {
|
||||||
|
outline: 1px solid var(--border-color);
|
||||||
|
color: var(--accent-color);
|
||||||
|
}
|
||||||
|
&.hide {
|
||||||
|
width: 32px;
|
||||||
|
}
|
||||||
|
.icon {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
@include flex-center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.processDisplayer {
|
.processDisplayer {
|
||||||
border-radius: #{$border-radius-large};
|
border-radius: #{$border-radius-large};
|
||||||
outline: 1px solid var(--border-color);
|
outline: 1px solid var(--border-color);
|
||||||
|
@ -454,7 +544,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.storage-container{
|
.storage-container {
|
||||||
font-size: var(--font-size-tiny);
|
font-size: var(--font-size-tiny);
|
||||||
color: var(--highlight-text-color);
|
color: var(--highlight-text-color);
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,12 +12,16 @@
|
||||||
box-shadow: #{$box-shadow-medium};
|
box-shadow: #{$box-shadow-medium};
|
||||||
border-radius: #{$border-radius-large};
|
border-radius: #{$border-radius-large};
|
||||||
width: fit-content;
|
width: fit-content;
|
||||||
transition: width 0.2s;
|
|
||||||
background: var(--background-color);
|
background: var(--background-color);
|
||||||
backdrop-filter: blur(20px);
|
backdrop-filter: blur(20px);
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
outline: 1px solid var(--border-color);
|
outline: 1px solid var(--border-color);
|
||||||
outline-offset: -1px;
|
outline-offset: -1px;
|
||||||
|
transition: transform 0.4s ease-in-out 0.01s;
|
||||||
|
|
||||||
|
&.visible {
|
||||||
|
transform: translate(-50%, -310px);
|
||||||
|
}
|
||||||
|
|
||||||
.split {
|
.split {
|
||||||
height: 20px;
|
height: 20px;
|
||||||
|
@ -47,9 +51,11 @@
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: color-mix(in srgb,
|
background: color-mix(
|
||||||
|
in srgb,
|
||||||
var(--highlight-accent-color) 60%,
|
var(--highlight-accent-color) 60%,
|
||||||
transparent);
|
transparent
|
||||||
|
);
|
||||||
|
|
||||||
.tooltip {
|
.tooltip {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
|
@ -78,9 +84,11 @@
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: color-mix(in srgb,
|
background: color-mix(
|
||||||
|
in srgb,
|
||||||
var(--highlight-accent-color) 60%,
|
var(--highlight-accent-color) 60%,
|
||||||
transparent);
|
transparent
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
.drop-down-container {
|
.drop-down-container {
|
||||||
|
@ -178,52 +186,6 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.tools-container {
|
|
||||||
transition: transform 0.4s ease-in-out 0.01s;
|
|
||||||
|
|
||||||
&.visible {
|
|
||||||
transform: translate(-50%, -310px);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.exitPlay {
|
|
||||||
width: 30px;
|
|
||||||
height: 30px;
|
|
||||||
border-radius: #{$border-radius-circle};
|
|
||||||
background: var(--highlight-accent-color);
|
|
||||||
cursor: pointer;
|
|
||||||
@include flex-center;
|
|
||||||
position: fixed;
|
|
||||||
bottom: 60px;
|
|
||||||
left: 50%;
|
|
||||||
transform: translate(-50%, 0);
|
|
||||||
color: var(--accent-color);
|
|
||||||
z-index: 100;
|
|
||||||
isolation: isolate;
|
|
||||||
font-weight: 700;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
font-weight: 500;
|
|
||||||
background: var(--accent-color);
|
|
||||||
color: var(--highlight-accent-color);
|
|
||||||
|
|
||||||
&::after {
|
|
||||||
animation: pulse 1s ease-out infinite;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&::after {
|
|
||||||
content: "";
|
|
||||||
position: absolute;
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
background: var(--background-color-secondary);
|
|
||||||
border-radius: #{$border-radius-circle};
|
|
||||||
z-index: -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes pulse {
|
@keyframes pulse {
|
||||||
0% {
|
0% {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
|
|
|
@ -0,0 +1,267 @@
|
||||||
|
.compare-container {
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
|
||||||
|
background: var(--background-color-secondary);
|
||||||
|
backdrop-filter: blur(2px);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.compare-wrapper {
|
||||||
|
min-width: 312px;
|
||||||
|
min-height: 363px;
|
||||||
|
background: var(--background-color);
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 24px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
backdrop-filter: blur(50px);
|
||||||
|
outline: 1px solid var(--border-color);
|
||||||
|
box-shadow: 0 12px 24px rgba(0, 0, 0, 0.1);
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.grid-wrapper {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 20px;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
background-size: 52px 52px;
|
||||||
|
background-repeat: repeat;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
>* {
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
text-align: center;
|
||||||
|
font-size: var(--font-size-small);
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cards-container {
|
||||||
|
margin-top: 30px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.card {
|
||||||
|
background: var(--background-color);
|
||||||
|
backdrop-filter: blur(20px);
|
||||||
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
|
margin: 10px;
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
position: absolute;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-layout-wrapper {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
padding: 10px;
|
||||||
|
|
||||||
|
background: var(--background-color);
|
||||||
|
backdrop-filter: blur(20px);
|
||||||
|
border-radius: 20px;
|
||||||
|
outline: 1px solid var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-layout-container {
|
||||||
|
width: 130px;
|
||||||
|
height: 130px;
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-around;
|
||||||
|
|
||||||
|
align-items: center;
|
||||||
|
padding: 10px;
|
||||||
|
|
||||||
|
outline: 1px solid var(--border-color);
|
||||||
|
outline-offset: -1px;
|
||||||
|
border-radius: 12px;
|
||||||
|
background: var(--background-color);
|
||||||
|
|
||||||
|
.tab-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.label-tab {
|
||||||
|
font-size: var(--font-size-small);
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status {
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: #5a33a3;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.skeleton-wrapper {
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 6px;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.skeleton {
|
||||||
|
height: 2.662480115890503px;
|
||||||
|
|
||||||
|
&:nth-child(1) {
|
||||||
|
width: 40%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:nth-child(2) {
|
||||||
|
width: 70%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:nth-child(3) {
|
||||||
|
width: 40%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-wrapper {
|
||||||
|
margin-top: 20px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
|
||||||
|
.button-group {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
padding: 10px 16px;
|
||||||
|
font-size: var(--font-size-small);
|
||||||
|
font-weight: 600;
|
||||||
|
border-radius: 30px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.save {
|
||||||
|
background-color: #6f42c1;
|
||||||
|
color: white;
|
||||||
|
box-shadow: 0px 2px 8px rgba(111, 66, 193, 0.4);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #5a33a3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.replace {
|
||||||
|
border: 1px solid #6f42c1;
|
||||||
|
color: #6f42c1;
|
||||||
|
background: transparent;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: rgba(111, 66, 193, 0.08);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.cancel {
|
||||||
|
color: red;
|
||||||
|
font-size: var(--font-size-small);
|
||||||
|
font-weight: 500;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
font-size: var(--font-size-small);
|
||||||
|
opacity: 0.7;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.cards-container {
|
||||||
|
margin-top: 30px;
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 150px;
|
||||||
|
|
||||||
|
.card {
|
||||||
|
position: absolute;
|
||||||
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
|
background: white;
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
z-index: 1;
|
||||||
|
|
||||||
|
&:nth-child(1) {
|
||||||
|
left: -10px;
|
||||||
|
transform: scale(0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:nth-child(2) {
|
||||||
|
right: -10px;
|
||||||
|
transform: scale(0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:nth-child(4) {
|
||||||
|
left: 20px;
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:nth-child(5) {
|
||||||
|
right: 20px;
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-layout-wrapper {
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
top: 0;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
z-index: 2;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
padding: 10px;
|
||||||
|
|
||||||
|
background: var(--background-color);
|
||||||
|
backdrop-filter: blur(20px);
|
||||||
|
border-radius: 20px;
|
||||||
|
outline: 1px solid var(--border-color);
|
||||||
|
}
|
||||||
|
}
|
|
@ -33,7 +33,8 @@
|
||||||
@use 'layout/sidebar';
|
@use 'layout/sidebar';
|
||||||
@use 'layout/popup';
|
@use 'layout/popup';
|
||||||
@use 'layout/toast';
|
@use 'layout/toast';
|
||||||
@use 'layout/skeleton.scss';
|
@use 'layout/skeleton';
|
||||||
|
@use 'layout/compareLayoutPopUp';
|
||||||
|
|
||||||
// pages
|
// pages
|
||||||
@use 'pages/dashboard';
|
@use 'pages/dashboard';
|
||||||
|
|
|
@ -22,7 +22,6 @@
|
||||||
|
|
||||||
.floating {
|
.floating {
|
||||||
// width: calc(var(--realTimeViz-container-width) * 0.2px);
|
// width: calc(var(--realTimeViz-container-width) * 0.2px);
|
||||||
|
|
||||||
// transform: scale(min(1, calc(var(--realTimeViz-container-width) / 1000)));
|
// transform: scale(min(1, calc(var(--realTimeViz-container-width) / 1000)));
|
||||||
|
|
||||||
min-width: 230px;
|
min-width: 230px;
|
||||||
|
@ -54,7 +53,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.zone-wrapper {
|
.zone-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
background: var(--background-color);
|
background: var(--background-color);
|
||||||
backdrop-filter: blur(10px);
|
backdrop-filter: blur(10px);
|
||||||
|
@ -63,7 +62,6 @@
|
||||||
left: 50%;
|
left: 50%;
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
border-radius: #{$border-radius-medium};
|
border-radius: #{$border-radius-medium};
|
||||||
|
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
max-width: calc(100% - 500px);
|
max-width: calc(100% - 500px);
|
||||||
z-index: 3;
|
z-index: 3;
|
||||||
|
@ -71,6 +69,10 @@
|
||||||
pointer-events: all;
|
pointer-events: all;
|
||||||
transition: all 0.3s linear;
|
transition: all 0.3s linear;
|
||||||
|
|
||||||
|
&.bottom{
|
||||||
|
bottom: var(--bottomWidth);
|
||||||
|
}
|
||||||
|
|
||||||
&::-webkit-scrollbar {
|
&::-webkit-scrollbar {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
@ -117,6 +119,13 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.zone-container.visualization-playing {
|
||||||
|
bottom: 74px;
|
||||||
|
&.bottom{
|
||||||
|
bottom: var(--bottomWidth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.zone-wrapper.bottom {
|
.zone-wrapper.bottom {
|
||||||
bottom: var(--bottomWidth);
|
bottom: var(--bottomWidth);
|
||||||
}
|
}
|
||||||
|
@ -363,6 +372,9 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Side Buttons
|
// Side Buttons
|
||||||
.side-button-container {
|
.side-button-container {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
interface Asset {
|
||||||
|
modelUuid: string;
|
||||||
|
modelName: string;
|
||||||
|
assetId: string;
|
||||||
|
position: [number, number, number];
|
||||||
|
rotation: [number, number, number];
|
||||||
|
isLocked: boolean;
|
||||||
|
isCollidable: boolean;
|
||||||
|
isVisible: boolean;
|
||||||
|
opacity: number;
|
||||||
|
animations?: string[];
|
||||||
|
animationState?: {
|
||||||
|
current: string;
|
||||||
|
playing: boolean;
|
||||||
|
};
|
||||||
|
eventData?: {
|
||||||
|
type: string;
|
||||||
|
point?: {
|
||||||
|
uuid: string;
|
||||||
|
position: [number, number, number];
|
||||||
|
rotation: [number, number, number];
|
||||||
|
}
|
||||||
|
points?: {
|
||||||
|
uuid: string;
|
||||||
|
position: [number, number, number];
|
||||||
|
rotation: [number, number, number];
|
||||||
|
}[];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
type Assets = Asset[];
|
|
@ -11,7 +11,7 @@ import {
|
||||||
useToggleView,
|
useToggleView,
|
||||||
useToolMode,
|
useToolMode,
|
||||||
} from "../../store/builder/store";
|
} from "../../store/builder/store";
|
||||||
import { usePlayButtonStore } from "../../store/usePlayButtonStore";
|
import useCameraModeStore, { usePlayButtonStore } from "../../store/usePlayButtonStore";
|
||||||
import { detectModifierKeys } from "./detectModifierKeys";
|
import { detectModifierKeys } from "./detectModifierKeys";
|
||||||
import { useSelectedZoneStore } from "../../store/visualization/useZoneStore";
|
import { useSelectedZoneStore } from "../../store/visualization/useZoneStore";
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ const KeyPressListener: React.FC = () => {
|
||||||
const { toggleUILeft, toggleUIRight, setToggleUI } = useToggleStore();
|
const { toggleUILeft, toggleUIRight, setToggleUI } = useToggleStore();
|
||||||
const { setToggleThreeD } = useThreeDStore();
|
const { setToggleThreeD } = useThreeDStore();
|
||||||
const { setToolMode } = useToolMode();
|
const { setToolMode } = useToolMode();
|
||||||
const { setIsPlaying } = usePlayButtonStore();
|
const { isPlaying, setIsPlaying } = usePlayButtonStore();
|
||||||
const { toggleView, setToggleView } = useToggleView();
|
const { toggleView, setToggleView } = useToggleView();
|
||||||
const { setDeleteTool } = useDeleteTool();
|
const { setDeleteTool } = useDeleteTool();
|
||||||
const { setAddAction } = useAddAction();
|
const { setAddAction } = useAddAction();
|
||||||
|
@ -29,6 +29,7 @@ const KeyPressListener: React.FC = () => {
|
||||||
const { setActiveTool } = useActiveTool();
|
const { setActiveTool } = useActiveTool();
|
||||||
const { clearSelectedZone } = useSelectedZoneStore();
|
const { clearSelectedZone } = useSelectedZoneStore();
|
||||||
const { showShortcuts, setShowShortcuts } = useShortcutStore();
|
const { showShortcuts, setShowShortcuts } = useShortcutStore();
|
||||||
|
const { setWalkMode } = useCameraModeStore();
|
||||||
|
|
||||||
const isTextInput = (element: Element | null): boolean =>
|
const isTextInput = (element: Element | null): boolean =>
|
||||||
element instanceof HTMLInputElement ||
|
element instanceof HTMLInputElement ||
|
||||||
|
@ -66,7 +67,7 @@ const KeyPressListener: React.FC = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleBuilderShortcuts = (key: string) => {
|
const handleBuilderShortcuts = (key: string) => {
|
||||||
if (activeModule !== "builder") return;
|
if (activeModule !== "builder" || isPlaying) return;
|
||||||
|
|
||||||
if (key === "TAB") {
|
if (key === "TAB") {
|
||||||
const toggleTo2D = toggleView;
|
const toggleTo2D = toggleView;
|
||||||
|
@ -168,6 +169,7 @@ const KeyPressListener: React.FC = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (keyCombination === "ESCAPE") {
|
if (keyCombination === "ESCAPE") {
|
||||||
|
setWalkMode(false);
|
||||||
setActiveTool("cursor");
|
setActiveTool("cursor");
|
||||||
setActiveSubTool("cursor");
|
setActiveSubTool("cursor");
|
||||||
setIsPlaying(false);
|
setIsPlaying(false);
|
||||||
|
@ -189,7 +191,7 @@ const KeyPressListener: React.FC = () => {
|
||||||
window.addEventListener("keydown", handleKeyPress);
|
window.addEventListener("keydown", handleKeyPress);
|
||||||
return () => window.removeEventListener("keydown", handleKeyPress);
|
return () => window.removeEventListener("keydown", handleKeyPress);
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [activeModule, toggleUIRight, toggleUILeft, toggleView, showShortcuts]);
|
}, [activeModule, toggleUIRight, toggleUILeft, toggleView, showShortcuts, isPlaying]);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue