diff --git a/app/src/components/Dashboard/DashboardCard.tsx b/app/src/components/Dashboard/DashboardCard.tsx index 68d91e7..0f75d19 100644 --- a/app/src/components/Dashboard/DashboardCard.tsx +++ b/app/src/components/Dashboard/DashboardCard.tsx @@ -2,289 +2,314 @@ import React, { useState, useRef, useEffect, act } from "react"; import img from "../../assets/image/image.png"; import { useNavigate } from "react-router-dom"; import { getUserData } from "../../functions/getUserData"; -import { useLoadingProgress, useProjectName, useSocketStore } from "../../store/builder/store"; +import { + useLoadingProgress, + 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"; +import { updateProject } from "../../services/dashboard/updateProject"; interface DashBoardCardProps { - projectName: string; - thumbnail: any; - projectId: string; - createdAt?: string; - isViewed?: string; - createdBy?: { _id: string, userName: string }; - handleDeleteProject?: (projectId: string) => Promise; - handleTrashDeleteProject?: (projectId: string) => Promise; - handleRestoreProject?: (projectId: string) => Promise; - handleDuplicateWorkspaceProject?: ( - projectId: string, - projectName: string, - thumbnail: string, - userId?: string - ) => Promise; - handleDuplicateRecentProject?: ( - projectId: string, - projectName: string, - thumbnail: string - ) => Promise; - active?: string; - setIsSearchActive?: React.Dispatch>; - setRecentDuplicateData?: React.Dispatch>; - setProjectDuplicateData?: React.Dispatch>; - setActiveFolder?: React.Dispatch>; + projectName: string; + thumbnail: any; + projectId: string; + createdAt?: string; + isViewed?: string; + createdBy?: { _id: string; userName: string }; + handleDeleteProject?: (projectId: string) => Promise; + handleTrashDeleteProject?: (projectId: string) => Promise; + handleRestoreProject?: (projectId: string) => Promise; + handleDuplicateWorkspaceProject?: ( + projectId: string, + projectName: string, + thumbnail: string, + userId?: string + ) => Promise; + handleDuplicateRecentProject?: ( + projectId: string, + projectName: string, + thumbnail: string + ) => Promise; + active?: string; + setIsSearchActive?: React.Dispatch>; + setRecentDuplicateData?: React.Dispatch>; + setProjectDuplicateData?: React.Dispatch>; + setActiveFolder?: React.Dispatch>; } type RelativeTimeFormatUnit = any; const DashboardCard: React.FC = ({ - projectName, - thumbnail, - projectId, - active, - handleDeleteProject, - handleRestoreProject, - handleTrashDeleteProject, - handleDuplicateWorkspaceProject, - handleDuplicateRecentProject, - createdAt, - createdBy, - setRecentDuplicateData, - setProjectDuplicateData, - setActiveFolder + projectName, + thumbnail, + projectId, + active, + handleDeleteProject, + handleRestoreProject, + handleTrashDeleteProject, + handleDuplicateWorkspaceProject, + handleDuplicateRecentProject, + createdAt, + createdBy, + setRecentDuplicateData, + setProjectDuplicateData, + setActiveFolder, }) => { - 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 { setLoadingProgress } = useLoadingProgress(); - const kebabRef = useRef(null); + 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 { setLoadingProgress } = useLoadingProgress(); + const kebabRef = useRef(null); - const navigateToProject = async (e: any) => { - console.log('active: ', active); - if (active && active == "trash") return; - try { - const viewProjects = await viewProject(organization, projectId, userId) - console.log('viewProjects: ', viewProjects); - console.log('projectName: ', projectName); - setLoadingProgress(1) - setProjectName(projectName); - navigate(`/${projectId}`); - } catch { + const navigateToProject = async (e: any) => { + if (active && active == "trash") return; + try { + const viewProjects = await viewProject(organization, projectId, userId); + setLoadingProgress(1); + setProjectName(projectName); + navigate(`/${projectId}`); + } catch {} + }; + + const handleOptionClick = async (option: string) => { + switch (option) { + case "delete": + if (handleDeleteProject) { + handleDeleteProject(projectId); + } else if (handleTrashDeleteProject) { + handleTrashDeleteProject(projectId); } - }; - - const handleOptionClick = async (option: string) => { - switch (option) { - case "delete": - if (handleDeleteProject) { - handleDeleteProject(projectId); - } else if (handleTrashDeleteProject) { - handleTrashDeleteProject(projectId); - } - break; - case "restore": - if (handleRestoreProject) { - await handleRestoreProject(projectId); - } - break; - case "open in new tab": - try { - if (active === "shared" && createdBy) { - console.log("ihreq"); - const newTab = await viewProject(organization, projectId, createdBy?._id); - console.log('newTab: ', newTab); - } else { - const newTab = await viewProject(organization, projectId, userId); - console.log('newTab: ', newTab); - setProjectName(projectName); - setIsKebabOpen(false); - } - } catch (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, userId); - if (active === "shared" && setActiveFolder) { - setActiveFolder("myProjects") - } - } else if (handleDuplicateRecentProject) { - setRecentDuplicateData && - setRecentDuplicateData({ - projectId, - projectName, - thumbnail, - userId - }); - await handleDuplicateRecentProject(projectId, projectName, thumbnail); - } - break; - default: - break; + break; + case "restore": + if (handleRestoreProject) { + await handleRestoreProject(projectId); } - setIsKebabOpen(false); - }; - - OuterClick({ - contextClassName: ["kebab-wrapper", "kebab-options-wrapper"], - setMenuVisible: () => setIsKebabOpen(false), - }); - - const handleProjectName = async (projectName: string) => { - setRenameValue(projectName); - if (!projectId) return; + break; + case "open in new tab": try { - const projects = await getAllProjects(userId, organization); - if (!projects || !projects.Projects) return; - let projectUuid = projects.Projects.find( - (val: any) => val.projectUuid === projectId || val._id === projectId + if (active === "shared" && createdBy) { + const newTab = await viewProject( + organization, + projectId, + createdBy?._id ); - const updateProjects = { - projectId: projectUuid, - organization, - userId, - projectName, - thumbnail: undefined, - }; + } else { + const newTab = await viewProject(organization, projectId, userId); - 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); - } + setProjectName(projectName); + setIsKebabOpen(false); + } + } catch (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, + userId + ); + if (active === "shared" && setActiveFolder) { + setActiveFolder("myProjects"); + } + } else if (handleDuplicateRecentProject) { + setRecentDuplicateData && + setRecentDuplicateData({ + projectId, + projectName, + thumbnail, + userId, + }); + await handleDuplicateRecentProject(projectId, projectName, thumbnail); } - return "just now"; + break; + default: + break; } + setIsKebabOpen(false); + }; - const kebabOptionsMap: Record = { - default: ["rename", "delete", "duplicate", "open in new tab"], - trash: ["restore", "delete"], - shared: ["duplicate", "open in new tab"], + 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); + if (!projects || !projects.Projects) return; + let projectUuid = projects.Projects.find( + (val: any) => val.projectUuid === projectId || val._id === projectId + ); + const updateProjects = { + projectId: projectUuid?._id, + organization, + userId, + projectName, + thumbnail: undefined, + }; + // const updatedProjectName = await updateProject( + // projectUuid._id, + // userId, + // organization, + // undefined, + // projectName + // ); + 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 getOptions = () => { - if (active === "trash") return kebabOptionsMap.trash; - if (active === "shared") return kebabOptionsMap.shared; - if (createdBy && createdBy?._id !== userId) return kebabOptionsMap.shared; - return kebabOptionsMap.default; - }; + const rtf = new Intl.RelativeTimeFormat("en", { numeric: "auto" }); - return ( - - - - - {isKebabOpen && ( -
- {getOptions().map((option) => ( - - ))} -
+
+ {isRenaming ? ( + { + e.stopPropagation(); + handleProjectName(e.target.value); + }} + onBlur={() => { + setIsRenaming(false); + setProjectName(renameValue); + }} + onKeyDown={(e) => { + if (e.key === "Enter") { + setProjectName(renameValue); + setIsRenaming(false); + } + }} + autoFocus + /> + ) : ( + {renameValue} )} - - ); + {createdAt && ( +
+ {active && active == "trash" ? `Trashed by you` : `Edited `}{" "} + {getRelativeTime(createdAt)} +
+ )} +
+
+
+ {!createdBy + ? userName + ? userName?.charAt(0).toUpperCase() + : "A" + : createdBy?.userName?.charAt(0).toUpperCase()} +
+ +
+ + + {isKebabOpen && ( +
+ {getOptions().map((option) => ( + + ))} +
+ )} + + ); }; -export default DashboardCard; \ No newline at end of file +export default DashboardCard; diff --git a/app/src/components/Dashboard/DashboardHome.tsx b/app/src/components/Dashboard/DashboardHome.tsx index 6e9dce7..0569c08 100644 --- a/app/src/components/Dashboard/DashboardHome.tsx +++ b/app/src/components/Dashboard/DashboardHome.tsx @@ -8,15 +8,16 @@ import { recentlyViewed } from "../../services/dashboard/recentlyViewed"; import { searchProject } from "../../services/dashboard/searchProjects"; import { deleteProject } from "../../services/dashboard/deleteProject"; import ProjectSocketRes from "./socket/projectSocketRes.dev"; +import { generateUniqueId } from "../../functions/generateUniqueId"; interface Project { _id: string; projectName: string; thumbnail: string; - createdBy: { _id: string, userName: string }; + createdBy: { _id: string; userName: string }; projectUuid?: string; createdAt: string; - isViewed?: string + isViewed?: string; } interface RecentProjectsData { @@ -25,12 +26,12 @@ interface RecentProjectsData { const DashboardHome: React.FC = () => { const [recentProjects, setRecentProjects] = useState({}); + const [isSearchActive, setIsSearchActive] = useState(false); const { userId, organization } = getUserData(); const { projectSocket } = useSocketStore(); const [recentDuplicateData, setRecentDuplicateData] = useState({}); - const fetchRecentProjects = async () => { try { const projects = await recentlyViewed(organization, userId); @@ -38,9 +39,7 @@ const DashboardHome: React.FC = () => { if (JSON.stringify(projects) !== JSON.stringify(recentProjects)) { setRecentProjects(projects); } - } catch (error) { - console.error("Error fetching recent projects:", error); - } + } catch (error) {} }; const handleRecentProjectSearch = async (inputValue: string) => { @@ -65,7 +64,7 @@ const DashboardHome: React.FC = () => { // userId, // organization // ); - // console.log('deletedProject: ', deletedProject); + // //socket for delete Project const deleteProject = { @@ -91,9 +90,7 @@ const DashboardHome: React.FC = () => { }; }); setIsSearchActive(false); - } catch (error) { - console.error("Error deleting project:", error); - } + } catch (error) {} }; const handleDuplicateRecentProject = async ( @@ -105,7 +102,8 @@ const DashboardHome: React.FC = () => { userId, thumbnail, organization, - projectUuid: projectId, + projectUuid: generateUniqueId(), + refProjectID: projectId, projectName, }; projectSocket.emit("v1:project:Duplicate", duplicateRecentProjectData); @@ -163,4 +161,4 @@ const DashboardHome: React.FC = () => { ); }; -export default DashboardHome; \ No newline at end of file +export default DashboardHome; diff --git a/app/src/components/Dashboard/DashboardProjects.tsx b/app/src/components/Dashboard/DashboardProjects.tsx index ccb56b2..ba2db7b 100644 --- a/app/src/components/Dashboard/DashboardProjects.tsx +++ b/app/src/components/Dashboard/DashboardProjects.tsx @@ -9,6 +9,7 @@ import { deleteProject } from "../../services/dashboard/deleteProject"; import ProjectSocketRes from "./socket/projectSocketRes.dev"; import { sharedWithMeProjects } from "../../services/dashboard/sharedWithMeProject"; import { duplicateProject } from "../../services/dashboard/duplicateProject"; +import { generateUniqueId } from "../../functions/generateUniqueId"; interface Project { _id: string; @@ -27,7 +28,7 @@ const DashboardProjects: React.FC = () => { const [workspaceProjects, setWorkspaceProjects] = useState( {} ); - const [sharedwithMeProject, setSharedWithMeProjects] = useState([]) + const [sharedwithMeProject, setSharedWithMeProjects] = useState([]); const [projectDuplicateData, setProjectDuplicateData] = useState({}); const [isSearchActive, setIsSearchActive] = useState(false); const [activeFolder, setActiveFolder] = useState("myProjects"); @@ -41,7 +42,11 @@ const DashboardProjects: React.FC = () => { } if (!setWorkspaceProjects || !setIsSearchActive) return; - const searchedProject = await searchProject(organization, userId, inputValue); + const searchedProject = await searchProject( + organization, + userId, + inputValue + ); setIsSearchActive(true); setWorkspaceProjects(searchedProject.message ? {} : searchedProject); }; @@ -49,14 +54,13 @@ const DashboardProjects: React.FC = () => { const fetchAllProjects = async () => { try { const projects = await getAllProjects(userId, organization); + if (!projects || !projects.Projects) return; if (JSON.stringify(projects) !== JSON.stringify(workspaceProjects)) { setWorkspaceProjects(projects); } - } catch (error) { - console.error("Error fetching projects:", error); - } + } catch (error) {} }; const handleDeleteProject = async (projectId: any) => { @@ -66,7 +70,7 @@ const DashboardProjects: React.FC = () => { // userId, // organization // ); - // console.log('deletedProject: ', deletedProject); + // const deleteProjects = { projectId, organization, @@ -77,7 +81,6 @@ const DashboardProjects: React.FC = () => { if (projectSocket) { projectSocket.emit("v1:project:delete", deleteProjects); } else { - console.error("Socket is not connected."); } setWorkspaceProjects((prevDiscardedProjects: WorkspaceProjects) => { if (!Array.isArray(prevDiscardedProjects?.Projects)) { @@ -92,22 +95,28 @@ const DashboardProjects: React.FC = () => { }; }); setIsSearchActive(false); - } catch (error) { - console.error("Error deleting project:", error); - } + } catch (error) {} }; const handleDuplicateWorkspaceProject = async ( projectId: string, projectName: string, - thumbnail: string, + thumbnail: string ) => { + // const duplicatedProject = await duplicateProject( + // projectId, + // generateUniqueId(), + // thumbnail, + // projectName + // ); + // console.log("duplicatedProject: ", duplicatedProject); const duplicateProjectData = { userId, thumbnail, organization, - projectUuid: projectId, + projectUuid: generateUniqueId(), + refProjectID: projectId, projectName, }; projectSocket.emit("v1:project:Duplicate", duplicateProjectData); @@ -153,16 +162,12 @@ const DashboardProjects: React.FC = () => { )); }; - const sharedProject = async () => { try { const sharedWithMe = await sharedWithMeProjects(); - setSharedWithMeProjects(sharedWithMe) - } catch { - - } - } - + setSharedWithMeProjects(sharedWithMe); + } catch {} + }; useEffect(() => { if (!isSearchActive) { @@ -172,10 +177,9 @@ const DashboardProjects: React.FC = () => { useEffect(() => { if (activeFolder === "shared") { - sharedProject() + sharedProject(); } - }, [activeFolder]) - + }, [activeFolder]); return (
@@ -184,7 +188,10 @@ const DashboardProjects: React.FC = () => { handleProjectsSearch={handleProjectsSearch} /> -
+
-
{activeFolder == "myProjects" ? renderProjects() : renderSharedProjects()}
+
+ {activeFolder == "myProjects" + ? renderProjects() + : renderSharedProjects()} +
- {projectDuplicateData && Object.keys(projectDuplicateData).length > 0 && ( - - )} + {projectDuplicateData && + Object.keys(projectDuplicateData).length > 0 && ( + + )}
); }; export default DashboardProjects; - - diff --git a/app/src/components/icons/ContextMenuIcons.tsx b/app/src/components/icons/ContextMenuIcons.tsx index e249bad..210f1b9 100644 --- a/app/src/components/icons/ContextMenuIcons.tsx +++ b/app/src/components/icons/ContextMenuIcons.tsx @@ -160,3 +160,153 @@ export function RenameIcon() { ); } + +export function FocusIcon() { + return ( + + + + + ); +} + +export function TransformIcon() { + return ( + + + + + + + + + + + + ); +} + +export function DublicateIcon() { + return ( + + + + + + + + + + + + + + ); +} + +export function CopyIcon() { + return ( + + + + + + + + + + + + ); +} + +export function PasteIcon() { + return ( + + + + + + + + + + + + ); +} + +export function ModifiersIcon() { + return ( + + + + + ); +} + +export function DeleteIcon() { + return ( + + + + + + + + + + + ); +} + +export function MoveIcon() { + return ( + + + + + + + + + + + ); +} + +export function RotateIcon() { + return ( + + + + ); +} + +export function GroupIcon() { + return ( + + + + ); +} + +export function ArrayIcon() { + return ( + + + + + + ); +} + +export function SubMenuIcon() { + return ( + + + + + ); +} + diff --git a/app/src/components/layout/scenes/MainScene.tsx b/app/src/components/layout/scenes/MainScene.tsx index 6b55a26..e283986 100644 --- a/app/src/components/layout/scenes/MainScene.tsx +++ b/app/src/components/layout/scenes/MainScene.tsx @@ -3,6 +3,7 @@ import { useLoadingProgress, useRenameModeStore, useSaveVersion, + useSelectedAssets, useSelectedComment, useSelectedFloorItem, useSocketStore, @@ -58,6 +59,7 @@ function MainScene() { const { setFloatingWidget } = useFloatingWidget(); const { clearComparisonProduct } = useComparisonProduct(); const { selectedFloorItem, setSelectedFloorItem } = useSelectedFloorItem(); + const { selectedAssets,setSelectedAssets } = useSelectedAssets(); const { assetStore, productStore } = useSceneContext(); const { products } = productStore(); const { setName } = assetStore(); @@ -97,18 +99,40 @@ function MainScene() { const handleObjectRename = async (newName: string) => { if (!projectId) return - let response = await setAssetsApi({ - modelUuid: selectedFloorItem.userData.modelUuid, - modelName: newName, - projectId - }); - selectedFloorItem.userData = { - ...selectedFloorItem.userData, - modelName: newName - }; - setSelectedFloorItem(selectedFloorItem); - setIsRenameMode(false); - setName(selectedFloorItem.userData.modelUuid, response.modelName); + if (selectedFloorItem) { + console.log('selectedFloorItem.userData.modelUuid: ', selectedFloorItem.userData.modelUuid); + console.log(' newName: ', newName); + console.log('projectId: ', projectId); + setAssetsApi({ + modelUuid: selectedFloorItem.userData.modelUuid, + modelName: newName, + projectId, + versionId: selectedVersion?.versionId || '' + }).then(() => { + selectedFloorItem.userData = { + ...selectedFloorItem.userData, + modelName: newName + }; + setSelectedFloorItem(selectedFloorItem); + setIsRenameMode(false); + setName(selectedFloorItem.userData.modelUuid, newName); + }) + } else if (selectedAssets.length === 1) { + setAssetsApi({ + modelUuid: selectedAssets[0].userData.modelUuid, + modelName: newName, + projectId, + versionId: selectedVersion?.versionId || '' + }).then(() => { + selectedAssets[0].userData = { + ...selectedAssets[0].userData, + modelName: newName + }; + setSelectedAssets(selectedAssets); + setIsRenameMode(false); + setName(selectedAssets[0].userData.modelUuid, newName); + }) + } } return ( @@ -135,7 +159,7 @@ function MainScene() { {(isPlaying) && activeModule !== "simulation" && } - {isRenameMode && selectedFloorItem?.userData.modelName && } + {isRenameMode && (selectedFloorItem?.userData.modelName || selectedAssets.length === 1) && } {/* remove this later */} {activeModule === "builder" && !toggleThreeD && } @@ -188,7 +212,7 @@ function MainScene() { {activeModule !== "market" && !selectedUser &&