diff --git a/app/package-lock.json b/app/package-lock.json index 7e7ebe1..a752e9c 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -29,10 +29,11 @@ "@use-gesture/react": "^10.3.1", "chart.js": "^4.4.8", "chartjs-plugin-annotation": "^3.1.0", + "dxf-parser": "^1.1.2", "glob": "^11.0.0", "gsap": "^3.12.5", "html2canvas": "^1.4.1", - "immer": "^10.1.1", + "immer": "^9.0.21", "leva": "^0.10.0", "mqtt": "^5.10.4", "postprocessing": "^6.36.4", @@ -4182,7 +4183,6 @@ "version": "10.4.0", "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz", "integrity": "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==", - "license": "MIT", "peer": true, "dependencies": { "@babel/code-frame": "^7.10.4", @@ -10129,6 +10129,14 @@ "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==" }, + "node_modules/dxf-parser": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/dxf-parser/-/dxf-parser-1.1.2.tgz", + "integrity": "sha512-GPTumUvRkounlIazLIyJMmTWt+nlg+ksS0Hdm8jWvejmZKBTz6gvHTam76wRm4PQMma5sgKLThblQyeIJcH79Q==", + "dependencies": { + "loglevel": "^1.7.1" + } + }, "node_modules/earcut": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/earcut/-/earcut-2.2.4.tgz", @@ -12788,10 +12796,9 @@ "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==" }, "node_modules/immer": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/immer/-/immer-10.1.1.tgz", - "integrity": "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==", - "license": "MIT", + "version": "9.0.21", + "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz", + "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==", "funding": { "type": "opencollective", "url": "https://opencollective.com/immer" @@ -15244,6 +15251,18 @@ "node": ">=8" } }, + "node_modules/loglevel": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.9.2.tgz", + "integrity": "sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg==", + "engines": { + "node": ">= 0.6.0" + }, + "funding": { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/loglevel" + } + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -18053,16 +18072,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/react-dev-utils/node_modules/immer": { - "version": "9.0.21", - "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz", - "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/immer" - } - }, "node_modules/react-dev-utils/node_modules/loader-utils": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.3.1.tgz", diff --git a/app/package.json b/app/package.json index 2efffdb..aeeb4ca 100644 --- a/app/package.json +++ b/app/package.json @@ -24,10 +24,11 @@ "@use-gesture/react": "^10.3.1", "chart.js": "^4.4.8", "chartjs-plugin-annotation": "^3.1.0", + "dxf-parser": "^1.1.2", "glob": "^11.0.0", "gsap": "^3.12.5", "html2canvas": "^1.4.1", - "immer": "^10.1.1", + "immer": "^9.0.21", "leva": "^0.10.0", "mqtt": "^5.10.4", "postprocessing": "^6.36.4", diff --git a/app/src/components/Dashboard/DashboardCard.tsx b/app/src/components/Dashboard/DashboardCard.tsx index 7ff0caf..5d11950 100644 --- a/app/src/components/Dashboard/DashboardCard.tsx +++ b/app/src/components/Dashboard/DashboardCard.tsx @@ -1,160 +1,293 @@ -import React, { useState, useRef } from "react"; +import React, { useState, useRef, useEffect } from "react"; import img from "../../assets/image/image.png"; import { useNavigate } from "react-router-dom"; import { getUserData } from "./functions/getUserData"; -import { useProjectName } from "../../store/builder/store"; +import { useProjectName, useSocketStore } from "../../store/builder/store"; import { viewProject } from "../../services/dashboard/viewProject"; import OuterClick from "../../utils/outerClick"; import { KebabIcon } from "../icons/ExportCommonIcons"; +import darkThemeImage from "../../assets/image/darkThemeProject.png"; +import lightThemeImage from "../../assets/image/lightThemeProject.png"; +import { updateProject } from "../../services/dashboard/updateProject"; +import { getAllProjects } from "../../services/dashboard/getAllProjects"; +import { duplicateProject } from "../../services/dashboard/duplicateProject"; interface DashBoardCardProps { - projectName: string; - thumbnail: any; - projectId: string; - handleDeleteProject?: (projectId: string) => Promise; - handleRestoreProject?: (projectId: string) => Promise; - active?: string; + projectName: string; + thumbnail: any; + projectId: string; + handleDeleteProject?: (projectId: string) => Promise; + handleTrashDeleteProject?: (projectId: string) => Promise; + handleRestoreProject?: (projectId: string) => Promise; + handleDuplicateWorkspaceProject?: (projectId: string, projectName: string, thumbnail: string) => Promise; + handleDuplicateRecentProject?: (projectId: string, projectName: string, thumbnail: string) => Promise; + active?: string; + setIsSearchActive?: React.Dispatch>; } const DashboardCard: React.FC = ({ - projectName, - handleDeleteProject, - thumbnail, - projectId, - handleRestoreProject, - active + projectName, + thumbnail, + projectId, + active, + handleDeleteProject, + handleRestoreProject, + handleTrashDeleteProject, + setIsSearchActive, + handleDuplicateWorkspaceProject, + handleDuplicateRecentProject }) => { - const navigate = useNavigate(); - const { setProjectName } = useProjectName(); - const { userId, organization, userName } = getUserData(); - const [isKebabOpen, setIsKebabOpen] = useState(false); + const navigate = useNavigate(); + const { setProjectName } = useProjectName(); + const { userId, organization, userName } = getUserData(); + const [isKebabOpen, setIsKebabOpen] = useState(false); + const [renameValue, setRenameValue] = useState(projectName); + const [isRenaming, setIsRenaming] = useState(false); + const { projectSocket } = useSocketStore(); - const kebabRef = useRef(null); + const kebabRef = useRef(null); + const savedTheme = localStorage.getItem("theme") ?? "light"; - const navigateToProject = async () => { - try { - const viewedProject = await viewProject(organization, projectId, userId); - console.log("Viewed project:", viewedProject); - } catch (error) { - console.error("Error opening project:", error); - } - - setProjectName(projectName); - navigate(`/${projectId}`); - }; - - const handleOptionClick = async (option: string) => { - switch (option) { - case "delete": - if (handleDeleteProject) { - await handleDeleteProject(projectId); - } - break; - case "restore": - if (handleRestoreProject) { - await handleRestoreProject(projectId); - } - break; - case "open in new tab": + const navigateToProject = async (e: any) => { + if (active && active == "trash") return try { - await viewProject(organization, projectId, userId); - setProjectName(projectName); // optional depending on scope + const viewedProject = await viewProject(organization, projectId, userId); + console.log("Viewed project:", viewedProject); } catch (error) { - console.error("Error opening project in new tab:", error); + console.error("Error opening project:", error); } - window.open(`/${projectId}`, "_blank"); - break; - case "rename": - // Add rename logic here - break; - case "duplicate": - // Add duplication logic here - break; - default: - break; - } - setIsKebabOpen(false); - }; - OuterClick({ - contextClassName: ["kebab-wrapper", "kebab-options-wrapper"], - setMenuVisible: () => setIsKebabOpen(false), - }); - return ( - + + - - - - - - {isKebabOpen && active !== "trash" && ( -
- {["rename", "delete", "duplicate", "open in new tab"].map((option) => ( - - ))} -
- )} - {isKebabOpen && active && active == "trash" && ( - < div className="kebab-options-wrapper"> - {["restore", "delete"].map((option) => ( - - ))} - - ) - } - - ); + {isKebabOpen && active !== "trash" && ( +
+ {["rename", "delete", "duplicate", "open in new tab"].map((option) => ( + + ))} +
+ )} + {isKebabOpen && active && active == "trash" && ( + < div className="kebab-options-wrapper"> + {["restore", "delete"].map((option) => ( + + ))} + + ) + } + + ); }; export default DashboardCard; diff --git a/app/src/components/Dashboard/DashboardHome.tsx b/app/src/components/Dashboard/DashboardHome.tsx index bda6323..8da0571 100644 --- a/app/src/components/Dashboard/DashboardHome.tsx +++ b/app/src/components/Dashboard/DashboardHome.tsx @@ -7,6 +7,7 @@ import { useSocketStore } from "../../store/builder/store"; import { recentlyViewed } from "../../services/dashboard/recentlyViewed"; import { searchProject } from "../../services/dashboard/searchProjects"; import { deleteProject } from "../../services/dashboard/deleteProject"; +import { handleDuplicateProjects } from "./functions/handleDuplicateProject"; interface Project { _id: string; @@ -24,7 +25,7 @@ const DashboardHome: React.FC = () => { const [recentProjects, setRecentProjects] = useState({}); const [isSearchActive, setIsSearchActive] = useState(false); const { userId, organization } = getUserData(); - const { dashBoardSocket } = useSocketStore(); + const { projectSocket } = useSocketStore(); const fetchRecentProjects = async () => { try { @@ -49,35 +50,38 @@ const DashboardHome: React.FC = () => { userId, inputValue ); + console.log('filterRecentProcess: ', filterRecentProcess); setIsSearchActive(true); setRecentProjects(filterRecentProcess.message ? {} : filterRecentProcess); }; const handleDeleteProject = async (projectId: any) => { + console.log('projectId:delete ', projectId); try { //API for delete project - const deletedProject = await deleteProject( - projectId, - userId, - organization - ); + // const deletedProject = await deleteProject( + // projectId, + // userId, + // organization + // ); + // console.log('deletedProject: ', deletedProject); //socket for delete Project - // const deleteProject = { - // projectId, - // organization, - // userId, - // }; + const deleteProject = { + projectId, + organization, + userId, + }; - // if (dashBoardSocket) { - // const handleResponse = (data: any) => { - // console.log("Project add response:", data); - // dashBoardSocket.off("v1-project:response:delete", handleResponse); - // }; + if (projectSocket) { + const handleResponse = (data: any) => { + console.log("Project add response:", data); + projectSocket.off("v1-project:response:delete", handleResponse); + }; - // dashBoardSocket.on("v1-project:response:delete", handleResponse); - // dashBoardSocket.emit("v1:project:delete", deleteProject); - // } + projectSocket.on("v1-project:response:delete", handleResponse); + projectSocket.emit("v1:project:delete", deleteProject); + } setRecentProjects((prevDiscardedProjects: RecentProjectsData) => { if (!Array.isArray(prevDiscardedProjects?.RecentlyViewed)) { @@ -97,6 +101,21 @@ const DashboardHome: React.FC = () => { } }; + const handleDuplicateRecentProject = async (projectId: string, projectName: string, thumbnail: string) => { + + await handleDuplicateProjects({ + userId, + organization, + projectId, + projectName, + projectSocket, + thumbnail, + setRecentProjects, + setIsSearchActive + }); + + } + const renderProjects = () => { const projectList = recentProjects[Object.keys(recentProjects)[0]]; @@ -111,12 +130,12 @@ const DashboardHome: React.FC = () => { thumbnail={project.thumbnail} projectId={project._id} handleDeleteProject={handleDeleteProject} + handleDuplicateRecentProject={handleDuplicateRecentProject} /> )); }; useEffect(() => { - console.log('isSearchActive: ', isSearchActive); if (!isSearchActive) { fetchRecentProjects(); } diff --git a/app/src/components/Dashboard/DashboardProjects.tsx b/app/src/components/Dashboard/DashboardProjects.tsx index 1f15c7c..2199f19 100644 --- a/app/src/components/Dashboard/DashboardProjects.tsx +++ b/app/src/components/Dashboard/DashboardProjects.tsx @@ -6,6 +6,7 @@ import { useSocketStore } from "../../store/builder/store"; import { getAllProjects } from "../../services/dashboard/getAllProjects"; import { searchProject } from "../../services/dashboard/searchProjects"; import { deleteProject } from "../../services/dashboard/deleteProject"; +import { handleDuplicateProjects } from "./functions/handleDuplicateProject"; interface Project { _id: string; @@ -23,9 +24,11 @@ const DashboardProjects: React.FC = () => { const [workspaceProjects, setWorkspaceProjects] = useState( {} ); + console.log('workspaceProjects: ', workspaceProjects); const [isSearchActive, setIsSearchActive] = useState(false); + const [activeFolder, setActiveFolder] = useState("myProjects"); - const { dashBoardSocket } = useSocketStore(); + const { projectSocket } = useSocketStore(); const { userId, organization } = getUserData(); const fetchAllProjects = async () => { @@ -39,6 +42,7 @@ const DashboardProjects: React.FC = () => { console.error("Error fetching projects:", error); } }; + const handleProjectsSearch = async (inputValue: string) => { if (!inputValue.trim()) { setIsSearchActive(false); @@ -54,14 +58,15 @@ const DashboardProjects: React.FC = () => { setIsSearchActive(true); setWorkspaceProjects(searchedProject.message ? {} : searchedProject); }; + const handleDeleteProject = async (projectId: any) => { try { - const Organization = organization; - const deletedProject = await deleteProject( - projectId, - userId, - Organization - ); + // const deletedProject = await deleteProject( + // projectId, + // userId, + // organization + // ); + // console.log('deletedProject: ', deletedProject); const deleteProjects = { projectId, organization: organization, @@ -69,18 +74,18 @@ const DashboardProjects: React.FC = () => { }; //socket for deleting the project - // if (dashBoardSocket) { - // const handleResponse = (data: any) => { - // console.log("Project add response:", data); - // dashBoardSocket.off("v1-project:response:delete", handleResponse); // Clean up - // }; + if (projectSocket) { + const handleResponse = (data: any) => { + console.log("Project add response:", data); + projectSocket.off("v1-project:response:delete", handleResponse); // Clean up + }; - // dashBoardSocket.on("v1-project:response:delete", handleResponse); + projectSocket.on("v1-project:response:delete", handleResponse); - // dashBoardSocket.emit("v1:project:delete", deleteProjects); - // } else { - // console.error("Socket is not connected."); - // } + projectSocket.emit("v1:project:delete", deleteProjects); + } else { + console.error("Socket is not connected."); + } setWorkspaceProjects((prevDiscardedProjects: WorkspaceProjects) => { if (!Array.isArray(prevDiscardedProjects?.Projects)) { return prevDiscardedProjects; @@ -99,6 +104,21 @@ const DashboardProjects: React.FC = () => { } }; + const handleDuplicateWorkspaceProject = async (projectId: string, projectName: string, thumbnail: string) => { + + await handleDuplicateProjects({ + userId, + organization, + projectId, + projectName, + projectSocket, + thumbnail, + setWorkspaceProjects, + setIsSearchActive + }); + + } + const renderProjects = () => { if (activeFolder !== "myProjects") return null; @@ -115,6 +135,8 @@ const DashboardProjects: React.FC = () => { thumbnail={project.thumbnail} projectId={project._id} handleDeleteProject={handleDeleteProject} + setIsSearchActive={setIsSearchActive} + handleDuplicateWorkspaceProject={handleDuplicateWorkspaceProject} /> )); }; diff --git a/app/src/components/Dashboard/DashboardTrash.tsx b/app/src/components/Dashboard/DashboardTrash.tsx index 06bf179..1aeae11 100644 --- a/app/src/components/Dashboard/DashboardTrash.tsx +++ b/app/src/components/Dashboard/DashboardTrash.tsx @@ -5,6 +5,8 @@ import { getUserData } from "./functions/getUserData"; import { trashSearchProject } from "../../services/dashboard/trashSearchProject"; import { restoreTrash } from "../../services/dashboard/restoreTrash"; import { getTrash } from "../../services/dashboard/getTrash"; +import { deleteTrash } from "../../services/dashboard/deleteTrash"; +import { useSocketStore } from "../../store/builder/store"; interface Project { _id: string; @@ -22,15 +24,14 @@ const DashboardTrash: React.FC = () => { const [discardedProjects, setDiscardedProjects] = useState( {} ); - console.log("discardedProjects: ", discardedProjects); const [isSearchActive, setIsSearchActive] = useState(false); const { userId, organization } = getUserData(); + const { projectSocket } = useSocketStore(); + const fetchTrashProjects = async () => { try { const projects = await getTrash(organization); - console.log("organization: ", organization); - console.log("trashedprojects: ", projects); if (JSON.stringify(projects) !== JSON.stringify(discardedProjects)) { setDiscardedProjects(projects); @@ -59,9 +60,8 @@ const DashboardTrash: React.FC = () => { const handleRestoreProject = async (projectId: any) => { console.log('projectId: ', projectId); try { - const Organization = organization; - const restoreProject = await restoreTrash(Organization, projectId); - console.log('restoreProject: ', restoreProject); + const restoreProject = await restoreTrash(organization, projectId); + // console.log('restoreProject: ', restoreProject); setDiscardedProjects((prevDiscardedProjects: DiscardedProjects) => { // Check if TrashDatas exists and is an array @@ -82,6 +82,46 @@ const DashboardTrash: React.FC = () => { console.error("Error deleting project:", error); } }; + const handleTrashDeleteProject = async (projectId: any) => { + try { + // const deletedProject = await deleteTrash( + // organization, projectId + // ); + + + const deleteProjectTrash = { + projectId, + organization, + userId, + }; + if (projectSocket) { + const handleResponse = (data: any) => { + console.log('data:deleteTrash ', data); + projectSocket.off("v1:trash:response:delete", handleResponse); + }; + projectSocket.on("v1:trash:response:delete", handleResponse); + console.log("duplicate: ", deleteProjectTrash); + projectSocket.emit("v1:trash:delete", deleteProjectTrash); + } + setDiscardedProjects((prevDiscardedProjects: DiscardedProjects) => { + if (!Array.isArray(prevDiscardedProjects?.TrashDatas)) { + return prevDiscardedProjects; + } + const updatedProjectDatas = prevDiscardedProjects.TrashDatas.filter( + (project) => project._id !== projectId + ); + // console.log('updatedProjectDatas: ', updatedProjectDatas); + return { + ...prevDiscardedProjects, + TrashDatas: updatedProjectDatas, + }; + }); + setIsSearchActive(false); + } catch (error) { + console.error("Error deleting project:", error); + } + }; + const renderTrashProjects = () => { const projectList = discardedProjects[Object.keys(discardedProjects)[0]]; @@ -96,13 +136,13 @@ const DashboardTrash: React.FC = () => { thumbnail={project.thumbnail} projectId={project._id} handleRestoreProject={handleRestoreProject} + handleTrashDeleteProject={handleTrashDeleteProject} active={"trash"} /> )); }; useEffect(() => { - console.log("isSearchActive:trash ", isSearchActive); if (!isSearchActive) { fetchTrashProjects(); } diff --git a/app/src/components/Dashboard/SidePannel.tsx b/app/src/components/Dashboard/SidePannel.tsx index 13f8e8c..4bb7f61 100644 --- a/app/src/components/Dashboard/SidePannel.tsx +++ b/app/src/components/Dashboard/SidePannel.tsx @@ -13,7 +13,7 @@ import darkThemeImage from "../../assets/image/darkThemeProject.png"; import lightThemeImage from "../../assets/image/lightThemeProject.png"; import { SettingsIcon, TrashIcon } from "../icons/ExportCommonIcons"; import { getUserData } from "./functions/getUserData"; -import { useSocketStore } from "../../store/builder/store"; +import { useLoadingProgress, useSocketStore } from "../../store/builder/store"; import { createProject } from "../../services/dashboard/createProject"; interface SidePannelProps { setActiveTab: React.Dispatch>; @@ -21,9 +21,10 @@ interface SidePannelProps { } const SidePannel: React.FC = ({ setActiveTab, activeTab }) => { - const { userName, userId, organization } = getUserData(); + const { email, userName, userId, organization } = getUserData(); const navigate = useNavigate(); - const { dashBoardSocket } = useSocketStore(); + const { loadingProgress, setLoadingProgress } = useLoadingProgress(); + const { dashBoardSocket, projectSocket } = useSocketStore(); const savedTheme = localStorage.getItem("theme") ?? "light"; function generateProjectId() { const randomBytes = new Uint8Array(12); @@ -34,18 +35,21 @@ const SidePannel: React.FC = ({ setActiveTab, activeTab }) => { const handleCreateNewProject = async () => { + const token = localStorage.getItem("token"); try { const projectId = generateProjectId(); + useSocketStore.getState().initializeSocket(email, organization, token); console.log('projectId: ', projectId); navigate(`/${projectId}`); + setLoadingProgress(1) //API for creating new Project - const project = await createProject( - projectId, - userId, - savedTheme === "dark" ? darkThemeImage : lightThemeImage, - organization - ); - console.log('Created project: ', project); + // const project = await createProject( + // projectId, + // userId, + // savedTheme === "dark" ? darkThemeImage : lightThemeImage, + // organization + // ); + // console.log('Created project: ', project); const addProject = { userId, thumbnail: savedTheme === "dark" ? darkThemeImage : lightThemeImage, @@ -54,19 +58,20 @@ const SidePannel: React.FC = ({ setActiveTab, activeTab }) => { }; // socket for creating new project // console.log('dashBoardSocket: ', dashBoardSocket); - // if (dashBoardSocket) { - // // console.log('addProject: ', addProject); - // const handleResponse = (data: any) => { - // console.log('Project add response:', data); - // dashBoardSocket.off("v1-project:response:add", handleResponse); // Clean up - // }; - // dashBoardSocket.on("v1-project:response:add", handleResponse); + console.log('projectSocket: ', projectSocket); + if (projectSocket) { + // console.log('addProject: ', addProject); + const handleResponse = (data: any) => { + console.log('Project add response:', data); + projectSocket.off("v1-project:response:add", handleResponse); // Clean up + }; + projectSocket.on("v1-project:response:add", handleResponse); - // console.log('addProject: ', addProject); - // dashBoardSocket.emit("v1:project:add", addProject); - // } else { - // console.error("Socket is not connected."); - // } + console.log('addProject: ', addProject); + projectSocket.emit("v1:project:add", addProject); + } else { + console.error("Socket is not connected."); + } } catch (error) { console.error("Error creating project:", error); } diff --git a/app/src/components/Dashboard/functions/handleDuplicateProject.ts b/app/src/components/Dashboard/functions/handleDuplicateProject.ts new file mode 100644 index 0000000..c17c9a3 --- /dev/null +++ b/app/src/components/Dashboard/functions/handleDuplicateProject.ts @@ -0,0 +1,117 @@ +// import { getAllProjects } from "../../../services/dashboard/getAllProjects"; + +// interface HandleDuplicateProjectsParams { +// userId: string; +// organization: string; +// projectId: string; +// projectName: string; +// projectSocket: any; +// thumbnail: string; +// setWorkspaceProjects?: React.Dispatch>; +// setRecentProjects?: React.Dispatch>; +// setIsSearchActive: React.Dispatch>; +// } + +// export const handleDuplicateProjects = async ({ +// userId, +// organization, +// projectId, +// projectName, +// projectSocket, +// thumbnail, +// setWorkspaceProjects, +// setRecentProjects, +// setIsSearchActive, +// }: HandleDuplicateProjectsParams): Promise => { +// const duplicateProjectData = { +// userId, +// thumbnail, +// organization, +// projectUuid: projectId, +// projectName, +// }; + +// if (projectSocket) { +// const handleResponse = (data: any) => { +// console.log("Project add response:", data); +// projectSocket.off("v1-project:response:Duplicate", handleResponse); // Clean up +// }; + +// projectSocket.on("v1-project:response:Duplicate", handleResponse); +// console.log("duplicate: ", duplicateProjectData); +// projectSocket.emit("v1:project:Duplicate", duplicateProjectData); + +// const projects = await getAllProjects(userId, organization); +// console.log("projects: works", projects); +// if (setIsSearchActive) { +// if (setWorkspaceProjects) { +// setWorkspaceProjects(projects); +// } else if (setRecentProjects) { +// setRecentProjects(projects); +// } +// setIsSearchActive(false); +// } +// } +// }; + +import { getAllProjects } from "../../../services/dashboard/getAllProjects"; +import { recentlyViewed } from "../../../services/dashboard/recentlyViewed"; + +interface HandleDuplicateProjectsParams { + userId: string; + organization: string; + projectId: string; + projectName: string; + projectSocket: any; + thumbnail: string; + setWorkspaceProjects?: React.Dispatch>; + setRecentProjects?: React.Dispatch>; + setIsSearchActive: React.Dispatch>; +} + +export const handleDuplicateProjects = async ({ + userId, + organization, + projectId, + projectName, + projectSocket, + thumbnail, + setWorkspaceProjects, + setRecentProjects, + setIsSearchActive, +}: HandleDuplicateProjectsParams): Promise => { + const duplicateProjectData = { + userId, + thumbnail, + organization, + projectUuid: projectId, + projectName, + }; + + if (projectSocket) { + const handleResponse = async (data: any) => { + console.log("Project add response:", data); + + if (data?.message === "Project Duplicated successfully") { + if (setWorkspaceProjects) { + const allProjects = await getAllProjects(userId, organization); + setWorkspaceProjects(allProjects); + } else if (setRecentProjects) { + const recentProjects = await recentlyViewed(organization, userId); + console.log('recentProjects: ', recentProjects); + setRecentProjects(recentProjects); + } + + setIsSearchActive(false); + } else { + console.warn("Duplication failed or unexpected response."); + } + + projectSocket.off("v1-project:response:Duplicate", handleResponse); // Clean up + }; + + projectSocket.on("v1-project:response:Duplicate", handleResponse); + console.log("Emitting duplicate:", duplicateProjectData); + projectSocket.emit("v1:project:Duplicate", duplicateProjectData); + } +}; diff --git a/app/src/components/templates/LoadingPage.tsx b/app/src/components/templates/LoadingPage.tsx index 9f1acde..329c19f 100644 --- a/app/src/components/templates/LoadingPage.tsx +++ b/app/src/components/templates/LoadingPage.tsx @@ -11,7 +11,6 @@ interface LoadingPageProps { const LoadingPage: React.FC = ({ progress }) => { const { projectName, setProjectName } = useProjectName(); - console.log('projectName: ', projectName); const { projectId } = useParams(); const validatedProgress = Math.min(100, Math.max(0, progress)); const generateThumbnail = async () => { diff --git a/app/src/components/temporary/SelectFloorPlan.tsx b/app/src/components/temporary/SelectFloorPlan.tsx index 7a95e8e..5056e4c 100644 --- a/app/src/components/temporary/SelectFloorPlan.tsx +++ b/app/src/components/temporary/SelectFloorPlan.tsx @@ -1,31 +1,122 @@ -import React from "react"; +import React, { useEffect, useState } from "react"; import useLayoutStore from "../../store/builder/uselayoutStore"; +import { useDfxUpload } from "../../store/builder/store"; +import DxfParser from "dxf-parser"; +import { getWallPointsFromBlueprint } from "../../modules/builder/dfx/functions/getWallPointsFromBlueprint"; +import { convertDXFToThree } from "../../modules/builder/dfx/functions/convertDxfToThree"; const SelectFloorPlan: React.FC = () => { + // Access layout state and state setters const { currentLayout, setLayout } = useLayoutStore(); + // Access DXF-related states and setters + const { setDfxUploaded, setDfxGenerate, setObjValue, objValue } = useDfxUpload(); + + // Local state to store the parsed DXF file + const [parsedFile, setParsedFile] = useState(undefined); + + // Flag to trigger generation after file upload + const [generate, setGenerate] = useState(false); + + // Handles file upload and DXF parsing + const handleFileUpload = async (event: React.ChangeEvent) => { + setLayout(null); // Reset current layout + setParsedFile(undefined); // Clear any previously parsed file + const file = event.target.files?.[0]; + if (!file || !file.name.endsWith(".dxf")) { + alert("Please upload a valid .dxf file."); + return; + } + + const reader = new FileReader(); + reader.onload = async (e) => { + const dxfContent = e.target?.result as string; + + try { + const parser = new DxfParser(); + const parsedDatas = parser.parse(dxfContent) as DXFData; + const geometries = convertDXFToThree(parsedDatas); + setParsedFile(parsedDatas); + setObjValue({ x: 0, y: 0, z: 0 }); + setDfxUploaded(geometries); + } catch (error) { + console.error("Failed to import your .dxf file", error); + } finally { + // ✅ Reset input AFTER processing + event.target.value = ""; + } + }; + + reader.readAsText(file); // Read the uploaded file as text + }; + + // Trigger wall point generation when `generate` flag changes + useEffect(() => { + if (parsedFile !== undefined) { + getWallPointsFromBlueprint({ parsedData: parsedFile, setDfxGenerate, objValue }); + } + }, [generate]); + + return (
Preset Layouts +
+ + + + + + +
); }; -export default SelectFloorPlan; +export default SelectFloorPlan; \ No newline at end of file diff --git a/app/src/components/ui/features/RenameTooltip.tsx b/app/src/components/ui/features/RenameTooltip.tsx index 180ba85..87de122 100644 --- a/app/src/components/ui/features/RenameTooltip.tsx +++ b/app/src/components/ui/features/RenameTooltip.tsx @@ -4,6 +4,7 @@ import { useLeftData, useTopData, } from "../../../store/visualization/useZone3DWidgetStore"; +import { useRenameModeStore } from "../../../store/builder/store"; type RenameTooltipProps = { name: string; @@ -13,12 +14,14 @@ type RenameTooltipProps = { const RenameTooltip: React.FC = ({ name, onSubmit }) => { const [value, setValue] = useState(name); - const { top } = useTopData(); - const { left } = useLeftData(); + const { top, setTop } = useTopData(); + const { left, setLeft } = useLeftData(); const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); onSubmit(value.trim()); + setTop(0); + setLeft(0); }; return ( diff --git a/app/src/modules/builder/builder.tsx b/app/src/modules/builder/builder.tsx index 18d407b..d6df0d0 100644 --- a/app/src/modules/builder/builder.tsx +++ b/app/src/modules/builder/builder.tsx @@ -49,6 +49,7 @@ import CalculateAreaGroup from "./groups/calculateAreaGroup"; import LayoutImage from "./layout/layoutImage"; import AssetsGroup from "./assetGroup/assetsGroup"; import { Bvh } from "@react-three/drei"; +import DxfFile from "./dfx/LoadBlueprint"; export default function Builder() { const state = useThree(); // Importing the state from the useThree hook, which contains the scene, camera, and other Three.js elements. @@ -311,9 +312,16 @@ export default function Builder() { - + + diff --git a/app/src/modules/builder/dfx/LoadBlueprint.tsx b/app/src/modules/builder/dfx/LoadBlueprint.tsx new file mode 100644 index 0000000..8e0a1ab --- /dev/null +++ b/app/src/modules/builder/dfx/LoadBlueprint.tsx @@ -0,0 +1,146 @@ +import { useEffect, useRef } from 'react'; +import { useDfxUpload, useSocketStore, useToggleView, useUpdateScene } from '../../../store/builder/store'; +import { LineBasicMaterial, Line } from 'three'; +import loadInitialPoint from '../IntialLoad/loadInitialPoint'; +import loadInitialLine from '../IntialLoad/loadInitialLine'; +import { TransformControls } from '@react-three/drei'; +import { getWallPointsFromBlueprint } from './functions/getWallPointsFromBlueprint'; +import * as Types from '../../../types/world/worldTypes'; +import arrayLineToObject from '../geomentries/lines/lineConvertions/arrayLineToObject'; + +// Interface defining the props for the DxfFile component +interface DxfFileProps { + lines: Types.RefLines; // Reference to lines in the DXF file + floorPlanGroupPoint: Types.RefGroup; // Reference to floor plan points group + dragPointControls: Types.RefDragControl; // Reference to drag controls + floorPlanGroupLine: Types.RefGroup; // Reference to floor plan lines group + currentLayerPoint: Types.RefMeshArray; // Reference to current layer points +} + +/** + * DxfFile component handles the rendering and manipulation of DXf file data in a 3D scene. + * It processes the DXF data to create points and lines representing walls and allows + * transformation controls for interactive editing. + */ +const DxfFile = ({ + floorPlanGroupPoint, + currentLayerPoint, + dragPointControls, + floorPlanGroupLine, + lines, +}: DxfFileProps) => { + // State management hooks + const { dfxuploaded, dfxWallGenerate, setObjValue, objValue } = useDfxUpload(); + const { setUpdateScene } = useUpdateScene(); + const { toggleView } = useToggleView(); + const { socket } = useSocketStore(); + + // Refs for storing line objects + const lineRefs = useRef([]); + + /** + * Effect hook that runs when DXF wall generation is triggered. + * Loads initial points and lines from the DXF data and updates the scene. + */ + useEffect(() => { + if ( + dfxWallGenerate && + dragPointControls && + floorPlanGroupPoint && + currentLayerPoint && + floorPlanGroupLine + ) { + // Store generated lines in ref + lines.current.push(...dfxWallGenerate); + dfxWallGenerate.map((line: any) => { + const lineData = arrayLineToObject(line as Types.Line); + const email = localStorage.getItem('email') + const organization = (email!.split("@")[1]).split(".")[0]; + + //REST + + // setLine(organization, lineData.layer!, lineData.line!, lineData.type!); + + //SOCKET + + const input = { + organization: organization, + layer: lineData.layer, + line: lineData.line, + type: lineData.type, + socketId: socket.id + } + + socket.emit('v1:Line:create', input); + + }) + + // Load initial points and lines from DXF data + loadInitialPoint(lines, floorPlanGroupPoint, currentLayerPoint, dragPointControls); + loadInitialLine(floorPlanGroupLine, lines); + + // Trigger scene update + setUpdateScene(true); + } + }, [ + lines, + floorPlanGroupLine, + floorPlanGroupPoint, + currentLayerPoint, + dragPointControls, + dfxWallGenerate, + ]); + + /** + * Handles transformation changes for individual lines. + * Updates the object value state with new position and recalculates wall points. + * @param index - Index of the line being transformed + */ + const handleTransformChange = (index: number) => { + const line = lineRefs.current[index]; + if (!line) return; + + // Get current position of the line + const position = line.position; + + // Update state with new position + setObjValue({ x: position.x, y: position.y, z: position.z }); + + // Recalculate wall points based on new position + getWallPointsFromBlueprint({ + objValue: { x: position.x, y: position.y, z: position.z }, + setDfxGenerate: () => { }, + }); + }; + + return ( + <> + {/* Render DXF lines with transform controls when DXF data is available and view is toggled */} + {dfxuploaded && + dfxuploaded.length > 0 && + toggleView && + dfxuploaded?.map((geometry: any, index: number) => { + // Create a new line object for each geometry in the DXF data + const line = new Line(geometry, new LineBasicMaterial({ color: 'red' })); + line.rotation.set(-Math.PI / 2, 0, 0); + + // Store line reference + lineRefs.current[index] = line; + + return ( + handleTransformChange(index)} + > + {/* Render the line with current position from state */} + + + ); + })} + + ); +}; + +export default DxfFile; \ No newline at end of file diff --git a/app/src/modules/builder/dfx/functions/convertDxfToThree.ts b/app/src/modules/builder/dfx/functions/convertDxfToThree.ts new file mode 100644 index 0000000..7bf2634 --- /dev/null +++ b/app/src/modules/builder/dfx/functions/convertDxfToThree.ts @@ -0,0 +1,65 @@ +import { BufferGeometry, Vector3 } from "three"; + +export const convertDXFToThree = (dxfData: DXFData): BufferGeometry[] => { + const geometries: BufferGeometry[] = []; + const UNIT = 1000; + if (dxfData.entities) { + dxfData.entities.forEach((entity) => { + // LINE + if (entity.type === "LINE" && entity.vertices) { + const points = [ + new Vector3(entity.vertices[0].x, entity.vertices[0].y, 0), + new Vector3(entity.vertices[1].x, entity.vertices[1].y, 0), + ]; + const geometry = new BufferGeometry().setFromPoints(points); + geometry.scale(1 / UNIT, 1 / UNIT, 1 / UNIT); + geometries.push(geometry); + } + + // LWPOLYLINE + else if (entity.type === "LWPOLYLINE" && entity.vertices) { + const points: Vector3[] = entity.vertices.map( + (v: any) => new Vector3(v.x, v.y, 0) + ); + + for (let i = 0; i < points.length - 1; i++) { + const segment = [points[i], points[i + 1]]; + const geometry = new BufferGeometry().setFromPoints(segment); + geometry.scale(1 / UNIT, 1 / UNIT, 1 / UNIT); + geometries.push(geometry); + } + } + + // ARC + else if ( + entity.type === "ARC" && + entity.center && + entity.radius !== undefined + ) { + const { center, radius, startAngle, endAngle } = entity; + if ( + center === undefined || + radius === undefined || + startAngle === undefined || + endAngle === undefined + ) + return; + const numSegments = 32; + const points: Vector3[] = []; + + for (let i = 0; i <= numSegments; i++) { + const t = i / numSegments; + const angle = startAngle + t * (endAngle - startAngle); // linear interpolation + const x = center.x + radius * Math.cos(angle); + const y = center.y + radius * Math.sin(angle); + points.push(new Vector3(x, y, 0)); + } + + const geometry = new BufferGeometry().setFromPoints(points); + geometry.scale(1 / UNIT, 1 / UNIT, 1 / UNIT); + geometries.push(geometry); + } + }); + } + return geometries; +}; diff --git a/app/src/modules/builder/dfx/functions/getWallPointsFromBlueprint.ts b/app/src/modules/builder/dfx/functions/getWallPointsFromBlueprint.ts new file mode 100644 index 0000000..13cae84 --- /dev/null +++ b/app/src/modules/builder/dfx/functions/getWallPointsFromBlueprint.ts @@ -0,0 +1,185 @@ +import { MathUtils, Vector3, BufferGeometry } from "three"; + +interface Props { + parsedData?: DXFData; // Parsed DXF file data + setDfxGenerate?: (walls: WallLineVertex[][]) => void; // Callback to set generated walls + objValue: any; // Object position values for offset calculation +} + +/** + * Processes DXF entities to generate wall points for a blueprint. + * Handles LINE, LWPOLYLINE, and ARC entity types, converting them to wall segments. + * Applies unit conversion and positional offsets to all points. + * + * @param {Props} params - Configuration parameters + * @param {DXFData} params.parsedData - Parsed DXF file data + * @param {Function} params.setDfxGenerate - Callback to store generated walls + * @param {Object} params.objValue - Contains x,y,z offsets for position adjustment + */ +export function getWallPointsFromBlueprint({ + parsedData, + setDfxGenerate, + objValue, +}: Props) { + // Early return if no data is provided + if (!parsedData) return; + if (!parsedData.entities) return; + + const unit = 1000; // Conversion factor from millimeters to meters + const wallVertex: WallLineVertex[][] = []; // Stores all generated wall segments + + // Process each entity in the DXF file + parsedData.entities.forEach((entity: DXFEntity) => { + // Handle LINE entities + if (entity.type === "LINE" && entity.vertices) { + // Create start and end vectors with unit conversion and position offset + const startVec = new Vector3( + entity.vertices[0].x / unit, + 0.01, // Slightly above ground to avoid z-fighting + -entity.vertices[0].y / unit // Invert Y-axis to match Three.js coordinate system + ).add(new Vector3(objValue.x, 0, objValue.z)); + + const endVec = new Vector3( + entity.vertices[1].x / unit, + 0.01, + -entity.vertices[1].y / unit + ).add(new Vector3(objValue.x, 0, objValue.z)); + + // Check if points already exist to avoid duplicates + const existingStart = wallVertex + .flat() + .find((v) => v[0].equals(startVec)); + const startPoint: WallLineVertex = existingStart || [ + startVec, + MathUtils.generateUUID(), // Generate unique ID for new points + 1, // Default weight + "WallLine", // Type identifier + ]; + + const existingEnd = wallVertex.flat().find((v) => v[0].equals(endVec)); + const endPoint: WallLineVertex = existingEnd || [ + endVec, + MathUtils.generateUUID(), + 1, + "WallLine", + ]; + + // Add the line segment to our collection + wallVertex.push([startPoint, endPoint]); + } + // Handle LWPOLYLINE entities (connected line segments) + else if (entity.type === "LWPOLYLINE" && entity.vertices) { + let firstPoint: WallLineVertex | undefined; // Store first point for closing the polyline + + // Process each vertex pair in the polyline + for (let i = 0; i < entity.vertices.length - 1; i++) { + // Convert vertices to Three.js vectors with offset + const startVec = new Vector3( + entity.vertices[i].x / unit, + 0.01, + -entity.vertices[i].y / unit + ).add(new Vector3(objValue.x, 0, objValue.z)); + + const endVec = new Vector3( + entity.vertices[i + 1].x / unit, + 0.01, + -entity.vertices[i + 1].y / unit + ).add(new Vector3(objValue.x, 0, objValue.z)); + + // Check for existing points + const existingStart = wallVertex + .flat() + .find((v) => v[0].equals(startVec)); + const startPoint: WallLineVertex = existingStart || [ + startVec, + MathUtils.generateUUID(), + 1, + "WallLine", + ]; + + const existingEnd = wallVertex.flat().find((v) => v[0].equals(endVec)); + const endPoint: WallLineVertex = existingEnd || [ + endVec, + MathUtils.generateUUID(), + 1, + "WallLine", + ]; + + wallVertex.push([startPoint, endPoint]); + + // Store first point and create closing segment if this is the last vertex + if (i === 0) firstPoint = startPoint; + if (i === entity.vertices.length - 2 && firstPoint) { + wallVertex.push([endPoint, firstPoint]); + } + } + } + // Handle ARC entities + else if (entity.type === "ARC") { + const { center, radius, startAngle, endAngle } = entity; + // Validate required ARC properties + if ( + !center || + radius === undefined || + startAngle === undefined || + endAngle === undefined + ) { + return; + } + + // Convert ARC to series of line segments + const numSegments = 16; // Number of segments to approximate the arc + const angleStep = (endAngle - startAngle) / numSegments; + const arcPoints: Vector3[] = []; // Stores points along the arc + + // Generate points along the arc + for (let i = 0; i <= numSegments; i++) { + const angle = startAngle + i * angleStep; + // Calculate arc point in DXF coordinate system + const x = center.x + radius * Math.cos(angle); + const y = -center.y + radius * Math.sin(angle); // Invert Y-axis + + // Convert to Three.js vector with offset + const vec = new Vector3(x / unit, 0.01, y / unit).add( + new Vector3(objValue.x, 0, objValue.z) + ); + + arcPoints.push(vec); + } + + // Create line segments between arc points + for (let i = 0; i < arcPoints.length - 1; i++) { + const startVec = arcPoints[i]; + const endVec = arcPoints[i + 1]; + + // Check for existing points + const existingStart = wallVertex + .flat() + .find((v) => v[0].equals(startVec)); + const startPoint: WallLineVertex = existingStart || [ + startVec, + MathUtils.generateUUID(), + 1, + "WallLine", + ]; + + const existingEnd = wallVertex.flat().find((v) => v[0].equals(endVec)); + const endPoint: WallLineVertex = existingEnd || [ + endVec, + MathUtils.generateUUID(), + 1, + "WallLine", + ]; + + wallVertex.push([startPoint, endPoint]); + } + } + // Log unsupported entity types + else { + console.error("Unsupported entity type:", entity.type); + } + }); + + // Return the generated walls through callback if provided + setDfxGenerate && setDfxGenerate(wallVertex); +} diff --git a/app/src/modules/builder/groups/floorGroup.tsx b/app/src/modules/builder/groups/floorGroup.tsx index 2c2a8df..2cf5586 100644 --- a/app/src/modules/builder/groups/floorGroup.tsx +++ b/app/src/modules/builder/groups/floorGroup.tsx @@ -6,6 +6,7 @@ import { useToggleView, useWallVisibility, useUpdateScene, + useRenameModeStore, } from "../../../store/builder/store"; import hideRoof from "../geomentries/roofs/hideRoof"; import hideWalls from "../geomentries/walls/hideWalls"; @@ -15,6 +16,7 @@ import addPillar from "../geomentries/pillars/addPillar"; import DeletePillar from "../geomentries/pillars/deletePillar"; import DeletableHoveredPillar from "../geomentries/pillars/deletableHoveredPillar"; import loadFloor from "../geomentries/floors/loadFloor"; +import { useLeftData, useTopData } from "../../../store/visualization/useZone3DWidgetStore"; const FloorGroup = ({ floorGroup, @@ -30,6 +32,9 @@ const FloorGroup = ({ const { addAction } = useAddAction(); const { deleteTool } = useDeleteTool(); const { updateScene, setUpdateScene } = useUpdateScene(); + const { setTop } = useTopData(); + const { setLeft } = useLeftData(); + const { isRenameMode, setIsRenameMode } = useRenameModeStore(); useEffect(() => { if (updateScene) { @@ -55,6 +60,7 @@ const FloorGroup = ({ let isLeftMouseDown = false; const onMouseDown = (evt: any) => { + if (evt.button === 0) { isLeftMouseDown = true; drag = false; @@ -62,6 +68,7 @@ const FloorGroup = ({ }; const onMouseUp = (evt: any) => { + setIsRenameMode(false); if (evt.button === 0) { isLeftMouseDown = false; if (!drag) { @@ -75,7 +82,15 @@ const FloorGroup = ({ } }; - const onMouseMove = () => { + const onMouseMove = (evt: any) => { + if (!canvasElement) return; + const canvasRect = canvasElement.getBoundingClientRect(); + const relativeX = evt.clientX - canvasRect.left; + const relativeY = evt.clientY - canvasRect.top; + if (!isRenameMode) { + setTop(relativeY); + setLeft(relativeX); + } if (isLeftMouseDown) { drag = true; } @@ -90,7 +105,7 @@ const FloorGroup = ({ canvasElement.removeEventListener("mouseup", onMouseUp); canvasElement.removeEventListener("mousemove", onMouseMove); }; - }, [deleteTool, addAction]); + }, [deleteTool, addAction, isRenameMode]); useFrame(() => { hideRoof(roofVisibility, floorGroup, camera); diff --git a/app/src/pages/Dashboard.tsx b/app/src/pages/Dashboard.tsx index 1658fc2..0a26a18 100644 --- a/app/src/pages/Dashboard.tsx +++ b/app/src/pages/Dashboard.tsx @@ -15,16 +15,35 @@ const Dashboard: React.FC = () => { const [activeTab, setActiveTab] = useState("Home"); const { setUserName } = useUserName(); const { setOrganization } = useOrganization(); + const { socket } = useSocketStore(); const { userId, organization, email, userName } = getUserData(); + // const token = localStorage.getItem("token") + // useEffect(() => { + // + // useSocketStore.getState().initializeSocket(email, organization, token) + // + // // useSocketStore.getState().initializeSocket(email, organization, userId); + // if (organization && userName) { + // setOrganization(organization); + // setUserName(userName); + // } + + // }, [socket]); useEffect(() => { - if (email) { - useSocketStore.getState().initializeSocket(email, organization, userId); - if (organization && userName) { - setOrganization(organization); - setUserName(userName); + const token = localStorage.getItem("token"); + if (token) { + try { + } catch (e) { } + useSocketStore.getState().initializeSocket(email, organization, token); + } else { + } - }, []); + // return () => { + // useSocketStore.getState().disconnectSocket(); + // }; + }, [socket]); + return (
diff --git a/app/src/pages/Project.tsx b/app/src/pages/Project.tsx index 4ca4362..643a15c 100644 --- a/app/src/pages/Project.tsx +++ b/app/src/pages/Project.tsx @@ -16,6 +16,8 @@ import { useWidgetSubOption, useSaveVersion, useProjectName, + useRenameModeStore, + useSelectedFloorItem, } from "../store/builder/store"; import { useNavigate, useParams } from "react-router-dom"; import { usePlayButtonStore } from "../store/usePlayButtonStore"; @@ -42,6 +44,8 @@ import SimulationPlayer from "../components/ui/simulation/simulationPlayer"; import { useProductStore } from "../store/simulation/useProductStore"; import { getAllProjects } from "../services/dashboard/getAllProjects"; import { viewProject } from "../services/dashboard/viewProject"; +import RenameTooltip from "../components/ui/features/RenameTooltip"; +import { setFloorItemApi } from "../services/factoryBuilder/assest/floorAsset/setFloorItemApi"; const Project: React.FC = () => { let navigate = useNavigate(); @@ -53,6 +57,7 @@ const Project: React.FC = () => { const { setUserName } = useUserName(); const { setOrganization } = useOrganization(); const { setFloorItems } = useFloorItems(); + const { selectedFloorItem, setSelectedFloorItem } = useSelectedFloorItem(); const { setWallItems } = useWallItems(); const { setZones } = useZones(); const { isVersionSaved } = useSaveVersion(); @@ -98,6 +103,8 @@ const Project: React.FC = () => { useEffect(() => { generateThumbnail(); }, []); + const { isRenameMode, setIsRenameMode } = useRenameModeStore(); + // console.log('isRenameMode: ', isRenameMode); useEffect(() => { if (!isVersionSaved) { @@ -156,6 +163,29 @@ const Project: React.FC = () => { setSelectedLayout(option); // Set selected layout console.log("Selected layout:", option); }; + + const handleObjectRename = async (newName: string) => { + const email = localStorage.getItem("email") ?? ""; + const organization = email?.split("@")[1]?.split(".")[0]; + let response = await setFloorItemApi( + organization, + selectedFloorItem.userData.modelUuid, + newName + ); + selectedFloorItem.userData.name = newName; + setSelectedFloorItem(selectedFloorItem); + setIsRenameMode(false); + setFloorItems((prevFloorItems: any[]) => + prevFloorItems.map((floorItems) => + floorItems.modelUuid === selectedFloorItem.userData.modelUuid + .id + ? { ...floorItems, modelName: response.modelName } + : floorItems + ) + ); + } + + return (
{!selectedUser && ( @@ -205,6 +235,7 @@ const Project: React.FC = () => { >
+ {isRenameMode && selectedFloorItem?.userData.name && } {selectedUser && } {isLogListVisible && ( diff --git a/app/src/pages/UserAuth.tsx b/app/src/pages/UserAuth.tsx index 8b8453b..3871394 100644 --- a/app/src/pages/UserAuth.tsx +++ b/app/src/pages/UserAuth.tsx @@ -41,6 +41,7 @@ const UserAuth: React.FC = () => { try { const res = await signInApi(email, password, organization, fingerprint); + console.log('res: ', res); if (res.message.message === "login successfull") { setError(""); setOrganization(organization); diff --git a/app/src/services/dashboard/createProject.ts b/app/src/services/dashboard/createProject.ts index dead45b..7aada60 100644 --- a/app/src/services/dashboard/createProject.ts +++ b/app/src/services/dashboard/createProject.ts @@ -3,7 +3,7 @@ let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_UR export const createProject = async (projectUuid: string, userId: string, thumbnail: string, organization: string) => { try { - const response = await fetch(`${url_Backend_dwinzo}/api/v2/upsertProject`, { + const response = await fetch(`${url_Backend_dwinzo}/api/V1/NewProject`, { method: "POST", headers: { Authorization: "Bearer ", // Replace with actual token @@ -14,6 +14,7 @@ export const createProject = async (projectUuid: string, userId: string, thumbna body: JSON.stringify({ projectUuid, userId, thumbnail, organization, }), }); + console.log('response: ', response); if (!response.ok) { throw new Error("Failed to add project"); } diff --git a/app/src/services/dashboard/deleteProject.ts b/app/src/services/dashboard/deleteProject.ts index be8abea..e804099 100644 --- a/app/src/services/dashboard/deleteProject.ts +++ b/app/src/services/dashboard/deleteProject.ts @@ -1,26 +1,27 @@ let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; // let url_Backend_dwinzo = `http://192.168.0.102:5000`; - + export const deleteProject = async ( projectId: string, userId: string, organization: string ) => { try { + console.log("projectId", userId, organization, projectId); const response = await fetch( - `${url_Backend_dwinzo}/api/v2/Project/archive/${projectId}`, + `${url_Backend_dwinzo}/api/V1/Projects/Archive/${projectId}`, { method: "PATCH", headers: { - Authorization: "Bearer ", // Replace with actual token + Authorization: "Bearer ", // Replace with actual token "Content-Type": "application/json", - token: localStorage.getItem("token") || "", // Coerce null to empty string + token: localStorage.getItem("token") || "", // Coerce null to empty string refresh_token: localStorage.getItem("refreshToken") || "", }, body: JSON.stringify({ userId, organization }), } ); - + console.log("response: ", response); if (!response.ok) { throw new Error("Failed to clearPanel in the zone"); } diff --git a/app/src/services/dashboard/deleteTrash.ts b/app/src/services/dashboard/deleteTrash.ts new file mode 100644 index 0000000..6898914 --- /dev/null +++ b/app/src/services/dashboard/deleteTrash.ts @@ -0,0 +1,30 @@ +const url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; +// const url_Backend_dwinzo = `http://192.168.0.102:5000`; + +export const deleteTrash = async (organization: string, projectId: string) => { + try { + const response = await fetch( + `${url_Backend_dwinzo}/api/V1/Trash/Delete?projectId=${projectId}`, + { + method: "PATCH", + headers: { + Authorization: "Bearer ", // Replace with actual token + "Content-Type": "application/json", + token: localStorage.getItem("token") || "", // Coerce null to empty string + refresh_token: localStorage.getItem("refreshToken") || "", + }, + } + ); + console.log("restore: ", response); + + if (!response.ok) { + throw new Error("Failed to fetch trash data"); + } + + const data = await response.json(); + return data; + } catch (error: any) { + console.error("Failed to fetch trash data:", error); + throw new Error(error.message || "Unknown error"); + } +}; diff --git a/app/src/services/dashboard/duplicateProject.ts b/app/src/services/dashboard/duplicateProject.ts new file mode 100644 index 0000000..911d824 --- /dev/null +++ b/app/src/services/dashboard/duplicateProject.ts @@ -0,0 +1,38 @@ +let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; + +export const duplicateProject = async ( + projectUuid: string, + thumbnail: string, + projectName: string +) => { + try { + const response = await fetch( + `${url_Backend_dwinzo}/api/V1/project/Duplicate`, + { + method: "POST", + headers: { + Authorization: "Bearer ", // Replace with actual token + "Content-Type": "application/json", + token: localStorage.getItem("token") || "", // Coerce null to empty string + refresh_token: localStorage.getItem("refreshToken") || "", + }, + body: JSON.stringify({ projectUuid, thumbnail, projectName }), + } + ); + + console.log("response: ", response); + if (!response.ok) { + throw new Error("Failed to add project"); + } + + const result = await response.json(); + console.log("result: ", result); + return result; + } catch (error) { + if (error instanceof Error) { + throw new Error(error.message); + } else { + throw new Error("An unknown error occurred"); + } + } +}; diff --git a/app/src/services/dashboard/getAllProjects.ts b/app/src/services/dashboard/getAllProjects.ts index dc2e3b9..caebc0a 100644 --- a/app/src/services/dashboard/getAllProjects.ts +++ b/app/src/services/dashboard/getAllProjects.ts @@ -3,7 +3,7 @@ let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_UR export const getAllProjects = async (userId: string, organization: string) => { try { const response = await fetch( - `${url_Backend_dwinzo}/api/v2/Projects`, + `${url_Backend_dwinzo}/api/V1/Projects`, { method: "GET", headers: { diff --git a/app/src/services/dashboard/getTrash.ts b/app/src/services/dashboard/getTrash.ts index f9db953..acdcad8 100644 --- a/app/src/services/dashboard/getTrash.ts +++ b/app/src/services/dashboard/getTrash.ts @@ -2,11 +2,10 @@ const url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_ // const url_Backend_dwinzo = `http://192.168.0.102:5000`; export const getTrash = async (organization: string) => { - console.log("Organization:", organization); try { const response = await fetch( - `${url_Backend_dwinzo}/api/v2/TrashItems`, + `${url_Backend_dwinzo}/api/V1/TrashItems`, { method: "GET", headers: { @@ -23,7 +22,6 @@ export const getTrash = async (organization: string) => { } const data = await response.json(); - console.log('TrashItems: ', data); return data; } catch (error: any) { console.error("Failed to fetch trash data:", error); diff --git a/app/src/services/dashboard/projectTutorial.ts b/app/src/services/dashboard/projectTutorial.ts index bf7b121..5bd84a0 100644 --- a/app/src/services/dashboard/projectTutorial.ts +++ b/app/src/services/dashboard/projectTutorial.ts @@ -2,7 +2,7 @@ let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_UR export const projectTutorial = async () => { try { - const response = await fetch(`${url_Backend_dwinzo}/api/v1/tutorials`, { + const response = await fetch(`${url_Backend_dwinzo}/api/V1/tutorials`, { method: "GET", headers: { "Content-Type": "application/json", diff --git a/app/src/services/dashboard/recentlyViewed.ts b/app/src/services/dashboard/recentlyViewed.ts index 78a0696..c77c679 100644 --- a/app/src/services/dashboard/recentlyViewed.ts +++ b/app/src/services/dashboard/recentlyViewed.ts @@ -3,7 +3,7 @@ let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_UR export const recentlyViewed = async (organization: string, userId: string) => { try { const response = await fetch( - `${url_Backend_dwinzo}/api/v2/RecentlyViewed`, + `${url_Backend_dwinzo}/api/V1/RecentlyViewed`, { method: "GET", headers: { diff --git a/app/src/services/dashboard/restoreTrash.ts b/app/src/services/dashboard/restoreTrash.ts index 2af5d06..fdf544d 100644 --- a/app/src/services/dashboard/restoreTrash.ts +++ b/app/src/services/dashboard/restoreTrash.ts @@ -4,7 +4,7 @@ const url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_ export const restoreTrash = async (organization: string, projectId: string) => { try { const response = await fetch( - `${url_Backend_dwinzo}/api/v2/Trash/restore?projectId=${projectId}`, + `${url_Backend_dwinzo}/api/V1/Trash/restore?projectId=${projectId}`, { method: "PATCH", headers: { diff --git a/app/src/services/dashboard/searchProjects.ts b/app/src/services/dashboard/searchProjects.ts index 30a7062..a1b1cee 100644 --- a/app/src/services/dashboard/searchProjects.ts +++ b/app/src/services/dashboard/searchProjects.ts @@ -7,26 +7,26 @@ export const searchProject = async ( ) => { try { const response = await fetch( - `${url_Backend_dwinzo}/api/v2/searchProjects?searchName=${searchName}`, + `${url_Backend_dwinzo}/api/V1/search/searchProjects?searchName=${searchName}`, { method: "GET", headers: { - Authorization: "Bearer ", // Replace with actual token + Authorization: "Bearer ", // Replace with actual token "Content-Type": "application/json", - token: localStorage.getItem("token") || "", // Coerce null to empty string + token: localStorage.getItem("token") || "", // Coerce null to empty string refresh_token: localStorage.getItem("refreshToken") || "", }, } ); + console.log("response: ", response); if (!response.ok) { throw new Error("Failed to Search project"); } - const result = await response.json(); console.log("searchProjects: ", result); - + return result; } catch (error) { if (error instanceof Error) { diff --git a/app/src/services/dashboard/trashSearchProject.ts b/app/src/services/dashboard/trashSearchProject.ts index 14c213f..1e25082 100644 --- a/app/src/services/dashboard/trashSearchProject.ts +++ b/app/src/services/dashboard/trashSearchProject.ts @@ -7,13 +7,13 @@ export const trashSearchProject = async ( ) => { try { const response = await fetch( - `${url_Backend_dwinzo}/api/v2/searchTrashProjects?searchName=${searchName}`, + `${url_Backend_dwinzo}/api/V1/search/searchTrashProjects?searchName=${searchName}`, { method: "GET", headers: { - Authorization: "Bearer ", // Replace with actual token + Authorization: "Bearer ", // Replace with actual token "Content-Type": "application/json", - token: localStorage.getItem("token") || "", // Coerce null to empty string + token: localStorage.getItem("token") || "", // Coerce null to empty string refresh_token: localStorage.getItem("refreshToken") || "", }, } @@ -24,7 +24,7 @@ export const trashSearchProject = async ( } const result = await response.json(); - console.log('searchTrashProjects: ', result); + console.log("searchTrashProjects: ", result); return result; } catch (error) { if (error instanceof Error) { @@ -34,4 +34,3 @@ export const trashSearchProject = async ( } } }; - \ No newline at end of file diff --git a/app/src/services/dashboard/updateProject.ts b/app/src/services/dashboard/updateProject.ts index cf0d1a6..7bd0f4a 100644 --- a/app/src/services/dashboard/updateProject.ts +++ b/app/src/services/dashboard/updateProject.ts @@ -19,7 +19,7 @@ export const updateProject = async ( try { const response = await fetch( - `${url_Backend_dwinzo}/api/v2/Project/${projectId}`, + `${url_Backend_dwinzo}/api/V1/Projects/${projectId}`, { method: "PATCH", headers: { diff --git a/app/src/services/dashboard/viewProject.ts b/app/src/services/dashboard/viewProject.ts index 67a4316..07a9e00 100644 --- a/app/src/services/dashboard/viewProject.ts +++ b/app/src/services/dashboard/viewProject.ts @@ -1,14 +1,19 @@ let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; -export const viewProject = async (organization: string,projectId: string,userId: string) => { +export const viewProject = async ( + organization: string, + projectId: string, + userId: string +) => { try { - const response = await fetch(`${url_Backend_dwinzo}/api/v2/Project/${projectId}`, + const response = await fetch( + `${url_Backend_dwinzo}/api/V1/Project/${projectId}`, { method: "GET", headers: { - Authorization: "Bearer ", // Replace with actual token + Authorization: "Bearer ", // Replace with actual token "Content-Type": "application/json", - token: localStorage.getItem("token") || "", // Coerce null to empty string + token: localStorage.getItem("token") || "", // Coerce null to empty string refresh_token: localStorage.getItem("refreshToken") || "", }, } @@ -24,4 +29,3 @@ export const viewProject = async (organization: string,projectId: string,userId: throw new Error(error.message); } }; - \ No newline at end of file diff --git a/app/src/services/factoryBuilder/signInSignUp/signInApi.ts b/app/src/services/factoryBuilder/signInSignUp/signInApi.ts index 9acd244..06f2a82 100644 --- a/app/src/services/factoryBuilder/signInSignUp/signInApi.ts +++ b/app/src/services/factoryBuilder/signInSignUp/signInApi.ts @@ -2,7 +2,7 @@ let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_UR export const signInApi = async (Email: string,Password: Object,organization: Object,fingerprint:any) => { try { - const response = await fetch(`${url_Backend_dwinzo}/api/v2/Auth/login`, { + const response = await fetch(`${url_Backend_dwinzo}/api/V1/Auth/login`, { method: "POST", headers: {"Content-Type": "application/json",}, body: JSON.stringify({ Email, Password, organization,fingerprint}), diff --git a/app/src/services/factoryBuilder/signInSignUp/signUpApi.ts b/app/src/services/factoryBuilder/signInSignUp/signUpApi.ts index 859b8ec..18992dc 100644 --- a/app/src/services/factoryBuilder/signInSignUp/signUpApi.ts +++ b/app/src/services/factoryBuilder/signInSignUp/signUpApi.ts @@ -7,7 +7,7 @@ export const signUpApi = async ( organization: Object ) => { try { - const response = await fetch(`${url_Backend_dwinzo}/api/v2/Auth/signup`, { + const response = await fetch(`${url_Backend_dwinzo}/api/V1/Auth/signup`, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ userName, Email, Password, organization }), diff --git a/app/src/store/builder/store.ts b/app/src/store/builder/store.ts index 9d18843..2b0f26d 100644 --- a/app/src/store/builder/store.ts +++ b/app/src/store/builder/store.ts @@ -5,48 +5,110 @@ import * as CONSTANTS from "../../types/world/worldConstants"; export const useSocketStore = create((set: any, get: any) => ({ socket: null, - initializeSocket: (email: string, organization: string, userId?: string) => { + initializeSocket: (email?: string, organization?: string, token?: string) => { const existingSocket = get().socket; if (existingSocket) { return; } const socket = io( - `http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/Builder`, + // `http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/Builder`, { reconnection: true, - auth: { email, organization }, + // auth: { email, organization }, } ); const visualizationSocket = io( - `http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/Visualization`, + // `http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/Visualization`, { reconnection: true, - auth: { email, organization }, + // auth: { email, organization }, } ); const dashBoardSocket = io( + `http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/dashboard`, + { + reconnection: true, + auth: { token }, + } + ); + const projectSocket = io( `http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/project`, { reconnection: true, - auth: { email, organization, userId }, + auth: { token }, } ); - set({ socket, visualizationSocket, dashBoardSocket }); + set({ socket, visualizationSocket, dashBoardSocket, projectSocket }); }, disconnectSocket: () => { set((state: any) => { state.socket?.disconnect(); state.visualizationSocket?.disconnect(); state.dashBoardSocket?.disconnect(); + state.projectSocket?.disconnect(); return { socket: null }; }); }, })); +// export const useSocketStore = create((set: any, get: any) => ({ +// socket: null, +// initializeSocket: ( +// email: string, +// organization: string, +// userId?: string, +// token?: string +// ) => { +// const existingSocket = get().socket; +// if (existingSocket) { +// return; +// } +// const socket = io( +// `http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/Builder`, +// { +// reconnection: true, +// auth: { email, organization }, +// } +// ); + +// const visualizationSocket = io( +// `http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/Visualization`, +// { +// reconnection: true, +// auth: { email, organization }, +// } +// ); + +// const dashBoardSocket = io( +// `http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/dashboard`, +// { +// reconnection: true, +// auth: { token }, +// } +// ); +// // const dashBoardSocket = io( +// // `http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/project`, +// // { +// // reconnection: true, +// // auth: { token }, +// // } +// // ); + +// set({ socket, visualizationSocket, dashBoardSocket }); +// }, +// disconnectSocket: () => { +// set((state: any) => { +// state.socket?.disconnect(); +// state.visualizationSocket?.disconnect(); +// state.dashBoardSocket?.disconnect(); +// return { socket: null }; +// }); +// }, +// })); export const useLoadingProgress = create<{ loadingProgress: number; setLoadingProgress: (x: number) => void; @@ -311,6 +373,11 @@ export const useUserName = create((set: any) => ({ setUserName: (x: any) => set({ userName: x }), })); +export const useRenameModeStore = create((set: any) => ({ + isRenameMode: false, + setIsRenameMode: (state: boolean) => set({ isRenameMode: state }), +})); + export const useObjectPosition = create((set: any) => ({ objectPosition: { x: undefined, y: undefined, z: undefined }, setObjectPosition: (newObjectPosition: any) => @@ -505,6 +572,14 @@ export const useProcessBar = create((set: any) => ({ processBar: [], setProcessBar: (x: any) => set({ processBar: x }), })); +export const useDfxUpload = create((set: any) => ({ + dfxuploaded: [], + dfxWallGenerate: [], + objValue: { x: 0, y: 0, z: 0 }, + setDfxUploaded: (x: any) => set({ dfxuploaded: x }), + setDfxGenerate: (x: any) => set({ dfxWallGenerate: x }), + setObjValue: (x: any) => set({ objValue: x }), +})); type InputValuesStore = { inputValues: Record; @@ -559,7 +634,7 @@ interface CompareStore { } export const useCompareStore = create((set) => ({ - comparePopUp: false, + comparePopUp: true, setComparePopUp: (value) => set({ comparePopUp: value }), toggleComparePopUp: () => set((state) => ({ comparePopUp: !state.comparePopUp })), diff --git a/app/src/store/builder/uselayoutStore.ts b/app/src/store/builder/uselayoutStore.ts index acaeb50..a8de713 100644 --- a/app/src/store/builder/uselayoutStore.ts +++ b/app/src/store/builder/uselayoutStore.ts @@ -1,6 +1,6 @@ import { create } from 'zustand'; -type Layout = null | 'layout1' | 'layout2'; +type Layout = null | 'layout1' | 'layout2' ; type LayoutState = { currentLayout: Layout; diff --git a/app/src/styles/pages/dashboard.scss b/app/src/styles/pages/dashboard.scss index d6d650e..8962365 100644 --- a/app/src/styles/pages/dashboard.scss +++ b/app/src/styles/pages/dashboard.scss @@ -207,7 +207,7 @@ // backdrop-filter: blur(18px); border-radius: #{$border-radius-xlarge}; - transform: translateY(100%); + // transform: translateY(100%); transition: transform 0.25s linear; .project-details { diff --git a/app/src/types/builderTypes.d.ts b/app/src/types/builderTypes.d.ts index adfdc78..3c5bdfb 100644 --- a/app/src/types/builderTypes.d.ts +++ b/app/src/types/builderTypes.d.ts @@ -1,31 +1,53 @@ interface Asset { - modelUuid: string; - modelName: string; - assetId: string; - position: [number, number, number]; - rotation: [number, number, number]; - isLocked: boolean; - isCollidable: boolean; - isVisible: boolean; - opacity: number; - animations?: string[]; - animationState?: { - current: string; - playing: boolean; + modelUuid: string; + modelName: string; + assetId: string; + position: [number, number, number]; + rotation: [number, number, number]; + isLocked: boolean; + isCollidable: boolean; + isVisible: boolean; + opacity: number; + animations?: string[]; + animationState?: { + current: string; + playing: boolean; + }; + eventData?: { + type: string; + point?: { + uuid: string; + position: [number, number, number]; + rotation: [number, number, number]; }; - eventData?: { - type: string; - point?: { - uuid: string; - position: [number, number, number]; - rotation: [number, number, number]; - } - points?: { - uuid: string; - position: [number, number, number]; - rotation: [number, number, number]; - }[]; - } -}; + points?: { + uuid: string; + position: [number, number, number]; + rotation: [number, number, number]; + }[]; + }; +} -type Assets = Asset[]; \ No newline at end of file +type Assets = Asset[]; + +// DXF Entity Schema + +interface DXFEntity { + type: string; + vertices?: { x: number; y: number }[]; + center?: { x: number; y: number; z: number }; + radius?: number; + startAngle?: number; + endAngle?: number; +} + +interface DXFData { + entities?: DXFEntity[]; +} + +type WallLineVertex = [ + Vector3, + string, // uuid + number, + string // "WallLine" +]; diff --git a/app/src/utils/shortcutkeys/handleShortcutKeys.ts b/app/src/utils/shortcutkeys/handleShortcutKeys.ts index eb0e3c3..14403fd 100644 --- a/app/src/utils/shortcutkeys/handleShortcutKeys.ts +++ b/app/src/utils/shortcutkeys/handleShortcutKeys.ts @@ -6,7 +6,9 @@ import { useActiveTool, useAddAction, useDeleteTool, + useRenameModeStore, useSaveVersion, + useSelectedFloorItem, useSelectedWallItem, useShortcutStore, useToggleView, @@ -37,6 +39,8 @@ const KeyPressListener: React.FC = () => { const { setIsVersionSaved } = useSaveVersion(); const { isLogListVisible, setIsLogListVisible } = useLogger(); const { hidePlayer, setHidePlayer } = usePlayerStore(); + const { isRenameMode, setIsRenameMode } = useRenameModeStore(); + const { selectedFloorItem } = useSelectedFloorItem(); const isTextInput = (element: Element | null): boolean => element instanceof HTMLInputElement || @@ -152,9 +156,24 @@ const KeyPressListener: React.FC = () => { }; const handleKeyPress = (event: KeyboardEvent) => { - if (isTextInput(document.activeElement)) return; - const keyCombination = detectModifierKeys(event); + + if (isTextInput(document.activeElement) && keyCombination !== "ESCAPE") + return; + + if (keyCombination === "ESCAPE") { + console.log("esc"); + setWalkMode(false); + setActiveTool("cursor"); + setActiveSubTool("cursor"); + setIsPlaying(false); + clearSelectedZone(); + setShowShortcuts(false); + setIsVersionSaved(false); + setIsLogListVisible(false); + setIsRenameMode(false); + } + if ( !keyCombination || ["F5", "F11", "F12"].includes(event.key) || @@ -186,26 +205,17 @@ const KeyPressListener: React.FC = () => { setHidePlayer(!hidePlayer); } - if (keyCombination === "ESCAPE") { - setWalkMode(false); - setActiveTool("cursor"); - setActiveSubTool("cursor"); - setIsPlaying(false); - clearSelectedZone(); - setShowShortcuts(false); - setIsVersionSaved(false); - setIsLogListVisible(false); - } - if (keyCombination === "Ctrl+Shift+?") { setShowShortcuts(!showShortcuts); } + if (selectedFloorItem && keyCombination === "F2") { + setIsRenameMode(true); + } + // Placeholder for future implementation if ( - ["Ctrl+Z", "Ctrl+Y", "Ctrl+Shift+Z", "Ctrl+F"].includes( - keyCombination - ) + ["Ctrl+Z", "Ctrl+Y", "Ctrl+Shift+Z", "Ctrl+F"].includes(keyCombination) ) { // Implement undo/redo/help/find/shortcuts } @@ -223,7 +233,9 @@ const KeyPressListener: React.FC = () => { showShortcuts, isPlaying, isLogListVisible, - hidePlayer + hidePlayer, + selectedFloorItem, + isRenameMode, ]); return null; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..5ef6c4e --- /dev/null +++ b/package-lock.json @@ -0,0 +1,6 @@ +{ + "name": "Dwinzo_dev", + "lockfileVersion": 3, + "requires": true, + "packages": {} +}