diff --git a/app/package-lock.json b/app/package-lock.json index f95a0c3..ed85f25 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -10,6 +10,8 @@ "dependencies": { "@dnd-kit/core": "^6.3.1", "@dnd-kit/sortable": "^10.0.0", + "@fingerprintjs/fingerprintjs": "^4.6.2", + "@fingerprintjs/fingerprintjs-pro-react": "^2.6.3", "@react-three/csg": "^3.2.0", "@react-three/drei": "^9.113.0", "@react-three/fiber": "^8.17.7", @@ -27,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", @@ -2537,6 +2540,45 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@fingerprintjs/fingerprintjs": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/@fingerprintjs/fingerprintjs/-/fingerprintjs-4.6.2.tgz", + "integrity": "sha512-g8mXuqcFKbgH2CZKwPfVtsUJDHyvcgIABQI7Y0tzWEFXpGxJaXuAuzlifT2oTakjDBLTK4Gaa9/5PERDhqUjtw==", + "license": "BUSL-1.1", + "dependencies": { + "tslib": "^2.4.1" + } + }, + "node_modules/@fingerprintjs/fingerprintjs-pro": { + "version": "3.11.10", + "resolved": "https://registry.npmjs.org/@fingerprintjs/fingerprintjs-pro/-/fingerprintjs-pro-3.11.10.tgz", + "integrity": "sha512-zuQWT0YQLT0T//KcjEnyn4YBPlxXuXCtPiwEt2eTv03I/k2m370X+pCOfgU26AEjkmPrhCE77FLpRnm0IwiWrg==", + "license": "SEE LICENSE IN LICENSE", + "dependencies": { + "tslib": "^2.4.1" + } + }, + "node_modules/@fingerprintjs/fingerprintjs-pro-react": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/@fingerprintjs/fingerprintjs-pro-react/-/fingerprintjs-pro-react-2.6.3.tgz", + "integrity": "sha512-/axCq/cfjZkIM+WFZM/05FQvqtNfdKbIFKU6b2yrwPKlgT8BqWkAq8XvFX6JCPlq8/udVLJjFEDCK+1JQh1L6g==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@fingerprintjs/fingerprintjs-pro-spa": "^1.3.1", + "fast-deep-equal": "3.1.3" + } + }, + "node_modules/@fingerprintjs/fingerprintjs-pro-spa": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@fingerprintjs/fingerprintjs-pro-spa/-/fingerprintjs-pro-spa-1.3.2.tgz", + "integrity": "sha512-s1YGsx1XQLmjU+av4UrUHNxyzwPHyZRB0GXJQFOJK8ZHCYc2SNukxnJmZA++bNBa8twU3wW+QgSJhA4Prjnd0g==", + "license": "MIT", + "dependencies": { + "@fingerprintjs/fingerprintjs-pro": "^3.11.0", + "tslib": "^2.7.0" + } + }, "node_modules/@floating-ui/core": { "version": "0.7.3", "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-0.7.3.tgz", @@ -4142,7 +4184,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", @@ -10089,6 +10130,15 @@ "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==", + "license": "MIT", + "dependencies": { + "loglevel": "^1.7.1" + } + }, "node_modules/earcut": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/earcut/-/earcut-2.2.4.tgz", @@ -12748,10 +12798,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" @@ -15204,6 +15253,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", @@ -18013,16 +18074,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 18a8228..ede2f49 100644 --- a/app/package.json +++ b/app/package.json @@ -5,6 +5,8 @@ "dependencies": { "@dnd-kit/core": "^6.3.1", "@dnd-kit/sortable": "^10.0.0", + "@fingerprintjs/fingerprintjs": "^4.6.2", + "@fingerprintjs/fingerprintjs-pro-react": "^2.6.3", "@react-three/csg": "^3.2.0", "@react-three/drei": "^9.113.0", "@react-three/fiber": "^8.17.7", @@ -22,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/app.tsx b/app/src/app.tsx index 0b6fd3e..b545413 100644 --- a/app/src/app.tsx +++ b/app/src/app.tsx @@ -13,7 +13,7 @@ const App: React.FC = () => { } /> } /> - } /> + } /> diff --git a/app/src/assets/image/darkThemeProject.png b/app/src/assets/image/darkThemeProject.png new file mode 100644 index 0000000..3189860 Binary files /dev/null and b/app/src/assets/image/darkThemeProject.png differ diff --git a/app/src/assets/image/lightThemeProject.png b/app/src/assets/image/lightThemeProject.png new file mode 100644 index 0000000..a5096ab Binary files /dev/null and b/app/src/assets/image/lightThemeProject.png differ diff --git a/app/src/components/Dashboard/DashboardCard.tsx b/app/src/components/Dashboard/DashboardCard.tsx new file mode 100644 index 0000000..951cab1 --- /dev/null +++ b/app/src/components/Dashboard/DashboardCard.tsx @@ -0,0 +1,280 @@ +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, useSocketStore } from "../../store/builder/store"; +import { viewProject } from "../../services/dashboard/viewProject"; +import OuterClick from "../../utils/outerClick"; +import { KebabIcon } from "../icons/ExportCommonIcons"; +import { getAllProjects } from "../../services/dashboard/getAllProjects"; + +interface DashBoardCardProps { + projectName: string; + thumbnail: any; + projectId: string; + createdAt?: 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>; + setRecentDuplicateData?: React.Dispatch>; + setProjectDuplicateData?: React.Dispatch>; +} +type RelativeTimeFormatUnit = any; + +const DashboardCard: React.FC = ({ + projectName, + thumbnail, + projectId, + active, + handleDeleteProject, + handleRestoreProject, + handleTrashDeleteProject, + handleDuplicateWorkspaceProject, + handleDuplicateRecentProject, + createdAt, + setRecentDuplicateData, + setProjectDuplicateData, +}) => { + 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 navigateToProject = async (e: any) => { + if (active && active == "trash") return; + try { + const viewedProject = await viewProject(organization, projectId, userId); + console.log("Viewed project:", viewedProject); + } catch (error) { + console.error("Error opening project:", error); + } + + setProjectName(projectName); + console.log('projectId: ', projectId); + navigate(`/${projectId}`); + }; + + const handleOptionClick = async (option: string) => { + switch (option) { + case "delete": + if (handleDeleteProject) { + await handleDeleteProject(projectId); + } else if (handleTrashDeleteProject) { + await handleTrashDeleteProject(projectId); + } + break; + case "restore": + if (handleRestoreProject) { + await handleRestoreProject(projectId); + } + break; + case "open in new tab": + try { + await viewProject(organization, projectId, userId); + setProjectName(projectName); + setIsKebabOpen(false); + } catch (error) { + console.error("Error opening project in new tab:", error); + } + window.open(`/${projectId}`, "_blank"); + break; + case "rename": + setIsRenaming(true); + break; + case "duplicate": + if (handleDuplicateWorkspaceProject) { + setProjectDuplicateData && + setProjectDuplicateData({ + projectId, + projectName, + thumbnail, + }); + await handleDuplicateWorkspaceProject(projectId, projectName, thumbnail); + } else if (handleDuplicateRecentProject) { + setRecentDuplicateData && + setRecentDuplicateData({ + projectId, + projectName, + thumbnail, + }); + await handleDuplicateRecentProject(projectId, projectName, thumbnail); + } + break; + default: + break; + } + setIsKebabOpen(false); + }; + + OuterClick({ + contextClassName: ["kebab-wrapper", "kebab-options-wrapper"], + setMenuVisible: () => setIsKebabOpen(false), + }); + + const handleProjectName = async (projectName: string) => { + setRenameValue(projectName); + if (!projectId) return; + try { + const projects = await getAllProjects(userId, organization); + console.log("projects: ", projects); + let projectUuid = projects.Projects.find( + (val: any) => val.projectUuid === projectId || val._id === projectId + ); + const updateProjects = { + projectId: projectUuid, + organization, + userId, + projectName, + thumbnail: undefined, + }; + + if (projectSocket) { + projectSocket.emit("v1:project:update", updateProjects); + } + } catch (error) { } + }; + + function getRelativeTime(dateString: string): string { + const date = new Date(dateString); + const now = new Date(); + const diffInSeconds = Math.floor((now.getTime() - date.getTime()) / 1000); + + const intervals: Record = { + year: 31536000, + month: 2592000, + week: 604800, + day: 86400, + hour: 3600, + minute: 60, + second: 1, + }; + + const rtf = new Intl.RelativeTimeFormat("en", { numeric: "auto" }); + + for (const key in intervals) { + const unit = key as RelativeTimeFormatUnit; + const diff = Math.floor(diffInSeconds / intervals[unit]); + if (diff >= 1) { + return rtf.format(-diff, unit); + } + } + return "just now"; + } + + return ( + + + + + + {isKebabOpen && active !== "trash" && ( +
+ {["rename", "delete", "duplicate", "open in new tab"].map( + (option) => ( + + ) + )} +
+ )} + {isKebabOpen && active && active == "trash" && ( +
+ {["restore", "delete"].map((option) => ( + + ))} +
+ )} + + ); +}; + +export default DashboardCard; \ No newline at end of file diff --git a/app/src/components/Dashboard/DashboardHome.tsx b/app/src/components/Dashboard/DashboardHome.tsx new file mode 100644 index 0000000..2627a65 --- /dev/null +++ b/app/src/components/Dashboard/DashboardHome.tsx @@ -0,0 +1,166 @@ +import React, { useEffect, useState } from "react"; +import DashboardCard from "./DashboardCard"; +import DashboardNavBar from "./DashboardNavBar"; +import MarketPlaceBanner from "./MarketPlaceBanner"; +import { getUserData } from "./functions/getUserData"; +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 ProjectSocketRes from "./socket/projectSocketRes.dev"; + +interface Project { + _id: string; + projectName: string; + thumbnail: string; + createdBy: string; + projectUuid?: string; + createdAt: string; +} + +interface RecentProjectsData { + [key: string]: Project[]; +} + +const DashboardHome: React.FC = () => { + const [recentProjects, setRecentProjects] = useState({}); + const [isSearchActive, setIsSearchActive] = useState(false); + const { userId, organization } = getUserData(); + const { projectSocket } = useSocketStore(); + const [recentDuplicateData, setRecentDuplicateData] = useState({}); + console.log("duplicateData: ", recentDuplicateData); + + const fetchRecentProjects = async () => { + try { + const projects = await recentlyViewed(organization, userId); + console.log("RecentlyViewed: ", projects); + + if (JSON.stringify(projects) !== JSON.stringify(recentProjects)) { + setRecentProjects(projects); + } + } catch (error) { + console.error("Error fetching recent projects:", error); + } + }; + + const handleRecentProjectSearch = async (inputValue: string) => { + if (!inputValue.trim()) { + setIsSearchActive(false); + return; + } + const filterRecentProcess = await searchProject( + organization, + userId, + inputValue + ); + 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 + // ); + // console.log('deletedProject: ', deletedProject); + + //socket for delete Project + const deleteProject = { + projectId, + organization, + userId, + }; + + if (projectSocket) { + projectSocket.emit("v1:project:delete", deleteProject); + } + + setRecentProjects((prevDiscardedProjects: RecentProjectsData) => { + if (!Array.isArray(prevDiscardedProjects?.RecentlyViewed)) { + return prevDiscardedProjects; + } + const updatedProjectDatas = prevDiscardedProjects.RecentlyViewed.filter( + (project) => project._id !== projectId + ); + return { + ...prevDiscardedProjects, + RecentlyViewed: updatedProjectDatas, + }; + }); + setIsSearchActive(false); + } catch (error) { + console.error("Error deleting project:", error); + } + }; + + const handleDuplicateRecentProject = async ( + projectId: string, + projectName: string, + thumbnail: string + ) => { + const duplicateRecentProjectData = { + userId, + thumbnail, + organization, + projectUuid: projectId, + projectName, + }; + projectSocket.emit("v1:project:Duplicate", duplicateRecentProjectData); + }; + + const renderProjects = () => { + const projectList = recentProjects[Object.keys(recentProjects)[0]]; + + if (!projectList?.length) { + return
No recent projects found
; + } + + return ( + projectList && + projectList.map((project) => ( + + )) + ); + }; + + useEffect(() => { + if (!isSearchActive) { + fetchRecentProjects(); + } + }, [isSearchActive]); + + return ( +
+ + +
+

Recents

+
{renderProjects()}
+
+ {recentDuplicateData && Object.keys(recentDuplicateData).length > 0 && ( + + )} +
+ ); +}; + +export default DashboardHome; \ No newline at end of file diff --git a/app/src/components/Dashboard/DashboardNavBar.tsx b/app/src/components/Dashboard/DashboardNavBar.tsx new file mode 100644 index 0000000..c86ef69 --- /dev/null +++ b/app/src/components/Dashboard/DashboardNavBar.tsx @@ -0,0 +1,43 @@ +import React from "react"; +import Search from "../ui/inputs/Search"; +import { CartIcon } from "../icons/ExportModuleIcons"; + +interface DashboardNavBarProps { + page: React.ReactNode; + handleProjectsSearch?: (inputValue: string) => Promise; + handleTrashSearch?: (inputValue: string) => Promise; + handleRecentProjectSearch?: (inputValue: string) => Promise; +} + +const DashboardNavBar: React.FC = ({ + page, + handleProjectsSearch, + handleTrashSearch, + handleRecentProjectSearch, +}) => { + const handleSearch = async (inputValue: string) => { + try { + if (handleProjectsSearch) { + await handleProjectsSearch(inputValue); + } else if (handleTrashSearch) { + await handleTrashSearch(inputValue); + } else if (handleRecentProjectSearch) { + await handleRecentProjectSearch(inputValue); + } + } catch (error) { + console.error("Search failed:", error); + } + }; + + return ( +
+
{page}
+
+ Market Place +
+ +
+ ); +}; + +export default DashboardNavBar; \ No newline at end of file diff --git a/app/src/components/Dashboard/DashboardProjects.tsx b/app/src/components/Dashboard/DashboardProjects.tsx new file mode 100644 index 0000000..e931328 --- /dev/null +++ b/app/src/components/Dashboard/DashboardProjects.tsx @@ -0,0 +1,185 @@ +import React, { useEffect, useState } from "react"; +import DashboardNavBar from "./DashboardNavBar"; +import DashboardCard from "./DashboardCard"; +import { getUserData } from "./functions/getUserData"; +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 ProjectSocketRes from "./socket/projectSocketRes.dev"; + +interface Project { + _id: string; + projectName: string; + thumbnail: string; + createdBy: string; + projectUuid?: string; + createdAt: string; +} + +interface WorkspaceProjects { + [key: string]: Project[]; +} + +const DashboardProjects: React.FC = () => { + const [workspaceProjects, setWorkspaceProjects] = useState( + {} + ); + const [projectDuplicateData, setProjectDuplicateData] = useState({}); + const [isSearchActive, setIsSearchActive] = useState(false); + const [activeFolder, setActiveFolder] = useState("myProjects"); + const { projectSocket } = useSocketStore(); + const { userId, organization } = getUserData(); + + const fetchAllProjects = async () => { + try { + const projects = await getAllProjects(userId, organization); + + if (JSON.stringify(projects) !== JSON.stringify(workspaceProjects)) { + setWorkspaceProjects(projects); + } + } catch (error) { + console.error("Error fetching projects:", error); + } + }; + + const handleProjectsSearch = async (inputValue: string) => { + if (!inputValue.trim()) { + setIsSearchActive(false); + return; + } + if (!setWorkspaceProjects || !setIsSearchActive) return; + + const searchedProject = await searchProject(organization, userId, inputValue); + setIsSearchActive(true); + setWorkspaceProjects(searchedProject.message ? {} : searchedProject); + }; + + const handleDeleteProject = async (projectId: any) => { + try { + // const deletedProject = await deleteProject( + // projectId, + // userId, + // organization + // ); + // console.log('deletedProject: ', deletedProject); + const deleteProjects = { + projectId, + organization: organization, + userId, + }; + + //socket for deleting the project + if (projectSocket) { + projectSocket.emit("v1:project:delete", deleteProjects); + } else { + console.error("Socket is not connected."); + } + setWorkspaceProjects((prevDiscardedProjects: WorkspaceProjects) => { + if (!Array.isArray(prevDiscardedProjects?.Projects)) { + return prevDiscardedProjects; + } + const updatedProjectDatas = prevDiscardedProjects.Projects.filter( + (project) => project._id !== projectId + ); + return { + ...prevDiscardedProjects, + Projects: updatedProjectDatas, + }; + }); + setIsSearchActive(false); + } catch (error) { + console.error("Error deleting project:", error); + } + }; + + const handleDuplicateWorkspaceProject = async ( + projectId: string, + projectName: string, + thumbnail: string + ) => { + // await handleDuplicateProjects({ + // userId, + // organization, + // projectId, + // projectName, + // projectSocket, + // thumbnail, + // setWorkspaceProjects, + // setIsSearchActive + // }); + const duplicateProjectData = { + userId, + thumbnail, + organization, + projectUuid: projectId, + projectName, + }; + projectSocket.emit("v1:project:Duplicate", duplicateProjectData); + }; + + const renderProjects = () => { + if (activeFolder !== "myProjects") return null; + + const projectList = workspaceProjects[Object.keys(workspaceProjects)[0]]; + + if (!projectList?.length) { + return
No projects found
; + } + + return projectList.map((project) => ( + + )); + }; + + useEffect(() => { + if (!isSearchActive) { + fetchAllProjects(); + } + }, [isSearchActive]); + + return ( +
+ + +
+
+ + +
+
{renderProjects()}
+ {projectDuplicateData && Object.keys(projectDuplicateData).length > 0 && ( + + )} +
+
+ ); +}; + +export default DashboardProjects; \ No newline at end of file diff --git a/app/src/components/Dashboard/DashboardTrash.tsx b/app/src/components/Dashboard/DashboardTrash.tsx new file mode 100644 index 0000000..e1329fc --- /dev/null +++ b/app/src/components/Dashboard/DashboardTrash.tsx @@ -0,0 +1,160 @@ +import React, { useEffect, useState } from "react"; +import DashboardCard from "./DashboardCard"; +import DashboardNavBar from "./DashboardNavBar"; +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; + projectName: string; + thumbnail: string; + createdBy: string; + projectUuid?: string; + DeletedAt: string; +} + +interface DiscardedProjects { + [key: string]: Project[]; +} + +const DashboardTrash: React.FC = () => { + const [discardedProjects, setDiscardedProjects] = useState( + {} + ); + const [isSearchActive, setIsSearchActive] = useState(false); + const { userId, organization } = getUserData(); + const { projectSocket } = useSocketStore(); + + const fetchTrashProjects = async () => { + try { + const projects = await getTrash(organization); + + if (JSON.stringify(projects) !== JSON.stringify(discardedProjects)) { + setDiscardedProjects(projects); + } + } catch (error) { + console.error("Error fetching trash projects:", error); + } + }; + + const handleTrashSearch = async (inputValue: string) => { + if (!inputValue.trim()) { + setIsSearchActive(false); + return; + } + if (!setDiscardedProjects || !setIsSearchActive) return; + + const filterTrashedProcess = await trashSearchProject( + organization, + userId, + inputValue + ); + setIsSearchActive(true); + setDiscardedProjects( + filterTrashedProcess.message ? {} : filterTrashedProcess + ); + }; + + const handleRestoreProject = async (projectId: any) => { + console.log("projectId: ", projectId); + try { + const restoreProject = await restoreTrash(organization, projectId); + // console.log('restoreProject: ', restoreProject); + + setDiscardedProjects((prevDiscardedProjects: DiscardedProjects) => { + // Check if TrashDatas exists and is an array + if (!Array.isArray(prevDiscardedProjects?.TrashDatas)) { + console.error("TrashDatas is not an array", prevDiscardedProjects); + return prevDiscardedProjects; + } + const updatedTrashDatas = prevDiscardedProjects.TrashDatas.filter( + (project) => project._id !== projectId + ); + return { + ...prevDiscardedProjects, + TrashDatas: updatedTrashDatas, + }; + }); + setIsSearchActive(false); + } catch (error) { + 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) { + 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]]; + + if (!projectList?.length) { + return
No deleted projects found
; + } + + return projectList.map((project) => ( + + )); + }; + + useEffect(() => { + if (!isSearchActive) { + fetchTrashProjects(); + } + }, [isSearchActive]); + + return ( +
+ + +
+
+
{renderTrashProjects()}
+
+
+ ); +}; + +export default DashboardTrash; \ No newline at end of file diff --git a/app/src/components/Dashboard/DashboardTutorial.tsx b/app/src/components/Dashboard/DashboardTutorial.tsx new file mode 100644 index 0000000..70a0fb7 --- /dev/null +++ b/app/src/components/Dashboard/DashboardTutorial.tsx @@ -0,0 +1,62 @@ +import React, { useEffect, useState } from 'react'; +import DashboardNavBar from './DashboardNavBar'; +import DashboardCard from './DashboardCard'; +import { projectTutorial } from '../../services/dashboard/projectTutorial'; +interface Project { + _id: string; + projectName: string; + thumbnail: string; + createdBy: string; + projectUuid?: string; +} + +interface DiscardedProjects { + [key: string]: Project[]; +} + +const DashboardTutorial = () => { + const [tutorialProject, setTutorialProject] = useState({}) + const handleIcon = async () => { + try { + let tutorial = await projectTutorial() + setTutorialProject(tutorial) + } catch { + + } + } + useEffect(() => { + handleIcon() + }, []) + const renderTrashProjects = () => { + const projectList = tutorialProject[Object.keys(tutorialProject)[0]]; + + if (!projectList?.length) { + return
No deleted projects found
; + } + + return projectList.map((tutorials: any) => ( + + )); + }; + return ( +
+ + +
+
+
+ {renderTrashProjects()} +
+
+
+ ); +} + +export default DashboardTutorial; diff --git a/app/src/components/layout/Dashboard/MarketPlaceBanner.tsx b/app/src/components/Dashboard/MarketPlaceBanner.tsx similarity index 95% rename from app/src/components/layout/Dashboard/MarketPlaceBanner.tsx rename to app/src/components/Dashboard/MarketPlaceBanner.tsx index ed22a9a..68b631a 100644 --- a/app/src/components/layout/Dashboard/MarketPlaceBanner.tsx +++ b/app/src/components/Dashboard/MarketPlaceBanner.tsx @@ -1,5 +1,5 @@ import React from "react"; -import banner from "../../../assets/image/banner.png"; +import banner from "../../assets/image/banner.png"; const MarketPlaceBanner = () => { return ( diff --git a/app/src/components/Dashboard/SidePannel.tsx b/app/src/components/Dashboard/SidePannel.tsx new file mode 100644 index 0000000..2e6c1ea --- /dev/null +++ b/app/src/components/Dashboard/SidePannel.tsx @@ -0,0 +1,180 @@ +import React from "react"; +import { + DocumentationIcon, + HelpIcon, + HomeIcon, + LogoutIcon, + NotificationIcon, + ProjectsIcon, + TutorialsIcon, +} from "../icons/DashboardIcon"; +import { useNavigate } from "react-router-dom"; +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 { useLoadingProgress, useSocketStore } from "../../store/builder/store"; +import { createProject } from "../../services/dashboard/createProject"; + +interface SidePannelProps { + setActiveTab: React.Dispatch>; + activeTab: string; +} + +const SidePannel: React.FC = ({ setActiveTab, activeTab }) => { + const { email, userName, userId, organization } = getUserData(); + const navigate = useNavigate(); + const { setLoadingProgress } = useLoadingProgress(); + const { projectSocket } = useSocketStore(); + const savedTheme = localStorage.getItem("theme") ?? "light"; + + function generateProjectId() { + const randomBytes = new Uint8Array(12); + crypto.getRandomValues(randomBytes); + return Array.from(randomBytes, (byte) => + byte.toString(16).padStart(2, "0") + ).join(""); + } + + const handleCreateNewProject = async () => { + const token = localStorage.getItem("token"); + try { + const projectId = generateProjectId(); + useSocketStore.getState().initializeSocket(email, organization, token); + + + //API for creating new 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, + organization: organization, + projectUuid: projectId, + }; + console.log("projectSocket: ", projectSocket); + if (projectSocket) { + // console.log('addProject: ', addProject); + const handleResponse = (data: any) => { + console.log('Project add response:', data); + if (data.message === "Project created successfully") { + setLoadingProgress(1) + navigate(`/${data.data.projectId}`); + } + projectSocket.off("v1-project:response:add", handleResponse); // Clean up + }; + projectSocket.on("v1-project:response:add", handleResponse); + + 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); + } + }; + + return ( +
+
+
+
+ {userName?.charAt(0).toUpperCase()} +
+
+ {userName + ? userName.charAt(0).toUpperCase() + userName.slice(1).toLowerCase() + : "Anonymous"} +
+
+
+ +
+
+
+ + New project +
+
+
+
setActiveTab("Home")} + > + + Home +
+
setActiveTab("Projects")} + > + + Projects +
+
setActiveTab("Trash")} + > + + Trash +
+
setActiveTab("Tutorials")} + > + + Tutorials +
+
setActiveTab("Documentation")} + > + + Documentation +
+
+
+
+ + Settings +
+
+ + Log out +
+
+ + Help & Feedback +
+
+
+
+ ); +}; + +export default SidePannel; \ No newline at end of file diff --git a/app/src/components/Dashboard/functions/getUserData.ts b/app/src/components/Dashboard/functions/getUserData.ts new file mode 100644 index 0000000..132dcc2 --- /dev/null +++ b/app/src/components/Dashboard/functions/getUserData.ts @@ -0,0 +1,31 @@ +interface UserData { + email: string; + userId: string; + userName?: string; // Optional + organization: string; +} + +export const getUserData = (): UserData => { + const email = localStorage.getItem("email"); + const userId = localStorage.getItem("userId"); + const userName = localStorage.getItem("userName"); + + if (!email || !userId) { + throw new Error("User data not found in localStorage"); + } + + const [_, emailDomain] = email.split("@"); + + if (!emailDomain) { + throw new Error("Invalid email format"); + } + + const [organization] = emailDomain.split("."); + + return { + email: email, + userId: userId, + userName: userName ?? undefined, + organization, + }; +}; diff --git a/app/src/components/Dashboard/socket/projectSocketRes.dev.tsx b/app/src/components/Dashboard/socket/projectSocketRes.dev.tsx new file mode 100644 index 0000000..6ab556b --- /dev/null +++ b/app/src/components/Dashboard/socket/projectSocketRes.dev.tsx @@ -0,0 +1,88 @@ + +import React, { useEffect } from 'react'; +import { useSocketStore } from '../../../store/builder/store'; +import { getUserData } from '../functions/getUserData'; +import { getAllProjects } from '../../../services/dashboard/getAllProjects'; +import { recentlyViewed } from '../../../services/dashboard/recentlyViewed'; + +interface Project { + _id: string; + projectName: string; + thumbnail: string; + createdBy: string; + projectUuid?: string; + createdAt: string; +} + +interface ProjectsData { + [key: string]: Project[]; +} +interface ProjectSocketResProps { + setRecentProjects?: React.Dispatch>; + setWorkspaceProjects?: React.Dispatch>; + setIsSearchActive?: React.Dispatch>; +} + +const ProjectSocketRes = ({ + setRecentProjects, + setWorkspaceProjects, + setIsSearchActive, +}: ProjectSocketResProps) => { + const { projectSocket } = useSocketStore(); + const { userId, organization } = getUserData(); + + useEffect(() => { + if (!projectSocket) return; + + const handleAdd = (data: any) => { + // console.log('Add:', data); + }; + + const handleDelete = (data: any) => { + // console.log('Delete:', data); + }; + + const handleUpdate = (data: any) => { + // console.log('Update:', data); + }; + + const handleTrashDelete = (data: any) => { + // console.log('Trash Delete:', data); + }; + + const handleDuplicate = async (data: any) => { + console.log("Project duplicate response:", data); + if (data?.message === "Project Duplicated successfully") { + if (setWorkspaceProjects) { + const allProjects = await getAllProjects(userId, organization); + console.log('allProjects: ', allProjects); + setWorkspaceProjects(allProjects); + } else if (setRecentProjects) { + const recentProjects = await recentlyViewed(organization, userId); + setRecentProjects && setRecentProjects(recentProjects); + } + setIsSearchActive && setIsSearchActive(false); + } else { + console.warn("Duplication failed or unexpected response."); + } + }; + + projectSocket.on("v1-project:response:add", handleAdd); + projectSocket.on("v1-project:response:delete", handleDelete); + projectSocket.on("v1-project:response:update", handleUpdate); + projectSocket.on("v1-project:response:Duplicate", handleDuplicate); + projectSocket.on("v1:trash:response:delete", handleTrashDelete); + + return () => { + projectSocket.off("v1-project:response:add", handleAdd); + projectSocket.off("v1-project:response:delete", handleDelete); + projectSocket.off("v1-project:response:update", handleUpdate); + projectSocket.off("v1-project:response:Duplicate", handleDuplicate); + projectSocket.off("v1:trash:response:delete", handleTrashDelete); + }; + }, [projectSocket, userId, organization]); + + return null; +}; + +export default ProjectSocketRes; diff --git a/app/src/components/layout/Dashboard/DashboardCard.tsx b/app/src/components/layout/Dashboard/DashboardCard.tsx deleted file mode 100644 index d58bfb6..0000000 --- a/app/src/components/layout/Dashboard/DashboardCard.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import React from "react"; -import { KebabIcon } from "../../icons/ExportCommonIcons"; -import img from "../../../assets/image/image.png" - -const DashboardCard:React.FC = () => { - return ( -
-
- -
-
-
-
Untitled
-
24-12-2025
-
-
-
V
- -
-
-
- ); -}; - -export default DashboardCard; diff --git a/app/src/components/layout/Dashboard/DashboardHome.tsx b/app/src/components/layout/Dashboard/DashboardHome.tsx deleted file mode 100644 index 38e7bad..0000000 --- a/app/src/components/layout/Dashboard/DashboardHome.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import React from "react"; -import DashboardCard from "./DashboardCard"; -import DashboardNavBar from "./DashboardNavBar"; -import MarketPlaceBanner from "./MarketPlaceBanner"; - -const DashboardHome: React.FC = () => { - return ( -
- - -
-
Recents
-
- -
-
-
- ); -}; - -export default DashboardHome; diff --git a/app/src/components/layout/Dashboard/DashboardNavBar.tsx b/app/src/components/layout/Dashboard/DashboardNavBar.tsx deleted file mode 100644 index 3da1a61..0000000 --- a/app/src/components/layout/Dashboard/DashboardNavBar.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import React from "react"; -import { CartIcon } from "../../icons/ExportModuleIcons"; -import Search from "../../ui/inputs/Search"; - -interface DashboardNavBarProps { - page: React.ReactNode; -} - -const DashboardNavBar: React.FC = ({ page }) => { - return ( -
-
{page}
-
- Market Place -
- {}} /> -
- ); -}; - -export default DashboardNavBar; diff --git a/app/src/components/layout/Dashboard/SidePannel.tsx b/app/src/components/layout/Dashboard/SidePannel.tsx deleted file mode 100644 index 5d69ffc..0000000 --- a/app/src/components/layout/Dashboard/SidePannel.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import React from "react"; -import { - DocumentationIcon, - HelpIcon, - HomeIcon, - LogoutIcon, - NotificationIcon, - ProjectsIcon, - TutorialsIcon, -} from "../../icons/DashboardIcon"; -import { SettingsIcon, TrashIcon } from "../../icons/ExportCommonIcons"; - -const SidePannel: React.FC = () => { - const userName = localStorage.getItem("userName") ?? "Anonymous"; - return ( -
-
-
-
{userName[0]}
-
{userName}
-
-
- -
-
-
+ New project
-
-
-
- - Home -
-
- - Projects -
-
- - Trash -
-
- - Tutorials -
-
- - Documentation -
-
-
-
- - Settings -
-
- - Log out -
-
- - Help & Feedback -
-
-
-
- ); -}; - -export default SidePannel; diff --git a/app/src/components/layout/sidebarRight/properties/AisleProperties.tsx b/app/src/components/layout/sidebarRight/properties/AisleProperties.tsx index 96ef288..c343805 100644 --- a/app/src/components/layout/sidebarRight/properties/AisleProperties.tsx +++ b/app/src/components/layout/sidebarRight/properties/AisleProperties.tsx @@ -12,10 +12,11 @@ import Directional from "../../../../assets/image/aisleTypes/Directional.png"; import Dotted from "../../../../assets/image/aisleTypes/Dotted.png"; import Solid from "../../../../assets/image/aisleTypes/Solid.png"; import { useBuilderStore } from "../../../../store/builder/useBuilderStore"; +import InputToggle from "../../../ui/inputs/InputToggle"; interface TextureItem { - color: AisleColors; - id: string; + color: string; + id: AisleColors; brief: string; texture: string; } @@ -24,17 +25,17 @@ const AisleProperties: React.FC = () => { const [collapsePresets, setCollapsePresets] = useState(false); const [collapseTexture, setCollapseTexture] = useState(true); - const { aisleType, aisleWidth, aisleColor, dashLength, gapLength, dotRadius, aisleLength, setAisleType, setAisleColor, setAisleWidth, setDashLength, setGapLength, setDotRadius, setAisleLength } = useBuilderStore(); + const { aisleType, aisleWidth, aisleColor, dashLength, gapLength, dotRadius, aisleLength, isFlipped, setAisleType, setAisleColor, setAisleWidth, setDashLength, setGapLength, setDotRadius, setAisleLength, setIsFlipped } = useBuilderStore(); const aisleTextureList: TextureItem[] = [ - { color: "yellow", id: "yellow1", brief: "pedestrian walkways", texture: "" }, + { color: "yellow", id: "yellow", brief: "pedestrian walkways", texture: "" }, { color: "gray", id: "gray", brief: "basic", texture: "" }, - { color: "green", id: "green1", brief: "pedestrian walkways", texture: "" }, + { color: "green", id: "green", brief: "pedestrian walkways", texture: "" }, { color: "orange", id: "orange", brief: "material flow", texture: "" }, { color: "blue", id: "blue", brief: "vehicle paths", texture: "" }, { color: "purple", id: "purple", brief: "material flow", texture: "" }, { color: "red", id: "red", brief: "safety zone", texture: "" }, - { color: "bright green", id: "bright-green", brief: "safety zone", texture: "" }, + { color: "bright green", id: "#66FF00", brief: "safety zone", texture: "" }, { color: "yellow-black", id: "yellow-black", brief: "utility aisles", texture: "" }, { color: "white-black", id: "white-black", brief: "utility aisles", texture: "" }, ]; @@ -90,6 +91,10 @@ const AisleProperties: React.FC = () => { } }; + const handleIsFlippedChange = () => { + setIsFlipped(!aisleIsFlipped) + }; + const dashLengthValue = useMemo(() => { return dashLength.toString(); }, [aisleType, dashLength]); @@ -110,6 +115,10 @@ const AisleProperties: React.FC = () => { return aisleLength.toString(); }, [aisleType, aisleLength]); + const aisleIsFlipped = useMemo(() => { + return isFlipped; + }, [aisleType, isFlipped]); + const renderAdvancedProperties = () => { switch (aisleType) { case 'dashed-aisle': @@ -157,7 +166,8 @@ const AisleProperties: React.FC = () => { step={0.1} max={2} onChange={handleGapLengthChange} - /> + /> + } ); @@ -181,7 +191,21 @@ const AisleProperties: React.FC = () => { step={0.1} max={2} onChange={handleGapLengthChange} - /> + /> + + } + + ); + case 'junction-aisle': case 'arc-aisle': + return ( + <> + {aisleType && + } ); @@ -258,7 +282,7 @@ const AisleProperties: React.FC = () => { key={val.id} title={val.brief || val.id} className={`aisle-list ${aisleColor === val.color ? "selected" : ""}`} - onClick={() => setAisleColor(val.color)} + onClick={() => setAisleColor(val.id)} aria-pressed={aisleColor === val.id} >
{val.texture}
diff --git a/app/src/components/layout/sidebarRight/properties/GlobalProperties.tsx b/app/src/components/layout/sidebarRight/properties/GlobalProperties.tsx index 0422e2b..3ad8acd 100644 --- a/app/src/components/layout/sidebarRight/properties/GlobalProperties.tsx +++ b/app/src/components/layout/sidebarRight/properties/GlobalProperties.tsx @@ -19,7 +19,7 @@ import { } from "../../../../store/builder/store"; import { setEnvironment } from "../../../../services/factoryBuilder/environment/setEnvironment"; import * as CONSTANTS from "../../../../types/world/worldConstants"; -import { validateBBox } from "@turf/helpers"; +import { useParams } from "react-router-dom"; const GlobalProperties: React.FC = () => { const { toggleView, setToggleView } = useToggleView(); const { selectedWallItem, setSelectedWallItem } = useSelectedWallItem(); @@ -37,6 +37,7 @@ const GlobalProperties: React.FC = () => { const [limitGridDistance, setLimitGridDistance] = useState(false); const [gridDistance, setGridDistance] = useState(3); + const { projectId } = useParams(); const optimizeScene = async (value: any) => { const email = localStorage.getItem("email"); @@ -49,7 +50,8 @@ const GlobalProperties: React.FC = () => { roofVisibility, shadows, 30, - true + true, + projectId ); setRenderDistance(30); setLimitDistance(true); @@ -67,7 +69,8 @@ const GlobalProperties: React.FC = () => { roofVisibility, shadows, 75, - !limitDistance + !limitDistance, + projectId ); setRenderDistance(75); } else { @@ -78,7 +81,8 @@ const GlobalProperties: React.FC = () => { roofVisibility, shadows, renderDistance, - !limitDistance + !limitDistance, + projectId ); } setLimitDistance(!limitDistance); @@ -111,7 +115,8 @@ const GlobalProperties: React.FC = () => { roofVisibility, shadows, value, - limitDistance + limitDistance, + projectId ); }; @@ -128,7 +133,8 @@ const GlobalProperties: React.FC = () => { !roofVisibility, shadows, renderDistance, - limitDistance + limitDistance, + projectId ); // @@ -157,7 +163,7 @@ const GlobalProperties: React.FC = () => { roofVisibility, shadows, renderDistance, - limitDistance + limitDistance,projectId ); // @@ -186,7 +192,8 @@ const GlobalProperties: React.FC = () => { roofVisibility, !shadows, renderDistance, - limitDistance + limitDistance, + projectId ); // diff --git a/app/src/components/layout/sidebarRight/properties/ZoneProperties.tsx b/app/src/components/layout/sidebarRight/properties/ZoneProperties.tsx index e116c15..35f3bb5 100644 --- a/app/src/components/layout/sidebarRight/properties/ZoneProperties.tsx +++ b/app/src/components/layout/sidebarRight/properties/ZoneProperties.tsx @@ -28,7 +28,7 @@ const ZoneProperties: React.FC = () => { const organization = email?.split("@")[1]?.split(".")[0]; let zonesdata = { - zoneId: selectedZone.zoneId, + zoneUuid: selectedZone.zoneUuid, viewPortposition: zonePosition, viewPortCenter: zoneTarget, }; @@ -52,7 +52,7 @@ const ZoneProperties: React.FC = () => { const email = localStorage.getItem("email") || ""; const organization = email?.split("@")[1]?.split(".")[0]; const zonesdata = { - zoneId: selectedZone.zoneId, + zoneUuid: selectedZone.zoneUuid, zoneName: newName, }; // Call your API to update the zone @@ -61,7 +61,7 @@ const ZoneProperties: React.FC = () => { if (response.message === "updated successfully") { setZones((prevZones: any[]) => prevZones.map((zone) => - zone.zoneId === selectedZone.zoneId + zone.zoneUuid === selectedZone.zoneUuid ? { ...zone, zoneName: newName } : zone ) @@ -80,7 +80,7 @@ const ZoneProperties: React.FC = () => { return zones.some( (zone: any) => zone.zoneName.trim().toLowerCase() === name.trim().toLowerCase() && - zone.zoneId !== selectedZone.zoneId + zone.zoneUuid !== selectedZone.zoneUuid ); }; diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/storageMechanics.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/storageMechanics.tsx index 1c27ad1..2138f07 100644 --- a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/storageMechanics.tsx +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/storageMechanics.tsx @@ -8,11 +8,9 @@ import { upsertProductOrEventApi } from "../../../../../../services/simulation/p import { useProductStore } from "../../../../../../store/simulation/useProductStore"; import { useSelectedAction, useSelectedEventData } from "../../../../../../store/simulation/useSimulationStore"; import * as THREE from 'three'; -import { useSceneContext } from "../../../../../../modules/scene/sceneContext"; import { useProductContext } from "../../../../../../modules/simulation/products/productContext"; function StorageMechanics() { - const { storageUnitStore } = useSceneContext(); const [activeOption, setActiveOption] = useState<"default" | "store" | "spawn">("default"); const [selectedPointData, setSelectedPointData] = useState(); const { selectedEventData } = useSelectedEventData(); @@ -20,7 +18,6 @@ function StorageMechanics() { const { selectedProductStore } = useProductContext(); const { selectedProduct } = selectedProductStore(); const { setSelectedAction, clearSelectedAction } = useSelectedAction(); - const { setCurrentMaterials, clearCurrentMaterials, updateCurrentLoad, getStorageUnitById } = storageUnitStore(); const email = localStorage.getItem('email') const organization = (email!.split("@")[1]).split(".")[0]; @@ -92,22 +89,6 @@ function StorageMechanics() { ); updateSelectedPointData(); } - - if (option === "spawn") { - const storageUnit = getStorageUnitById(selectedEventData.data.modelUuid); - if (storageUnit) { - const materialType = selectedPointData.action.materialType || "Default material"; - const materials = Array.from({ length: selectedPointData.action.storageCapacity }, () => - createNewMaterial(materialType) - ).filter(Boolean) as { materialType: string; materialId: string }[]; - - setCurrentMaterials(selectedEventData.data.modelUuid, materials); - updateCurrentLoad(selectedEventData.data.modelUuid, selectedPointData.action.storageCapacity); - } - } else { - clearCurrentMaterials(selectedEventData.data.modelUuid); - updateCurrentLoad(selectedEventData.data.modelUuid, 0); - } }; const handleRenameAction = (newName: string) => { @@ -141,21 +122,6 @@ function StorageMechanics() { event ); updateSelectedPointData(); - - if (activeOption === "spawn") { - const storageUnit = getStorageUnitById(selectedEventData.data.modelUuid); - if (storageUnit) { - const materialType = selectedPointData.action.materialType || "Default material"; - const materials = Array.from({ length: selectedPointData.action.storageCapacity }, () => - createNewMaterial(materialType) - ).filter(Boolean) as { materialType: string; materialId: string }[]; - - setCurrentMaterials(selectedEventData.data.modelUuid, materials); - updateCurrentLoad(selectedEventData.data.modelUuid, selectedPointData.action.storageCapacity); - } - } else { - updateCurrentLoad(selectedEventData.data.modelUuid, 0); - } } }; @@ -183,17 +149,6 @@ function StorageMechanics() { event ); updateSelectedPointData(); - - if (activeOption === "spawn") { - const storageUnit = getStorageUnitById(selectedEventData.data.modelUuid); - if (storageUnit) { - const materials = Array.from({ length: storageUnit.currentMaterials.length }, () => - createNewMaterial(value) - ).filter(Boolean) as { materialType: string; materialId: string }[]; - - setCurrentMaterials(selectedEventData.data.modelUuid, materials); - } - } } }; diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/vehicleMechanics.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/vehicleMechanics.tsx index 786f38e..93c1bd8 100644 --- a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/vehicleMechanics.tsx +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/vehicleMechanics.tsx @@ -4,316 +4,293 @@ import RenameInput from "../../../../../ui/inputs/RenameInput"; import LabledDropdown from "../../../../../ui/inputs/LabledDropdown"; import Trigger from "../trigger/Trigger"; import { - useSelectedAction, - useSelectedEventData, - useSelectedEventSphere, + useSelectedAction, + useSelectedEventData, } from "../../../../../../store/simulation/useSimulationStore"; import { useProductStore } from "../../../../../../store/simulation/useProductStore"; import TravelAction from "../actions/TravelAction"; import ActionsList from "../components/ActionsList"; import { upsertProductOrEventApi } from "../../../../../../services/simulation/products/UpsertProductOrEventApi"; -import { useSceneContext } from "../../../../../../modules/scene/sceneContext"; import { useProductContext } from "../../../../../../modules/simulation/products/productContext"; function VehicleMechanics() { - const { vehicleStore } = useSceneContext(); - const [activeOption, setActiveOption] = useState<"default" | "travel">("default"); - const [selectedPointData, setSelectedPointData] = useState(); - const { selectedEventData } = useSelectedEventData(); - const { getPointByUuid, getEventByModelUuid, updateEvent, updateAction } = useProductStore(); - const { selectedProductStore } = useProductContext(); - const { selectedProduct } = selectedProductStore(); - const { setSelectedAction, clearSelectedAction } = useSelectedAction(); - const { getVehicleById } = vehicleStore(); + const [activeOption, setActiveOption] = useState<"default" | "travel">("default"); + const [selectedPointData, setSelectedPointData] = useState(); + const { selectedEventData } = useSelectedEventData(); + const { getPointByUuid, getEventByModelUuid, updateEvent, updateAction } = useProductStore(); + const { selectedProductStore } = useProductContext(); + const { selectedProduct } = selectedProductStore(); + const { setSelectedAction, clearSelectedAction } = useSelectedAction(); - const email = localStorage.getItem("email"); - const organization = email!.split("@")[1].split(".")[0]; + const email = localStorage.getItem("email"); + const organization = email!.split("@")[1].split(".")[0]; - useEffect(() => { - if (selectedEventData && selectedEventData.data.type === "vehicle") { - const point = getPointByUuid( - selectedProduct.productId, - selectedEventData.data.modelUuid, - selectedEventData.selectedPoint - ) as VehiclePointSchema | undefined; + useEffect(() => { + if (selectedEventData && selectedEventData.data.type === "vehicle") { + const point = getPointByUuid( + selectedProduct.productId, + selectedEventData.data.modelUuid, + selectedEventData.selectedPoint + ) as VehiclePointSchema | undefined; - if (point) { - setSelectedPointData(point); - setActiveOption(point.action.actionType as "travel"); - setSelectedAction(point.action.actionUuid, point.action.actionName); - } - } else { - clearSelectedAction(); - } - }, [selectedProduct, selectedEventData]); + if (point) { + setSelectedPointData(point); + setActiveOption(point.action.actionType as "travel"); + setSelectedAction(point.action.actionUuid, point.action.actionName); + } + } else { + clearSelectedAction(); + } + }, [selectedProduct, selectedEventData]); - const updateBackend = ( - productName: string, - productId: string, - organization: string, - eventData: EventsSchema - ) => { - upsertProductOrEventApi({ - productName: productName, - productId: productId, - organization: organization, - eventDatas: eventData, - }); - }; + const updateBackend = ( + productName: string, + productId: string, + organization: string, + eventData: EventsSchema + ) => { + upsertProductOrEventApi({ + productName: productName, + productId: productId, + organization: organization, + eventDatas: eventData, + }); + }; - const handleSpeedChange = (value: string) => { - if (!selectedEventData) return; - const event = updateEvent( - selectedProduct.productId, - selectedEventData.data.modelUuid, - { - speed: parseFloat(value), - } - ); + const handleSpeedChange = (value: string) => { + if (!selectedEventData) return; + const event = updateEvent( + selectedProduct.productId, + selectedEventData.data.modelUuid, + { + speed: parseFloat(value), + } + ); - if (event) { - updateBackend( - selectedProduct.productName, - selectedProduct.productId, - organization, - event - ); - } - }; + if (event) { + updateBackend( + selectedProduct.productName, + selectedProduct.productId, + organization, + event + ); + } + }; - const handleActionTypeChange = (option: string) => { - if (!selectedEventData || !selectedPointData) return; - const validOption = option as "travel"; - setActiveOption(validOption); + const handleActionTypeChange = (option: string) => { + if (!selectedEventData || !selectedPointData) return; + const validOption = option as "travel"; + setActiveOption(validOption); - const event = updateAction( - selectedProduct.productId, - selectedPointData.action.actionUuid, - { - actionType: validOption, - } - ); + const event = updateAction( + selectedProduct.productId, + selectedPointData.action.actionUuid, + { + actionType: validOption, + } + ); - if (event) { - updateBackend( - selectedProduct.productName, - selectedProduct.productId, - organization, - event - ); - } - }; + if (event) { + updateBackend( + selectedProduct.productName, + selectedProduct.productId, + organization, + event + ); + } + }; - const handleRenameAction = (newName: string) => { - if (!selectedPointData) return; - const event = updateAction( - selectedProduct.productId, - selectedPointData.action.actionUuid, - { actionName: newName } - ); + const handleRenameAction = (newName: string) => { + if (!selectedPointData) return; + const event = updateAction( + selectedProduct.productId, + selectedPointData.action.actionUuid, + { actionName: newName } + ); - if (event) { - updateBackend( - selectedProduct.productName, - selectedProduct.productId, - organization, - event - ); - } - }; + if (event) { + updateBackend( + selectedProduct.productName, + selectedProduct.productId, + organization, + event + ); + } + }; - const handleLoadCapacityChange = (value: string) => { - if (!selectedPointData) return; - const event = updateAction( - selectedProduct.productId, - selectedPointData.action.actionUuid, - { - loadCapacity: parseFloat(value), - } - ); + const handleLoadCapacityChange = (value: string) => { + if (!selectedPointData) return; + const event = updateAction( + selectedProduct.productId, + selectedPointData.action.actionUuid, + { + loadCapacity: parseFloat(value), + } + ); - if (event) { - updateBackend( - selectedProduct.productName, - selectedProduct.productId, - organization, - event - ); - } - }; + if (event) { + updateBackend( + selectedProduct.productName, + selectedProduct.productId, + organization, + event + ); + } + }; - const handleUnloadDurationChange = (value: string) => { - if (!selectedPointData) return; - const event = updateAction( - selectedProduct.productId, - selectedPointData.action.actionUuid, - { - unLoadDuration: parseFloat(value), - } - ); + const handleUnloadDurationChange = (value: string) => { + if (!selectedPointData) return; + const event = updateAction( + selectedProduct.productId, + selectedPointData.action.actionUuid, + { + unLoadDuration: parseFloat(value), + } + ); - if (event) { - updateBackend( - selectedProduct.productName, - selectedProduct.productId, - organization, - event - ); - } - }; + if (event) { + updateBackend( + selectedProduct.productName, + selectedProduct.productId, + organization, + event + ); + } + }; - const handlePickPointChange = (value: string) => { - if (!selectedPointData) return; - }; + const handlePickPointChange = (value: string) => { + if (!selectedPointData) return; + }; - const handleUnloadPointChange = (value: string) => { - if (!selectedPointData) return; - }; + const handleUnloadPointChange = (value: string) => { + if (!selectedPointData) return; + }; - // Get current values from store + // Get current values from store - const currentSpeed = - ( - getEventByModelUuid( - selectedProduct.productId, - selectedEventData?.data.modelUuid || "" - ) as VehicleEventSchema | undefined - )?.speed?.toString() || "0.5"; + const currentSpeed = + ( + getEventByModelUuid( + selectedProduct.productId, + selectedEventData?.data.modelUuid || "" + ) as VehicleEventSchema | undefined + )?.speed?.toString() || "0.5"; - const currentActionName = selectedPointData - ? selectedPointData.action.actionName - : "Action Name"; + const currentActionName = selectedPointData + ? selectedPointData.action.actionName + : "Action Name"; - const currentLoadCapacity = selectedPointData - ? selectedPointData.action.loadCapacity.toString() - : "1"; + const currentLoadCapacity = selectedPointData + ? selectedPointData.action.loadCapacity.toString() + : "1"; - const currentUnloadDuration = selectedPointData - ? selectedPointData.action.unLoadDuration.toString() - : "1"; + const currentUnloadDuration = selectedPointData + ? selectedPointData.action.unLoadDuration.toString() + : "1"; - const { selectedEventSphere } = useSelectedEventSphere(); + function handleClearPoints() { - function handleClearPoints() { - const updatedVehicle = getVehicleById( - selectedEventSphere?.userData.modelUuid - ); + if (!selectedEventData || !selectedPointData?.action.actionUuid) return; - if ( - !selectedProduct?.productId || - !selectedEventSphere?.userData?.modelUuid || - !updatedVehicle?.point - ) - return; - - const event = updateEvent( - selectedProduct.productId, - selectedEventSphere.userData.modelUuid, - { - point: { - ...updatedVehicle.point, - action: { - ...updatedVehicle.point?.action, + const event = updateAction( + selectedProduct.productId, selectedPointData.action.actionUuid, { pickUpPoint: null, unLoadPoint: null, steeringAngle: 0, - }, - }, - } - ); + }) - if (event) { - updateBackend( - selectedProduct.productName, - selectedProduct.productId, - organization, - event - ); + if (event) { + updateBackend( + selectedProduct.productName, + selectedProduct.productId, + organization, + event + ); + } } - } - const availableActions = { - defaultOption: "travel", - options: ["travel"], - }; + const availableActions = { + defaultOption: "travel", + options: ["travel"], + }; - return ( - <> - {selectedEventData && ( + return ( <> -
-
-
- { }} - onChange={handleSpeedChange} - /> -
-
-
-
- -
-
- -
-
- + {selectedEventData && ( + <> +
+
+
+ { }} + onChange={handleSpeedChange} + /> +
+
+
+
+ +
+
+ +
+
+ - {activeOption === "travel" && ( - - )} -
-
-
- -
-
+ {activeOption === "travel" && ( + + )} +
+
+
+ +
+
+ + )} - )} - - ); + ); } export default VehicleMechanics; diff --git a/app/src/components/layout/sidebarRight/visualization/IotInputCards/BarChartInput.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/BarChartInput.tsx index 742569c..9ec8196 100644 --- a/app/src/components/layout/sidebarRight/visualization/IotInputCards/BarChartInput.tsx +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/BarChartInput.tsx @@ -7,6 +7,7 @@ import { useSelectedZoneStore } from "../../../../../store/visualization/useZone import { useWidgetStore } from "../../../../../store/useWidgetStore"; import axios from "axios"; import RenameInput from "../../../../ui/inputs/RenameInput"; +import { useParams } from "react-router-dom"; type Props = {}; @@ -24,6 +25,8 @@ const BarChartInput = (props: Props) => { const email = localStorage.getItem("email") || ""; const organization = email?.split("@")[1]?.split(".")[0]; const [isLoading, setLoading] = useState(true); + const { projectId } = useParams(); + useEffect(() => { const fetchZoneData = async () => { @@ -50,7 +53,15 @@ const BarChartInput = (props: Props) => { if (selectedChartId.id !== "") { try { const response = await axios.get( - `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/WidgetData/${selectedChartId.id}/${organization}` + `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/V1/widget/data?widgetID=${selectedChartId.id}&zoneUuid=${selectedZone.zoneUuid}&projectId=${projectId}`, + { + 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") || "", + }, + } ); if (response.status === 200) { setSelections(response.data.Data.measurements); @@ -83,10 +94,18 @@ const BarChartInput = (props: Props) => { ) => { try { const response = await axios.post( - `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/widget/save`, + `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/V1/widget/save`, + { + 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") || "", + }, + }, { organization: organization, - zoneId: selectedZone.zoneId, + zoneUuid: selectedZone.zoneUuid, widget: { id: selectedChartId.id, panel: selectedChartId.panel, @@ -123,7 +142,7 @@ const BarChartInput = (props: Props) => { newSelections[inputKey] = selectedData; } // setMeasurements(newSelections); // Update Zustand store - console.log(newSelections); + // console.log(newSelections); if (await sendInputes(newSelections, duration, widgetName)) { setSelections(newSelections); } diff --git a/app/src/components/layout/sidebarRight/visualization/IotInputCards/FleetEfficiencyInputComponent.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/FleetEfficiencyInputComponent.tsx index 6b3559c..9ebf11d 100644 --- a/app/src/components/layout/sidebarRight/visualization/IotInputCards/FleetEfficiencyInputComponent.tsx +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/FleetEfficiencyInputComponent.tsx @@ -87,10 +87,10 @@ const FleetEfficiencyInputComponent = (props: Props) => { ) => { try { const response = await axios.post( - `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/floatwidget/save`, + `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/V1/floatWidget/save`, { organization: organization, - zoneId: selectedZone.zoneId, + zoneUuid: selectedZone.zoneUuid, widget: { id: selectedChartId.id, header: inputName, @@ -127,7 +127,7 @@ const FleetEfficiencyInputComponent = (props: Props) => { newSelections[inputKey] = selectedData; } // setMeasurements(newSelections); // Update Zustand store - console.log(newSelections); + // console.log(newSelections); if (await sendInputes(newSelections, duration, widgetName)) { setSelections(newSelections); } diff --git a/app/src/components/layout/sidebarRight/visualization/IotInputCards/FlotingWidgetInput.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/FlotingWidgetInput.tsx index 166070e..58a6093 100644 --- a/app/src/components/layout/sidebarRight/visualization/IotInputCards/FlotingWidgetInput.tsx +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/FlotingWidgetInput.tsx @@ -86,10 +86,10 @@ const FlotingWidgetInput = (props: Props) => { ) => { try { const response = await axios.post( - `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/floatwidget/save`, + `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/V1/floatWidget/save`, { organization: organization, - zoneId: selectedZone.zoneId, + zoneUuid: selectedZone.zoneUuid, widget: { id: selectedChartId.id, header: inputName, @@ -126,7 +126,7 @@ const FlotingWidgetInput = (props: Props) => { newSelections[inputKey] = selectedData; } // setMeasurements(newSelections); // Update Zustand store - console.log(newSelections); + // console.log(newSelections); if (await sendInputes(newSelections, duration, widgetName)) { setSelections(newSelections); } diff --git a/app/src/components/layout/sidebarRight/visualization/IotInputCards/LineGrapInput.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/LineGrapInput.tsx index 49d4525..63cc3be 100644 --- a/app/src/components/layout/sidebarRight/visualization/IotInputCards/LineGrapInput.tsx +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/LineGrapInput.tsx @@ -123,6 +123,7 @@ import { useSelectedZoneStore } from "../../../../../store/visualization/useZone import { useWidgetStore } from "../../../../../store/useWidgetStore"; import axios from "axios"; import RenameInput from "../../../../ui/inputs/RenameInput"; +import { useParams } from "react-router-dom"; type Props = {}; @@ -140,6 +141,7 @@ const LineGrapInput = (props: Props) => { const email = localStorage.getItem("email") || ""; const organization = email?.split("@")[1]?.split(".")[0]; const [isLoading, setLoading] = useState(true); + const { projectId } = useParams(); useEffect(() => { const fetchZoneData = async () => { @@ -166,7 +168,15 @@ const LineGrapInput = (props: Props) => { if (selectedChartId.id !== "") { try { const response = await axios.get( - `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/WidgetData/${selectedChartId.id}/${organization}` + `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/V1/widget/data?widgetID=${selectedChartId.id}&zoneUuid=${selectedZone.zoneUuid}&projectId=${projectId}`, + { + 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") || "", + }, + } ); if (response.status === 200) { setSelections(response.data.Data.measurements); @@ -199,10 +209,18 @@ const LineGrapInput = (props: Props) => { ) => { try { const response = await axios.post( - `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/widget/save`, + `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/V1/widget/save`, + { + 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") || "", + }, + }, { organization: organization, - zoneId: selectedZone.zoneId, + zoneUuid: selectedZone.zoneUuid, widget: { id: selectedChartId.id, panel: selectedChartId.panel, @@ -239,7 +257,7 @@ const LineGrapInput = (props: Props) => { newSelections[inputKey] = selectedData; } // setMeasurements(newSelections); // Update Zustand store - console.log(newSelections); + // console.log(newSelections); if (await sendInputes(newSelections, duration, widgetName)) { setSelections(newSelections); } diff --git a/app/src/components/layout/sidebarRight/visualization/IotInputCards/PieChartInput.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/PieChartInput.tsx index aa0acaa..98167b7 100644 --- a/app/src/components/layout/sidebarRight/visualization/IotInputCards/PieChartInput.tsx +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/PieChartInput.tsx @@ -7,6 +7,7 @@ import { useSelectedZoneStore } from "../../../../../store/visualization/useZone import { useWidgetStore } from "../../../../../store/useWidgetStore"; import axios from "axios"; import RenameInput from "../../../../ui/inputs/RenameInput"; +import { useParams } from "react-router-dom"; type Props = {}; @@ -24,6 +25,8 @@ const PieChartInput = (props: Props) => { const email = localStorage.getItem("email") || ""; const organization = email?.split("@")[1]?.split(".")[0]; const [isLoading, setLoading] = useState(true); + const { projectId } = useParams(); + useEffect(() => { const fetchZoneData = async () => { @@ -50,7 +53,15 @@ const PieChartInput = (props: Props) => { if (selectedChartId.id !== "") { try { const response = await axios.get( - `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/WidgetData/${selectedChartId.id}/${organization}` + `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/V1/widget/data?widgetID=${selectedChartId.id}&zoneUuid=${selectedZone.zoneUuid}&projectId=${projectId}`, + { + 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") || "", + }, + } ); if (response.status === 200) { setSelections(response.data.Data.measurements); @@ -83,10 +94,18 @@ const PieChartInput = (props: Props) => { ) => { try { const response = await axios.post( - `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/widget/save`, + `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/V1/widget/save`, + { + 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") || "", + }, + }, { organization: organization, - zoneId: selectedZone.zoneId, + zoneUuid: selectedZone.zoneUuid, widget: { id: selectedChartId.id, panel: selectedChartId.panel, @@ -123,7 +142,7 @@ const PieChartInput = (props: Props) => { newSelections[inputKey] = selectedData; } // setMeasurements(newSelections); // Update Zustand store - console.log(newSelections); + // console.log(newSelections); if (await sendInputes(newSelections, duration, widgetName)) { setSelections(newSelections); } diff --git a/app/src/components/layout/sidebarRight/visualization/IotInputCards/Progress1Input.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/Progress1Input.tsx index 2f1fdb5..b873e4e 100644 --- a/app/src/components/layout/sidebarRight/visualization/IotInputCards/Progress1Input.tsx +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/Progress1Input.tsx @@ -7,6 +7,7 @@ import { useSelectedZoneStore } from "../../../../../store/visualization/useZone import { useWidgetStore } from "../../../../../store/useWidgetStore"; import axios from "axios"; import RenameInput from "../../../../ui/inputs/RenameInput"; +import { useParams } from "react-router-dom"; type Props = {}; @@ -24,6 +25,8 @@ const Progress1Input = (props: Props) => { const email = localStorage.getItem("email") || ""; const organization = email?.split("@")[1]?.split(".")[0]; const [isLoading, setLoading] = useState(true); + const { projectId } = useParams(); + useEffect(() => { const fetchZoneData = async () => { @@ -50,7 +53,15 @@ const Progress1Input = (props: Props) => { if (selectedChartId.id !== "") { try { const response = await axios.get( - `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/WidgetData/${selectedChartId.id}/${organization}` + `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/V1/widget/data?widgetID=${selectedChartId.id}&zoneUuid=${selectedZone.zoneUuid}&projectId=${projectId}`, + { + 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") || "", + }, + } ); if (response.status === 200) { setSelections(response.data.Data.measurements); @@ -83,10 +94,18 @@ const Progress1Input = (props: Props) => { ) => { try { const response = await axios.post( - `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/widget/save`, + `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/V1/widget/save`, + { + 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") || "", + }, + }, { organization: organization, - zoneId: selectedZone.zoneId, + zoneUuid: selectedZone.zoneUuid, widget: { id: selectedChartId.id, panel: selectedChartId.panel, @@ -123,7 +142,7 @@ const Progress1Input = (props: Props) => { newSelections[inputKey] = selectedData; } // setMeasurements(newSelections); // Update Zustand store - console.log(newSelections); + // console.log(newSelections); if (await sendInputes(newSelections, duration, widgetName)) { setSelections(newSelections); } diff --git a/app/src/components/layout/sidebarRight/visualization/IotInputCards/Progress2Input.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/Progress2Input.tsx index 2d2e089..efbdd6d 100644 --- a/app/src/components/layout/sidebarRight/visualization/IotInputCards/Progress2Input.tsx +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/Progress2Input.tsx @@ -7,6 +7,7 @@ import { useSelectedZoneStore } from "../../../../../store/visualization/useZone import { useWidgetStore } from "../../../../../store/useWidgetStore"; import axios from "axios"; import RenameInput from "../../../../ui/inputs/RenameInput"; +import { useParams } from "react-router-dom"; type Props = {}; @@ -24,6 +25,8 @@ const Progress2Input = (props: Props) => { const email = localStorage.getItem("email") || ""; const organization = email?.split("@")[1]?.split(".")[0]; const [isLoading, setLoading] = useState(true); + const { projectId } = useParams(); + useEffect(() => { const fetchZoneData = async () => { @@ -50,7 +53,15 @@ const Progress2Input = (props: Props) => { if (selectedChartId.id !== "") { try { const response = await axios.get( - `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/WidgetData/${selectedChartId.id}/${organization}` + `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/V1/widget/data?widgetID=${selectedChartId.id}&zoneUuid=${selectedZone.zoneUuid}&projectId=${projectId}`, + { + 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") || "", + }, + } ); if (response.status === 200) { setSelections(response.data.Data.measurements); @@ -83,10 +94,18 @@ const Progress2Input = (props: Props) => { ) => { try { const response = await axios.post( - `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/widget/save`, + `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/V1/widget/save`, + { + 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") || "", + }, + }, { organization: organization, - zoneId: selectedZone.zoneId, + zoneUuid: selectedZone.zoneUuid, widget: { id: selectedChartId.id, panel: selectedChartId.panel, @@ -123,7 +142,7 @@ const Progress2Input = (props: Props) => { newSelections[inputKey] = selectedData; } // setMeasurements(newSelections); // Update Zustand store - console.log(newSelections); + // console.log(newSelections); if (await sendInputes(newSelections, duration, widgetName)) { setSelections(newSelections); } diff --git a/app/src/components/layout/sidebarRight/visualization/IotInputCards/WarehouseThroughputInputComponent.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/WarehouseThroughputInputComponent.tsx index 6c8eda4..d069b0f 100644 --- a/app/src/components/layout/sidebarRight/visualization/IotInputCards/WarehouseThroughputInputComponent.tsx +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/WarehouseThroughputInputComponent.tsx @@ -84,10 +84,10 @@ const WarehouseThroughputInputComponent = (props: Props) => { ) => { try { const response = await axios.post( - `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/floatwidget/save`, + `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/V1/floatWidget/save`, { organization: organization, - zoneId: selectedZone.zoneId, + zoneUuid: selectedZone.zoneUuid, widget: { id: selectedChartId.id, header: inputName, @@ -123,7 +123,7 @@ const WarehouseThroughputInputComponent = (props: Props) => { newSelections[inputKey] = selectedData; } // setMeasurements(newSelections); // Update Zustand store - console.log(newSelections); + // console.log(newSelections); if (await sendInputes(newSelections, duration, widgetName)) { setSelections(newSelections); } diff --git a/app/src/components/layout/sidebarRight/visualization/IotInputCards/Widget2InputCard3D.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/Widget2InputCard3D.tsx index 0723c80..02c76bb 100644 --- a/app/src/components/layout/sidebarRight/visualization/IotInputCards/Widget2InputCard3D.tsx +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/Widget2InputCard3D.tsx @@ -83,10 +83,10 @@ const Widget2InputCard3D = (props: Props) => { ) => { try { const response = await axios.post( - `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/3dwidget/save`, + `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/V1/widget3d/save`, { organization: organization, - zoneId: selectedZone.zoneId, + zoneUuid: selectedZone.zoneUuid, widget: { id: selectedChartId.id, widgetName: inputName, @@ -122,7 +122,7 @@ const Widget2InputCard3D = (props: Props) => { newSelections[inputKey] = selectedData; } // setMeasurements(newSelections); // Update Zustand store - console.log(newSelections); + // console.log(newSelections); if (await sendInputes(newSelections, duration, widgetName)) { setSelections(newSelections); } diff --git a/app/src/components/layout/sidebarRight/visualization/IotInputCards/Widget3InputCard3D.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/Widget3InputCard3D.tsx index 2e2b051..ebe1718 100644 --- a/app/src/components/layout/sidebarRight/visualization/IotInputCards/Widget3InputCard3D.tsx +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/Widget3InputCard3D.tsx @@ -80,10 +80,10 @@ const Widget3InputCard3D = () => { ) => { try { const response = await axios.post( - `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/3dwidget/save`, + `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/V1/widget3d/save`, { organization: organization, - zoneId: selectedZone.zoneId, + zoneUuid: selectedZone.zoneUuid, widget: { id: selectedChartId.id, widgetName: inputName, @@ -119,7 +119,7 @@ const Widget3InputCard3D = () => { newSelections[inputKey] = selectedData; } // setMeasurements(newSelections); // Update Zustand store - console.log(newSelections); + // console.log(newSelections); if (await sendInputes(newSelections, duration, widgetName)) { setSelections(newSelections); } diff --git a/app/src/components/layout/sidebarRight/visualization/IotInputCards/Widget4InputCard3D.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/Widget4InputCard3D.tsx index cc8d9ca..4a60b70 100644 --- a/app/src/components/layout/sidebarRight/visualization/IotInputCards/Widget4InputCard3D.tsx +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/Widget4InputCard3D.tsx @@ -83,10 +83,10 @@ const Widget4InputCard3D = (props: Props) => { ) => { try { const response = await axios.post( - `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/3dwidget/save`, + `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/V1/widget3d/save`, { organization: organization, - zoneId: selectedZone.zoneId, + zoneUuid: selectedZone.zoneUuid, widget: { id: selectedChartId.id, widgetName: inputName, @@ -122,7 +122,7 @@ const Widget4InputCard3D = (props: Props) => { newSelections[inputKey] = selectedData; } // setMeasurements(newSelections); // Update Zustand store - console.log(newSelections); + // console.log(newSelections); if (await sendInputes(newSelections, duration, widgetName)) { setSelections(newSelections); } diff --git a/app/src/components/templates/LoadingPage.tsx b/app/src/components/templates/LoadingPage.tsx index a5fef18..329c19f 100644 --- a/app/src/components/templates/LoadingPage.tsx +++ b/app/src/components/templates/LoadingPage.tsx @@ -1,19 +1,56 @@ -import React from "react"; +import React, { useEffect } from "react"; import RenderOverlay from "./Overlay"; import { LogoIconLarge } from "../icons/Logo"; +import { useParams } from "react-router-dom"; +import { useProjectName } from "../../store/builder/store"; +import { getAllProjects } from "../../services/dashboard/getAllProjects"; interface LoadingPageProps { progress: number; // Expect progress as a percentage (0-100) } const LoadingPage: React.FC = ({ progress }) => { + const { projectName, setProjectName } = useProjectName(); + const { projectId } = useParams(); const validatedProgress = Math.min(100, Math.max(0, progress)); + const generateThumbnail = async () => { + const email = localStorage.getItem("email"); + const userId = localStorage.getItem("userId"); + try { + if (!email || !userId) { + console.error("User data not found in localStorage"); + return; + } + const emailParts = email.split("@"); + if (emailParts.length < 2) { + console.error("Invalid email format"); + return; + } + + const domainParts = emailParts[1].split("."); + const Organization = domainParts[0]; + const projects = await getAllProjects( + userId, Organization + ); + const filterProject = projects?.Projects.find((val: any) => val.projectUuid === projectId || val._id + === projectId) + + setProjectName(filterProject.projectName) + + } + catch { + + } + } + useEffect(() => { + generateThumbnail(); + }, []); return (
-
Untitled
+
{projectName}
diff --git a/app/src/components/temporary/SelectFloorPlan.tsx b/app/src/components/temporary/SelectFloorPlan.tsx index 7a95e8e..6c45f08 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"; +type DXFData = any 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/FileMenu.tsx b/app/src/components/ui/FileMenu.tsx index 645f085..d65e04c 100644 --- a/app/src/components/ui/FileMenu.tsx +++ b/app/src/components/ui/FileMenu.tsx @@ -3,11 +3,18 @@ import RenameInput from "./inputs/RenameInput"; import { ArrowIcon } from "../icons/ExportCommonIcons"; import MenuBar from "./menu/menu"; import { ProjectIcon } from "../icons/HeaderIcons"; +import { useProjectName, useSocketStore } from "../../store/builder/store"; +import { useParams } from "react-router-dom"; +import { getAllProjects } from "../../services/dashboard/getAllProjects"; +import { updateProject } from "../../services/dashboard/updateProject"; const FileMenu: React.FC = () => { const [openMenu, setOpenMenu] = useState(false); const containerRef = useRef(null); let clickTimeout: NodeJS.Timeout | null = null; + const { projectName, setProjectName } = useProjectName(); + const { dashBoardSocket } = useSocketStore(); + const { projectId } = useParams(); const handleClick = () => { if (clickTimeout) return; @@ -32,21 +39,79 @@ const FileMenu: React.FC = () => { }, []); // project - const [projectName, setProjectName] = useState("Demo Project"); + // const [projectName, setProjectName] = useState("Demo Project"); // Load project name from localStorage on mount - useEffect(() => { - const savedName = localStorage.getItem("projectName"); - if (savedName) { - setProjectName(savedName); + // useEffect(() => { + // const savedName = localStorage.getItem("projectName"); + // if (savedName) { + // setProjectName(savedName); + // } + // }, []); + + // const handleProjectRename = (newName: string) => { + // setProjectName(newName); + // localStorage.setItem("projectName", newName); + // }; + const handleProjectRename = async (projectName: string) => { + setProjectName(projectName); + if (!projectId) return + // localStorage.setItem("projectName", newName); + try { + const email = localStorage.getItem("email"); + const userId = localStorage.getItem("userId"); + + if (!email || !userId) { + + return; + } + + const emailParts = email.split("@"); + if (emailParts.length < 2) { + + return; + } + + const domainParts = emailParts[1].split("."); + const Organization = domainParts[0]; + const projects = await getAllProjects( + userId, Organization + ); + console.log('projects: ', projects); + let projectUuid = projects.Projects.find((val: any) => val.projectUuid === projectId || val._id + === projectId) + const updateProjects = { + projectId: projectUuid, + organization: Organization, + userId, + projectName, + thumbnail: undefined + } + + // if (dashBoardSocket) { + // const handleResponse = (data: any) => { + // console.log('Project update response:', data); + // dashBoardSocket.off("v1-project:response:update", handleResponse); // Clean up + // }; + + // dashBoardSocket.on("v1-project:response:update", handleResponse); + + // dashBoardSocket.emit("v1:project:update", updateProjects); + // } + + //API for projects rename + const updatedProjectName = await updateProject( + projectId, + userId, + Organization, + undefined, + projectName + ); + // + } catch (error) { + } - }, []); - - const handleProjectRename = (newName: string) => { - setProjectName(newName); - localStorage.setItem("projectName", newName); }; - return (