(null);
- const [openDrop, setOpenDrop] = useState(false);
- const { selectedVersionStore } = useVersionContext();
- const { selectedVersion } = selectedVersionStore();
- const { projectId } = useParams();
-
- // 1. Set UI toggles on initial render
- useEffect(() => {
- setToggleUI(
- localStorage.getItem("navBarUiLeft") !== "false",
- localStorage.getItem("navBarUiRight") !== "false"
- );
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, []);
-
- // 2. Update tool based on subtool and module
- useEffect(() => {
- setActiveTool(activeSubTool);
- setActiveSubTool(activeSubTool);
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [activeModule]);
-
- // 3. Update tools behavior based on selected tool and view mode
- useEffect(() => {
- resetTools();
- updateToolBehavior(activeTool, toggleView);
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [activeTool, toggleView]);
-
- // 4. Dropdown auto-close
- useEffect(() => {
- const handleClickOutside = (e: MouseEvent) => {
- if (
- dropdownRef.current &&
- !dropdownRef.current.contains(e.target as Node)
- ) {
- setOpenDrop(false);
- }
- };
- document.addEventListener("mousedown", handleClickOutside);
- return () => document.removeEventListener("mousedown", handleClickOutside);
- }, []);
-
- const resetTools = () => {
- setToolMode(null);
- setAddAction(null);
- };
-
- const updateToolBehavior = (tool: string, is2D: boolean) => {
- switch (tool) {
- case "cursor":
- is2D ? setToolMode("move") : setToolMode("cursor");
- break;
- case "draw-wall":
- is2D && setToolMode("Wall");
- break;
- case "draw-aisle":
- is2D && setToolMode("Aisle");
- break;
- case "draw-zone":
- is2D && setToolMode("Zone");
- break;
- case "draw-floor":
- is2D && setToolMode("Floor");
- break;
- case "measure":
- setToolMode("MeasurementScale");
- break;
- case "Add pillar":
- if (!is2D) setAddAction("Pillar");
- break;
- case "delete":
- is2D ? setToolMode('2D-Delete') : setToolMode('3D-Delete');
- break;
- }
- };
-
- const toggle2D3D = () => {
- const toggleTo2D = toggleView;
- setToggleView(!toggleTo2D);
- setToggleThreeD(toggleTo2D);
- setToggleUI(toggleTo2D, toggleTo2D);
- if (toggleTo2D) {
- setSelectedWallItem(null);
- setAddAction(null);
- }
- setActiveTool("cursor");
- setActiveSubTool("cursor");
- setToggleUI(
- localStorage.getItem("navBarUiLeft") !== "false",
- localStorage.getItem("navBarUiRight") !== "false"
- );
- };
-
- const renderBuilderTools = () => (
- <>
- {!toggleThreeD && (
-
- setActiveTool("draw-wall")}
- />
- setActiveTool("draw-zone")}
- />
- setActiveTool("draw-aisle")}
- />
- setActiveTool("draw-floor")}
- />
-
- )}
-
- setActiveTool("measure")}
- />
-
- >
- );
-
- const renderSimulationTools = () => (
-
- setActiveTool("pen")}
- />
-
- );
-
- const renderVisualizationTools = () => (
-
-
- handleSaveTemplate({
- addTemplate,
- floatingWidget,
- widgets3D,
- selectedZone,
- templates,
- visualizationSocket,
- projectId,
- versionId: selectedVersion?.versionId || ''
- })
- }
- />
-
- );
-
- const renderModeSwitcher = () => (
-
- toggle view (tab)
- 2d
- 3d
-
- );
-
- const getIconByTool = (tool: string) => {
- switch (tool) {
- case "cursor":
- return CursorIcon;
- case "free-hand":
- return FreeMoveIcon;
- case "delete":
- return DeleteIcon;
- default:
- return CursorIcon;
- }
- };
-
- const getTooltipShortcut = (tool: string) => {
- switch (tool) {
- case "cursor":
- return "v";
- case "free-hand":
- return "h";
- case "delete":
- return "x";
- default:
- return "";
- }
- };
-
- const getIconComponent = (option: string) => {
- switch (option) {
- case "cursor":
- return ;
- case "free-hand":
- return ;
- case "delete":
- return ;
- default:
- return null;
- }
- };
-
- return (
-
-
- {/* Tool Picker (cursor, delete, etc.) */}
- {["cursor", "free-hand", "delete"].map(
- (tool) =>
- activeSubTool === tool && (
-
setActiveTool(tool)}
- />
- )
- )}
- {/* Dropdown Menu */}
- {activeModule !== "visualization" && (
- setOpenDrop(!openDrop)}
- >
-
- {openDrop && (
-
- {["cursor", "free-hand", "delete"].map((option) => (
-
{
- setActiveTool(option);
- setActiveSubTool(option);
- setOpenDrop(false);
- }}
- >
-
- {activeSubTool === option && }
-
- {getIconComponent(option)}
- {option}
-
- ))}
-
- )}
-
- )}
-
-
-
- {activeModule === "builder" && renderBuilderTools()}
- {activeModule === "simulation" && renderSimulationTools()}
- {activeModule === "visualization" && renderVisualizationTools()}
-
-
-
- setActiveTool("comment")}
- />
- {toggleThreeD && (
- setIsPlaying(!isPlaying)}
- />
- )}
-
-
- {activeModule === "builder" && (
- <>
-
- {renderModeSwitcher()}
- >
- )}
-
- );
-};
-
-// Extracted common store logic
-const useStoreHooks = () => {
- return {
- ...useActiveTool(),
- ...useToolMode(),
- ...useAddAction(),
- };
-};
-
-export default Tools;
+import React, { useEffect, useRef, useState } from "react";
+import {
+ AsileIcon,
+ CommentIcon,
+ CursorIcon,
+ DeleteIcon,
+ FloorIcon,
+ FreeMoveIcon,
+ MeasureToolIcon,
+ PenIcon,
+ PlayIcon,
+ SaveTemplateIcon,
+ WallIcon,
+ ZoneIcon,
+} from "../icons/ExportToolsIcons";
+import { ArrowIcon, TickIcon } from "../icons/ExportCommonIcons";
+import useModuleStore, { useThreeDStore } from "../../store/useModuleStore";
+import { handleSaveTemplate } from "../../modules/visualization/functions/handleSaveTemplate";
+import { usePlayButtonStore } from "../../store/usePlayButtonStore";
+import useTemplateStore from "../../store/useTemplateStore";
+import { useSelectedZoneStore } from "../../store/visualization/useZoneStore";
+import {
+ useActiveTool,
+ useAddAction,
+ useSelectedWallItem,
+ useSocketStore,
+ useToggleView,
+ useToolMode,
+ useActiveSubTool,
+ useShortcutStore,
+} from "../../store/builder/store";
+import { useToggleStore } from "../../store/useUIToggleStore";
+import {
+ use3DWidget,
+ useFloatingWidget,
+} from "../../store/visualization/useDroppedObjectsStore";
+import { useParams } from "react-router-dom";
+import { useVersionContext } from "../../modules/builder/version/versionContext";
+
+// Utility component
+const ToolButton = ({
+ toolKey,
+ toolId,
+ icon: Icon,
+ active,
+ onClick,
+ tooltip,
+}: any) => (
+
+ {tooltip}
+
+
+
+
+);
+
+const Tools: React.FC = () => {
+ const { activeModule } = useModuleStore();
+ const { toggleThreeD, setToggleThreeD } = useThreeDStore();
+ const { isPlaying, setIsPlaying } = usePlayButtonStore();
+ const { showShortcuts } = useShortcutStore();
+
+ const {
+ activeTool,
+ setActiveTool,
+ setToolMode,
+ setAddAction,
+ } = useStoreHooks();
+
+ const { setActiveSubTool, activeSubTool } = useActiveSubTool();
+ const { setSelectedWallItem } = useSelectedWallItem();
+ const { setToggleUI } = useToggleStore();
+ const { setToggleView, toggleView } = useToggleView();
+
+ const { addTemplate, templates } = useTemplateStore();
+ const { selectedZone } = useSelectedZoneStore();
+ const { floatingWidget } = useFloatingWidget();
+ const { widgets3D } = use3DWidget();
+ const { visualizationSocket } = useSocketStore();
+
+ const dropdownRef = useRef(null);
+ const [openDrop, setOpenDrop] = useState(false);
+ const { selectedVersionStore } = useVersionContext();
+ const { selectedVersion } = selectedVersionStore();
+ const { projectId } = useParams();
+
+ // 1. Set UI toggles on initial render
+ useEffect(() => {
+ setToggleUI(
+ localStorage.getItem("navBarUiLeft") !== "false",
+ localStorage.getItem("navBarUiRight") !== "false"
+ );
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, []);
+
+ // 2. Update tool based on subtool and module
+ useEffect(() => {
+ setActiveTool(activeSubTool);
+ setActiveSubTool(activeSubTool);
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [activeModule]);
+
+ // 3. Update tools behavior based on selected tool and view mode
+ useEffect(() => {
+ resetTools();
+ updateToolBehavior(activeTool, toggleView);
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [activeTool, toggleView]);
+
+ // 4. Dropdown auto-close
+ useEffect(() => {
+ const handleClickOutside = (e: MouseEvent) => {
+ if (
+ dropdownRef.current &&
+ !dropdownRef.current.contains(e.target as Node)
+ ) {
+ setOpenDrop(false);
+ }
+ };
+ document.addEventListener("mousedown", handleClickOutside);
+ return () => document.removeEventListener("mousedown", handleClickOutside);
+ }, []);
+
+ const resetTools = () => {
+ setToolMode(null);
+ setAddAction(null);
+ };
+
+ const updateToolBehavior = (tool: string, is2D: boolean) => {
+ switch (tool) {
+ case "cursor":
+ is2D ? setToolMode("move") : setToolMode("cursor");
+ break;
+ case "draw-wall":
+ is2D && setToolMode("Wall");
+ break;
+ case "draw-aisle":
+ is2D && setToolMode("Aisle");
+ break;
+ case "draw-zone":
+ is2D && setToolMode("Zone");
+ break;
+ case "draw-floor":
+ is2D && setToolMode("Floor");
+ break;
+ case "measure":
+ setToolMode("MeasurementScale");
+ break;
+ case "Add pillar":
+ if (!is2D) setAddAction("Pillar");
+ break;
+ case "delete":
+ is2D ? setToolMode('2D-Delete') : setToolMode('3D-Delete');
+ break;
+ }
+ };
+
+ const toggle2D3D = () => {
+ const toggleTo2D = toggleView;
+ setToggleView(!toggleTo2D);
+ setToggleThreeD(toggleTo2D);
+ setToggleUI(toggleTo2D, toggleTo2D);
+ if (toggleTo2D) {
+ setSelectedWallItem(null);
+ setAddAction(null);
+ }
+ setActiveTool("cursor");
+ setActiveSubTool("cursor");
+ setToggleUI(
+ localStorage.getItem("navBarUiLeft") !== "false",
+ localStorage.getItem("navBarUiRight") !== "false"
+ );
+ };
+
+ const renderBuilderTools = () => (
+ <>
+ {!toggleThreeD && (
+
+ setActiveTool("draw-wall")}
+ />
+ setActiveTool("draw-zone")}
+ />
+ setActiveTool("draw-aisle")}
+ />
+ setActiveTool("draw-floor")}
+ />
+
+ )}
+
+ setActiveTool("measure")}
+ />
+
+ >
+ );
+
+ const renderSimulationTools = () => (
+
+ setActiveTool("pen")}
+ />
+
+ );
+
+ const renderVisualizationTools = () => (
+
+
+ handleSaveTemplate({
+ addTemplate,
+ floatingWidget,
+ widgets3D,
+ selectedZone,
+ templates,
+ visualizationSocket,
+ projectId,
+ versionId: selectedVersion?.versionId || ''
+ })
+ }
+ />
+
+ );
+
+ const renderModeSwitcher = () => (
+
+ toggle view (tab)
+ 2d
+ 3d
+
+ );
+
+ const getIconByTool = (tool: string) => {
+ switch (tool) {
+ case "cursor":
+ return CursorIcon;
+ case "free-hand":
+ return FreeMoveIcon;
+ case "delete":
+ return DeleteIcon;
+ default:
+ return CursorIcon;
+ }
+ };
+
+ const getTooltipShortcut = (tool: string) => {
+ switch (tool) {
+ case "cursor":
+ return "v";
+ case "free-hand":
+ return "h";
+ case "delete":
+ return "x";
+ default:
+ return "";
+ }
+ };
+
+ const getIconComponent = (option: string) => {
+ switch (option) {
+ case "cursor":
+ return ;
+ case "free-hand":
+ return ;
+ case "delete":
+ return ;
+ default:
+ return null;
+ }
+ };
+
+ return (
+
+
+ {/* Tool Picker (cursor, delete, etc.) */}
+ {["cursor", "free-hand", "delete"].map(
+ (tool) =>
+ activeSubTool === tool && (
+
setActiveTool(tool)}
+ />
+ )
+ )}
+ {/* Dropdown Menu */}
+ {activeModule !== "visualization" && (
+ setOpenDrop(!openDrop)}
+ >
+
+ {openDrop && (
+
+ {["cursor", "free-hand", "delete"].map((option) => (
+
{
+ setActiveTool(option);
+ setActiveSubTool(option);
+ setOpenDrop(false);
+ }}
+ >
+
+ {activeSubTool === option && }
+
+ {getIconComponent(option)}
+ {option}
+
+ ))}
+
+ )}
+
+ )}
+
+
+
+ {activeModule === "builder" && renderBuilderTools()}
+ {activeModule === "simulation" && renderSimulationTools()}
+ {activeModule === "visualization" && renderVisualizationTools()}
+
+
+
+ setActiveTool("comment")}
+ />
+ {toggleThreeD && (
+ setIsPlaying(!isPlaying)}
+ />
+ )}
+
+
+ {activeModule === "builder" && (
+ <>
+
+ {renderModeSwitcher()}
+ >
+ )}
+
+ );
+};
+
+// Extracted common store logic
+const useStoreHooks = () => {
+ return {
+ ...useActiveTool(),
+ ...useToolMode(),
+ ...useAddAction(),
+ };
+};
+
+export default Tools;
diff --git a/src/components/ui/analysis/ProductionCapacity.tsx b/app/src/components/ui/analysis/ProductionCapacity.tsx
similarity index 100%
rename from src/components/ui/analysis/ProductionCapacity.tsx
rename to app/src/components/ui/analysis/ProductionCapacity.tsx
diff --git a/src/components/ui/analysis/ROISummary.tsx b/app/src/components/ui/analysis/ROISummary.tsx
similarity index 100%
rename from src/components/ui/analysis/ROISummary.tsx
rename to app/src/components/ui/analysis/ROISummary.tsx
diff --git a/src/components/ui/analysis/SemiCircleProgress.tsx b/app/src/components/ui/analysis/SemiCircleProgress.tsx
similarity index 100%
rename from src/components/ui/analysis/SemiCircleProgress.tsx
rename to app/src/components/ui/analysis/SemiCircleProgress.tsx
diff --git a/src/components/ui/analysis/ThroughputSummary.tsx b/app/src/components/ui/analysis/ThroughputSummary.tsx
similarity index 100%
rename from src/components/ui/analysis/ThroughputSummary.tsx
rename to app/src/components/ui/analysis/ThroughputSummary.tsx
diff --git a/src/components/ui/collaboration/CommentThreads.tsx b/app/src/components/ui/collaboration/CommentThreads.tsx
similarity index 96%
rename from src/components/ui/collaboration/CommentThreads.tsx
rename to app/src/components/ui/collaboration/CommentThreads.tsx
index c06248c..f5b6237 100644
--- a/src/components/ui/collaboration/CommentThreads.tsx
+++ b/app/src/components/ui/collaboration/CommentThreads.tsx
@@ -1,115 +1,115 @@
-import React, { useEffect, useState } from "react";
-import { getAvatarColor } from "../../../modules/collaboration/functions/getAvatarColor";
-import { getUserData } from "../../../functions/getUserData";
-import { getAllThreads } from "../../../services/factoryBuilder/comments/getAllThreads";
-import { useParams } from "react-router-dom";
-import { useCommentStore } from "../../../store/collaboration/useCommentStore";
-import { getRelativeTime } from "./function/getRelativeTime";
-import { useSelectedComment } from "../../../store/builder/store";
-
-interface CommentThreadsProps {
- commentClicked: () => void;
- comment?: CommentSchema
-}
-
-const CommentThreads: React.FC = ({ commentClicked, comment }) => {
- const [expand, setExpand] = useState(false);
- const commentsedUsers = [{ creatorId: "1" }];
- const { userName } = getUserData();
-
- const CommentDetails = {
- state: "active",
- commentId: "c-1",
- creatorId: "12",
- createdAt: "2 hours ago",
- comment: "Thread check",
- lastUpdatedAt: "string",
- comments: [
- {
- replyId: "string",
- creatorId: "string",
- createdAt: "string",
- lastUpdatedAt: "string",
- comment: "string",
- },
- {
- replyId: "string",
- creatorId: "string",
- createdAt: "string",
- lastUpdatedAt: "string",
- comment: "string",
- },
- ],
- };
-
- function getUsername(userId: string) {
- const UserName = userName?.charAt(0).toUpperCase() || "user";
- return UserName;
- }
-
- function getDetails(type?: "clicked") {
- if (type === "clicked") {
- setExpand(true);
- commentClicked();
- } else {
- setExpand((prev) => !prev);
- }
- }
-
-
- return (
-
-
getDetails()}
- onPointerLeave={() => getDetails()}
- onClick={() => getDetails("clicked")}
- className={`comments-threads-container ${expand ? "open" : "closed"
- } unread`}
- >
-
- {commentsedUsers.map((val, i) => (
-
- {getUsername(val.creatorId)[0]}
-
- ))}
- {/* {commentsedUsers.map((val, i) => (
-
- {getUsername(val.creatorId)[0]}
-
- ))} */}
-
-
-
-
- {userName}
- {/* {getUsername(CommentDetails.creatorId)} */}
-
-
{comment?.createdAt && getRelativeTime(comment.createdAt)}
-
-
{comment?.threadTitle}
- {comment && comment?.comments.length > 0 && (
-
- {comment && comment?.comments.length}{" "}
- {comment && comment?.comments.length === 1 ? "comment" : "replies"}
-
- )}
-
-
-
-
- );
-};
-
-export default CommentThreads;
+import React, { useEffect, useState } from "react";
+import { getAvatarColor } from "../../../modules/collaboration/functions/getAvatarColor";
+import { getUserData } from "../../../functions/getUserData";
+import { getAllThreads } from "../../../services/factoryBuilder/comments/getAllThreads";
+import { useParams } from "react-router-dom";
+import { useCommentStore } from "../../../store/collaboration/useCommentStore";
+import { getRelativeTime } from "./function/getRelativeTime";
+import { useSelectedComment } from "../../../store/builder/store";
+
+interface CommentThreadsProps {
+ commentClicked: () => void;
+ comment?: CommentSchema
+}
+
+const CommentThreads: React.FC = ({ commentClicked, comment }) => {
+ const [expand, setExpand] = useState(false);
+ const commentsedUsers = [{ creatorId: "1" }];
+ const { userName } = getUserData();
+
+ const CommentDetails = {
+ state: "active",
+ commentId: "c-1",
+ creatorId: "12",
+ createdAt: "2 hours ago",
+ comment: "Thread check",
+ lastUpdatedAt: "string",
+ comments: [
+ {
+ replyId: "string",
+ creatorId: "string",
+ createdAt: "string",
+ lastUpdatedAt: "string",
+ comment: "string",
+ },
+ {
+ replyId: "string",
+ creatorId: "string",
+ createdAt: "string",
+ lastUpdatedAt: "string",
+ comment: "string",
+ },
+ ],
+ };
+
+ function getUsername(userId: string) {
+ const UserName = userName?.charAt(0).toUpperCase() || "user";
+ return UserName;
+ }
+
+ function getDetails(type?: "clicked") {
+ if (type === "clicked") {
+ setExpand(true);
+ commentClicked();
+ } else {
+ setExpand((prev) => !prev);
+ }
+ }
+
+
+ return (
+
+
getDetails()}
+ onPointerLeave={() => getDetails()}
+ onClick={() => getDetails("clicked")}
+ className={`comments-threads-container ${expand ? "open" : "closed"
+ } unread`}
+ >
+
+ {commentsedUsers.map((val, i) => (
+
+ {getUsername(val.creatorId)[0]}
+
+ ))}
+ {/* {commentsedUsers.map((val, i) => (
+
+ {getUsername(val.creatorId)[0]}
+
+ ))} */}
+
+
+
+
+ {userName}
+ {/* {getUsername(CommentDetails.creatorId)} */}
+
+
{comment?.createdAt && getRelativeTime(comment.createdAt)}
+
+
{comment?.threadTitle}
+ {comment && comment?.comments.length > 0 && (
+
+ {comment && comment?.comments.length}{" "}
+ {comment && comment?.comments.length === 1 ? "comment" : "replies"}
+
+ )}
+
+
+
+
+ );
+};
+
+export default CommentThreads;
diff --git a/src/components/ui/collaboration/Messages.tsx b/app/src/components/ui/collaboration/Messages.tsx
similarity index 97%
rename from src/components/ui/collaboration/Messages.tsx
rename to app/src/components/ui/collaboration/Messages.tsx
index b3f37e1..aa51deb 100644
--- a/src/components/ui/collaboration/Messages.tsx
+++ b/app/src/components/ui/collaboration/Messages.tsx
@@ -1,279 +1,279 @@
-import React, { useEffect, useRef, useState } from "react";
-import { getAvatarColor } from "../../../modules/collaboration/functions/getAvatarColor";
-import { KebabIcon } from "../../icons/ExportCommonIcons";
-import { adjustHeight } from "./function/textAreaHeightAdjust";
-import { getUserData } from "../../../functions/getUserData";
-import { useParams } from "react-router-dom";
-import { deleteCommentApi } from "../../../services/factoryBuilder/comments/deleteCommentApi";
-import { addCommentsApi } from "../../../services/factoryBuilder/comments/addCommentsApi";
-import { useCommentStore } from "../../../store/collaboration/useCommentStore";
-import { useSelectedComment, useSocketStore } from "../../../store/builder/store";
-import { getRelativeTime } from "./function/getRelativeTime";
-import { editThreadTitleApi } from "../../../services/factoryBuilder/comments/editThreadTitleApi";
-import { useVersionContext } from "../../../modules/builder/version/versionContext";
-
-
-interface MessageProps {
- val: Reply | CommentSchema;
- // val: Reply | CommentSchema;
- i: number;
- setMessages?: React.Dispatch>
- setIsEditable?: React.Dispatch>
- setEditedThread?: React.Dispatch>
- setMode?: React.Dispatch>
- isEditable?: boolean;
- isEditableThread?: boolean
- editedThread?: boolean;
- mode?: 'create' | 'edit' | null
-}
-
-const Messages: React.FC = ({ val, i, setMessages, mode, setIsEditable, setEditedThread, editedThread, isEditableThread, setMode }) => {
-
- const { comments, updateComment, updateReply, removeReply } = useCommentStore();
- const [openOptions, setOpenOptions] = useState(false);
- const { projectId } = useParams();
- const { threadSocket } = useSocketStore();
- const { userName, userId, organization } = getUserData();
- const [isEditComment, setIsEditComment] = useState(false)
- const { selectedComment, setCommentPositionState } = useSelectedComment();
- const { selectedVersionStore } = useVersionContext();
- const { selectedVersion } = selectedVersionStore();
-
- // input
- const [value, setValue] = useState(
- "comment" in val ? val.comment : val.threadTitle
- );
-
- const textareaRef = useRef(null);
- const currentUser = "1";
-
- // const UserName = "username";
-
- useEffect(() => {
- if (textareaRef.current) adjustHeight(textareaRef.current);
- }, [value]);
-
- function handleCancelAction() {
- setCommentPositionState(null)
- setIsEditable && setIsEditable(true);
- setIsEditComment(false)
- }
-
- const handleSaveAction = async () => {
-
- if (!projectId || !selectedVersion) return
-
- if (isEditableThread && editedThread) {
- try {
- // const editThreadTitle = await editThreadTitleApi(projectId, (val as CommentSchema).threadId, value, selectedVersion?.versionId || "")
- // if (editThreadTitle.message == "ThreadTitle updated Successfully") {
- // const editedThread: CommentSchema = {
- // state: 'active',
- // threadId: editThreadTitle.data.replyId,
- // creatorId: userId,
- // createdAt: getRelativeTime(editThreadTitle.data.createdAt),
- // threadTitle: value,
- // lastUpdatedAt: new Date().toISOString(),
- // position: editThreadTitle.data.position,
- // rotation: [0, 0, 0],
- // comments: [],
- // }
- // updateComment((val as CommentSchema).threadId, editedThread)
- // }
- // projectId, userId, threadTitle, organization, threadId
- const threadEdit = {
- projectId,
- userId,
- threadTitle: value,
- organization,
- threadId: (val as CommentSchema).threadId || selectedComment.threadId,
- versionId: selectedVersion?.versionId || ""
- }
-
- threadSocket.emit('v1:thread:updateTitle', threadEdit)
- } catch {
- }
- } else {
- if (mode === "edit") {
- try {
- // const editComments = await addCommentsApi(projectId, value, selectedComment?.threadId, (val as Reply).replyId, selectedVersion?.versionId || "")
- //
- // const commentData = {
- // replyId: `${editComments.data?.replyId}`,
- // creatorId: `${userId}`,
- // createdAt: getRelativeTime(editComments.data?.createdAt),
- // lastUpdatedAt: "2 hrs ago",
- // comment: value,
- // }
-
- // updateReply((val as CommentSchema).threadId, (val as Reply)?.replyId, commentData);
-
- if (threadSocket) {
- // projectId, userId, comment, organization, threadId
- const editComment = {
- projectId,
- userId,
- comment: value,
- organization,
- threadId: selectedComment?.threadId,
- commentId: (val as Reply).replyId ?? "",
- versionId: selectedVersion?.versionId || ""
- }
-
-
- threadSocket.emit("v1-Comment:add", editComment);
- setIsEditable && setIsEditable(true);
- setEditedThread && setEditedThread(false)
- }
-
- } catch {
- }
- }
-
- }
- // setValue("");
- setIsEditComment(false);
- }
-
- const handleDeleteAction = async (replyID: any) => {
- if (!projectId || !selectedVersion) return
- setOpenOptions(false);
- try {
- // const deletedComment = await deleteCommentApi(projectId, selectedComment?.threadId, (val as Reply).replyId , selectedVersion?.versionId || "")
- //
- // if (deletedComment === "'Thread comment deleted Successfully'") {
- // setMessages && setMessages(prev => prev.filter(message => message.replyId !== replyID));
- // removeReply(val.creatorId, replyID)
- // }
- if (threadSocket && setMessages) {
-
-
- // projectId, userId, commentId, organization, threadId
- const deleteComment = {
- projectId,
- userId,
- commentId: (val as Reply).replyId,
- organization,
- threadId: selectedComment?.threadId,
- versionId: selectedVersion?.versionId || ""
- }
- setMessages(prev => {
- // 👈 logs the current state
- return prev.filter(message => message.replyId !== (val as Reply).replyId);
- });
- removeReply(selectedComment?.threadId, (val as Reply).replyId); // Remove listener after response
-
-
- threadSocket.emit("v1-Comment:delete", deleteComment);
- }
- } catch {
-
- }
- }
-
- const handleFocus = (e: React.FocusEvent) => {
- requestAnimationFrame(() => {
- if (textareaRef.current) {
- const length = textareaRef.current.value.length;
- textareaRef.current.setSelectionRange(length, length);
- }
- });
- };
-
- return (
- <>
- {isEditComment ? (
-
-
-
-
-
-
- {
- handleCancelAction();
- }}
- >
- Cancel
-
- {
- handleSaveAction();
- }}
- >
- Save
-
-
-
-
- ) : (
-
-
- {userName?.charAt(0).toUpperCase() || "user"}
-
-
-
-
{userName}
-
{isEditableThread ? getRelativeTime(val.createdAt) : val.createdAt}
-
- {(val as Reply).creatorId === userId && (
-
-
{
- setOpenOptions(!openOptions);
- }}
- >
-
-
- {openOptions && (
-
- {
- e.preventDefault();
- setMode && setMode("edit")
- setOpenOptions(false);
- setEditedThread && setEditedThread(true);
- setIsEditComment(true)
- }}
- >
- Edit
-
- {!(isEditableThread) && {
- handleDeleteAction((val as Reply).replyId);
- }}
- >
- Delete
- }
-
- )}
-
- )}
-
- {"comment" in val ? val.comment : val.threadTitle}
-
-
-
- )}
- >
- );
-};
-
-export default Messages;
+import React, { useEffect, useRef, useState } from "react";
+import { getAvatarColor } from "../../../modules/collaboration/functions/getAvatarColor";
+import { KebabIcon } from "../../icons/ExportCommonIcons";
+import { adjustHeight } from "./function/textAreaHeightAdjust";
+import { getUserData } from "../../../functions/getUserData";
+import { useParams } from "react-router-dom";
+import { deleteCommentApi } from "../../../services/factoryBuilder/comments/deleteCommentApi";
+import { addCommentsApi } from "../../../services/factoryBuilder/comments/addCommentsApi";
+import { useCommentStore } from "../../../store/collaboration/useCommentStore";
+import { useSelectedComment, useSocketStore } from "../../../store/builder/store";
+import { getRelativeTime } from "./function/getRelativeTime";
+import { editThreadTitleApi } from "../../../services/factoryBuilder/comments/editThreadTitleApi";
+import { useVersionContext } from "../../../modules/builder/version/versionContext";
+
+
+interface MessageProps {
+ val: Reply | CommentSchema;
+ // val: Reply | CommentSchema;
+ i: number;
+ setMessages?: React.Dispatch>
+ setIsEditable?: React.Dispatch>
+ setEditedThread?: React.Dispatch>
+ setMode?: React.Dispatch>
+ isEditable?: boolean;
+ isEditableThread?: boolean
+ editedThread?: boolean;
+ mode?: 'create' | 'edit' | null
+}
+
+const Messages: React.FC = ({ val, i, setMessages, mode, setIsEditable, setEditedThread, editedThread, isEditableThread, setMode }) => {
+
+ const { comments, updateComment, updateReply, removeReply } = useCommentStore();
+ const [openOptions, setOpenOptions] = useState(false);
+ const { projectId } = useParams();
+ const { threadSocket } = useSocketStore();
+ const { userName, userId, organization } = getUserData();
+ const [isEditComment, setIsEditComment] = useState(false)
+ const { selectedComment, setCommentPositionState } = useSelectedComment();
+ const { selectedVersionStore } = useVersionContext();
+ const { selectedVersion } = selectedVersionStore();
+
+ // input
+ const [value, setValue] = useState(
+ "comment" in val ? val.comment : val.threadTitle
+ );
+
+ const textareaRef = useRef(null);
+ const currentUser = "1";
+
+ // const UserName = "username";
+
+ useEffect(() => {
+ if (textareaRef.current) adjustHeight(textareaRef.current);
+ }, [value]);
+
+ function handleCancelAction() {
+ setCommentPositionState(null)
+ setIsEditable && setIsEditable(true);
+ setIsEditComment(false)
+ }
+
+ const handleSaveAction = async () => {
+
+ if (!projectId || !selectedVersion) return
+
+ if (isEditableThread && editedThread) {
+ try {
+ // const editThreadTitle = await editThreadTitleApi(projectId, (val as CommentSchema).threadId, value, selectedVersion?.versionId || "")
+ // if (editThreadTitle.message == "ThreadTitle updated Successfully") {
+ // const editedThread: CommentSchema = {
+ // state: 'active',
+ // threadId: editThreadTitle.data.replyId,
+ // creatorId: userId,
+ // createdAt: getRelativeTime(editThreadTitle.data.createdAt),
+ // threadTitle: value,
+ // lastUpdatedAt: new Date().toISOString(),
+ // position: editThreadTitle.data.position,
+ // rotation: [0, 0, 0],
+ // comments: [],
+ // }
+ // updateComment((val as CommentSchema).threadId, editedThread)
+ // }
+ // projectId, userId, threadTitle, organization, threadId
+ const threadEdit = {
+ projectId,
+ userId,
+ threadTitle: value,
+ organization,
+ threadId: (val as CommentSchema).threadId || selectedComment.threadId,
+ versionId: selectedVersion?.versionId || ""
+ }
+
+ threadSocket.emit('v1:thread:updateTitle', threadEdit)
+ } catch {
+ }
+ } else {
+ if (mode === "edit") {
+ try {
+ // const editComments = await addCommentsApi(projectId, value, selectedComment?.threadId, (val as Reply).replyId, selectedVersion?.versionId || "")
+ //
+ // const commentData = {
+ // replyId: `${editComments.data?.replyId}`,
+ // creatorId: `${userId}`,
+ // createdAt: getRelativeTime(editComments.data?.createdAt),
+ // lastUpdatedAt: "2 hrs ago",
+ // comment: value,
+ // }
+
+ // updateReply((val as CommentSchema).threadId, (val as Reply)?.replyId, commentData);
+
+ if (threadSocket) {
+ // projectId, userId, comment, organization, threadId
+ const editComment = {
+ projectId,
+ userId,
+ comment: value,
+ organization,
+ threadId: selectedComment?.threadId,
+ commentId: (val as Reply).replyId ?? "",
+ versionId: selectedVersion?.versionId || ""
+ }
+
+
+ threadSocket.emit("v1-Comment:add", editComment);
+ setIsEditable && setIsEditable(true);
+ setEditedThread && setEditedThread(false)
+ }
+
+ } catch {
+ }
+ }
+
+ }
+ // setValue("");
+ setIsEditComment(false);
+ }
+
+ const handleDeleteAction = async (replyID: any) => {
+ if (!projectId || !selectedVersion) return
+ setOpenOptions(false);
+ try {
+ // const deletedComment = await deleteCommentApi(projectId, selectedComment?.threadId, (val as Reply).replyId , selectedVersion?.versionId || "")
+ //
+ // if (deletedComment === "'Thread comment deleted Successfully'") {
+ // setMessages && setMessages(prev => prev.filter(message => message.replyId !== replyID));
+ // removeReply(val.creatorId, replyID)
+ // }
+ if (threadSocket && setMessages) {
+
+
+ // projectId, userId, commentId, organization, threadId
+ const deleteComment = {
+ projectId,
+ userId,
+ commentId: (val as Reply).replyId,
+ organization,
+ threadId: selectedComment?.threadId,
+ versionId: selectedVersion?.versionId || ""
+ }
+ setMessages(prev => {
+ // 👈 logs the current state
+ return prev.filter(message => message.replyId !== (val as Reply).replyId);
+ });
+ removeReply(selectedComment?.threadId, (val as Reply).replyId); // Remove listener after response
+
+
+ threadSocket.emit("v1-Comment:delete", deleteComment);
+ }
+ } catch {
+
+ }
+ }
+
+ const handleFocus = (e: React.FocusEvent) => {
+ requestAnimationFrame(() => {
+ if (textareaRef.current) {
+ const length = textareaRef.current.value.length;
+ textareaRef.current.setSelectionRange(length, length);
+ }
+ });
+ };
+
+ return (
+ <>
+ {isEditComment ? (
+
+
+
+
+
+
+ {
+ handleCancelAction();
+ }}
+ >
+ Cancel
+
+ {
+ handleSaveAction();
+ }}
+ >
+ Save
+
+
+
+
+ ) : (
+
+
+ {userName?.charAt(0).toUpperCase() || "user"}
+
+
+
+
{userName}
+
{isEditableThread ? getRelativeTime(val.createdAt) : val.createdAt}
+
+ {(val as Reply).creatorId === userId && (
+
+
{
+ setOpenOptions(!openOptions);
+ }}
+ >
+
+
+ {openOptions && (
+
+ {
+ e.preventDefault();
+ setMode && setMode("edit")
+ setOpenOptions(false);
+ setEditedThread && setEditedThread(true);
+ setIsEditComment(true)
+ }}
+ >
+ Edit
+
+ {!(isEditableThread) && {
+ handleDeleteAction((val as Reply).replyId);
+ }}
+ >
+ Delete
+ }
+
+ )}
+
+ )}
+
+ {"comment" in val ? val.comment : val.threadTitle}
+
+
+
+ )}
+ >
+ );
+};
+
+export default Messages;
diff --git a/src/components/ui/collaboration/ThreadChat.tsx b/app/src/components/ui/collaboration/ThreadChat.tsx
similarity index 97%
rename from src/components/ui/collaboration/ThreadChat.tsx
rename to app/src/components/ui/collaboration/ThreadChat.tsx
index 8e4b24b..b055f85 100644
--- a/src/components/ui/collaboration/ThreadChat.tsx
+++ b/app/src/components/ui/collaboration/ThreadChat.tsx
@@ -1,394 +1,394 @@
-import React, { useEffect, useRef, useState } from "react";
-import { CloseIcon, KebabIcon } from "../../icons/ExportCommonIcons";
-import Messages from "./Messages";
-import { ExpandIcon } from "../../icons/SimulationIcons";
-import { adjustHeight } from "./function/textAreaHeightAdjust";
-import { useParams } from "react-router-dom";
-import { useSelectedComment, useSocketStore } from "../../../store/builder/store";
-import { useCommentStore } from "../../../store/collaboration/useCommentStore";
-import { getUserData } from "../../../functions/getUserData";
-import ThreadSocketResponsesDev from "../../../modules/collaboration/socket/threadSocketResponses.dev";
-import { addCommentsApi } from "../../../services/factoryBuilder/comments/addCommentsApi";
-import { deleteThreadApi } from "../../../services/factoryBuilder/comments/deleteThreadApi";
-import { createThreadApi } from "../../../services/factoryBuilder/comments/createThreadApi";
-import { getRelativeTime } from "./function/getRelativeTime";
-import { useVersionContext } from "../../../modules/builder/version/versionContext";
-
-const ThreadChat: React.FC = () => {
- const { userId, organization } = getUserData();
- const [openThreadOptions, setOpenThreadOptions] = useState(false);
- const [inputActive, setInputActive] = useState(false);
- const [value, setValue] = useState("");
- const { addComment, removeReply, removeComment, addReply, comments } = useCommentStore();
- const { selectedComment, setSelectedComment, setCommentPositionState, commentPositionState, position2Dstate } = useSelectedComment()
- const [mode, setMode] = useState<'create' | 'edit' | null>('create');
- const [isEditable, setIsEditable] = useState(false);
- const [editedThread, setEditedThread] = useState(false);
- const textareaRef = useRef(null);
- const wrapperRef = useRef(null);
- const [messages, setMessages] = useState([])
- const { projectId } = useParams();
- const [dragging, setDragging] = useState(false);
- const [selectedDiv, setSelectedDiv] = useState(true);
- const [dragOffset, setDragOffset] = useState({ x: 0, y: 0 });
- const [position, setPosition] = useState({ x: position2Dstate.x, y: position2Dstate.y });
- const { threadSocket } = useSocketStore();
- const modeRef = useRef<'create' | 'edit' | null>(null);
- const messagesRef = useRef(null);
- const { selectedVersionStore } = useVersionContext();
- const { selectedVersion } = selectedVersionStore();
-
- useEffect(() => {
- modeRef.current = mode;
- }, [mode]);
-
- useEffect(() => {
- if (comments.length > 0 && selectedComment) {
-
- const allMessages = comments
- .flatMap((val: any) =>
- val?.threadId === selectedComment?.threadId ? val.comments : []
- )
- .map((c) => {
- return {
- replyId: c._id ?? c.replyId,
- creatorId: c.creatorId || c.userId,
- createdAt: c.createdAt,
- lastUpdatedAt: "1 hr ago",
- comment: c.comment,
- _id: c._id ?? c.replyId,
- };
- });
-
- setMessages(allMessages);
-
- }
-
- }, [])
-
- useEffect(() => {
- if (textareaRef.current) adjustHeight(textareaRef.current);
- }, [value]);
-
- const clamp = (val: number, min: number, max: number) => {
- return Math.min(Math.max(val, min), max);
- };
-
- const handlePointerDown = (event: React.PointerEvent) => {
- if (event.button !== 0) return;
- // Avoid dragging if a button, icon, textarea etc. was clicked
- const target = event.target as HTMLElement;
- if (
- target.closest("button") ||
- target.closest(".sent-button") ||
- target.closest("textarea") ||
- target.closest(".options-button") ||
- target.closest(".options-list") ||
- target.closest(".send-message-wrapper") ||
- target.closest(".options delete")
- ) {
- return;
- }
-
- const wrapper = wrapperRef.current;
- if (!wrapper) return;
-
- const rect = wrapper.getBoundingClientRect();
- const offsetX = event.clientX - rect.left;
- const offsetY = event.clientY - rect.top;
-
- setDragging(true);
- setDragOffset({ x: offsetX, y: offsetY });
-
- wrapper.setPointerCapture(event.pointerId);
- };
-
- const updatePosition = (
- { clientX, clientY }: { clientX: number; clientY: number },
- allowMove: boolean = true
- ) => {
- if (!allowMove || !wrapperRef.current) return;
-
- const container = document.getElementById("work-space-three-d-canvas");
- const wrapper = wrapperRef.current;
- if (!container || !wrapper) return;
-
- const containerRect = container.getBoundingClientRect();
-
- let newX = clientX - containerRect.left - dragOffset.x;
- let newY = clientY - containerRect.top - dragOffset.y;
-
- const maxX = containerRect.width - wrapper.offsetWidth;
- const maxY = containerRect.height - wrapper.offsetHeight;
-
- newX = clamp(newX, 0, maxX);
- newY = clamp(newY, 0, maxY);
-
- setPosition({ x: newX, y: newY });
- };
-
- const handlePointerMove = (e: { clientX: number; clientY: number }) => {
- if (dragging) updatePosition(e, true);
- };
-
- useEffect(() => {
- updatePosition({ clientX: position.x, clientY: position.y }, true);
- }, [selectedComment]);
-
-
- const handlePointerUp = (event: React.PointerEvent) => {
- if (!dragging) return;
- setDragging(false);
- const wrapper = wrapperRef.current;
- if (wrapper) wrapper.releasePointerCapture(event.pointerId);
- };
-
- const handleCreateComments = async (e: any) => {
- e.preventDefault();
- try {
- // const createComments = await addCommentsApi(projectId, value, selectedComment?.threadId, selectedVersion?.versionId || "")/
- // if (createComments.message === 'Thread comments add Successfully' && createComments.data) {
- // const commentData = {
- // replyId: `${createComments.data?._id}`,
- // creatorId: `${selectedComment?.threadId}`,
- // createdAt: "2 hrs ago",
- // lastUpdatedAt: "2 hrs ago",
- // comment: value,
- // }
- // setMessages((prevMessages) => [
- // ...prevMessages,
- // commentData,
- // ]);
- // addReply(selectedComment?.threadId, commentData)
-
- // }
-
- if (threadSocket && mode === "create") {
- const addComment = {
- versionId: selectedVersion?.versionId || "",
- projectId,
- userId,
- comment: value,
- organization,
- threadId: selectedComment?.threadId,
-
- }
-
- threadSocket.emit("v1-Comment:add", addComment);
- }
- } catch {
-
- }
- setInputActive(false)
- }
- const handleDeleteThread = async () => {
- if (!projectId) return;
- try {
- // const deleteThread = await deleteThreadApi(projectId, selectedComment?.threadId, selectedVersion?.versionId || "")
- //
- // if (deleteThread.message === "Thread deleted Successfully") {
- // removeComment(selectedComment?.threadId)
- // setSelectedComment([])
- // }
- console.log('threadSocket:threadChat ', threadSocket);
- if (threadSocket) {
- // projectId, userId, organization, threadId
- const deleteThread = {
- projectId,
- userId,
- organization,
- threadId: selectedComment?.threadId,
- versionId: selectedVersion?.versionId || ""
- }
- setSelectedComment(null)
- removeComment(selectedComment?.threadId)
- threadSocket.emit("v1:thread:delete", deleteThread);
- }
- }
- catch {
-
- }
- }
-
- const handleCreateThread = async (e: any) => {
- e.preventDefault();
- if (!projectId) return;
-
- try {
- // try {
- // const thread = await createThreadApi(
- // projectId,
- // "active",
- // commentPositionState[0].position,
- // [0, 0, 0],
- // value,
- // selectedVersion?.versionId || ""
- // );
- //
- //
-
- // if (thread.message === "Thread created Successfully" && thread?.threadData) {
- //
- // const comment: CommentSchema = {
- // state: 'active',
- // threadId: thread?.threadData?._id,
- // creatorId: userId,
- // createdAt: getRelativeTime(thread.threadData?.createdAt),
- // threadTitle: value,
- // lastUpdatedAt: new Date().toISOString(),
- // position: commentPositionState[0].position,
- // rotation: [0, 0, 0],
- // comments: []
- // }
- // addComment(comment);
- // setCommentPositionState(null);
- // setInputActive(false);
- // setSelectedComment([])
- // }
-
- const createThread = {
- projectId,
- versionId: selectedVersion?.versionId || "",
- userId,
- organization,
- state: "active",
- position: commentPositionState.position,
- rotation: [0, 0, 0],
- threadTitle: value,
- };
-
- if (threadSocket) {
- console.log('createThread: ', createThread);
-
- setInputActive(false);
- threadSocket.emit("v1:thread:create", createThread);
- }
- } catch (err) {
-
- }
- };
-
- const scrollToBottom = () => {
- const messagesWrapper = document.querySelector(".messages-wrapper");
- if (messagesWrapper) {
- messagesWrapper.scrollTop = messagesWrapper.scrollHeight;
- }
- };
-
- useEffect(() => {
- if (messages.length > 0)
- scrollToBottom();
- }, [messages])
-
- return (
- handlePointerMove({ clientX: e.clientX, clientY: e.clientY })}
- onPointerUp={handlePointerUp}
- style={{
- position: "absolute",
- left: position.x,
- top: position.y,
- cursor: dragging ? "grabbing" : "grab",
- userSelect: "none",
- zIndex: 9999,
- }}
- >
-
-
-
Comment
-
-
{
- e.preventDefault();
- setOpenThreadOptions((prev) => !prev);
- }}
- >
-
-
- {openThreadOptions && (
-
-
Mark as Unread
-
Mark as Resolved
-
Delete Thread
-
- )}
-
{
- setSelectedComment(null)
- setCommentPositionState(null)
- }}>
-
-
-
-
-
-
- {selectedComment &&
-
- }
- {messages && messages.map((val, i) => (
-
- ))}
-
-
-
-
-
-
-
- );
-};
-
-export default ThreadChat;
+import React, { useEffect, useRef, useState } from "react";
+import { CloseIcon, KebabIcon } from "../../icons/ExportCommonIcons";
+import Messages from "./Messages";
+import { ExpandIcon } from "../../icons/SimulationIcons";
+import { adjustHeight } from "./function/textAreaHeightAdjust";
+import { useParams } from "react-router-dom";
+import { useSelectedComment, useSocketStore } from "../../../store/builder/store";
+import { useCommentStore } from "../../../store/collaboration/useCommentStore";
+import { getUserData } from "../../../functions/getUserData";
+import ThreadSocketResponsesDev from "../../../modules/collaboration/socket/threadSocketResponses.dev";
+import { addCommentsApi } from "../../../services/factoryBuilder/comments/addCommentsApi";
+import { deleteThreadApi } from "../../../services/factoryBuilder/comments/deleteThreadApi";
+import { createThreadApi } from "../../../services/factoryBuilder/comments/createThreadApi";
+import { getRelativeTime } from "./function/getRelativeTime";
+import { useVersionContext } from "../../../modules/builder/version/versionContext";
+
+const ThreadChat: React.FC = () => {
+ const { userId, organization } = getUserData();
+ const [openThreadOptions, setOpenThreadOptions] = useState(false);
+ const [inputActive, setInputActive] = useState(false);
+ const [value, setValue] = useState("");
+ const { addComment, removeReply, removeComment, addReply, comments } = useCommentStore();
+ const { selectedComment, setSelectedComment, setCommentPositionState, commentPositionState, position2Dstate } = useSelectedComment()
+ const [mode, setMode] = useState<'create' | 'edit' | null>('create');
+ const [isEditable, setIsEditable] = useState(false);
+ const [editedThread, setEditedThread] = useState(false);
+ const textareaRef = useRef(null);
+ const wrapperRef = useRef(null);
+ const [messages, setMessages] = useState([])
+ const { projectId } = useParams();
+ const [dragging, setDragging] = useState(false);
+ const [selectedDiv, setSelectedDiv] = useState(true);
+ const [dragOffset, setDragOffset] = useState({ x: 0, y: 0 });
+ const [position, setPosition] = useState({ x: position2Dstate.x, y: position2Dstate.y });
+ const { threadSocket } = useSocketStore();
+ const modeRef = useRef<'create' | 'edit' | null>(null);
+ const messagesRef = useRef(null);
+ const { selectedVersionStore } = useVersionContext();
+ const { selectedVersion } = selectedVersionStore();
+
+ useEffect(() => {
+ modeRef.current = mode;
+ }, [mode]);
+
+ useEffect(() => {
+ if (comments.length > 0 && selectedComment) {
+
+ const allMessages = comments
+ .flatMap((val: any) =>
+ val?.threadId === selectedComment?.threadId ? val.comments : []
+ )
+ .map((c) => {
+ return {
+ replyId: c._id ?? c.replyId,
+ creatorId: c.creatorId || c.userId,
+ createdAt: c.createdAt,
+ lastUpdatedAt: "1 hr ago",
+ comment: c.comment,
+ _id: c._id ?? c.replyId,
+ };
+ });
+
+ setMessages(allMessages);
+
+ }
+
+ }, [])
+
+ useEffect(() => {
+ if (textareaRef.current) adjustHeight(textareaRef.current);
+ }, [value]);
+
+ const clamp = (val: number, min: number, max: number) => {
+ return Math.min(Math.max(val, min), max);
+ };
+
+ const handlePointerDown = (event: React.PointerEvent) => {
+ if (event.button !== 0) return;
+ // Avoid dragging if a button, icon, textarea etc. was clicked
+ const target = event.target as HTMLElement;
+ if (
+ target.closest("button") ||
+ target.closest(".sent-button") ||
+ target.closest("textarea") ||
+ target.closest(".options-button") ||
+ target.closest(".options-list") ||
+ target.closest(".send-message-wrapper") ||
+ target.closest(".options delete")
+ ) {
+ return;
+ }
+
+ const wrapper = wrapperRef.current;
+ if (!wrapper) return;
+
+ const rect = wrapper.getBoundingClientRect();
+ const offsetX = event.clientX - rect.left;
+ const offsetY = event.clientY - rect.top;
+
+ setDragging(true);
+ setDragOffset({ x: offsetX, y: offsetY });
+
+ wrapper.setPointerCapture(event.pointerId);
+ };
+
+ const updatePosition = (
+ { clientX, clientY }: { clientX: number; clientY: number },
+ allowMove: boolean = true
+ ) => {
+ if (!allowMove || !wrapperRef.current) return;
+
+ const container = document.getElementById("work-space-three-d-canvas");
+ const wrapper = wrapperRef.current;
+ if (!container || !wrapper) return;
+
+ const containerRect = container.getBoundingClientRect();
+
+ let newX = clientX - containerRect.left - dragOffset.x;
+ let newY = clientY - containerRect.top - dragOffset.y;
+
+ const maxX = containerRect.width - wrapper.offsetWidth;
+ const maxY = containerRect.height - wrapper.offsetHeight;
+
+ newX = clamp(newX, 0, maxX);
+ newY = clamp(newY, 0, maxY);
+
+ setPosition({ x: newX, y: newY });
+ };
+
+ const handlePointerMove = (e: { clientX: number; clientY: number }) => {
+ if (dragging) updatePosition(e, true);
+ };
+
+ useEffect(() => {
+ updatePosition({ clientX: position.x, clientY: position.y }, true);
+ }, [selectedComment]);
+
+
+ const handlePointerUp = (event: React.PointerEvent) => {
+ if (!dragging) return;
+ setDragging(false);
+ const wrapper = wrapperRef.current;
+ if (wrapper) wrapper.releasePointerCapture(event.pointerId);
+ };
+
+ const handleCreateComments = async (e: any) => {
+ e.preventDefault();
+ try {
+ // const createComments = await addCommentsApi(projectId, value, selectedComment?.threadId, selectedVersion?.versionId || "")/
+ // if (createComments.message === 'Thread comments add Successfully' && createComments.data) {
+ // const commentData = {
+ // replyId: `${createComments.data?._id}`,
+ // creatorId: `${selectedComment?.threadId}`,
+ // createdAt: "2 hrs ago",
+ // lastUpdatedAt: "2 hrs ago",
+ // comment: value,
+ // }
+ // setMessages((prevMessages) => [
+ // ...prevMessages,
+ // commentData,
+ // ]);
+ // addReply(selectedComment?.threadId, commentData)
+
+ // }
+
+ if (threadSocket && mode === "create") {
+ const addComment = {
+ versionId: selectedVersion?.versionId || "",
+ projectId,
+ userId,
+ comment: value,
+ organization,
+ threadId: selectedComment?.threadId,
+
+ }
+
+ threadSocket.emit("v1-Comment:add", addComment);
+ }
+ } catch {
+
+ }
+ setInputActive(false)
+ }
+ const handleDeleteThread = async () => {
+ if (!projectId) return;
+ try {
+ // const deleteThread = await deleteThreadApi(projectId, selectedComment?.threadId, selectedVersion?.versionId || "")
+ //
+ // if (deleteThread.message === "Thread deleted Successfully") {
+ // removeComment(selectedComment?.threadId)
+ // setSelectedComment([])
+ // }
+ console.log('threadSocket:threadChat ', threadSocket);
+ if (threadSocket) {
+ // projectId, userId, organization, threadId
+ const deleteThread = {
+ projectId,
+ userId,
+ organization,
+ threadId: selectedComment?.threadId,
+ versionId: selectedVersion?.versionId || ""
+ }
+ setSelectedComment(null)
+ removeComment(selectedComment?.threadId)
+ threadSocket.emit("v1:thread:delete", deleteThread);
+ }
+ }
+ catch {
+
+ }
+ }
+
+ const handleCreateThread = async (e: any) => {
+ e.preventDefault();
+ if (!projectId) return;
+
+ try {
+ // try {
+ // const thread = await createThreadApi(
+ // projectId,
+ // "active",
+ // commentPositionState[0].position,
+ // [0, 0, 0],
+ // value,
+ // selectedVersion?.versionId || ""
+ // );
+ //
+ //
+
+ // if (thread.message === "Thread created Successfully" && thread?.threadData) {
+ //
+ // const comment: CommentSchema = {
+ // state: 'active',
+ // threadId: thread?.threadData?._id,
+ // creatorId: userId,
+ // createdAt: getRelativeTime(thread.threadData?.createdAt),
+ // threadTitle: value,
+ // lastUpdatedAt: new Date().toISOString(),
+ // position: commentPositionState[0].position,
+ // rotation: [0, 0, 0],
+ // comments: []
+ // }
+ // addComment(comment);
+ // setCommentPositionState(null);
+ // setInputActive(false);
+ // setSelectedComment([])
+ // }
+
+ const createThread = {
+ projectId,
+ versionId: selectedVersion?.versionId || "",
+ userId,
+ organization,
+ state: "active",
+ position: commentPositionState.position,
+ rotation: [0, 0, 0],
+ threadTitle: value,
+ };
+
+ if (threadSocket) {
+ console.log('createThread: ', createThread);
+
+ setInputActive(false);
+ threadSocket.emit("v1:thread:create", createThread);
+ }
+ } catch (err) {
+
+ }
+ };
+
+ const scrollToBottom = () => {
+ const messagesWrapper = document.querySelector(".messages-wrapper");
+ if (messagesWrapper) {
+ messagesWrapper.scrollTop = messagesWrapper.scrollHeight;
+ }
+ };
+
+ useEffect(() => {
+ if (messages.length > 0)
+ scrollToBottom();
+ }, [messages])
+
+ return (
+ handlePointerMove({ clientX: e.clientX, clientY: e.clientY })}
+ onPointerUp={handlePointerUp}
+ style={{
+ position: "absolute",
+ left: position.x,
+ top: position.y,
+ cursor: dragging ? "grabbing" : "grab",
+ userSelect: "none",
+ zIndex: 9999,
+ }}
+ >
+
+
+
Comment
+
+
{
+ e.preventDefault();
+ setOpenThreadOptions((prev) => !prev);
+ }}
+ >
+
+
+ {openThreadOptions && (
+
+
Mark as Unread
+
Mark as Resolved
+
Delete Thread
+
+ )}
+
{
+ setSelectedComment(null)
+ setCommentPositionState(null)
+ }}>
+
+
+
+
+
+
+ {selectedComment &&
+
+ }
+ {messages && messages.map((val, i) => (
+
+ ))}
+
+
+
+
+
+
+
+ );
+};
+
+export default ThreadChat;
diff --git a/src/components/ui/collaboration/function/getRelativeTime.ts b/app/src/components/ui/collaboration/function/getRelativeTime.ts
similarity index 100%
rename from src/components/ui/collaboration/function/getRelativeTime.ts
rename to app/src/components/ui/collaboration/function/getRelativeTime.ts
diff --git a/src/components/ui/collaboration/function/textAreaHeightAdjust.ts b/app/src/components/ui/collaboration/function/textAreaHeightAdjust.ts
similarity index 97%
rename from src/components/ui/collaboration/function/textAreaHeightAdjust.ts
rename to app/src/components/ui/collaboration/function/textAreaHeightAdjust.ts
index 63640d1..3eb8272 100644
--- a/src/components/ui/collaboration/function/textAreaHeightAdjust.ts
+++ b/app/src/components/ui/collaboration/function/textAreaHeightAdjust.ts
@@ -1,17 +1,17 @@
-export const adjustHeight = (textareaRef: HTMLTextAreaElement) => {
- const el = textareaRef;
- if (el) {
- el.style.height = "auto";
- el.style.height = "38px";
-
- // Clamp to max height for 6 lines
- const lineHeight = 18; // px, adjust if needed
- const maxHeight = lineHeight * 6;
- if (el.scrollHeight > maxHeight) {
- el.style.overflowY = "auto";
- el.style.height = `${maxHeight}px`;
- } else {
- el.style.overflowY = "hidden";
- }
- }
+export const adjustHeight = (textareaRef: HTMLTextAreaElement) => {
+ const el = textareaRef;
+ if (el) {
+ el.style.height = "auto";
+ el.style.height = "38px";
+
+ // Clamp to max height for 6 lines
+ const lineHeight = 18; // px, adjust if needed
+ const maxHeight = lineHeight * 6;
+ if (el.scrollHeight > maxHeight) {
+ el.style.overflowY = "auto";
+ el.style.height = `${maxHeight}px`;
+ } else {
+ el.style.overflowY = "hidden";
+ }
+ }
};
\ No newline at end of file
diff --git a/src/components/ui/compareVersion/Compare.tsx b/app/src/components/ui/compareVersion/Compare.tsx
similarity index 100%
rename from src/components/ui/compareVersion/Compare.tsx
rename to app/src/components/ui/compareVersion/Compare.tsx
diff --git a/src/components/ui/compareVersion/CompareLayOut.tsx b/app/src/components/ui/compareVersion/CompareLayOut.tsx
similarity index 100%
rename from src/components/ui/compareVersion/CompareLayOut.tsx
rename to app/src/components/ui/compareVersion/CompareLayOut.tsx
diff --git a/src/components/ui/compareVersion/ComparisonResult.tsx b/app/src/components/ui/compareVersion/ComparisonResult.tsx
similarity index 100%
rename from src/components/ui/compareVersion/ComparisonResult.tsx
rename to app/src/components/ui/compareVersion/ComparisonResult.tsx
diff --git a/src/components/ui/compareVersion/result-card/EnergyUsage.tsx b/app/src/components/ui/compareVersion/result-card/EnergyUsage.tsx
similarity index 100%
rename from src/components/ui/compareVersion/result-card/EnergyUsage.tsx
rename to app/src/components/ui/compareVersion/result-card/EnergyUsage.tsx
diff --git a/src/components/ui/compareVersion/result-card/PerformanceResult.tsx b/app/src/components/ui/compareVersion/result-card/PerformanceResult.tsx
similarity index 100%
rename from src/components/ui/compareVersion/result-card/PerformanceResult.tsx
rename to app/src/components/ui/compareVersion/result-card/PerformanceResult.tsx
diff --git a/src/components/ui/features/RenameTooltip.tsx b/app/src/components/ui/features/RenameTooltip.tsx
similarity index 95%
rename from src/components/ui/features/RenameTooltip.tsx
rename to app/src/components/ui/features/RenameTooltip.tsx
index 87de122..00be2c2 100644
--- a/src/components/ui/features/RenameTooltip.tsx
+++ b/app/src/components/ui/features/RenameTooltip.tsx
@@ -1,53 +1,53 @@
-import React, { useState } from "react";
-import { RenameIcon } from "../../icons/ContextMenuIcons";
-import {
- useLeftData,
- useTopData,
-} from "../../../store/visualization/useZone3DWidgetStore";
-import { useRenameModeStore } from "../../../store/builder/store";
-
-type RenameTooltipProps = {
- name: string;
- onSubmit: (newName: string) => void;
-};
-
-const RenameTooltip: React.FC = ({ name, onSubmit }) => {
- const [value, setValue] = useState(name);
-
- const { top, setTop } = useTopData();
- const { left, setLeft } = useLeftData();
-
- const handleSubmit = (e: React.FormEvent) => {
- e.preventDefault();
- onSubmit(value.trim());
- setTop(0);
- setLeft(0);
- };
-
- return (
-
- );
-};
-
-export default RenameTooltip;
+import React, { useState } from "react";
+import { RenameIcon } from "../../icons/ContextMenuIcons";
+import {
+ useLeftData,
+ useTopData,
+} from "../../../store/visualization/useZone3DWidgetStore";
+import { useRenameModeStore } from "../../../store/builder/store";
+
+type RenameTooltipProps = {
+ name: string;
+ onSubmit: (newName: string) => void;
+};
+
+const RenameTooltip: React.FC = ({ name, onSubmit }) => {
+ const [value, setValue] = useState(name);
+
+ const { top, setTop } = useTopData();
+ const { left, setLeft } = useLeftData();
+
+ const handleSubmit = (e: React.FormEvent) => {
+ e.preventDefault();
+ onSubmit(value.trim());
+ setTop(0);
+ setLeft(0);
+ };
+
+ return (
+
+ );
+};
+
+export default RenameTooltip;
diff --git a/src/components/ui/inputs/EyeDropInput.tsx b/app/src/components/ui/inputs/EyeDropInput.tsx
similarity index 96%
rename from src/components/ui/inputs/EyeDropInput.tsx
rename to app/src/components/ui/inputs/EyeDropInput.tsx
index ac92397..e029b31 100644
--- a/src/components/ui/inputs/EyeDropInput.tsx
+++ b/app/src/components/ui/inputs/EyeDropInput.tsx
@@ -1,35 +1,35 @@
-import React from "react";
-import { EyeDroperIcon } from "../../icons/ExportCommonIcons";
-
-interface EyeDropInputProps {
- label: string;
- value: string;
- onChange: (value: string) => void;
-}
-
-const EyeDropInput: React.FC = ({
- label = "Object",
- value,
- onChange,
-}) => {
- const handleEyeDropClick = () => {
- const simulatedValue = "picked_value"; // Replace with actual eye dropper logic
- onChange(simulatedValue);
- };
-
- return (
-
- );
-};
-
-export default EyeDropInput;
+import React from "react";
+import { EyeDroperIcon } from "../../icons/ExportCommonIcons";
+
+interface EyeDropInputProps {
+ label: string;
+ value: string;
+ onChange: (value: string) => void;
+}
+
+const EyeDropInput: React.FC = ({
+ label = "Object",
+ value,
+ onChange,
+}) => {
+ const handleEyeDropClick = () => {
+ const simulatedValue = "picked_value"; // Replace with actual eye dropper logic
+ onChange(simulatedValue);
+ };
+
+ return (
+
+ );
+};
+
+export default EyeDropInput;
diff --git a/src/components/ui/inputs/InputRange.tsx b/app/src/components/ui/inputs/InputRange.tsx
similarity index 96%
rename from src/components/ui/inputs/InputRange.tsx
rename to app/src/components/ui/inputs/InputRange.tsx
index 1a2f6f5..a58f35b 100644
--- a/src/components/ui/inputs/InputRange.tsx
+++ b/app/src/components/ui/inputs/InputRange.tsx
@@ -1,92 +1,92 @@
-import React, { useEffect, useState } from "react";
-import * as CONSTANTS from "../../../types/world/worldConstants";
-interface InputToggleProps {
- label: string; // Represents the toggle state (on/off)
- min?: number;
- max?: number;
- onClick?: () => void; // Function to handle toggle clicks
- onChange?: (value: number) => void; // Function to handle toggle clicks
- disabled?: boolean;
- value?: number;
- onPointerUp?: (value: number) => void;
-}
-
-const InputRange: React.FC = ({
- label,
- onClick,
- onChange,
- min,
- max,
- disabled,
- value,
- onPointerUp,
-}) => {
- const [rangeValue, setRangeValue] = useState(value ? value : 5);
-
- function handleChange(e: React.ChangeEvent) {
- const newValue = parseInt(e.target.value); // Parse the value to an integer
-
- setRangeValue(newValue); // Update the local state
-
- if (onChange) {
- onChange(newValue); // Call the onChange function if it exists
- }
- }
- useEffect(() => {
- value && setRangeValue(value);
- }, [value]);
- function handlePointerUp(e: React.PointerEvent) {
- const newValue = parseInt(e.currentTarget.value, 10); // Parse value correctly
-
- if (onPointerUp) {
- onPointerUp(newValue); // Call the callback function if it exists
- }
- }
- function handlekey(e: React.KeyboardEvent) {
- const newValue = parseInt(e.currentTarget.value, 10); // Parse value correctly
-
- if (onPointerUp) {
- onPointerUp(newValue); // Call the callback function if it exists
- }
- }
-
- return (
-
- );
-};
-
-export default InputRange;
+import React, { useEffect, useState } from "react";
+import * as CONSTANTS from "../../../types/world/worldConstants";
+interface InputToggleProps {
+ label: string; // Represents the toggle state (on/off)
+ min?: number;
+ max?: number;
+ onClick?: () => void; // Function to handle toggle clicks
+ onChange?: (value: number) => void; // Function to handle toggle clicks
+ disabled?: boolean;
+ value?: number;
+ onPointerUp?: (value: number) => void;
+}
+
+const InputRange: React.FC = ({
+ label,
+ onClick,
+ onChange,
+ min,
+ max,
+ disabled,
+ value,
+ onPointerUp,
+}) => {
+ const [rangeValue, setRangeValue] = useState(value ? value : 5);
+
+ function handleChange(e: React.ChangeEvent) {
+ const newValue = parseInt(e.target.value); // Parse the value to an integer
+
+ setRangeValue(newValue); // Update the local state
+
+ if (onChange) {
+ onChange(newValue); // Call the onChange function if it exists
+ }
+ }
+ useEffect(() => {
+ value && setRangeValue(value);
+ }, [value]);
+ function handlePointerUp(e: React.PointerEvent) {
+ const newValue = parseInt(e.currentTarget.value, 10); // Parse value correctly
+
+ if (onPointerUp) {
+ onPointerUp(newValue); // Call the callback function if it exists
+ }
+ }
+ function handlekey(e: React.KeyboardEvent) {
+ const newValue = parseInt(e.currentTarget.value, 10); // Parse value correctly
+
+ if (onPointerUp) {
+ onPointerUp(newValue); // Call the callback function if it exists
+ }
+ }
+
+ return (
+
+ );
+};
+
+export default InputRange;
diff --git a/src/components/ui/inputs/InputToggle.tsx b/app/src/components/ui/inputs/InputToggle.tsx
similarity index 96%
rename from src/components/ui/inputs/InputToggle.tsx
rename to app/src/components/ui/inputs/InputToggle.tsx
index d2968bf..07aeed7 100644
--- a/src/components/ui/inputs/InputToggle.tsx
+++ b/app/src/components/ui/inputs/InputToggle.tsx
@@ -1,55 +1,55 @@
-import React from "react";
-
-interface InputToggleProps {
- label: string; // Represents the toggle state (on/off)
- onClick?: () => void; // Function to handle toggle clicks
- value?: boolean;
- inputKey: string;
-}
-
-// Update InputToggle.tsx to be fully controlled
-const InputToggle: React.FC = ({
- label,
- onClick,
- value = false,
- inputKey,
-}) => {
- // Remove internal state and use the value prop directly
- function handleOnClick() {
- if (onClick) onClick();
- }
-
- return (
-
- );
-};
-
-export default InputToggle;
+import React from "react";
+
+interface InputToggleProps {
+ label: string; // Represents the toggle state (on/off)
+ onClick?: () => void; // Function to handle toggle clicks
+ value?: boolean;
+ inputKey: string;
+}
+
+// Update InputToggle.tsx to be fully controlled
+const InputToggle: React.FC = ({
+ label,
+ onClick,
+ value = false,
+ inputKey,
+}) => {
+ // Remove internal state and use the value prop directly
+ function handleOnClick() {
+ if (onClick) onClick();
+ }
+
+ return (
+
+ );
+};
+
+export default InputToggle;
diff --git a/src/components/ui/inputs/InputWithDropDown.tsx b/app/src/components/ui/inputs/InputWithDropDown.tsx
similarity index 96%
rename from src/components/ui/inputs/InputWithDropDown.tsx
rename to app/src/components/ui/inputs/InputWithDropDown.tsx
index 486faea..7d5b7cd 100644
--- a/src/components/ui/inputs/InputWithDropDown.tsx
+++ b/app/src/components/ui/inputs/InputWithDropDown.tsx
@@ -1,90 +1,90 @@
-import React, { useState } from "react";
-import RenameInput from "./RenameInput";
-
-type InputWithDropDownProps = {
- label: string;
- value: string;
- min?: number;
- max?: number;
- step?: number;
- defaultValue?: string;
- disabled?: boolean;
- options?: string[];
- activeOption?: string;
- onClick?: () => void;
- onChange: (newValue: string) => void;
- editableLabel?: boolean;
- placeholder?: string;
-};
-
-const InputWithDropDown: React.FC = ({
- label,
- value,
- min,
- max,
- step,
- defaultValue,
- disabled = false,
- options,
- activeOption,
- onClick,
- onChange,
- editableLabel = false,
- placeholder = "Inherit",
-}) => {
- const [openDropdown, setOpenDropdown] = useState(false);
-
- const separatedWords = label
- .split(/(?=[A-Z])/)
- .map((word) => word.trim())
- .join(" ");
-
- return (
-
- {editableLabel ? (
-
- ) : (
-
- {label}
-
- )}
-
-
-
onChange(e.target.value)}
- placeholder={placeholder}
- />
-
- {activeOption && (
-
setOpenDropdown((prev) => !prev)}
- >
-
{activeOption}
- {options && openDropdown && (
-
- {options.map((option, index) => (
-
- {option}
-
- ))}
-
- )}
-
- )}
-
-
- );
-};
-
-export default InputWithDropDown;
+import React, { useState } from "react";
+import RenameInput from "./RenameInput";
+
+type InputWithDropDownProps = {
+ label: string;
+ value: string;
+ min?: number;
+ max?: number;
+ step?: number;
+ defaultValue?: string;
+ disabled?: boolean;
+ options?: string[];
+ activeOption?: string;
+ onClick?: () => void;
+ onChange: (newValue: string) => void;
+ editableLabel?: boolean;
+ placeholder?: string;
+};
+
+const InputWithDropDown: React.FC = ({
+ label,
+ value,
+ min,
+ max,
+ step,
+ defaultValue,
+ disabled = false,
+ options,
+ activeOption,
+ onClick,
+ onChange,
+ editableLabel = false,
+ placeholder = "Inherit",
+}) => {
+ const [openDropdown, setOpenDropdown] = useState(false);
+
+ const separatedWords = label
+ .split(/(?=[A-Z])/)
+ .map((word) => word.trim())
+ .join(" ");
+
+ return (
+
+ {editableLabel ? (
+
+ ) : (
+
+ {label}
+
+ )}
+
+
+
onChange(e.target.value)}
+ placeholder={placeholder}
+ />
+
+ {activeOption && (
+
setOpenDropdown((prev) => !prev)}
+ >
+
{activeOption}
+ {options && openDropdown && (
+
+ {options.map((option, index) => (
+
+ {option}
+
+ ))}
+
+ )}
+
+ )}
+
+
+ );
+};
+
+export default InputWithDropDown;
diff --git a/src/components/ui/inputs/LabledButton.tsx b/app/src/components/ui/inputs/LabledButton.tsx
similarity index 95%
rename from src/components/ui/inputs/LabledButton.tsx
rename to app/src/components/ui/inputs/LabledButton.tsx
index b9e2e04..0a0f2be 100644
--- a/src/components/ui/inputs/LabledButton.tsx
+++ b/app/src/components/ui/inputs/LabledButton.tsx
@@ -1,31 +1,31 @@
-import React from "react";
-
-interface LabeledButtonProps {
- label: string; // Label for the button
- onClick?: () => void; // Function to call when the button is clicked
- disabled?: boolean; // Optional prop to disable the button
- value?: string;
-}
-
-const LabeledButton: React.FC = ({
- label,
- onClick,
- disabled = false,
- value = "Click here",
-}) => {
- return (
-
-
{label}
-
- {value}
-
-
- );
-};
-
-export default LabeledButton;
+import React from "react";
+
+interface LabeledButtonProps {
+ label: string; // Label for the button
+ onClick?: () => void; // Function to call when the button is clicked
+ disabled?: boolean; // Optional prop to disable the button
+ value?: string;
+}
+
+const LabeledButton: React.FC = ({
+ label,
+ onClick,
+ disabled = false,
+ value = "Click here",
+}) => {
+ return (
+
+
{label}
+
+ {value}
+
+
+ );
+};
+
+export default LabeledButton;
diff --git a/src/components/ui/inputs/LabledDropdown.tsx b/app/src/components/ui/inputs/LabledDropdown.tsx
similarity index 96%
rename from src/components/ui/inputs/LabledDropdown.tsx
rename to app/src/components/ui/inputs/LabledDropdown.tsx
index 7b86cd3..fbe8bbc 100644
--- a/src/components/ui/inputs/LabledDropdown.tsx
+++ b/app/src/components/ui/inputs/LabledDropdown.tsx
@@ -1,49 +1,49 @@
-import React, { useState, useEffect } from "react";
-import RegularDropDown from "./RegularDropDown";
-
-type LabledDropdownProps = {
- defaultOption: string; // Initial active option
- options: string[]; // Array of dropdown options
- label?: string; // Customizable label text
- onSelect?: (option: string) => void; // Callback when option is selected
- className?: string; // Additional className for styling
- disabled?: boolean; // Disable dropdown
- search?: boolean; // Enable/disable search functionality
-};
-
-const LabledDropdown: React.FC = ({
- defaultOption,
- options,
- label = "Type",
- onSelect,
- className = "",
- search = false
-}) => {
- const [activeOption, setActiveOption] = useState(defaultOption);
-
- // Update active option if defaultOption changes
- useEffect(() => {
- setActiveOption(defaultOption);
- }, [defaultOption]);
-
- const handleSelect = (option: string) => {
- setActiveOption(option);
- if (onSelect) {
- onSelect(option);
- }
- };
-
- return (
-
- );
-};
-
+import React, { useState, useEffect } from "react";
+import RegularDropDown from "./RegularDropDown";
+
+type LabledDropdownProps = {
+ defaultOption: string; // Initial active option
+ options: string[]; // Array of dropdown options
+ label?: string; // Customizable label text
+ onSelect?: (option: string) => void; // Callback when option is selected
+ className?: string; // Additional className for styling
+ disabled?: boolean; // Disable dropdown
+ search?: boolean; // Enable/disable search functionality
+};
+
+const LabledDropdown: React.FC = ({
+ defaultOption,
+ options,
+ label = "Type",
+ onSelect,
+ className = "",
+ search = false
+}) => {
+ const [activeOption, setActiveOption] = useState(defaultOption);
+
+ // Update active option if defaultOption changes
+ useEffect(() => {
+ setActiveOption(defaultOption);
+ }, [defaultOption]);
+
+ const handleSelect = (option: string) => {
+ setActiveOption(option);
+ if (onSelect) {
+ onSelect(option);
+ }
+ };
+
+ return (
+
+ );
+};
+
export default LabledDropdown;
\ No newline at end of file
diff --git a/src/components/ui/inputs/MultiEmailInvite.tsx b/app/src/components/ui/inputs/MultiEmailInvite.tsx
similarity index 96%
rename from src/components/ui/inputs/MultiEmailInvite.tsx
rename to app/src/components/ui/inputs/MultiEmailInvite.tsx
index 9fe160c..1251ec6 100644
--- a/src/components/ui/inputs/MultiEmailInvite.tsx
+++ b/app/src/components/ui/inputs/MultiEmailInvite.tsx
@@ -1,143 +1,143 @@
-import React, { useState } from "react";
-import { useParams } from "react-router-dom";
-
-import { getSearchUsers } from "../../../services/factoryBuilder/collab/getSearchUsers";
-import { shareProject } from "../../../services/factoryBuilder/collab/shareProject";
-import { getUserData } from "../../../functions/getUserData";
-
-interface UserData {
- _id: string;
- Email: string;
- userName: string;
-}
-
-interface MultiEmailProps {
- users: Array;
- getData: () => void;
-}
-
-const MultiEmailInvite: React.FC = ({ users, getData }) => {
- const [selectedUsers, setSelectedUsers] = useState([]);
- const [searchResults, setSearchResults] = useState([]);
- const [inputValue, setInputValue] = useState("");
- const [isFocused, setIsFocused] = useState(false);
-
- const { projectId } = useParams();
- const { userId } = getUserData();
-
-
- const handleSearchInput = async (e: React.ChangeEvent) => {
- const value = e.target.value.trim();
- setInputValue(value);
-
- if (value.length < 3) return;
-
- try {
- const result = await getSearchUsers(value);
- const filtered = result?.sharchMail?.filtered || [];
- setSearchResults(filtered);
- } catch (err) {
- console.error("Search failed:", err);
- }
- };
-
- const handleAddUser = (user: UserData) => {
- if (!user || user._id === userId) return;
-
- const isAlreadySelected = selectedUsers.some(u => u._id === user._id);
- const isAlreadyShared = users.some(u => u.userId === user._id);
-
- if (!isAlreadySelected && !isAlreadyShared) {
- setSelectedUsers(prev => [...prev, user]);
- }
-
- setInputValue("");
- };
-
- const handleKeyDown = (e: React.KeyboardEvent) => {
- if ((e.key === "Enter" || e.key === ",") && searchResults.length > 0) {
- e.preventDefault();
- handleAddUser(searchResults[0]);
- }
- };
-
- const handleRemoveUser = (userIdToRemove: string) => {
- setSelectedUsers(prev => prev.filter(user => user._id !== userIdToRemove));
- };
-
- const handleRemoveEmail = (idToRemove: string) => {
- setSelectedUsers((prev: any) => prev.filter((email: any) => email._id !== idToRemove));
- };
-
-
- const validateEmail = (email: string) => {
- const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
- return emailRegex.test(email);
- };
-
- const handleAddEmail = async () => {
- if (!projectId) return;
-
- try {
- await Promise.all(
- selectedUsers.map(user =>
- shareProject(user._id, projectId)
- .then(res => console.log("Shared:", res))
- .catch(err => console.error("Share error:", err))
- )
- );
-
- setSelectedUsers([]);
- setInputValue("");
- setTimeout(getData, 1000);
- } catch (error) {
- console.error("Invite error:", error);
- }
- };
-
-
- return (
-
-
- {selectedUsers.map(user => (
-
- {user.Email}
- handleRemoveUser(user._id)}>×
-
- ))}
-
-
setIsFocused(true)}
- onKeyDown={handleKeyDown}
- placeholder="Enter email and press Enter or comma"
- />
-
-
-
- Add
-
-
- {isFocused && inputValue.length > 2 && searchResults.length > 0 && (
-
- {searchResults.map(user => (
-
{
- handleAddUser(user);
- setIsFocused(false);
- }}
- >
- {user.Email}
-
- ))}
-
- )}
-
- );
-};
-
-
+import React, { useState } from "react";
+import { useParams } from "react-router-dom";
+
+import { getSearchUsers } from "../../../services/factoryBuilder/collab/getSearchUsers";
+import { shareProject } from "../../../services/factoryBuilder/collab/shareProject";
+import { getUserData } from "../../../functions/getUserData";
+
+interface UserData {
+ _id: string;
+ Email: string;
+ userName: string;
+}
+
+interface MultiEmailProps {
+ users: Array;
+ getData: () => void;
+}
+
+const MultiEmailInvite: React.FC = ({ users, getData }) => {
+ const [selectedUsers, setSelectedUsers] = useState([]);
+ const [searchResults, setSearchResults] = useState([]);
+ const [inputValue, setInputValue] = useState("");
+ const [isFocused, setIsFocused] = useState(false);
+
+ const { projectId } = useParams();
+ const { userId } = getUserData();
+
+
+ const handleSearchInput = async (e: React.ChangeEvent) => {
+ const value = e.target.value.trim();
+ setInputValue(value);
+
+ if (value.length < 3) return;
+
+ try {
+ const result = await getSearchUsers(value);
+ const filtered = result?.sharchMail?.filtered || [];
+ setSearchResults(filtered);
+ } catch (err) {
+ console.error("Search failed:", err);
+ }
+ };
+
+ const handleAddUser = (user: UserData) => {
+ if (!user || user._id === userId) return;
+
+ const isAlreadySelected = selectedUsers.some(u => u._id === user._id);
+ const isAlreadyShared = users.some(u => u.userId === user._id);
+
+ if (!isAlreadySelected && !isAlreadyShared) {
+ setSelectedUsers(prev => [...prev, user]);
+ }
+
+ setInputValue("");
+ };
+
+ const handleKeyDown = (e: React.KeyboardEvent) => {
+ if ((e.key === "Enter" || e.key === ",") && searchResults.length > 0) {
+ e.preventDefault();
+ handleAddUser(searchResults[0]);
+ }
+ };
+
+ const handleRemoveUser = (userIdToRemove: string) => {
+ setSelectedUsers(prev => prev.filter(user => user._id !== userIdToRemove));
+ };
+
+ const handleRemoveEmail = (idToRemove: string) => {
+ setSelectedUsers((prev: any) => prev.filter((email: any) => email._id !== idToRemove));
+ };
+
+
+ const validateEmail = (email: string) => {
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
+ return emailRegex.test(email);
+ };
+
+ const handleAddEmail = async () => {
+ if (!projectId) return;
+
+ try {
+ await Promise.all(
+ selectedUsers.map(user =>
+ shareProject(user._id, projectId)
+ .then(res => console.log("Shared:", res))
+ .catch(err => console.error("Share error:", err))
+ )
+ );
+
+ setSelectedUsers([]);
+ setInputValue("");
+ setTimeout(getData, 1000);
+ } catch (error) {
+ console.error("Invite error:", error);
+ }
+ };
+
+
+ return (
+
+
+ {selectedUsers.map(user => (
+
+ {user.Email}
+ handleRemoveUser(user._id)}>×
+
+ ))}
+
+
setIsFocused(true)}
+ onKeyDown={handleKeyDown}
+ placeholder="Enter email and press Enter or comma"
+ />
+
+
+
+ Add
+
+
+ {isFocused && inputValue.length > 2 && searchResults.length > 0 && (
+
+ {searchResults.map(user => (
+
{
+ handleAddUser(user);
+ setIsFocused(false);
+ }}
+ >
+ {user.Email}
+
+ ))}
+
+ )}
+
+ );
+};
+
+
export default MultiEmailInvite;
\ No newline at end of file
diff --git a/src/components/ui/inputs/MultiLevelDropDown.tsx b/app/src/components/ui/inputs/MultiLevelDropDown.tsx
similarity index 96%
rename from src/components/ui/inputs/MultiLevelDropDown.tsx
rename to app/src/components/ui/inputs/MultiLevelDropDown.tsx
index 3be1e17..dcfd3d6 100644
--- a/src/components/ui/inputs/MultiLevelDropDown.tsx
+++ b/app/src/components/ui/inputs/MultiLevelDropDown.tsx
@@ -1,176 +1,176 @@
-import React, { useState, useRef, useEffect } from "react";
-import { ArrowIcon } from "../../icons/ExportCommonIcons";
-
-// Dropdown Item Component
-const DropdownItem = ({
- label,
- onClick,
- disabled = false,
-}: {
- label: string;
- onClick: () => void;
- disabled?: boolean;
-}) => (
- {
- if (!disabled) onClick();
- }}
- style={{
- cursor: disabled ? "not-allowed" : "default",
- opacity: disabled ? 0.5 : 1,
- }}
- >
- {label}
-
-);
-
-// Nested Dropdown Component
-const NestedDropdown = ({
- label,
- fields,
- onSelect,
- disabledFields = [],
-}: {
- label: string;
- fields: string[];
- onSelect: (selectedData: { name: string; fields: string }) => void;
- disabledFields?: string[];
-}) => {
- const [open, setOpen] = useState(false);
-
- return (
-
-
setOpen(!open)}
- >
- {label}
-
-
- {open && (
-
- {fields.map((field) => {
- const isDisabled = disabledFields.includes(`${label}-${field}`);
- return (
- onSelect({ name: label, fields: field })}
- disabled={isDisabled}
- />
- );
- })}
-
- )}
-
- );
-};
-
-// Props type for MultiLevelDropdown
-interface MultiLevelDropdownProps {
- data: Record;
- onSelect: (selectedData: { name: string; fields: string }) => void;
- onUnselect: () => void;
- selectedValue?: { name: string; fields: string };
- allSelections?: Record;
- isLoading?: boolean;
-}
-
-// Main Multi-Level Dropdown Component
-const MultiLevelDropdown = ({
- data,
- onSelect,
- onUnselect,
- selectedValue,
- allSelections = {},
- isLoading = false,
-}: MultiLevelDropdownProps) => {
- const [open, setOpen] = useState(false);
- const dropdownRef = useRef(null);
-
- useEffect(() => {
- const handleClickOutside = (event: MouseEvent) => {
- if (
- dropdownRef.current &&
- !dropdownRef.current.contains(event.target as Node)
- ) {
- setOpen(false);
- }
- };
- document.addEventListener("mousedown", handleClickOutside);
- return () => {
- document.removeEventListener("mousedown", handleClickOutside);
- };
- }, []);
-
- // Handle item selection
- const handleItemSelect = (selectedData: { name: string; fields: string }) => {
- onSelect(selectedData);
- setOpen(false);
- };
-
- // Handle unselect
- const handleItemUnselect = () => {
- onUnselect();
- setOpen(false);
- };
-
- // Determine the display label
- const displayLabel = selectedValue
- ? `${selectedValue.name} - ${selectedValue.fields}`
- : "Dropdown trigger";
-
- // Build list of disabled selections
- const disabledFieldsList = Object.values(allSelections)
- .filter(
- (sel) =>
- !(
- sel.name === selectedValue?.name &&
- sel.fields === selectedValue?.fields
- )
- )
- .map((sel) => `${sel.name}-${sel.fields}`);
-
- return (
-
-
setOpen(!open)}
- >
- {displayLabel}
- ▾
-
- {open && (
-
-
- {isLoading ? (
-
- ) : (
- <>
-
- {Object.entries(data).map(([key, value]) => (
-
- ))}
- >
- )}
-
-
- )}
-
- );
-};
-
-export default MultiLevelDropdown;
+import React, { useState, useRef, useEffect } from "react";
+import { ArrowIcon } from "../../icons/ExportCommonIcons";
+
+// Dropdown Item Component
+const DropdownItem = ({
+ label,
+ onClick,
+ disabled = false,
+}: {
+ label: string;
+ onClick: () => void;
+ disabled?: boolean;
+}) => (
+ {
+ if (!disabled) onClick();
+ }}
+ style={{
+ cursor: disabled ? "not-allowed" : "default",
+ opacity: disabled ? 0.5 : 1,
+ }}
+ >
+ {label}
+
+);
+
+// Nested Dropdown Component
+const NestedDropdown = ({
+ label,
+ fields,
+ onSelect,
+ disabledFields = [],
+}: {
+ label: string;
+ fields: string[];
+ onSelect: (selectedData: { name: string; fields: string }) => void;
+ disabledFields?: string[];
+}) => {
+ const [open, setOpen] = useState(false);
+
+ return (
+
+
setOpen(!open)}
+ >
+ {label}
+
+
+ {open && (
+
+ {fields.map((field) => {
+ const isDisabled = disabledFields.includes(`${label}-${field}`);
+ return (
+ onSelect({ name: label, fields: field })}
+ disabled={isDisabled}
+ />
+ );
+ })}
+
+ )}
+
+ );
+};
+
+// Props type for MultiLevelDropdown
+interface MultiLevelDropdownProps {
+ data: Record;
+ onSelect: (selectedData: { name: string; fields: string }) => void;
+ onUnselect: () => void;
+ selectedValue?: { name: string; fields: string };
+ allSelections?: Record;
+ isLoading?: boolean;
+}
+
+// Main Multi-Level Dropdown Component
+const MultiLevelDropdown = ({
+ data,
+ onSelect,
+ onUnselect,
+ selectedValue,
+ allSelections = {},
+ isLoading = false,
+}: MultiLevelDropdownProps) => {
+ const [open, setOpen] = useState(false);
+ const dropdownRef = useRef(null);
+
+ useEffect(() => {
+ const handleClickOutside = (event: MouseEvent) => {
+ if (
+ dropdownRef.current &&
+ !dropdownRef.current.contains(event.target as Node)
+ ) {
+ setOpen(false);
+ }
+ };
+ document.addEventListener("mousedown", handleClickOutside);
+ return () => {
+ document.removeEventListener("mousedown", handleClickOutside);
+ };
+ }, []);
+
+ // Handle item selection
+ const handleItemSelect = (selectedData: { name: string; fields: string }) => {
+ onSelect(selectedData);
+ setOpen(false);
+ };
+
+ // Handle unselect
+ const handleItemUnselect = () => {
+ onUnselect();
+ setOpen(false);
+ };
+
+ // Determine the display label
+ const displayLabel = selectedValue
+ ? `${selectedValue.name} - ${selectedValue.fields}`
+ : "Dropdown trigger";
+
+ // Build list of disabled selections
+ const disabledFieldsList = Object.values(allSelections)
+ .filter(
+ (sel) =>
+ !(
+ sel.name === selectedValue?.name &&
+ sel.fields === selectedValue?.fields
+ )
+ )
+ .map((sel) => `${sel.name}-${sel.fields}`);
+
+ return (
+
+
setOpen(!open)}
+ >
+ {displayLabel}
+ ▾
+
+ {open && (
+
+
+ {isLoading ? (
+
+ ) : (
+ <>
+
+ {Object.entries(data).map(([key, value]) => (
+
+ ))}
+ >
+ )}
+
+
+ )}
+
+ );
+};
+
+export default MultiLevelDropdown;
diff --git a/src/components/ui/inputs/PreviewSelectionWithUpload.tsx b/app/src/components/ui/inputs/PreviewSelectionWithUpload.tsx
similarity index 96%
rename from src/components/ui/inputs/PreviewSelectionWithUpload.tsx
rename to app/src/components/ui/inputs/PreviewSelectionWithUpload.tsx
index 430e9ce..8065375 100644
--- a/src/components/ui/inputs/PreviewSelectionWithUpload.tsx
+++ b/app/src/components/ui/inputs/PreviewSelectionWithUpload.tsx
@@ -1,75 +1,75 @@
-import React, { useState } from "react";
-import { ArrowIcon } from "../../icons/ExportCommonIcons";
-import LabledDropdown from "./LabledDropdown";
-
-interface PreviewSelectionWithUploadProps {
- preview?: boolean;
- upload?: boolean;
- label?: string;
- onSelect: (option: string) => void;
- defaultOption: string;
- options: string[];
-}
-
-const PreviewSelectionWithUpload: React.FC = ({
- preview = false,
- upload = false,
- onSelect,
- label,
- defaultOption,
- options,
-}) => {
- const [showPreview, setShowPreview] = useState(false);
- return (
-
- {preview && (
- <>
-
setShowPreview(!showPreview)}
- >
- Preview
-
-
- {showPreview && (
-
- )}
- >
- )}
- {upload && (
-
-
-
Upload Product
-
-
- Upload here
-
-
-
- )}
-
-
- );
-};
-
-export default PreviewSelectionWithUpload;
+import React, { useState } from "react";
+import { ArrowIcon } from "../../icons/ExportCommonIcons";
+import LabledDropdown from "./LabledDropdown";
+
+interface PreviewSelectionWithUploadProps {
+ preview?: boolean;
+ upload?: boolean;
+ label?: string;
+ onSelect: (option: string) => void;
+ defaultOption: string;
+ options: string[];
+}
+
+const PreviewSelectionWithUpload: React.FC = ({
+ preview = false,
+ upload = false,
+ onSelect,
+ label,
+ defaultOption,
+ options,
+}) => {
+ const [showPreview, setShowPreview] = useState(false);
+ return (
+
+ {preview && (
+ <>
+
setShowPreview(!showPreview)}
+ >
+ Preview
+
+
+ {showPreview && (
+
+ )}
+ >
+ )}
+ {upload && (
+
+
+
Upload Product
+
+
+ Upload here
+
+
+
+ )}
+
+
+ );
+};
+
+export default PreviewSelectionWithUpload;
diff --git a/src/components/ui/inputs/RegularDropDown.tsx b/app/src/components/ui/inputs/RegularDropDown.tsx
similarity index 96%
rename from src/components/ui/inputs/RegularDropDown.tsx
rename to app/src/components/ui/inputs/RegularDropDown.tsx
index f1be25c..f08cc46 100644
--- a/src/components/ui/inputs/RegularDropDown.tsx
+++ b/app/src/components/ui/inputs/RegularDropDown.tsx
@@ -1,127 +1,127 @@
-import React, { useState, useEffect, useRef } from "react";
-
-interface DropdownProps {
- header: string;
- options: string[];
- onSelect: (option: string) => void;
- search?: boolean;
- onClick?: () => void;
- onChange?: () => void;
-}
-
-const RegularDropDown: React.FC = ({
- header,
- options,
- onSelect,
- search = true,
- onClick,
- onChange,
-}) => {
- const [isOpen, setIsOpen] = useState(false);
- const [selectedOption, setSelectedOption] = useState(null);
- const [searchTerm, setSearchTerm] = useState(""); // State to store search term
- const [filteredOptions, setFilteredOptions] = useState(options); // State for filtered options
- const dropdownRef = useRef(null); // Ref for the dropdown container
-
- // Reset selectedOption when the dropdown closes
- useEffect(() => {
- if (!isOpen) {
- setSelectedOption(null);
- setSearchTerm(""); // Clear the search term when the dropdown closes
- setFilteredOptions(options); // Reset filtered options when the dropdown closes
- }
- }, [isOpen, options]);
-
- // Reset selectedOption when the header prop changes
- useEffect(() => {
- setSelectedOption(null);
- setSearchTerm(""); // Reset search term if header changes
- setFilteredOptions(options); // Reset options if header changes
- }, [header, options]);
-
- // Close dropdown if clicked outside
- useEffect(() => {
- const handleClickOutside = (event: MouseEvent) => {
- if (
- dropdownRef.current &&
- !dropdownRef.current.contains(event.target as Node)
- ) {
- setIsOpen(false);
- }
- };
-
- document.addEventListener("click", handleClickOutside);
-
- return () => {
- document.removeEventListener("click", handleClickOutside);
- };
- }, []);
-
- // Toggle the dropdown
- const toggleDropdown = () => {
- setIsOpen((prev) => !prev);
- };
-
- // Handle option selection
- const handleOptionClick = (option: string) => {
- setSelectedOption(option);
- onSelect(option);
- setIsOpen(false);
- };
-
- // Handle search input change
- const handleSearchChange = (event: React.ChangeEvent) => {
- const term = event.target.value;
- setSearchTerm(term);
-
- // Filter options based on the search term
- const filtered = options.filter((option) =>
- option.toLowerCase().includes(term.toLowerCase())
- );
- setFilteredOptions(filtered);
- };
-
- return (
-
- {/* Dropdown Header */}
-
-
{selectedOption || header}
-
▾
-
-
- {/* Dropdown Options */}
- {isOpen && (
-
- {/* Search Bar */}
- {search && (
-
-
-
- )}
-
- {/* Filtered Options */}
- {filteredOptions.length > 0 ? (
- filteredOptions.map((option, index) => (
-
handleOptionClick(option)}
- >
- {option}
-
- ))
- ) : (
-
No options found
- )}
-
- )}
-
- );
-};
-
-export default RegularDropDown;
+import React, { useState, useEffect, useRef } from "react";
+
+interface DropdownProps {
+ header: string;
+ options: string[];
+ onSelect: (option: string) => void;
+ search?: boolean;
+ onClick?: () => void;
+ onChange?: () => void;
+}
+
+const RegularDropDown: React.FC = ({
+ header,
+ options,
+ onSelect,
+ search = true,
+ onClick,
+ onChange,
+}) => {
+ const [isOpen, setIsOpen] = useState(false);
+ const [selectedOption, setSelectedOption] = useState(null);
+ const [searchTerm, setSearchTerm] = useState(""); // State to store search term
+ const [filteredOptions, setFilteredOptions] = useState(options); // State for filtered options
+ const dropdownRef = useRef(null); // Ref for the dropdown container
+
+ // Reset selectedOption when the dropdown closes
+ useEffect(() => {
+ if (!isOpen) {
+ setSelectedOption(null);
+ setSearchTerm(""); // Clear the search term when the dropdown closes
+ setFilteredOptions(options); // Reset filtered options when the dropdown closes
+ }
+ }, [isOpen, options]);
+
+ // Reset selectedOption when the header prop changes
+ useEffect(() => {
+ setSelectedOption(null);
+ setSearchTerm(""); // Reset search term if header changes
+ setFilteredOptions(options); // Reset options if header changes
+ }, [header, options]);
+
+ // Close dropdown if clicked outside
+ useEffect(() => {
+ const handleClickOutside = (event: MouseEvent) => {
+ if (
+ dropdownRef.current &&
+ !dropdownRef.current.contains(event.target as Node)
+ ) {
+ setIsOpen(false);
+ }
+ };
+
+ document.addEventListener("click", handleClickOutside);
+
+ return () => {
+ document.removeEventListener("click", handleClickOutside);
+ };
+ }, []);
+
+ // Toggle the dropdown
+ const toggleDropdown = () => {
+ setIsOpen((prev) => !prev);
+ };
+
+ // Handle option selection
+ const handleOptionClick = (option: string) => {
+ setSelectedOption(option);
+ onSelect(option);
+ setIsOpen(false);
+ };
+
+ // Handle search input change
+ const handleSearchChange = (event: React.ChangeEvent) => {
+ const term = event.target.value;
+ setSearchTerm(term);
+
+ // Filter options based on the search term
+ const filtered = options.filter((option) =>
+ option.toLowerCase().includes(term.toLowerCase())
+ );
+ setFilteredOptions(filtered);
+ };
+
+ return (
+
+ {/* Dropdown Header */}
+
+
{selectedOption || header}
+
▾
+
+
+ {/* Dropdown Options */}
+ {isOpen && (
+
+ {/* Search Bar */}
+ {search && (
+
+
+
+ )}
+
+ {/* Filtered Options */}
+ {filteredOptions.length > 0 ? (
+ filteredOptions.map((option, index) => (
+
handleOptionClick(option)}
+ >
+ {option}
+
+ ))
+ ) : (
+
No options found
+ )}
+
+ )}
+
+ );
+};
+
+export default RegularDropDown;
diff --git a/src/components/ui/inputs/RenameInput.tsx b/app/src/components/ui/inputs/RenameInput.tsx
similarity index 96%
rename from src/components/ui/inputs/RenameInput.tsx
rename to app/src/components/ui/inputs/RenameInput.tsx
index 637dec3..40332b6 100644
--- a/src/components/ui/inputs/RenameInput.tsx
+++ b/app/src/components/ui/inputs/RenameInput.tsx
@@ -1,83 +1,83 @@
-import React, { useState, useRef, useEffect } from "react";
-
-// interface RenameInputProps {
-// value: string;
-// onRename?: (newText: string) => void;
-// }
-
-interface RenameInputProps {
- value: string;
- onRename?: (newText: string) => void;
- checkDuplicate?: (name: string) => boolean;
- canEdit?: boolean;
-}
-
-const RenameInput: React.FC = ({ value, onRename, checkDuplicate, canEdit = true }) => {
- const [isEditing, setIsEditing] = useState(false);
- const [text, setText] = useState(value);
- const [isDuplicate, setIsDuplicate] = useState(false);
- const inputRef = useRef(null);
-
- useEffect(() => {
- setText(value);
- }, [value]);
-
- useEffect(() => {
- if (checkDuplicate) {
- setIsDuplicate(checkDuplicate(text));
- }
- }, [text, checkDuplicate]);
-
- const handleDoubleClick = () => {
- if (canEdit) {
- setIsEditing(true);
- setTimeout(() => inputRef.current?.focus(), 0);
- }
- };
-
- const handleBlur = () => {
-
- if (isDuplicate) return
- setIsEditing(false);
- if (onRename && !isDuplicate) {
- onRename(text);
- }
- };
-
- const handleChange = (e: React.ChangeEvent) => {
- setText(e.target.value);
- };
-
- const handleKeyDown = (e: React.KeyboardEvent) => {
- if (e.key === "Enter" && !isDuplicate) {
- setIsEditing(false);
- if (onRename) {
- onRename(text);
- }
- }
- };
-
- return (
- <>
- {isEditing ? (
- <>
-
- {/* {isDuplicate && Name already exists!
} */}
- >
- ) : (
-
- {text}
-
- )}
- >
- );
-};
-export default RenameInput
+import React, { useState, useRef, useEffect } from "react";
+
+// interface RenameInputProps {
+// value: string;
+// onRename?: (newText: string) => void;
+// }
+
+interface RenameInputProps {
+ value: string;
+ onRename?: (newText: string) => void;
+ checkDuplicate?: (name: string) => boolean;
+ canEdit?: boolean;
+}
+
+const RenameInput: React.FC = ({ value, onRename, checkDuplicate, canEdit = true }) => {
+ const [isEditing, setIsEditing] = useState(false);
+ const [text, setText] = useState(value);
+ const [isDuplicate, setIsDuplicate] = useState(false);
+ const inputRef = useRef(null);
+
+ useEffect(() => {
+ setText(value);
+ }, [value]);
+
+ useEffect(() => {
+ if (checkDuplicate) {
+ setIsDuplicate(checkDuplicate(text));
+ }
+ }, [text, checkDuplicate]);
+
+ const handleDoubleClick = () => {
+ if (canEdit) {
+ setIsEditing(true);
+ setTimeout(() => inputRef.current?.focus(), 0);
+ }
+ };
+
+ const handleBlur = () => {
+
+ if (isDuplicate) return
+ setIsEditing(false);
+ if (onRename && !isDuplicate) {
+ onRename(text);
+ }
+ };
+
+ const handleChange = (e: React.ChangeEvent) => {
+ setText(e.target.value);
+ };
+
+ const handleKeyDown = (e: React.KeyboardEvent) => {
+ if (e.key === "Enter" && !isDuplicate) {
+ setIsEditing(false);
+ if (onRename) {
+ onRename(text);
+ }
+ }
+ };
+
+ return (
+ <>
+ {isEditing ? (
+ <>
+
+ {/* {isDuplicate && Name already exists!
} */}
+ >
+ ) : (
+
+ {text}
+
+ )}
+ >
+ );
+};
+export default RenameInput
diff --git a/src/components/ui/inputs/Search.tsx b/app/src/components/ui/inputs/Search.tsx
similarity index 96%
rename from src/components/ui/inputs/Search.tsx
rename to app/src/components/ui/inputs/Search.tsx
index ff9c6ef..9bb528e 100644
--- a/src/components/ui/inputs/Search.tsx
+++ b/app/src/components/ui/inputs/Search.tsx
@@ -1,71 +1,71 @@
-import React, { ChangeEvent, useState } from "react";
-import { CloseIcon, SearchIcon } from "../../icons/ExportCommonIcons";
-
-interface SearchProps {
- value?: string; // The current value of the search input
- placeholder?: string; // Placeholder text for the input
- onChange: (value: string) => void; // Callback function to handle input changes
-}
-
-const Search: React.FC = ({
- value = "",
- placeholder = "Search",
- onChange,
-}) => {
- // State to track the input value and focus status
- const [inputValue, setInputValue] = useState(value);
- const [isFocused, setIsFocused] = useState(false);
-
- const handleInputChange = (event: ChangeEvent) => {
- const newValue = event.target.value;
- setInputValue(newValue);
- onChange(newValue); // Call the onChange prop with the new value
- };
-
- const handleClear = () => {
- setInputValue("");
- onChange(""); // Clear the input value
- };
-
- const handleFocus = () => {
- setIsFocused(true); // Set focus state to true
- };
-
- const handleBlur = () => {
- setIsFocused(false); // Set focus state to false
- };
-
- return (
-
-
-
-
-
-
- {inputValue && (
-
-
-
- )}
-
-
- );
-};
-
-export default Search;
+import React, { ChangeEvent, useState } from "react";
+import { CloseIcon, SearchIcon } from "../../icons/ExportCommonIcons";
+
+interface SearchProps {
+ value?: string; // The current value of the search input
+ placeholder?: string; // Placeholder text for the input
+ onChange: (value: string) => void; // Callback function to handle input changes
+}
+
+const Search: React.FC = ({
+ value = "",
+ placeholder = "Search",
+ onChange,
+}) => {
+ // State to track the input value and focus status
+ const [inputValue, setInputValue] = useState(value);
+ const [isFocused, setIsFocused] = useState(false);
+
+ const handleInputChange = (event: ChangeEvent) => {
+ const newValue = event.target.value;
+ setInputValue(newValue);
+ onChange(newValue); // Call the onChange prop with the new value
+ };
+
+ const handleClear = () => {
+ setInputValue("");
+ onChange(""); // Clear the input value
+ };
+
+ const handleFocus = () => {
+ setIsFocused(true); // Set focus state to true
+ };
+
+ const handleBlur = () => {
+ setIsFocused(false); // Set focus state to false
+ };
+
+ return (
+
+
+
+
+
+
+ {inputValue && (
+
+
+
+ )}
+
+
+ );
+};
+
+export default Search;
diff --git a/src/components/ui/inputs/ToggleHeader.tsx b/app/src/components/ui/inputs/ToggleHeader.tsx
similarity index 96%
rename from src/components/ui/inputs/ToggleHeader.tsx
rename to app/src/components/ui/inputs/ToggleHeader.tsx
index d816bd8..d0c5f83 100644
--- a/src/components/ui/inputs/ToggleHeader.tsx
+++ b/app/src/components/ui/inputs/ToggleHeader.tsx
@@ -1,31 +1,31 @@
-import React from "react";
-
-interface ToggleHeaderProps {
- options: string[]; // Array of strings representing the options
- activeOption: string; // The currently active option
- handleClick: (option: string) => void; // Function to handle click events
-}
-
-const ToggleHeader: React.FC = ({
- options,
- activeOption,
- handleClick,
-}) => {
- return (
-
- {options.map((option, index) => (
- handleClick(option)} // Call handleClick when an option is clicked
- >
- {option}
-
- ))}
-
- );
-};
-
-export default ToggleHeader;
+import React from "react";
+
+interface ToggleHeaderProps {
+ options: string[]; // Array of strings representing the options
+ activeOption: string; // The currently active option
+ handleClick: (option: string) => void; // Function to handle click events
+}
+
+const ToggleHeader: React.FC = ({
+ options,
+ activeOption,
+ handleClick,
+}) => {
+ return (
+
+ {options.map((option, index) => (
+ handleClick(option)} // Call handleClick when an option is clicked
+ >
+ {option}
+
+ ))}
+
+ );
+};
+
+export default ToggleHeader;
diff --git a/src/components/ui/list/DropDownList.tsx b/app/src/components/ui/list/DropDownList.tsx
similarity index 96%
rename from src/components/ui/list/DropDownList.tsx
rename to app/src/components/ui/list/DropDownList.tsx
index b158c80..69c38b3 100644
--- a/src/components/ui/list/DropDownList.tsx
+++ b/app/src/components/ui/list/DropDownList.tsx
@@ -1,164 +1,164 @@
-import React, { useEffect, useState } from "react";
-import List from "./List";
-import { AddIcon, ArrowIcon, FocusIcon } from "../../icons/ExportCommonIcons";
-import KebabMenuListMultiSelect from "./KebebMenuListMultiSelect";
-import { useSceneContext } from "../../../modules/scene/sceneContext";
-
-interface DropDownListProps {
- value?: string; // Value to display in the DropDownList
- items?: { id: string; name: string }[]; // Items to display in the dropdown list
- showFocusIcon?: boolean; // Determines if the FocusIcon should be displayed
- showAddIcon?: boolean; // Determines if the AddIcon should be displayed
- showKebabMenu?: boolean; // Determines if the KebabMenuList should be displayed
- kebabMenuItems?: { id: string; name: string }[]; // Items for the KebabMenuList
- defaultOpen?: boolean; // Determines if the dropdown list should be open by default
- listType?: string; // Type of list to display
- remove?: boolean;
-}
-
-interface Zone {
- zoneUuid: string;
- zoneName: string;
- points: [number, number, number][]; // polygon vertices
-}
-interface ZoneData {
- id: string;
- name: string;
- assets: { id: string; name: string; position?: []; rotation?: {} }[];
-}
-
-const DropDownList: React.FC = ({
- value = "Dropdown",
- items = [],
- showFocusIcon = false,
- showAddIcon = true,
- showKebabMenu = true,
- kebabMenuItems = [
- { id: "Buildings", name: "Buildings" },
- { id: "Paths", name: "Paths" },
- { id: "Zones", name: "Zones" },
- ],
- defaultOpen = false,
- listType = "default",
- remove,
-}) => {
- const [isOpen, setIsOpen] = useState(defaultOpen);
-
- const handleToggle = () => {
- setIsOpen((prev) => !prev); // Toggle the state
- };
-
- const [zoneDataList, setZoneDataList] = useState([]);
- const { assetStore, zoneStore } = useSceneContext();
- const { assets } = assetStore();
- const { zones } = zoneStore()
-
-
- const isPointInsidePolygon = (
- point: [number, number],
- polygon: [number, number][]
- ) => {
- let inside = false;
- for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
- const xi = polygon[i][0],
- zi = polygon[i][1];
- const xj = polygon[j][0],
- zj = polygon[j][1];
-
- const intersect =
- // eslint-disable-next-line no-mixed-operators
- zi > point[1] !== zj > point[1] &&
- point[0] < ((xj - xi) * (point[1] - zi)) / (zj - zi + 0.000001) + xi;
-
- if (intersect) inside = !inside;
- }
- return inside;
- };
-
- useEffect(() => {
- const updatedZoneList: ZoneData[] = zones?.map((zone: any) => {
- const polygon2D = zone.points.map((p: [number, number, number]) => [
- p[0],
- p[2],
- ]);
-
- const assetsInZone = assets
- .filter((item: any) => {
- const [x, , z] = item.position;
- return isPointInsidePolygon([x, z], polygon2D as [number, number][]);
- })
- .map((item: any) => ({
- id: item.modelUuid,
- name: item.modelName,
- position: item.position,
- rotation: item.rotation,
- }));
-
- return {
- id: zone.zoneUuid,
- name: zone.zoneName,
- assets: assetsInZone,
- };
- });
-
- setZoneDataList(updatedZoneList);
- }, [zones, assets]);
-
- return (
-
- {/* eslint-disable-next-line */}
-
-
{value}
-
- {showFocusIcon && (
-
-
-
- )}
- {showAddIcon && (
-
- )}
- {showKebabMenu && (
-
-
-
- )}
-
-
-
-
-
- {isOpen && (
-
- {listType === "default" &&
}
- {listType === "outline" && (
- <>
-
-
- >
- )}
-
- )}
-
- );
-};
-
-export default DropDownList;
+import React, { useEffect, useState } from "react";
+import List from "./List";
+import { AddIcon, ArrowIcon, FocusIcon } from "../../icons/ExportCommonIcons";
+import KebabMenuListMultiSelect from "./KebebMenuListMultiSelect";
+import { useSceneContext } from "../../../modules/scene/sceneContext";
+
+interface DropDownListProps {
+ value?: string; // Value to display in the DropDownList
+ items?: { id: string; name: string }[]; // Items to display in the dropdown list
+ showFocusIcon?: boolean; // Determines if the FocusIcon should be displayed
+ showAddIcon?: boolean; // Determines if the AddIcon should be displayed
+ showKebabMenu?: boolean; // Determines if the KebabMenuList should be displayed
+ kebabMenuItems?: { id: string; name: string }[]; // Items for the KebabMenuList
+ defaultOpen?: boolean; // Determines if the dropdown list should be open by default
+ listType?: string; // Type of list to display
+ remove?: boolean;
+}
+
+interface Zone {
+ zoneUuid: string;
+ zoneName: string;
+ points: [number, number, number][]; // polygon vertices
+}
+interface ZoneData {
+ id: string;
+ name: string;
+ assets: { id: string; name: string; position?: []; rotation?: {} }[];
+}
+
+const DropDownList: React.FC = ({
+ value = "Dropdown",
+ items = [],
+ showFocusIcon = false,
+ showAddIcon = true,
+ showKebabMenu = true,
+ kebabMenuItems = [
+ { id: "Buildings", name: "Buildings" },
+ { id: "Paths", name: "Paths" },
+ { id: "Zones", name: "Zones" },
+ ],
+ defaultOpen = false,
+ listType = "default",
+ remove,
+}) => {
+ const [isOpen, setIsOpen] = useState(defaultOpen);
+
+ const handleToggle = () => {
+ setIsOpen((prev) => !prev); // Toggle the state
+ };
+
+ const [zoneDataList, setZoneDataList] = useState([]);
+ const { assetStore, zoneStore } = useSceneContext();
+ const { assets } = assetStore();
+ const { zones } = zoneStore()
+
+
+ const isPointInsidePolygon = (
+ point: [number, number],
+ polygon: [number, number][]
+ ) => {
+ let inside = false;
+ for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
+ const xi = polygon[i][0],
+ zi = polygon[i][1];
+ const xj = polygon[j][0],
+ zj = polygon[j][1];
+
+ const intersect =
+ // eslint-disable-next-line no-mixed-operators
+ zi > point[1] !== zj > point[1] &&
+ point[0] < ((xj - xi) * (point[1] - zi)) / (zj - zi + 0.000001) + xi;
+
+ if (intersect) inside = !inside;
+ }
+ return inside;
+ };
+
+ useEffect(() => {
+ const updatedZoneList: ZoneData[] = zones?.map((zone: any) => {
+ const polygon2D = zone.points.map((p: [number, number, number]) => [
+ p[0],
+ p[2],
+ ]);
+
+ const assetsInZone = assets
+ .filter((item: any) => {
+ const [x, , z] = item.position;
+ return isPointInsidePolygon([x, z], polygon2D as [number, number][]);
+ })
+ .map((item: any) => ({
+ id: item.modelUuid,
+ name: item.modelName,
+ position: item.position,
+ rotation: item.rotation,
+ }));
+
+ return {
+ id: zone.zoneUuid,
+ name: zone.zoneName,
+ assets: assetsInZone,
+ };
+ });
+
+ setZoneDataList(updatedZoneList);
+ }, [zones, assets]);
+
+ return (
+
+ {/* eslint-disable-next-line */}
+
+
{value}
+
+ {showFocusIcon && (
+
+
+
+ )}
+ {showAddIcon && (
+
+ )}
+ {showKebabMenu && (
+
+
+
+ )}
+
+
+
+
+
+ {isOpen && (
+
+ {listType === "default" &&
}
+ {listType === "outline" && (
+ <>
+
+
+ >
+ )}
+
+ )}
+
+ );
+};
+
+export default DropDownList;
diff --git a/src/components/ui/list/KebebMenuList.tsx b/app/src/components/ui/list/KebebMenuList.tsx
similarity index 96%
rename from src/components/ui/list/KebebMenuList.tsx
rename to app/src/components/ui/list/KebebMenuList.tsx
index 0e45ed0..2597eb3 100644
--- a/src/components/ui/list/KebebMenuList.tsx
+++ b/app/src/components/ui/list/KebebMenuList.tsx
@@ -1,45 +1,45 @@
-import React, { useState } from "react";
-import { KebebIcon } from "../../icons/ExportCommonIcons";
-
-interface KebabMenuListProps {
- items: string[]; // Array of menu items
- onSelect?: (item: string) => void; // Callback when a menu item is selected
-}
-
-const KebabMenuList: React.FC = ({ items, onSelect }) => {
- const [isOpen, setIsOpen] = useState(false);
-
- const handleToggle = () => {
- setIsOpen((prev) => !prev);
- };
-
- const handleItemClick = (item: string) => {
- if (onSelect) {
- onSelect(item);
- }
- setIsOpen(false); // Close menu after selection
- };
-
- return (
-
-
-
-
- {isOpen && (
-
- {items.map((item, index) => (
-
handleItemClick(item)}
- >
- {item}
-
- ))}
-
- )}
-
- );
-};
-
-export default KebabMenuList;
+import React, { useState } from "react";
+import { KebebIcon } from "../../icons/ExportCommonIcons";
+
+interface KebabMenuListProps {
+ items: string[]; // Array of menu items
+ onSelect?: (item: string) => void; // Callback when a menu item is selected
+}
+
+const KebabMenuList: React.FC = ({ items, onSelect }) => {
+ const [isOpen, setIsOpen] = useState(false);
+
+ const handleToggle = () => {
+ setIsOpen((prev) => !prev);
+ };
+
+ const handleItemClick = (item: string) => {
+ if (onSelect) {
+ onSelect(item);
+ }
+ setIsOpen(false); // Close menu after selection
+ };
+
+ return (
+
+
+
+
+ {isOpen && (
+
+ {items.map((item, index) => (
+
handleItemClick(item)}
+ >
+ {item}
+
+ ))}
+
+ )}
+
+ );
+};
+
+export default KebabMenuList;
diff --git a/src/components/ui/list/KebebMenuListMultiSelect.tsx b/app/src/components/ui/list/KebebMenuListMultiSelect.tsx
similarity index 96%
rename from src/components/ui/list/KebebMenuListMultiSelect.tsx
rename to app/src/components/ui/list/KebebMenuListMultiSelect.tsx
index 5f28e7f..a4d82be 100644
--- a/src/components/ui/list/KebebMenuListMultiSelect.tsx
+++ b/app/src/components/ui/list/KebebMenuListMultiSelect.tsx
@@ -1,82 +1,82 @@
-import React, { useEffect, useRef, useState } from "react";
-import { KebebIcon, TickIcon } from "../../icons/ExportCommonIcons";
-
-interface KebabMenuListMultiSelectProps {
- items: { id: string; name: string }[]; // Array of menu items with id and name
- onSelectionChange?: (selectedItems: string[]) => void; // Callback for selected items
-}
-
-const KebabMenuListMultiSelect: React.FC = ({
- items,
- onSelectionChange,
-}) => {
- const [isOpen, setIsOpen] = useState(false);
- const [selectedItems, setSelectedItems] = useState([]);
- const menuRef = useRef(null); // Ref to track the container
-
- const handleToggle = () => {
- setIsOpen((prev) => !prev);
- };
-
- const handleItemToggle = (id: string) => {
- setSelectedItems((prevSelected) => {
- const isAlreadySelected = prevSelected.includes(id);
- const updatedSelection = isAlreadySelected
- ? prevSelected.filter((item) => item !== id) // Deselect if already selected
- : [...prevSelected, id]; // Add to selection if not selected
-
- if (onSelectionChange) {
- onSelectionChange(updatedSelection);
- }
-
- return updatedSelection;
- });
- };
-
- // Close menu if clicked outside
- useEffect(() => {
- const handleClickOutside = (event: MouseEvent) => {
- if (menuRef.current && !menuRef.current.contains(event.target as Node)) {
- setIsOpen(false);
- }
- };
-
- document.addEventListener("mousedown", handleClickOutside);
- return () => {
- document.removeEventListener("mousedown", handleClickOutside);
- };
- }, []);
-
- return (
-
-
-
-
- {isOpen && (
-
- {items.map((item) => (
-
handleItemToggle(item.id)}
- >
-
handleItemToggle(item.id)}
- />
-
- {selectedItems.includes(item.id) && }
-
- {item.name}
-
- ))}
-
- )}
-
- );
-};
-
-export default KebabMenuListMultiSelect;
+import React, { useEffect, useRef, useState } from "react";
+import { KebebIcon, TickIcon } from "../../icons/ExportCommonIcons";
+
+interface KebabMenuListMultiSelectProps {
+ items: { id: string; name: string }[]; // Array of menu items with id and name
+ onSelectionChange?: (selectedItems: string[]) => void; // Callback for selected items
+}
+
+const KebabMenuListMultiSelect: React.FC = ({
+ items,
+ onSelectionChange,
+}) => {
+ const [isOpen, setIsOpen] = useState(false);
+ const [selectedItems, setSelectedItems] = useState([]);
+ const menuRef = useRef(null); // Ref to track the container
+
+ const handleToggle = () => {
+ setIsOpen((prev) => !prev);
+ };
+
+ const handleItemToggle = (id: string) => {
+ setSelectedItems((prevSelected) => {
+ const isAlreadySelected = prevSelected.includes(id);
+ const updatedSelection = isAlreadySelected
+ ? prevSelected.filter((item) => item !== id) // Deselect if already selected
+ : [...prevSelected, id]; // Add to selection if not selected
+
+ if (onSelectionChange) {
+ onSelectionChange(updatedSelection);
+ }
+
+ return updatedSelection;
+ });
+ };
+
+ // Close menu if clicked outside
+ useEffect(() => {
+ const handleClickOutside = (event: MouseEvent) => {
+ if (menuRef.current && !menuRef.current.contains(event.target as Node)) {
+ setIsOpen(false);
+ }
+ };
+
+ document.addEventListener("mousedown", handleClickOutside);
+ return () => {
+ document.removeEventListener("mousedown", handleClickOutside);
+ };
+ }, []);
+
+ return (
+
+
+
+
+ {isOpen && (
+
+ {items.map((item) => (
+
handleItemToggle(item.id)}
+ >
+
handleItemToggle(item.id)}
+ />
+
+ {selectedItems.includes(item.id) && }
+
+ {item.name}
+
+ ))}
+
+ )}
+
+ );
+};
+
+export default KebabMenuListMultiSelect;
diff --git a/src/components/ui/list/List.tsx b/app/src/components/ui/list/List.tsx
similarity index 97%
rename from src/components/ui/list/List.tsx
rename to app/src/components/ui/list/List.tsx
index fc0f5ec..d45d896 100644
--- a/src/components/ui/list/List.tsx
+++ b/app/src/components/ui/list/List.tsx
@@ -1,323 +1,323 @@
-import React, { useEffect, useState } from "react";
-import RenameInput from "../inputs/RenameInput";
-
-import { useSelectedZoneStore } from "../../../store/visualization/useZoneStore";
-import { getZoneData } from "../../../services/visulization/zone/getZones";
-import useModuleStore, {
- useSubModuleStore,
-} from "../../../store/useModuleStore";
-import {
- ArrowIcon,
- EyeIcon,
- LockIcon,
- RemoveIcon,
-} from "../../icons/ExportCommonIcons";
-import {
- useZoneAssetId,
-} from "../../../store/builder/store";
-import { zoneCameraUpdate } from "../../../services/visulization/zone/zoneCameraUpdation";
-import { setAssetsApi } from "../../../services/factoryBuilder/asset/floorAsset/setAssetsApi";
-import { useParams } from "react-router-dom";
-import { getUserData } from "../../../functions/getUserData";
-import { useSceneContext } from "../../../modules/scene/sceneContext";
-import { useVersionContext } from "../../../modules/builder/version/versionContext";
-
-interface Asset {
- id: string;
- name: string;
- position?: [number, number, number]; // Proper 3D vector
- rotation?: { x: number; y: number; z: number }; // Proper rotation format
-}
-
-interface ZoneItem {
- id: string;
- name: string;
- assets?: Asset[];
- active?: boolean;
-}
-
-interface ListProps {
- items?: ZoneItem[];
- remove?: boolean;
-}
-
-const List: React.FC = ({ items = [], remove }) => {
- const { activeModule } = useModuleStore();
- const { selectedZone, setSelectedZone } = useSelectedZoneStore();
- const { zoneAssetId, setZoneAssetId } = useZoneAssetId();
-
- const { setSubModule } = useSubModuleStore();
- const [expandedZones, setExpandedZones] = useState>({});
- const { projectId } = useParams();
- const { assetStore } = useSceneContext();
- const { setName } = assetStore();
- const { organization } = getUserData();
- const { selectedVersionStore } = useVersionContext();
- const { selectedVersion } = selectedVersionStore();
- const { zoneStore } = useSceneContext();
- const { zones, setZoneName } = zoneStore()
-
- useEffect(() => {
- useSelectedZoneStore.getState().setSelectedZone({
- zoneName: "",
- activeSides: [],
- panelOrder: [],
- lockedPanels: [],
- zoneUuid: "",
- zoneViewPortTarget: [],
- zoneViewPortPosition: [],
- widgets: [],
- });
- }, [activeModule]);
-
- const toggleZoneExpansion = (zoneUuid: string) => {
- setExpandedZones((prev) => ({
- ...prev,
- [zoneUuid]: !prev[zoneUuid],
- }));
- };
-
- async function handleSelectZone(id: string) {
- try {
- if (selectedZone?.zoneUuid === id) {
- return;
- }
-
- setSubModule("zoneProperties");
-
- let response = await getZoneData(id, organization, projectId, selectedVersion?.versionId || "");
- setSelectedZone({
- zoneName: response?.zoneName,
- activeSides: response?.activeSides ?? [],
- panelOrder: response?.panelOrder ?? [],
- lockedPanels: response?.lockedPanels ?? [],
- widgets: response?.widgets ?? [],
- zoneUuid: response?.zoneUuid,
- zoneViewPortTarget: response?.viewPortTarget ?? [],
- zoneViewPortPosition: response?.viewPortPosition ?? [],
- });
- } catch (error) {
- echo.error("Failed to select zone");
- console.log(error);
- }
- }
-
- function handleAssetClick(asset: Asset) {
- setZoneAssetId(asset);
- }
-
- async function handleZoneNameChange(newName: string) {
- const isDuplicate = zones.some(
- (zone: any) =>
- zone.zoneName?.trim().toLowerCase() === newName?.trim().toLowerCase() &&
- zone.zoneUuid !== selectedZone.zoneUuid
- );
-
- if (isDuplicate) {
- alert("Zone name already exists. Please choose a different name.");
- return; // DO NOT update state
- }
- const zonesdata = {
- zoneUuid: selectedZone.zoneUuid,
- zoneName: newName,
- };
- const response = await zoneCameraUpdate(zonesdata, organization, projectId, selectedVersion?.versionId || "");
- if (response.message === "zone updated") {
- setSelectedZone((prev) => ({ ...prev, zoneName: newName }));
- setZoneName(selectedZone.zoneUuid, newName)
- // setZones((prevZones: any[]) =>
- // prevZones.map((zone) =>
- // zone.zoneUuid === selectedZone.zoneUuid
- // ? { ...zone, zoneName: newName }
- // : zone
- // )
- // );
- }
- }
-
- async function handleZoneAssetName(newName: string) {
-
- if (zoneAssetId?.id) {
- let response = await setAssetsApi({
- modelUuid: zoneAssetId.id,
- modelName: newName,
- projectId
- });
- // console.log("response: ", response);
-
- setName(zoneAssetId.id, response.modelName);
- }
- }
- const checkZoneNameDuplicate = (name: string) => {
- return zones.some(
- (zone: any) =>
- zone.zoneName?.trim().toLowerCase() === name?.trim().toLowerCase() &&
- zone.zoneUuid !== selectedZone.zoneUuid
- );
- };
-
- useEffect(() => {
- let drag = false;
- let isLeftMouseDown = false;
-
- const contextClassNames = ["list-wrapper", "zone-properties-container", "list-container"];
-
- const isOutsideClick = (target: EventTarget | null) => {
- if (!(target instanceof HTMLElement)) return true;
- return !contextClassNames.some(className =>
- target.closest(`.${className}`)
- );
- };
-
- const onMouseDown = (evt: MouseEvent) => {
- if (evt.button === 0) {
- isLeftMouseDown = true;
- drag = false;
- }
- };
-
- const onMouseMove = () => {
- if (isLeftMouseDown) {
- drag = true;
- }
- };
-
- const onMouseUp = (evt: MouseEvent) => {
- if (evt.button === 0) {
- isLeftMouseDown = false;
- if (drag) return;
-
- if (isOutsideClick(evt.target)) {
- // Clear selected zone
- setSelectedZone({
- zoneUuid: '',
- zoneName: '',
- activeSides: [],
- panelOrder: [],
- lockedPanels: [],
- widgets: [],
- zoneViewPortTarget: [],
- zoneViewPortPosition: []
- });
- setZoneAssetId({
- id: '',
- name: '',
- });
- setSubModule("properties")
- }
- }
- };
-
- if (selectedZone.zoneName! === '' && activeModule === 'Builder') {
- document.addEventListener('mousedown', onMouseDown);
- document.addEventListener('mousemove', onMouseMove);
- document.addEventListener('mouseup', onMouseUp);
- }
-
- return () => {
- document.removeEventListener('mousedown', onMouseDown);
- document.removeEventListener('mousemove', onMouseMove);
- document.removeEventListener('mouseup', onMouseUp);
- };
- }, [selectedZone, activeModule]);
-
-
- return (
- <>
- {items?.length > 0 ? (
-
- {items?.map((item) => (
-
- {
- handleSelectZone(item.id);
- toggleZoneExpansion(item.id);
- }}
- >
-
-
-
-
-
-
-
-
-
-
-
-
-
- {remove && (
-
-
-
- )}
- {item.assets && item.assets.length > 0 && (
-
toggleZoneExpansion(item.id)}
- >
-
-
- )}
-
-
-
- {/* Nested assets list - only shown when expanded */}
- {item.assets &&
- item.assets.length > 0 &&
- expandedZones[item.id] && (
-
- {item.assets.map((asset) => (
-
-
-
handleAssetClick(asset)}
- >
-
-
-
-
-
-
-
-
-
- {remove && (
-
-
-
- )}
-
-
-
- ))}
-
- )}
-
- ))}
-
- ) : (
-
- )}
- >
- );
-};
-
-export default List;
+import React, { useEffect, useState } from "react";
+import RenameInput from "../inputs/RenameInput";
+
+import { useSelectedZoneStore } from "../../../store/visualization/useZoneStore";
+import { getZoneData } from "../../../services/visulization/zone/getZones";
+import useModuleStore, {
+ useSubModuleStore,
+} from "../../../store/useModuleStore";
+import {
+ ArrowIcon,
+ EyeIcon,
+ LockIcon,
+ RemoveIcon,
+} from "../../icons/ExportCommonIcons";
+import {
+ useZoneAssetId,
+} from "../../../store/builder/store";
+import { zoneCameraUpdate } from "../../../services/visulization/zone/zoneCameraUpdation";
+import { setAssetsApi } from "../../../services/factoryBuilder/asset/floorAsset/setAssetsApi";
+import { useParams } from "react-router-dom";
+import { getUserData } from "../../../functions/getUserData";
+import { useSceneContext } from "../../../modules/scene/sceneContext";
+import { useVersionContext } from "../../../modules/builder/version/versionContext";
+
+interface Asset {
+ id: string;
+ name: string;
+ position?: [number, number, number]; // Proper 3D vector
+ rotation?: { x: number; y: number; z: number }; // Proper rotation format
+}
+
+interface ZoneItem {
+ id: string;
+ name: string;
+ assets?: Asset[];
+ active?: boolean;
+}
+
+interface ListProps {
+ items?: ZoneItem[];
+ remove?: boolean;
+}
+
+const List: React.FC = ({ items = [], remove }) => {
+ const { activeModule } = useModuleStore();
+ const { selectedZone, setSelectedZone } = useSelectedZoneStore();
+ const { zoneAssetId, setZoneAssetId } = useZoneAssetId();
+
+ const { setSubModule } = useSubModuleStore();
+ const [expandedZones, setExpandedZones] = useState>({});
+ const { projectId } = useParams();
+ const { assetStore } = useSceneContext();
+ const { setName } = assetStore();
+ const { organization } = getUserData();
+ const { selectedVersionStore } = useVersionContext();
+ const { selectedVersion } = selectedVersionStore();
+ const { zoneStore } = useSceneContext();
+ const { zones, setZoneName } = zoneStore()
+
+ useEffect(() => {
+ useSelectedZoneStore.getState().setSelectedZone({
+ zoneName: "",
+ activeSides: [],
+ panelOrder: [],
+ lockedPanels: [],
+ zoneUuid: "",
+ zoneViewPortTarget: [],
+ zoneViewPortPosition: [],
+ widgets: [],
+ });
+ }, [activeModule]);
+
+ const toggleZoneExpansion = (zoneUuid: string) => {
+ setExpandedZones((prev) => ({
+ ...prev,
+ [zoneUuid]: !prev[zoneUuid],
+ }));
+ };
+
+ async function handleSelectZone(id: string) {
+ try {
+ if (selectedZone?.zoneUuid === id) {
+ return;
+ }
+
+ setSubModule("zoneProperties");
+
+ let response = await getZoneData(id, organization, projectId, selectedVersion?.versionId || "");
+ setSelectedZone({
+ zoneName: response?.zoneName,
+ activeSides: response?.activeSides ?? [],
+ panelOrder: response?.panelOrder ?? [],
+ lockedPanels: response?.lockedPanels ?? [],
+ widgets: response?.widgets ?? [],
+ zoneUuid: response?.zoneUuid,
+ zoneViewPortTarget: response?.viewPortTarget ?? [],
+ zoneViewPortPosition: response?.viewPortPosition ?? [],
+ });
+ } catch (error) {
+ echo.error("Failed to select zone");
+ console.log(error);
+ }
+ }
+
+ function handleAssetClick(asset: Asset) {
+ setZoneAssetId(asset);
+ }
+
+ async function handleZoneNameChange(newName: string) {
+ const isDuplicate = zones.some(
+ (zone: any) =>
+ zone.zoneName?.trim().toLowerCase() === newName?.trim().toLowerCase() &&
+ zone.zoneUuid !== selectedZone.zoneUuid
+ );
+
+ if (isDuplicate) {
+ alert("Zone name already exists. Please choose a different name.");
+ return; // DO NOT update state
+ }
+ const zonesdata = {
+ zoneUuid: selectedZone.zoneUuid,
+ zoneName: newName,
+ };
+ const response = await zoneCameraUpdate(zonesdata, organization, projectId, selectedVersion?.versionId || "");
+ if (response.message === "zone updated") {
+ setSelectedZone((prev) => ({ ...prev, zoneName: newName }));
+ setZoneName(selectedZone.zoneUuid, newName)
+ // setZones((prevZones: any[]) =>
+ // prevZones.map((zone) =>
+ // zone.zoneUuid === selectedZone.zoneUuid
+ // ? { ...zone, zoneName: newName }
+ // : zone
+ // )
+ // );
+ }
+ }
+
+ async function handleZoneAssetName(newName: string) {
+
+ if (zoneAssetId?.id) {
+ let response = await setAssetsApi({
+ modelUuid: zoneAssetId.id,
+ modelName: newName,
+ projectId
+ });
+ // console.log("response: ", response);
+
+ setName(zoneAssetId.id, response.modelName);
+ }
+ }
+ const checkZoneNameDuplicate = (name: string) => {
+ return zones.some(
+ (zone: any) =>
+ zone.zoneName?.trim().toLowerCase() === name?.trim().toLowerCase() &&
+ zone.zoneUuid !== selectedZone.zoneUuid
+ );
+ };
+
+ useEffect(() => {
+ let drag = false;
+ let isLeftMouseDown = false;
+
+ const contextClassNames = ["list-wrapper", "zone-properties-container", "list-container"];
+
+ const isOutsideClick = (target: EventTarget | null) => {
+ if (!(target instanceof HTMLElement)) return true;
+ return !contextClassNames.some(className =>
+ target.closest(`.${className}`)
+ );
+ };
+
+ const onMouseDown = (evt: MouseEvent) => {
+ if (evt.button === 0) {
+ isLeftMouseDown = true;
+ drag = false;
+ }
+ };
+
+ const onMouseMove = () => {
+ if (isLeftMouseDown) {
+ drag = true;
+ }
+ };
+
+ const onMouseUp = (evt: MouseEvent) => {
+ if (evt.button === 0) {
+ isLeftMouseDown = false;
+ if (drag) return;
+
+ if (isOutsideClick(evt.target)) {
+ // Clear selected zone
+ setSelectedZone({
+ zoneUuid: '',
+ zoneName: '',
+ activeSides: [],
+ panelOrder: [],
+ lockedPanels: [],
+ widgets: [],
+ zoneViewPortTarget: [],
+ zoneViewPortPosition: []
+ });
+ setZoneAssetId({
+ id: '',
+ name: '',
+ });
+ setSubModule("properties")
+ }
+ }
+ };
+
+ if (selectedZone.zoneName! === '' && activeModule === 'Builder') {
+ document.addEventListener('mousedown', onMouseDown);
+ document.addEventListener('mousemove', onMouseMove);
+ document.addEventListener('mouseup', onMouseUp);
+ }
+
+ return () => {
+ document.removeEventListener('mousedown', onMouseDown);
+ document.removeEventListener('mousemove', onMouseMove);
+ document.removeEventListener('mouseup', onMouseUp);
+ };
+ }, [selectedZone, activeModule]);
+
+
+ return (
+ <>
+ {items?.length > 0 ? (
+
+ {items?.map((item) => (
+
+ {
+ handleSelectZone(item.id);
+ toggleZoneExpansion(item.id);
+ }}
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {remove && (
+
+
+
+ )}
+ {item.assets && item.assets.length > 0 && (
+
toggleZoneExpansion(item.id)}
+ >
+
+
+ )}
+
+
+
+ {/* Nested assets list - only shown when expanded */}
+ {item.assets &&
+ item.assets.length > 0 &&
+ expandedZones[item.id] && (
+
+ {item.assets.map((asset) => (
+
+
+
handleAssetClick(asset)}
+ >
+
+
+
+
+
+
+
+
+
+ {remove && (
+
+
+
+ )}
+
+
+
+ ))}
+
+ )}
+
+ ))}
+
+ ) : (
+
+ )}
+ >
+ );
+};
+
+export default List;
diff --git a/src/components/ui/log/LogList.tsx b/app/src/components/ui/log/LogList.tsx
similarity index 100%
rename from src/components/ui/log/LogList.tsx
rename to app/src/components/ui/log/LogList.tsx
diff --git a/src/components/ui/log/LoggerContext.tsx b/app/src/components/ui/log/LoggerContext.tsx
similarity index 100%
rename from src/components/ui/log/LoggerContext.tsx
rename to app/src/components/ui/log/LoggerContext.tsx
diff --git a/src/components/ui/menu/EditWidgetOption.tsx b/app/src/components/ui/menu/EditWidgetOption.tsx
similarity index 100%
rename from src/components/ui/menu/EditWidgetOption.tsx
rename to app/src/components/ui/menu/EditWidgetOption.tsx
diff --git a/src/components/ui/menu/menu.tsx b/app/src/components/ui/menu/menu.tsx
similarity index 100%
rename from src/components/ui/menu/menu.tsx
rename to app/src/components/ui/menu/menu.tsx
diff --git a/src/components/ui/simulation/AssetDetailsCard.tsx b/app/src/components/ui/simulation/AssetDetailsCard.tsx
similarity index 96%
rename from src/components/ui/simulation/AssetDetailsCard.tsx
rename to app/src/components/ui/simulation/AssetDetailsCard.tsx
index 34dfb6f..bf501a0 100644
--- a/src/components/ui/simulation/AssetDetailsCard.tsx
+++ b/app/src/components/ui/simulation/AssetDetailsCard.tsx
@@ -1,165 +1,165 @@
-import React, { useState } from "react";
-import {
- ExpandIcon,
- IndicationArrow,
- SimulationStatusIcon,
- StorageCapacityIcon,
-} from "../../icons/SimulationIcons";
-import { usePlayButtonStore } from "../../../store/usePlayButtonStore";
-
-interface AssetDetailsCardInterface {
- name: string;
- status: string;
- enableStatue?: boolean;
- count?: number;
- totalCapacity?: number;
- activeTime?: any;
- assetDetails?: {
- assetName: string;
- const: string;
- performance: string;
- };
-}
-
-const GetStatus = (status: string) => {
- // "idle" | "running" | "stopped" | "disabled" | "error"
- switch (status) {
- case "idle":
- return (
-
- );
- case "running":
- return (
-
- );
- case "stopped":
- return (
-
- );
- }
-};
-
-const AssetDetailsCard: React.FC = ({
- name,
- enableStatue = true,
- status,
- count,
- totalCapacity,
- activeTime,
- assetDetails,
-}) => {
- const [moreDetails, setMoreDetails] = useState(false);
- // hooks
- const { isPlaying } = usePlayButtonStore();
-
- return (
-
-
-
-
-
{name}
- {enableStatue && (
- <>
-
Active Time : {activeTime}
-
{GetStatus(status)}
- >
- )}
- {!enableStatue && totalCapacity && (
-
Storage/Inventory
- )}
-
-
{
- setMoreDetails(!moreDetails);
- }}
- >
-
-
-
-
- {totalCapacity && (
-
-
-
-
-
-
-
- {count?.toString()}/{totalCapacity}
-
-
- {/* progress ui */}
-
-
- {[...Array(5)].map((_, i) => {
- const percentage = (count! / totalCapacity) * 100;
- const start = i * 20;
- const end = (i + 1) * 20;
- // fill amount: 0 to 100
- let fillPercent = 0;
- if (percentage >= end) {
- fillPercent = 100;
- } else if (percentage <= start) {
- fillPercent = 1;
- } else {
- fillPercent = ((percentage - start) / 20) * 100;
- }
- return (
-
- );
- })}
-
-
- {Math.round((count! / totalCapacity) * 100).toString()}%
-
-
-
-
- )}
-
- {status === "running" && (
-
- )}
-
-
-
-
-
- );
-};
-
-export default AssetDetailsCard;
+import React, { useState } from "react";
+import {
+ ExpandIcon,
+ IndicationArrow,
+ SimulationStatusIcon,
+ StorageCapacityIcon,
+} from "../../icons/SimulationIcons";
+import { usePlayButtonStore } from "../../../store/usePlayButtonStore";
+
+interface AssetDetailsCardInterface {
+ name: string;
+ status: string;
+ enableStatue?: boolean;
+ count?: number;
+ totalCapacity?: number;
+ activeTime?: any;
+ assetDetails?: {
+ assetName: string;
+ const: string;
+ performance: string;
+ };
+}
+
+const GetStatus = (status: string) => {
+ // "idle" | "running" | "stopped" | "disabled" | "error"
+ switch (status) {
+ case "idle":
+ return (
+
+ );
+ case "running":
+ return (
+
+ );
+ case "stopped":
+ return (
+
+ );
+ }
+};
+
+const AssetDetailsCard: React.FC = ({
+ name,
+ enableStatue = true,
+ status,
+ count,
+ totalCapacity,
+ activeTime,
+ assetDetails,
+}) => {
+ const [moreDetails, setMoreDetails] = useState(false);
+ // hooks
+ const { isPlaying } = usePlayButtonStore();
+
+ return (
+
+
+
+
+
{name}
+ {enableStatue && (
+ <>
+
Active Time : {activeTime}
+
{GetStatus(status)}
+ >
+ )}
+ {!enableStatue && totalCapacity && (
+
Storage/Inventory
+ )}
+
+
{
+ setMoreDetails(!moreDetails);
+ }}
+ >
+
+
+
+
+ {totalCapacity && (
+
+
+
+
+
+
+
+ {count?.toString()}/{totalCapacity}
+
+
+ {/* progress ui */}
+
+
+ {[...Array(5)].map((_, i) => {
+ const percentage = (count! / totalCapacity) * 100;
+ const start = i * 20;
+ const end = (i + 1) * 20;
+ // fill amount: 0 to 100
+ let fillPercent = 0;
+ if (percentage >= end) {
+ fillPercent = 100;
+ } else if (percentage <= start) {
+ fillPercent = 1;
+ } else {
+ fillPercent = ((percentage - start) / 20) * 100;
+ }
+ return (
+
+ );
+ })}
+
+
+ {Math.round((count! / totalCapacity) * 100).toString()}%
+
+
+
+
+ )}
+
+ {status === "running" && (
+
+ )}
+
+
+
+
+
+ );
+};
+
+export default AssetDetailsCard;
diff --git a/src/components/ui/simulation/simulationPlayer.tsx b/app/src/components/ui/simulation/simulationPlayer.tsx
similarity index 97%
rename from src/components/ui/simulation/simulationPlayer.tsx
rename to app/src/components/ui/simulation/simulationPlayer.tsx
index e2ec99f..1266099 100644
--- a/src/components/ui/simulation/simulationPlayer.tsx
+++ b/app/src/components/ui/simulation/simulationPlayer.tsx
@@ -1,453 +1,453 @@
-import React, { useState, useRef, useEffect } from "react";
-import { ExitIcon, PlayStopIcon, ResetIcon } from "../../icons/SimulationIcons";
-import {
- useActiveTool,
- useProcessBar,
- useViewSceneStore,
-} from "../../../store/builder/store";
-import {
- useAnimationPlaySpeed,
- usePauseButtonStore,
- usePlayButtonStore,
- useResetButtonStore,
-} from "../../../store/usePlayButtonStore";
-import {
- DailyProductionIcon,
- EndIcon,
- ExpandIcon,
- EyeCloseIcon,
- HourlySimulationIcon,
- InfoIcon,
- MonthlyROI,
- SpeedIcon,
- StartIcon,
-} from "../../icons/ExportCommonIcons";
-import { getAvatarColor } from "../../../modules/collaboration/functions/getAvatarColor";
-import useModuleStore, {
- useSubModuleStore,
-} from "../../../store/useModuleStore";
-import ProductionCapacity from "../analysis/ThroughputSummary";
-import ThroughputSummary from "../analysis/ProductionCapacity";
-import ROISummary from "../analysis/ROISummary";
-import { usePlayerStore } from "../../../store/useUIToggleStore";
-import { useComparisonProduct } from "../../../store/simulation/useSimulationStore";
-import InputToggle from "../inputs/InputToggle";
-
-const SimulationPlayer: React.FC = () => {
- const MAX_SPEED = 8; // Maximum speed
-
- const isDragging = useRef(false);
- const sliderRef = useRef(null);
- const [expand, setExpand] = useState(true);
- const { hidePlayer, setHidePlayer } = usePlayerStore();
-
- const { speed, setSpeed } = useAnimationPlaySpeed();
- const { setIsPlaying } = usePlayButtonStore();
- const { setActiveTool } = useActiveTool();
- const { isPaused, setIsPaused } = usePauseButtonStore();
- const { isReset, setReset } = useResetButtonStore();
- const { subModule } = useSubModuleStore();
- const { clearComparisonProduct } = useComparisonProduct();
- const { viewSceneLabels, setViewSceneLabels } = useViewSceneStore();
-
- const { isPlaying } = usePlayButtonStore();
- const { activeModule } = useModuleStore();
- useEffect(() => {
- if (isReset) {
- setTimeout(() => {
- setReset(false);
- }, 0);
- }
- }, [isReset, setReset]);
-
- // Button functions
- const handleReset = () => {
- setReset(true);
- setIsPaused(false);
- setSpeed(1);
- echo.info("Simulation RESET.....");
- };
- const handlePlayStop = () => {
- setIsPaused(!isPaused);
- if (isPaused) {
- setIsPlaying(true);
- }
- echo.warn(`Simulation is ${isPaused ? "Resumed" : "Paused"}`);
- };
- const handleExit = () => {
- setIsPlaying(false);
- setIsPaused(false);
- clearComparisonProduct();
- setActiveTool("cursor");
- echo.info("Exit Simulation");
- };
-
- // Slider functions starts
- const handleSpeedChange = (event: React.ChangeEvent) => {
- setSpeed(parseFloat(event.target.value));
- };
-
- const calculateHandlePosition = () => {
- return ((speed - 0.5) / (MAX_SPEED - 0.5)) * 100;
- };
-
- const handleMouseDown = () => {
- isDragging.current = true;
- };
-
- const handleMouseMove = (e: MouseEvent) => {
- if (!isDragging.current || !sliderRef.current) return;
-
- const sliderRect = sliderRef.current.getBoundingClientRect();
- const offsetX = e.clientX - sliderRect.left;
- const percentage = Math.min(Math.max(offsetX / sliderRect.width, 0), 1);
- const newValue = 0.5 + percentage * (50 - 0.5);
- setSpeed(parseFloat(newValue.toFixed(1)));
- };
-
- const handleMouseUp = () => {
- isDragging.current = false;
- };
-
- const handleVisibility = () => {
- if (document.visibilityState !== 'visible' && isPlaying) {
- setIsPaused(!isPaused);
- if (isPaused) {
- setIsPlaying(true);
- }
- echo.warn(`Simulation is ${isPaused ? "Resumed" : "Paused"}`);
- }
- }
-
- useEffect(() => {
- document.addEventListener("mousemove", handleMouseMove);
- document.addEventListener("mouseup", handleMouseUp);
- document.addEventListener('visibilitychange', handleVisibility);
- return () => {
- document.removeEventListener("mousemove", handleMouseMove);
- document.removeEventListener("mouseup", handleMouseUp);
- document.removeEventListener('visibilitychange', handleVisibility);
- };
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, []);
- // Slider function ends
-
- // UI-Part
- const hourlySimulation = 25;
- const dailyProduction = 75;
- const monthlyROI = 10;
- const { processBar } = useProcessBar();
-
- useEffect(() => { }, [processBar]);
-
- const intervals = [50, 20, 30, 40, 50, 60]; // in minutes
- const totalSegments = intervals.length;
- const progress = 20; // percent (example)
-
- const processPlayerRef = useRef(null);
- const processWrapperRef = useRef(null);
- const [playerPosition, setPlayerPosition] = useState(20); // initial position
-
- const handleProcessMouseDown = (e: React.MouseEvent) => {
- if (!processWrapperRef.current) return;
-
- const rect = processWrapperRef.current.getBoundingClientRect();
- let x = e.clientX - rect.left;
- x = Math.max(0, Math.min(x, rect.width));
- setPlayerPosition(x);
-
- const onMouseMove = (e: MouseEvent) => {
- if (!processWrapperRef.current) return;
- const newRect = processWrapperRef.current.getBoundingClientRect();
- let newX = e.clientX - newRect.left;
- newX = Math.max(0, Math.min(newX, newRect.width));
- setPlayerPosition(newX);
-
- const progressPercent = (newX / newRect.width) * 100;
- console.log(`Dragging at progress: ${progressPercent.toFixed(1)}%`);
- };
-
- const onMouseUp = () => {
- document.removeEventListener("mousemove", onMouseMove);
- document.removeEventListener("mouseup", onMouseUp);
- };
-
- document.addEventListener("mousemove", onMouseMove);
- document.addEventListener("mouseup", onMouseUp);
- };
-
- return (
- <>
- {isPlaying && activeModule === "simulation" && (
- {/* bottom */}
- setViewSceneLabels(!viewSceneLabels)}
- />
-
- )}
-
-
-
- {!hidePlayer && subModule === "analysis" && (
-
- {/* hourlySimulation */}
-
- {/* dailyProduction */}
-
-
-
-
-
-
Production Capacity
-
-
-
- {/* monthlyROI */}
-
-
- )}
- {!hidePlayer && subModule !== "analysis" && (
-
-
- {isPaused ? "Paused - system idle." : "Running simulation..."}
-
- )}
-
- {!hidePlayer && (
-
{
- handleReset();
- }}
- >
-
- Reset
-
- )}
- {!hidePlayer && (
-
{
- handlePlayStop();
- }}
- >
-
- {isPaused ? "Play" : "Stop"}
-
- )}
-
{
- handleExit();
- }}
- >
-
- {!hidePlayer && "Exit"}
-
-
{
- setHidePlayer(!hidePlayer);
- }}
- >
-
- {!hidePlayer && "Hide"}
-
- {subModule === "analysis" && (
-
setExpand(!expand)}
- >
-
-
- )}
-
-
- {!hidePlayer && (
-
-
-
-
-
-
-
-
23 April ,25
-
04:41 PM
-
-
-
-
- {intervals.map((label, index) => {
- const segmentProgress = (index / totalSegments) * 100;
- const isFilled = progress >= segmentProgress;
- return (
-
-
- {index < intervals.length - 1 && (
- = ((index + 1) / totalSegments) * 100
- ? "filled"
- : ""
- }`}
- >
- )}
-
- );
- })}
-
-
-
-
-
-
-
-
-
0.5X
-
-
-
-
-
-
-
-
-
-
-
-
- {speed.toFixed(1)}x
-
-
-
-
{MAX_SPEED}x
-
-
-
-
- )}
- {!hidePlayer && subModule === "analysis" && (
-
-
00:00
-
24:00
-
- {/* eslint-disable-next-line */}
-
- {processBar?.length > 0 ? (
- processBar.map((item: any, index: any) => (
-
- ))
- ) : (
-
No process data available
- )}
-
-
-
- )}
-
-
- {/* {subModule === "analysis" && ( */}
- {subModule === "analysis" && (
-
- )}
- {/* )} */}
- >
- );
-};
-
-export default SimulationPlayer;
+import React, { useState, useRef, useEffect } from "react";
+import { ExitIcon, PlayStopIcon, ResetIcon } from "../../icons/SimulationIcons";
+import {
+ useActiveTool,
+ useProcessBar,
+ useViewSceneStore,
+} from "../../../store/builder/store";
+import {
+ useAnimationPlaySpeed,
+ usePauseButtonStore,
+ usePlayButtonStore,
+ useResetButtonStore,
+} from "../../../store/usePlayButtonStore";
+import {
+ DailyProductionIcon,
+ EndIcon,
+ ExpandIcon,
+ EyeCloseIcon,
+ HourlySimulationIcon,
+ InfoIcon,
+ MonthlyROI,
+ SpeedIcon,
+ StartIcon,
+} from "../../icons/ExportCommonIcons";
+import { getAvatarColor } from "../../../modules/collaboration/functions/getAvatarColor";
+import useModuleStore, {
+ useSubModuleStore,
+} from "../../../store/useModuleStore";
+import ProductionCapacity from "../analysis/ThroughputSummary";
+import ThroughputSummary from "../analysis/ProductionCapacity";
+import ROISummary from "../analysis/ROISummary";
+import { usePlayerStore } from "../../../store/useUIToggleStore";
+import { useComparisonProduct } from "../../../store/simulation/useSimulationStore";
+import InputToggle from "../inputs/InputToggle";
+
+const SimulationPlayer: React.FC = () => {
+ const MAX_SPEED = 8; // Maximum speed
+
+ const isDragging = useRef(false);
+ const sliderRef = useRef(null);
+ const [expand, setExpand] = useState(true);
+ const { hidePlayer, setHidePlayer } = usePlayerStore();
+
+ const { speed, setSpeed } = useAnimationPlaySpeed();
+ const { setIsPlaying } = usePlayButtonStore();
+ const { setActiveTool } = useActiveTool();
+ const { isPaused, setIsPaused } = usePauseButtonStore();
+ const { isReset, setReset } = useResetButtonStore();
+ const { subModule } = useSubModuleStore();
+ const { clearComparisonProduct } = useComparisonProduct();
+ const { viewSceneLabels, setViewSceneLabels } = useViewSceneStore();
+
+ const { isPlaying } = usePlayButtonStore();
+ const { activeModule } = useModuleStore();
+ useEffect(() => {
+ if (isReset) {
+ setTimeout(() => {
+ setReset(false);
+ }, 0);
+ }
+ }, [isReset, setReset]);
+
+ // Button functions
+ const handleReset = () => {
+ setReset(true);
+ setIsPaused(false);
+ setSpeed(1);
+ echo.info("Simulation RESET.....");
+ };
+ const handlePlayStop = () => {
+ setIsPaused(!isPaused);
+ if (isPaused) {
+ setIsPlaying(true);
+ }
+ echo.warn(`Simulation is ${isPaused ? "Resumed" : "Paused"}`);
+ };
+ const handleExit = () => {
+ setIsPlaying(false);
+ setIsPaused(false);
+ clearComparisonProduct();
+ setActiveTool("cursor");
+ echo.info("Exit Simulation");
+ };
+
+ // Slider functions starts
+ const handleSpeedChange = (event: React.ChangeEvent) => {
+ setSpeed(parseFloat(event.target.value));
+ };
+
+ const calculateHandlePosition = () => {
+ return ((speed - 0.5) / (MAX_SPEED - 0.5)) * 100;
+ };
+
+ const handleMouseDown = () => {
+ isDragging.current = true;
+ };
+
+ const handleMouseMove = (e: MouseEvent) => {
+ if (!isDragging.current || !sliderRef.current) return;
+
+ const sliderRect = sliderRef.current.getBoundingClientRect();
+ const offsetX = e.clientX - sliderRect.left;
+ const percentage = Math.min(Math.max(offsetX / sliderRect.width, 0), 1);
+ const newValue = 0.5 + percentage * (50 - 0.5);
+ setSpeed(parseFloat(newValue.toFixed(1)));
+ };
+
+ const handleMouseUp = () => {
+ isDragging.current = false;
+ };
+
+ const handleVisibility = () => {
+ if (document.visibilityState !== 'visible' && isPlaying) {
+ setIsPaused(!isPaused);
+ if (isPaused) {
+ setIsPlaying(true);
+ }
+ echo.warn(`Simulation is ${isPaused ? "Resumed" : "Paused"}`);
+ }
+ }
+
+ useEffect(() => {
+ document.addEventListener("mousemove", handleMouseMove);
+ document.addEventListener("mouseup", handleMouseUp);
+ document.addEventListener('visibilitychange', handleVisibility);
+ return () => {
+ document.removeEventListener("mousemove", handleMouseMove);
+ document.removeEventListener("mouseup", handleMouseUp);
+ document.removeEventListener('visibilitychange', handleVisibility);
+ };
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, []);
+ // Slider function ends
+
+ // UI-Part
+ const hourlySimulation = 25;
+ const dailyProduction = 75;
+ const monthlyROI = 10;
+ const { processBar } = useProcessBar();
+
+ useEffect(() => { }, [processBar]);
+
+ const intervals = [50, 20, 30, 40, 50, 60]; // in minutes
+ const totalSegments = intervals.length;
+ const progress = 20; // percent (example)
+
+ const processPlayerRef = useRef(null);
+ const processWrapperRef = useRef(null);
+ const [playerPosition, setPlayerPosition] = useState(20); // initial position
+
+ const handleProcessMouseDown = (e: React.MouseEvent) => {
+ if (!processWrapperRef.current) return;
+
+ const rect = processWrapperRef.current.getBoundingClientRect();
+ let x = e.clientX - rect.left;
+ x = Math.max(0, Math.min(x, rect.width));
+ setPlayerPosition(x);
+
+ const onMouseMove = (e: MouseEvent) => {
+ if (!processWrapperRef.current) return;
+ const newRect = processWrapperRef.current.getBoundingClientRect();
+ let newX = e.clientX - newRect.left;
+ newX = Math.max(0, Math.min(newX, newRect.width));
+ setPlayerPosition(newX);
+
+ const progressPercent = (newX / newRect.width) * 100;
+ console.log(`Dragging at progress: ${progressPercent.toFixed(1)}%`);
+ };
+
+ const onMouseUp = () => {
+ document.removeEventListener("mousemove", onMouseMove);
+ document.removeEventListener("mouseup", onMouseUp);
+ };
+
+ document.addEventListener("mousemove", onMouseMove);
+ document.addEventListener("mouseup", onMouseUp);
+ };
+
+ return (
+ <>
+ {isPlaying && activeModule === "simulation" && (
+ {/* bottom */}
+ setViewSceneLabels(!viewSceneLabels)}
+ />
+
+ )}
+
+
+
+ {!hidePlayer && subModule === "analysis" && (
+
+ {/* hourlySimulation */}
+