updated dashboard duplication

This commit is contained in:
2025-08-02 18:23:42 +05:30
parent a71346e7fd
commit 35153c8c79
9 changed files with 1782 additions and 1375 deletions

View File

@@ -2,289 +2,314 @@ import React, { useState, useRef, useEffect, act } from "react";
import img from "../../assets/image/image.png"; import img from "../../assets/image/image.png";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { getUserData } from "../../functions/getUserData"; 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 { viewProject } from "../../services/dashboard/viewProject";
import OuterClick from "../../utils/outerClick"; import OuterClick from "../../utils/outerClick";
import { KebabIcon } from "../icons/ExportCommonIcons"; import { KebabIcon } from "../icons/ExportCommonIcons";
import { getAllProjects } from "../../services/dashboard/getAllProjects"; import { getAllProjects } from "../../services/dashboard/getAllProjects";
import { updateProject } from "../../services/dashboard/updateProject";
interface DashBoardCardProps { interface DashBoardCardProps {
projectName: string; projectName: string;
thumbnail: any; thumbnail: any;
projectId: string; projectId: string;
createdAt?: string; createdAt?: string;
isViewed?: string; isViewed?: string;
createdBy?: { _id: string, userName: string }; createdBy?: { _id: string; userName: string };
handleDeleteProject?: (projectId: string) => Promise<void>; handleDeleteProject?: (projectId: string) => Promise<void>;
handleTrashDeleteProject?: (projectId: string) => Promise<void>; handleTrashDeleteProject?: (projectId: string) => Promise<void>;
handleRestoreProject?: (projectId: string) => Promise<void>; handleRestoreProject?: (projectId: string) => Promise<void>;
handleDuplicateWorkspaceProject?: ( handleDuplicateWorkspaceProject?: (
projectId: string, projectId: string,
projectName: string, projectName: string,
thumbnail: string, thumbnail: string,
userId?: string userId?: string
) => Promise<void>; ) => Promise<void>;
handleDuplicateRecentProject?: ( handleDuplicateRecentProject?: (
projectId: string, projectId: string,
projectName: string, projectName: string,
thumbnail: string thumbnail: string
) => Promise<void>; ) => Promise<void>;
active?: string; active?: string;
setIsSearchActive?: React.Dispatch<React.SetStateAction<boolean>>; setIsSearchActive?: React.Dispatch<React.SetStateAction<boolean>>;
setRecentDuplicateData?: React.Dispatch<React.SetStateAction<Object>>; setRecentDuplicateData?: React.Dispatch<React.SetStateAction<Object>>;
setProjectDuplicateData?: React.Dispatch<React.SetStateAction<Object>>; setProjectDuplicateData?: React.Dispatch<React.SetStateAction<Object>>;
setActiveFolder?: React.Dispatch<React.SetStateAction<string>>; setActiveFolder?: React.Dispatch<React.SetStateAction<string>>;
} }
type RelativeTimeFormatUnit = any; type RelativeTimeFormatUnit = any;
const DashboardCard: React.FC<DashBoardCardProps> = ({ const DashboardCard: React.FC<DashBoardCardProps> = ({
projectName, projectName,
thumbnail, thumbnail,
projectId, projectId,
active, active,
handleDeleteProject, handleDeleteProject,
handleRestoreProject, handleRestoreProject,
handleTrashDeleteProject, handleTrashDeleteProject,
handleDuplicateWorkspaceProject, handleDuplicateWorkspaceProject,
handleDuplicateRecentProject, handleDuplicateRecentProject,
createdAt, createdAt,
createdBy, createdBy,
setRecentDuplicateData, setRecentDuplicateData,
setProjectDuplicateData, setProjectDuplicateData,
setActiveFolder setActiveFolder,
}) => { }) => {
const navigate = useNavigate(); const navigate = useNavigate();
const { setProjectName } = useProjectName(); const { setProjectName } = useProjectName();
const { userId, organization, userName } = getUserData(); const { userId, organization, userName } = getUserData();
const [isKebabOpen, setIsKebabOpen] = useState(false); const [isKebabOpen, setIsKebabOpen] = useState(false);
const [renameValue, setRenameValue] = useState(projectName); const [renameValue, setRenameValue] = useState(projectName);
const [isRenaming, setIsRenaming] = useState(false); const [isRenaming, setIsRenaming] = useState(false);
const { projectSocket } = useSocketStore(); const { projectSocket } = useSocketStore();
const { setLoadingProgress } = useLoadingProgress(); const { setLoadingProgress } = useLoadingProgress();
const kebabRef = useRef<HTMLDivElement>(null); const kebabRef = useRef<HTMLDivElement>(null);
const navigateToProject = async (e: any) => { const navigateToProject = async (e: any) => {
console.log('active: ', active); if (active && active == "trash") return;
if (active && active == "trash") return; try {
try { const viewProjects = await viewProject(organization, projectId, userId);
const viewProjects = await viewProject(organization, projectId, userId)
console.log('viewProjects: ', viewProjects);
console.log('projectName: ', projectName);
setLoadingProgress(1)
setProjectName(projectName);
navigate(`/${projectId}`);
} catch {
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);
} }
}; break;
case "restore":
const handleOptionClick = async (option: string) => { if (handleRestoreProject) {
switch (option) { await handleRestoreProject(projectId);
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;
} }
setIsKebabOpen(false); break;
}; case "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 { try {
const projects = await getAllProjects(userId, organization); if (active === "shared" && createdBy) {
if (!projects || !projects.Projects) return; const newTab = await viewProject(
let projectUuid = projects.Projects.find( organization,
(val: any) => val.projectUuid === projectId || val._id === projectId projectId,
createdBy?._id
); );
const updateProjects = { } else {
projectId: projectUuid, const newTab = await viewProject(organization, projectId, userId);
organization,
userId,
projectName,
thumbnail: undefined,
};
if (projectSocket) { setProjectName(projectName);
projectSocket.emit("v1:project:update", updateProjects); setIsKebabOpen(false);
} }
} catch (error) { } } catch (error) {}
}; window.open(`/${projectId}`, "_blank");
break;
function getRelativeTime(dateString: string): string { case "rename":
const date = new Date(dateString); setIsRenaming(true);
const now = new Date(); break;
const diffInSeconds = Math.floor((now.getTime() - date.getTime()) / 1000); case "duplicate":
if (handleDuplicateWorkspaceProject) {
const intervals: Record<RelativeTimeFormatUnit, number> = { setProjectDuplicateData &&
year: 31536000, setProjectDuplicateData({
month: 2592000, projectId,
week: 604800, projectName,
day: 86400, thumbnail,
hour: 3600, });
minute: 60, await handleDuplicateWorkspaceProject(
second: 1, projectId,
}; projectName,
thumbnail,
const rtf = new Intl.RelativeTimeFormat("en", { numeric: "auto" }); userId
);
for (const key in intervals) { if (active === "shared" && setActiveFolder) {
const unit = key as RelativeTimeFormatUnit; setActiveFolder("myProjects");
const diff = Math.floor(diffInSeconds / intervals[unit]); }
if (diff >= 1) { } else if (handleDuplicateRecentProject) {
return rtf.format(-diff, unit); setRecentDuplicateData &&
} setRecentDuplicateData({
projectId,
projectName,
thumbnail,
userId,
});
await handleDuplicateRecentProject(projectId, projectName, thumbnail);
} }
return "just now"; break;
default:
break;
} }
setIsKebabOpen(false);
};
const kebabOptionsMap: Record<string, string[]> = { OuterClick({
default: ["rename", "delete", "duplicate", "open in new tab"], contextClassName: ["kebab-wrapper", "kebab-options-wrapper"],
trash: ["restore", "delete"], setMenuVisible: () => setIsKebabOpen(false),
shared: ["duplicate", "open in new tab"], });
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<RelativeTimeFormatUnit, number> = {
year: 31536000,
month: 2592000,
week: 604800,
day: 86400,
hour: 3600,
minute: 60,
second: 1,
}; };
const getOptions = () => { const rtf = new Intl.RelativeTimeFormat("en", { numeric: "auto" });
if (active === "trash") return kebabOptionsMap.trash;
if (active === "shared") return kebabOptionsMap.shared;
if (createdBy && createdBy?._id !== userId) return kebabOptionsMap.shared;
return kebabOptionsMap.default;
};
return ( for (const key in intervals) {
<button const unit = key as RelativeTimeFormatUnit;
className="dashboard-card-container" const diff = Math.floor(diffInSeconds / intervals[unit]);
onClick={navigateToProject} if (diff >= 1) {
title={projectName} return rtf.format(-diff, unit);
onMouseLeave={() => setIsKebabOpen(false)} }
}
return "just now";
}
const kebabOptionsMap: Record<string, string[]> = {
default: ["rename", "delete", "duplicate", "open in new tab"],
trash: ["restore", "delete"],
shared: ["duplicate", "open in new tab"],
};
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;
};
return (
<button
className="dashboard-card-container"
onClick={navigateToProject}
title={projectName}
onMouseLeave={() => setIsKebabOpen(false)}
>
<div className="dashboard-card-wrapper">
<div className="preview-container">
{thumbnail ? (
<img src={thumbnail} alt="" />
) : (
<img src={img} alt="" />
)}
</div>
<div
className="project-details-container"
onClick={(e) => {
e.stopPropagation();
}}
> >
<div className="dashboard-card-wrapper"> <div className="project-details">
<div className="preview-container"> {isRenaming ? (
{thumbnail ? <img src={thumbnail} alt="" /> : <img src={img} alt="" />} <input
</div> value={renameValue}
<div className="project-details-container" onClick={(e) => { e.stopPropagation() }}> onChange={(e) => {
<div className="project-details"> e.stopPropagation();
{isRenaming ? ( handleProjectName(e.target.value);
<input }}
value={renameValue} onBlur={() => {
onChange={(e) => { setIsRenaming(false);
e.stopPropagation(); setProjectName(renameValue);
handleProjectName(e.target.value); }}
}} onKeyDown={(e) => {
onBlur={() => { if (e.key === "Enter") {
setIsRenaming(false); setProjectName(renameValue);
setProjectName(renameValue); setIsRenaming(false);
}} }
onKeyDown={(e) => { }}
if (e.key === "Enter") { autoFocus
setProjectName(renameValue); />
setIsRenaming(false); ) : (
} <span>{renameValue}</span>
}}
autoFocus
/>
) : (
<span>{renameValue}</span>
)}
{createdAt && (
<div className="project-data">
{active && active == "trash" ? `Trashed by you` : `Edited `}{" "}
{getRelativeTime(createdAt)}
</div>
)}
</div>
<div className="users-list-container" ref={kebabRef}>
<div className="user-profile">
{(!createdBy) ? userName ? userName?.charAt(0).toUpperCase() : "A" : createdBy?.userName?.charAt(0).toUpperCase()}
</div>
<button
className="kebab-wrapper"
onClick={(e) => {
e.stopPropagation();
setIsKebabOpen((prev) => !prev);
}}
>
<KebabIcon />
</button>
</div>
</div>
</div>
{isKebabOpen && (
<div className="kebab-options-wrapper">
{getOptions().map((option) => (
<button
key={option}
className="option"
onClick={(e) => {
e.stopPropagation();
handleOptionClick(option);
}}
>
{option}
</button>
))}
</div>
)} )}
</button> {createdAt && (
); <div className="project-data">
{active && active == "trash" ? `Trashed by you` : `Edited `}{" "}
{getRelativeTime(createdAt)}
</div>
)}
</div>
<div className="users-list-container" ref={kebabRef}>
<div className="user-profile">
{!createdBy
? userName
? userName?.charAt(0).toUpperCase()
: "A"
: createdBy?.userName?.charAt(0).toUpperCase()}
</div>
<button
className="kebab-wrapper"
onClick={(e) => {
e.stopPropagation();
setIsKebabOpen((prev) => !prev);
}}
>
<KebabIcon />
</button>
</div>
</div>
</div>
{isKebabOpen && (
<div className="kebab-options-wrapper">
{getOptions().map((option) => (
<button
key={option}
className="option"
onClick={(e) => {
e.stopPropagation();
handleOptionClick(option);
}}
>
{option}
</button>
))}
</div>
)}
</button>
);
}; };
export default DashboardCard; export default DashboardCard;

View File

@@ -13,10 +13,10 @@ interface Project {
_id: string; _id: string;
projectName: string; projectName: string;
thumbnail: string; thumbnail: string;
createdBy: { _id: string, userName: string }; createdBy: { _id: string; userName: string };
projectUuid?: string; projectUuid?: string;
createdAt: string; createdAt: string;
isViewed?: string isViewed?: string;
} }
interface RecentProjectsData { interface RecentProjectsData {
@@ -25,12 +25,12 @@ interface RecentProjectsData {
const DashboardHome: React.FC = () => { const DashboardHome: React.FC = () => {
const [recentProjects, setRecentProjects] = useState<RecentProjectsData>({}); const [recentProjects, setRecentProjects] = useState<RecentProjectsData>({});
const [isSearchActive, setIsSearchActive] = useState<boolean>(false); const [isSearchActive, setIsSearchActive] = useState<boolean>(false);
const { userId, organization } = getUserData(); const { userId, organization } = getUserData();
const { projectSocket } = useSocketStore(); const { projectSocket } = useSocketStore();
const [recentDuplicateData, setRecentDuplicateData] = useState<Object>({}); const [recentDuplicateData, setRecentDuplicateData] = useState<Object>({});
const fetchRecentProjects = async () => { const fetchRecentProjects = async () => {
try { try {
const projects = await recentlyViewed(organization, userId); const projects = await recentlyViewed(organization, userId);
@@ -38,9 +38,7 @@ const DashboardHome: React.FC = () => {
if (JSON.stringify(projects) !== JSON.stringify(recentProjects)) { if (JSON.stringify(projects) !== JSON.stringify(recentProjects)) {
setRecentProjects(projects); setRecentProjects(projects);
} }
} catch (error) { } catch (error) {}
console.error("Error fetching recent projects:", error);
}
}; };
const handleRecentProjectSearch = async (inputValue: string) => { const handleRecentProjectSearch = async (inputValue: string) => {
@@ -65,7 +63,7 @@ const DashboardHome: React.FC = () => {
// userId, // userId,
// organization // organization
// ); // );
// console.log('deletedProject: ', deletedProject); //
//socket for delete Project //socket for delete Project
const deleteProject = { const deleteProject = {
@@ -91,9 +89,7 @@ const DashboardHome: React.FC = () => {
}; };
}); });
setIsSearchActive(false); setIsSearchActive(false);
} catch (error) { } catch (error) {}
console.error("Error deleting project:", error);
}
}; };
const handleDuplicateRecentProject = async ( const handleDuplicateRecentProject = async (
@@ -163,4 +159,4 @@ const DashboardHome: React.FC = () => {
); );
}; };
export default DashboardHome; export default DashboardHome;

View File

@@ -9,6 +9,7 @@ import { deleteProject } from "../../services/dashboard/deleteProject";
import ProjectSocketRes from "./socket/projectSocketRes.dev"; import ProjectSocketRes from "./socket/projectSocketRes.dev";
import { sharedWithMeProjects } from "../../services/dashboard/sharedWithMeProject"; import { sharedWithMeProjects } from "../../services/dashboard/sharedWithMeProject";
import { duplicateProject } from "../../services/dashboard/duplicateProject"; import { duplicateProject } from "../../services/dashboard/duplicateProject";
import { generateUniqueId } from "../../functions/generateUniqueId";
interface Project { interface Project {
_id: string; _id: string;
@@ -27,7 +28,7 @@ const DashboardProjects: React.FC = () => {
const [workspaceProjects, setWorkspaceProjects] = useState<WorkspaceProjects>( const [workspaceProjects, setWorkspaceProjects] = useState<WorkspaceProjects>(
{} {}
); );
const [sharedwithMeProject, setSharedWithMeProjects] = useState<any>([]) const [sharedwithMeProject, setSharedWithMeProjects] = useState<any>([]);
const [projectDuplicateData, setProjectDuplicateData] = useState<Object>({}); const [projectDuplicateData, setProjectDuplicateData] = useState<Object>({});
const [isSearchActive, setIsSearchActive] = useState<boolean>(false); const [isSearchActive, setIsSearchActive] = useState<boolean>(false);
const [activeFolder, setActiveFolder] = useState<string>("myProjects"); const [activeFolder, setActiveFolder] = useState<string>("myProjects");
@@ -41,7 +42,11 @@ const DashboardProjects: React.FC = () => {
} }
if (!setWorkspaceProjects || !setIsSearchActive) return; if (!setWorkspaceProjects || !setIsSearchActive) return;
const searchedProject = await searchProject(organization, userId, inputValue); const searchedProject = await searchProject(
organization,
userId,
inputValue
);
setIsSearchActive(true); setIsSearchActive(true);
setWorkspaceProjects(searchedProject.message ? {} : searchedProject); setWorkspaceProjects(searchedProject.message ? {} : searchedProject);
}; };
@@ -49,14 +54,13 @@ const DashboardProjects: React.FC = () => {
const fetchAllProjects = async () => { const fetchAllProjects = async () => {
try { try {
const projects = await getAllProjects(userId, organization); const projects = await getAllProjects(userId, organization);
if (!projects || !projects.Projects) return; if (!projects || !projects.Projects) return;
if (JSON.stringify(projects) !== JSON.stringify(workspaceProjects)) { if (JSON.stringify(projects) !== JSON.stringify(workspaceProjects)) {
setWorkspaceProjects(projects); setWorkspaceProjects(projects);
} }
} catch (error) { } catch (error) {}
console.error("Error fetching projects:", error);
}
}; };
const handleDeleteProject = async (projectId: any) => { const handleDeleteProject = async (projectId: any) => {
@@ -66,7 +70,7 @@ const DashboardProjects: React.FC = () => {
// userId, // userId,
// organization // organization
// ); // );
// console.log('deletedProject: ', deletedProject); //
const deleteProjects = { const deleteProjects = {
projectId, projectId,
organization, organization,
@@ -77,7 +81,6 @@ const DashboardProjects: React.FC = () => {
if (projectSocket) { if (projectSocket) {
projectSocket.emit("v1:project:delete", deleteProjects); projectSocket.emit("v1:project:delete", deleteProjects);
} else { } else {
console.error("Socket is not connected.");
} }
setWorkspaceProjects((prevDiscardedProjects: WorkspaceProjects) => { setWorkspaceProjects((prevDiscardedProjects: WorkspaceProjects) => {
if (!Array.isArray(prevDiscardedProjects?.Projects)) { if (!Array.isArray(prevDiscardedProjects?.Projects)) {
@@ -92,16 +95,21 @@ const DashboardProjects: React.FC = () => {
}; };
}); });
setIsSearchActive(false); setIsSearchActive(false);
} catch (error) { } catch (error) {}
console.error("Error deleting project:", error);
}
}; };
const handleDuplicateWorkspaceProject = async ( const handleDuplicateWorkspaceProject = async (
projectId: string, projectId: string,
projectName: string, projectName: string,
thumbnail: string, thumbnail: string
) => { ) => {
const duplicatedProject = await duplicateProject(
projectId,
generateUniqueId(),
thumbnail,
projectName
);
console.log("duplicatedProject: ", duplicatedProject);
const duplicateProjectData = { const duplicateProjectData = {
userId, userId,
@@ -110,7 +118,7 @@ const DashboardProjects: React.FC = () => {
projectUuid: projectId, projectUuid: projectId,
projectName, projectName,
}; };
projectSocket.emit("v1:project:Duplicate", duplicateProjectData); // projectSocket.emit("v1:project:Duplicate", duplicateProjectData);
}; };
const renderProjects = () => { const renderProjects = () => {
@@ -153,16 +161,12 @@ const DashboardProjects: React.FC = () => {
)); ));
}; };
const sharedProject = async () => { const sharedProject = async () => {
try { try {
const sharedWithMe = await sharedWithMeProjects(); const sharedWithMe = await sharedWithMeProjects();
setSharedWithMeProjects(sharedWithMe) setSharedWithMeProjects(sharedWithMe);
} catch { } catch {}
};
}
}
useEffect(() => { useEffect(() => {
if (!isSearchActive) { if (!isSearchActive) {
@@ -172,10 +176,9 @@ const DashboardProjects: React.FC = () => {
useEffect(() => { useEffect(() => {
if (activeFolder === "shared") { if (activeFolder === "shared") {
sharedProject() sharedProject();
} }
}, [activeFolder]) }, [activeFolder]);
return ( return (
<div className="dashboard-home-container"> <div className="dashboard-home-container">
@@ -184,7 +187,10 @@ const DashboardProjects: React.FC = () => {
handleProjectsSearch={handleProjectsSearch} handleProjectsSearch={handleProjectsSearch}
/> />
<div className="dashboard-container" style={{ height: "calc(100% - 87px)" }}> <div
className="dashboard-container"
style={{ height: "calc(100% - 87px)" }}
>
<div className="header-wrapper" style={{ display: "flex", gap: "7px" }}> <div className="header-wrapper" style={{ display: "flex", gap: "7px" }}>
<button <button
className={`header ${activeFolder === "myProjects" && "active"}`} className={`header ${activeFolder === "myProjects" && "active"}`}
@@ -199,19 +205,22 @@ const DashboardProjects: React.FC = () => {
Shared with me Shared with me
</button> </button>
</div> </div>
<div className="cards-container">{activeFolder == "myProjects" ? renderProjects() : renderSharedProjects()}</div> <div className="cards-container">
{activeFolder == "myProjects"
? renderProjects()
: renderSharedProjects()}
</div>
{projectDuplicateData && Object.keys(projectDuplicateData).length > 0 && ( {projectDuplicateData &&
<ProjectSocketRes Object.keys(projectDuplicateData).length > 0 && (
setIsSearchActive={setIsSearchActive} <ProjectSocketRes
setWorkspaceProjects={setWorkspaceProjects} setIsSearchActive={setIsSearchActive}
/> setWorkspaceProjects={setWorkspaceProjects}
)} />
)}
</div> </div>
</div> </div>
); );
}; };
export default DashboardProjects; export default DashboardProjects;

View File

@@ -10,100 +10,101 @@ import { updateProject } from "../../services/dashboard/updateProject";
import { getUserData } from "../../functions/getUserData"; import { getUserData } from "../../functions/getUserData";
const FileMenu: React.FC = () => { const FileMenu: React.FC = () => {
const [openMenu, setOpenMenu] = useState(false); const [openMenu, setOpenMenu] = useState(false);
const containerRef = useRef<HTMLButtonElement>(null); const containerRef = useRef<HTMLButtonElement>(null);
let clickTimeout: NodeJS.Timeout | null = null; let clickTimeout: NodeJS.Timeout | null = null;
const { projectName, setProjectName } = useProjectName(); const { projectName, setProjectName } = useProjectName();
const { dashBoardSocket } = useSocketStore(); const { dashBoardSocket } = useSocketStore();
const { projectId } = useParams(); const { projectId } = useParams();
const { userId, organization, email } = getUserData(); const { userId, organization, email } = getUserData();
const handleClick = () => { const handleClick = () => {
if (clickTimeout) return; if (clickTimeout) return;
setOpenMenu((prev) => !prev); setOpenMenu((prev) => !prev);
clickTimeout = setTimeout(() => { clickTimeout = setTimeout(() => {
clickTimeout = null; clickTimeout = null;
}, 800); }, 800);
};
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (
containerRef.current &&
!containerRef.current.contains(event.target as Node)
) {
setOpenMenu(false);
}
}; };
useEffect(() => { document.addEventListener("mousedown", handleClickOutside);
const handleClickOutside = (event: MouseEvent) => { return () => document.removeEventListener("mousedown", handleClickOutside);
if ( }, []);
containerRef.current &&
!containerRef.current.contains(event.target as Node)
) {
setOpenMenu(false);
}
};
document.addEventListener("mousedown", handleClickOutside); const handleProjectRename = async (projectName: string) => {
return () => document.removeEventListener("mousedown", handleClickOutside); setProjectName(projectName);
}, []); if (!projectId) return;
const handleProjectRename = async (projectName: string) => { // localStorage.setItem("projectName", newName);
setProjectName(projectName);
if (!projectId) return
// localStorage.setItem("projectName", newName); try {
if (!email || !userId) return;
try { const projects = await getAllProjects(userId, organization);
if (!projects || !projects.Projects) return;
// console.log('projects: ', projects);
let projectUuid = projects.Projects.find(
(val: any) => val.projectUuid === projectId || val._id === projectId
);
if (!email || !userId) return; const updateProjects = {
projectId: projectUuid?._id,
organization,
userId,
projectName,
thumbnail: undefined,
};
const projects = await getAllProjects(userId, organization); // if (dashBoardSocket) {
if (!projects || !projects.Projects) return; // const handleResponse = (data: any) => {
// console.log('projects: ', projects); // console.log('Project update response:', data);
let projectUuid = projects.Projects.find((val: any) => val.projectUuid === projectId || val._id === projectId) // dashBoardSocket.off("v1-project:response:update", handleResponse); // Clean up
// };
// dashBoardSocket.on("v1-project:response:update", handleResponse);
// dashBoardSocket.emit("v1:project:update", updateProjects);
// }
const updateProjects = { //API for projects rename
projectId: projectUuid, const updatedProjectName = await updateProject(
organization, projectId,
userId, userId,
projectName, organization,
thumbnail: undefined undefined,
} projectName
);
// if (dashBoardSocket) { console.log("updatedProjectName: ", updatedProjectName, projectId);
// const handleResponse = (data: any) => { } catch (error) {
// console.log('Project update response:', data); console.error("Error updating project name:", error);
// dashBoardSocket.off("v1-project:response:update", handleResponse); // Clean up }
// }; };
// dashBoardSocket.on("v1-project:response:update", handleResponse); return (
// dashBoardSocket.emit("v1:project:update", updateProjects); <button
// } id="project-dropdown-button"
className="project-dropdowm-container"
//API for projects rename ref={containerRef}
const updatedProjectName = await updateProject( onClick={handleClick}
projectId, >
userId, <div className="project-name">
organization, <div className="icon">
undefined, <ProjectIcon />
projectName </div>
); <RenameInput value={projectName} onRename={handleProjectRename} />
// </div>
} catch (error) { <div className="more-options-button">
console.error("Error updating project name:", error); <ArrowIcon />
} {openMenu && <MenuBar setOpenMenu={setOpenMenu} />}
}; </div>
return ( </button>
<button );
id="project-dropdown-button"
className="project-dropdowm-container"
ref={containerRef}
onClick={handleClick}
>
<div className="project-name">
<div className="icon">
<ProjectIcon />
</div>
<RenameInput value={projectName} onRename={handleProjectRename} />
</div>
<div className="more-options-button">
<ArrowIcon />
{openMenu && <MenuBar setOpenMenu={setOpenMenu} />}
</div>
</button>
);
}; };
export default FileMenu; export default FileMenu;

View File

@@ -16,69 +16,89 @@ import { useLoadingProgress, useSocketStore } from "../../store/builder/store";
import { Color, SRGBColorSpace } from "three"; import { Color, SRGBColorSpace } from "three";
import StatsHelper from "./helpers/StatsHelper"; import StatsHelper from "./helpers/StatsHelper";
export default function Scene({ layout }: { readonly layout: 'Main Layout' | 'Comparison Layout' }) { export default function Scene({
const map = useMemo(() => [ layout,
{ name: "forward", keys: ["ArrowUp", "w", "W"] }, }: {
{ name: "backward", keys: ["ArrowDown", "s", "S"] }, readonly layout: "Main Layout" | "Comparison Layout";
{ name: "left", keys: ["ArrowLeft", "a", "A"] }, }) {
{ name: "right", keys: ["ArrowRight", "d", "D"] }, const map = useMemo(
], []); () => [
const { assetStore } = useSceneContext(); { name: "forward", keys: ["ArrowUp", "w", "W"] },
const { assets } = assetStore(); { name: "backward", keys: ["ArrowDown", "s", "S"] },
const { userId, organization } = getUserData(); { name: "left", keys: ["ArrowLeft", "a", "A"] },
const { projectId } = useParams(); { name: "right", keys: ["ArrowRight", "d", "D"] },
const { projectSocket } = useSocketStore(); ],
const { activeModule } = useModuleStore(); []
const { loadingProgress } = useLoadingProgress(); );
const { assetStore } = useSceneContext();
const { assets } = assetStore();
const { userId, organization } = getUserData();
const { projectId } = useParams();
const { projectSocket } = useSocketStore();
const { activeModule } = useModuleStore();
const { loadingProgress } = useLoadingProgress();
useEffect(() => { useEffect(() => {
if (!projectId && loadingProgress > 1) return; if (!projectId && loadingProgress > 1) return;
getAllProjects(userId, organization) getAllProjects(userId, organization)
.then((projects) => { .then((projects) => {
if (!projects || !projects.Projects) return; if (!projects || !projects.Projects) return;
let project = projects.Projects.find((val: any) => val.projectUuid === projectId || val._id === projectId); let project = projects.Projects.find(
const canvas = document.getElementById("sceneCanvas")?.getElementsByTagName('canvas')[0]; (val: any) => val.projectUuid === projectId || val._id === projectId
if (!canvas) return; );
const screenshotDataUrl = (canvas as HTMLCanvasElement)?.toDataURL("image/png"); const canvas = document
const updateProjects = { .getElementById("sceneCanvas")
projectId: project?.projectUuid, ?.getElementsByTagName("canvas")[0];
organization, if (!canvas) return;
userId, const screenshotDataUrl = (canvas as HTMLCanvasElement)?.toDataURL(
projectName: project?.projectName, "image/png"
thumbnail: screenshotDataUrl, );
}; const updateProjects = {
if (projectSocket) { projectId: project?._id,
projectSocket.emit("v1:project:update", updateProjects); organization,
} userId,
}).catch((err) => { projectName: project?.projectName,
console.error(err); thumbnail: screenshotDataUrl,
}); };
// eslint-disable-next-line if (projectSocket) {
}, [activeModule, assets, loadingProgress]) projectSocket.emit("v1:project:update", updateProjects);
}
})
.catch((err) => {
console.error(err);
});
// eslint-disable-next-line
}, [activeModule, assets, loadingProgress]);
return ( return (
<KeyboardControls map={map}> <KeyboardControls map={map}>
<Canvas <Canvas
id="sceneCanvas" id="sceneCanvas"
shadows shadows
color="#aaaa" color="#aaaa"
eventPrefix="client" eventPrefix="client"
onContextMenu={(e) => { onContextMenu={(e) => {
e.preventDefault(); e.preventDefault();
}} }}
performance={{ min: 0.9, max: 1.0 }} performance={{ min: 0.9, max: 1.0 }}
onCreated={(e) => { onCreated={(e) => {
e.scene.background = layout === 'Main Layout' ? null : new Color(0x19191d); e.scene.background =
}} layout === "Main Layout" ? null : new Color(0x19191d);
gl={{ outputColorSpace: SRGBColorSpace, powerPreference: "high-performance", antialias: true, preserveDrawingBuffer: true }} }}
> gl={{
<Setup /> outputColorSpace: SRGBColorSpace,
<Collaboration /> powerPreference: "high-performance",
<Builder /> antialias: true,
<Simulation /> preserveDrawingBuffer: true,
<Visualization /> }}
<StatsHelper /> >
</Canvas> <Setup />
</KeyboardControls> <Collaboration />
); <Builder />
<Simulation />
<Visualization />
<StatsHelper />
</Canvas>
</KeyboardControls>
);
} }

View File

@@ -33,7 +33,8 @@ const Project: React.FC = () => {
const { setUserName } = useUserName(); const { setUserName } = useUserName();
const { setOrganization } = useOrganization(); const { setOrganization } = useOrganization();
const { projectId } = useParams(); const { projectId } = useParams();
const { setProjectName } = useProjectName(); const { projectName, setProjectName } = useProjectName();
console.log("projectName: ", projectName);
const { userId, email, organization, userName } = getUserData(); const { userId, email, organization, userName } = getUserData();
const { selectedUser } = useSelectedUserStore(); const { selectedUser } = useSelectedUserStore();
const { isLogListVisible } = useLogger(); const { isLogListVisible } = useLogger();
@@ -49,6 +50,7 @@ const Project: React.FC = () => {
const fetchProjects = async () => { const fetchProjects = async () => {
try { try {
const projects = await getAllProjects(userId, organization); const projects = await getAllProjects(userId, organization);
console.log("projects: ", projects);
const shared = await sharedWithMeProjects(); const shared = await sharedWithMeProjects();
const allProjects = [...(projects?.Projects || []), ...(shared || [])]; const allProjects = [...(projects?.Projects || []), ...(shared || [])];
@@ -56,7 +58,6 @@ const Project: React.FC = () => {
const matchedProject = allProjects.find( const matchedProject = allProjects.find(
(val: any) => val.projectUuid === projectId || val._id === projectId (val: any) => val.projectUuid === projectId || val._id === projectId
); );
if (matchedProject) { if (matchedProject) {
setProjectName(matchedProject.projectName); setProjectName(matchedProject.projectName);
await viewProject(organization, matchedProject._id, userId); await viewProject(organization, matchedProject._id, userId);

View File

@@ -1,6 +1,7 @@
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
export const duplicateProject = async ( export const duplicateProject = async (
refProjectID: string,
projectUuid: string, projectUuid: string,
thumbnail: string, thumbnail: string,
projectName: string projectName: string
@@ -16,7 +17,12 @@ export const duplicateProject = async (
token: localStorage.getItem("token") || "", // Coerce null to empty string token: localStorage.getItem("token") || "", // Coerce null to empty string
refresh_token: localStorage.getItem("refreshToken") || "", refresh_token: localStorage.getItem("refreshToken") || "",
}, },
body: JSON.stringify({ projectUuid, thumbnail, projectName }), body: JSON.stringify({
refProjectID,
projectUuid,
thumbnail,
projectName,
}),
} }
); );
const newAccessToken = response.headers.get("x-access-token"); const newAccessToken = response.headers.get("x-access-token");
@@ -24,7 +30,7 @@ export const duplicateProject = async (
//console.log("New token received:", newAccessToken); //console.log("New token received:", newAccessToken);
localStorage.setItem("token", newAccessToken); localStorage.setItem("token", newAccessToken);
} }
// console.log("response: ", response); console.log("response: ", response);
if (!response.ok) { if (!response.ok) {
console.error("Failed to add project"); console.error("Failed to add project");
} }

View File

@@ -30,6 +30,7 @@ export const updateProject = async (
body: JSON.stringify(body), body: JSON.stringify(body),
} }
); );
console.log('body: ', body);
const newAccessToken = response.headers.get("x-access-token"); const newAccessToken = response.headers.get("x-access-token");
if (newAccessToken) { if (newAccessToken) {
@@ -41,6 +42,7 @@ export const updateProject = async (
} }
const result = await response.json(); const result = await response.json();
console.log('result: ', result);
return result; return result;
} catch (error) { } catch (error) {
if (error instanceof Error) { if (error instanceof Error) {

File diff suppressed because it is too large Load Diff