Merge remote-tracking branch 'origin/dev-collaboration' into dev-r3f-wall
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import React, { useState, useRef, useEffect } from "react";
|
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";
|
||||||
@@ -14,13 +14,15 @@ interface DashBoardCardProps {
|
|||||||
projectId: string;
|
projectId: string;
|
||||||
createdAt?: string;
|
createdAt?: string;
|
||||||
isViewed?: string;
|
isViewed?: 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
|
||||||
) => Promise<void>;
|
) => Promise<void>;
|
||||||
handleDuplicateRecentProject?: (
|
handleDuplicateRecentProject?: (
|
||||||
projectId: string,
|
projectId: string,
|
||||||
@@ -31,6 +33,7 @@ interface DashBoardCardProps {
|
|||||||
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>>;
|
||||||
}
|
}
|
||||||
type RelativeTimeFormatUnit = any;
|
type RelativeTimeFormatUnit = any;
|
||||||
|
|
||||||
@@ -45,8 +48,10 @@ const DashboardCard: React.FC<DashBoardCardProps> = ({
|
|||||||
handleDuplicateWorkspaceProject,
|
handleDuplicateWorkspaceProject,
|
||||||
handleDuplicateRecentProject,
|
handleDuplicateRecentProject,
|
||||||
createdAt,
|
createdAt,
|
||||||
|
createdBy,
|
||||||
setRecentDuplicateData,
|
setRecentDuplicateData,
|
||||||
setProjectDuplicateData,
|
setProjectDuplicateData,
|
||||||
|
setActiveFolder
|
||||||
}) => {
|
}) => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { setProjectName } = useProjectName();
|
const { setProjectName } = useProjectName();
|
||||||
@@ -59,10 +64,18 @@ const DashboardCard: React.FC<DashBoardCardProps> = ({
|
|||||||
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;
|
||||||
setLoadingProgress(1)
|
try {
|
||||||
setProjectName(projectName);
|
const viewProjects = await viewProject(organization, projectId, userId)
|
||||||
navigate(`/${projectId}`);
|
console.log('viewProjects: ', viewProjects);
|
||||||
|
console.log('projectName: ', projectName);
|
||||||
|
setLoadingProgress(1)
|
||||||
|
setProjectName(projectName);
|
||||||
|
navigate(`/${projectId}`);
|
||||||
|
} catch {
|
||||||
|
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleOptionClick = async (option: string) => {
|
const handleOptionClick = async (option: string) => {
|
||||||
@@ -81,11 +94,18 @@ const DashboardCard: React.FC<DashBoardCardProps> = ({
|
|||||||
break;
|
break;
|
||||||
case "open in new tab":
|
case "open in new tab":
|
||||||
try {
|
try {
|
||||||
await viewProject(organization, projectId, userId);
|
if (active === "shared" && createdBy) {
|
||||||
setProjectName(projectName);
|
console.log("ihreq");
|
||||||
setIsKebabOpen(false);
|
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) {
|
} catch (error) {
|
||||||
console.error("Error opening project in new tab:", error);
|
|
||||||
}
|
}
|
||||||
window.open(`/${projectId}`, "_blank");
|
window.open(`/${projectId}`, "_blank");
|
||||||
break;
|
break;
|
||||||
@@ -100,13 +120,17 @@ const DashboardCard: React.FC<DashBoardCardProps> = ({
|
|||||||
projectName,
|
projectName,
|
||||||
thumbnail,
|
thumbnail,
|
||||||
});
|
});
|
||||||
await handleDuplicateWorkspaceProject(projectId, projectName, thumbnail);
|
await handleDuplicateWorkspaceProject(projectId, projectName, thumbnail, userId);
|
||||||
|
if (active === "shared" && setActiveFolder) {
|
||||||
|
setActiveFolder("myProjects")
|
||||||
|
}
|
||||||
} else if (handleDuplicateRecentProject) {
|
} else if (handleDuplicateRecentProject) {
|
||||||
setRecentDuplicateData &&
|
setRecentDuplicateData &&
|
||||||
setRecentDuplicateData({
|
setRecentDuplicateData({
|
||||||
projectId,
|
projectId,
|
||||||
projectName,
|
projectName,
|
||||||
thumbnail,
|
thumbnail,
|
||||||
|
userId
|
||||||
});
|
});
|
||||||
await handleDuplicateRecentProject(projectId, projectName, thumbnail);
|
await handleDuplicateRecentProject(projectId, projectName, thumbnail);
|
||||||
}
|
}
|
||||||
@@ -128,7 +152,6 @@ const DashboardCard: React.FC<DashBoardCardProps> = ({
|
|||||||
try {
|
try {
|
||||||
const projects = await getAllProjects(userId, organization);
|
const projects = await getAllProjects(userId, organization);
|
||||||
if (!projects || !projects.Projects) return;
|
if (!projects || !projects.Projects) return;
|
||||||
// console.log("projects: ", projects);
|
|
||||||
let projectUuid = projects.Projects.find(
|
let projectUuid = projects.Projects.find(
|
||||||
(val: any) => val.projectUuid === projectId || val._id === projectId
|
(val: any) => val.projectUuid === projectId || val._id === projectId
|
||||||
);
|
);
|
||||||
@@ -173,6 +196,19 @@ const DashboardCard: React.FC<DashBoardCardProps> = ({
|
|||||||
return "just now";
|
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 (
|
return (
|
||||||
<button
|
<button
|
||||||
className="dashboard-card-container"
|
className="dashboard-card-container"
|
||||||
@@ -212,13 +248,12 @@ const DashboardCard: React.FC<DashBoardCardProps> = ({
|
|||||||
<div className="project-data">
|
<div className="project-data">
|
||||||
{active && active == "trash" ? `Trashed by you` : `Edited `}{" "}
|
{active && active == "trash" ? `Trashed by you` : `Edited `}{" "}
|
||||||
{getRelativeTime(createdAt)}
|
{getRelativeTime(createdAt)}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="users-list-container" ref={kebabRef}>
|
<div className="users-list-container" ref={kebabRef}>
|
||||||
<div className="user-profile">
|
<div className="user-profile">
|
||||||
{userName ? userName.charAt(0).toUpperCase() : "A"}
|
{(!createdBy) ? userName ? userName?.charAt(0).toUpperCase() : "A" : createdBy?.userName?.charAt(0).toUpperCase()}
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
className="kebab-wrapper"
|
className="kebab-wrapper"
|
||||||
@@ -232,29 +267,9 @@ const DashboardCard: React.FC<DashBoardCardProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{isKebabOpen && (
|
||||||
{isKebabOpen && active !== "trash" && (
|
|
||||||
<div className="kebab-options-wrapper">
|
<div className="kebab-options-wrapper">
|
||||||
{["rename", "delete", "duplicate", "open in new tab"].map(
|
{getOptions().map((option) => (
|
||||||
(option) => (
|
|
||||||
<button
|
|
||||||
key={option}
|
|
||||||
className="option"
|
|
||||||
title={""}
|
|
||||||
onClick={(e) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
handleOptionClick(option);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{option}
|
|
||||||
</button>
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{isKebabOpen && active && active == "trash" && (
|
|
||||||
<div className="kebab-options-wrapper">
|
|
||||||
{["restore", "delete"].map((option) => (
|
|
||||||
<button
|
<button
|
||||||
key={option}
|
key={option}
|
||||||
className="option"
|
className="option"
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ interface Project {
|
|||||||
_id: string;
|
_id: string;
|
||||||
projectName: string;
|
projectName: string;
|
||||||
thumbnail: string;
|
thumbnail: string;
|
||||||
createdBy: string;
|
createdBy: { _id: string, userName: string };
|
||||||
projectUuid?: string;
|
projectUuid?: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
isViewed?: string
|
isViewed?: string
|
||||||
@@ -34,7 +34,8 @@ const DashboardHome: React.FC = () => {
|
|||||||
const fetchRecentProjects = async () => {
|
const fetchRecentProjects = async () => {
|
||||||
try {
|
try {
|
||||||
const projects = await recentlyViewed(organization, userId);
|
const projects = await recentlyViewed(organization, userId);
|
||||||
|
console.log('projects: ', projects);
|
||||||
|
|
||||||
if (JSON.stringify(projects) !== JSON.stringify(recentProjects)) {
|
if (JSON.stringify(projects) !== JSON.stringify(recentProjects)) {
|
||||||
setRecentProjects(projects);
|
setRecentProjects(projects);
|
||||||
}
|
}
|
||||||
@@ -126,7 +127,8 @@ const DashboardHome: React.FC = () => {
|
|||||||
projectName={project.projectName}
|
projectName={project.projectName}
|
||||||
thumbnail={project.thumbnail}
|
thumbnail={project.thumbnail}
|
||||||
projectId={project._id}
|
projectId={project._id}
|
||||||
createdAt={project.isViewed}
|
createdBy={project.createdBy}
|
||||||
|
createdAt={project.createdAt}
|
||||||
handleDeleteProject={handleDeleteProject}
|
handleDeleteProject={handleDeleteProject}
|
||||||
handleDuplicateRecentProject={handleDuplicateRecentProject}
|
handleDuplicateRecentProject={handleDuplicateRecentProject}
|
||||||
setRecentDuplicateData={setRecentDuplicateData}
|
setRecentDuplicateData={setRecentDuplicateData}
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import { getAllProjects } from "../../services/dashboard/getAllProjects";
|
|||||||
import { searchProject } from "../../services/dashboard/searchProjects";
|
import { searchProject } from "../../services/dashboard/searchProjects";
|
||||||
import { deleteProject } from "../../services/dashboard/deleteProject";
|
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 { duplicateProject } from "../../services/dashboard/duplicateProject";
|
||||||
|
|
||||||
interface Project {
|
interface Project {
|
||||||
_id: string;
|
_id: string;
|
||||||
@@ -25,12 +27,25 @@ const DashboardProjects: React.FC = () => {
|
|||||||
const [workspaceProjects, setWorkspaceProjects] = useState<WorkspaceProjects>(
|
const [workspaceProjects, setWorkspaceProjects] = useState<WorkspaceProjects>(
|
||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
|
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");
|
||||||
const { projectSocket } = useSocketStore();
|
const { projectSocket } = useSocketStore();
|
||||||
const { userId, organization } = getUserData();
|
const { userId, organization } = getUserData();
|
||||||
|
|
||||||
|
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 fetchAllProjects = async () => {
|
const fetchAllProjects = async () => {
|
||||||
try {
|
try {
|
||||||
const projects = await getAllProjects(userId, organization);
|
const projects = await getAllProjects(userId, organization);
|
||||||
@@ -44,18 +59,6 @@ const DashboardProjects: React.FC = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
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) => {
|
const handleDeleteProject = async (projectId: any) => {
|
||||||
try {
|
try {
|
||||||
// const deletedProject = await deleteProject(
|
// const deletedProject = await deleteProject(
|
||||||
@@ -97,18 +100,9 @@ const DashboardProjects: React.FC = () => {
|
|||||||
const handleDuplicateWorkspaceProject = async (
|
const handleDuplicateWorkspaceProject = async (
|
||||||
projectId: string,
|
projectId: string,
|
||||||
projectName: string,
|
projectName: string,
|
||||||
thumbnail: string
|
thumbnail: string,
|
||||||
) => {
|
) => {
|
||||||
// await handleDuplicateProjects({
|
|
||||||
// userId,
|
|
||||||
// organization,
|
|
||||||
// projectId,
|
|
||||||
// projectName,
|
|
||||||
// projectSocket,
|
|
||||||
// thumbnail,
|
|
||||||
// setWorkspaceProjects,
|
|
||||||
// setIsSearchActive
|
|
||||||
// });
|
|
||||||
const duplicateProjectData = {
|
const duplicateProjectData = {
|
||||||
userId,
|
userId,
|
||||||
thumbnail,
|
thumbnail,
|
||||||
@@ -121,9 +115,7 @@ const DashboardProjects: React.FC = () => {
|
|||||||
|
|
||||||
const renderProjects = () => {
|
const renderProjects = () => {
|
||||||
if (activeFolder !== "myProjects") return null;
|
if (activeFolder !== "myProjects") return null;
|
||||||
|
|
||||||
const projectList = workspaceProjects[Object.keys(workspaceProjects)[0]];
|
const projectList = workspaceProjects[Object.keys(workspaceProjects)[0]];
|
||||||
|
|
||||||
if (!projectList?.length) {
|
if (!projectList?.length) {
|
||||||
return <div className="empty-state">No projects found</div>;
|
return <div className="empty-state">No projects found</div>;
|
||||||
}
|
}
|
||||||
@@ -143,12 +135,49 @@ const DashboardProjects: React.FC = () => {
|
|||||||
));
|
));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const renderSharedProjects = () => {
|
||||||
|
return sharedwithMeProject?.map((project: any) => (
|
||||||
|
<DashboardCard
|
||||||
|
key={project._id}
|
||||||
|
projectName={project.projectName}
|
||||||
|
thumbnail={project.thumbnail}
|
||||||
|
projectId={project._id}
|
||||||
|
createdAt={project.createdAt}
|
||||||
|
setIsSearchActive={setIsSearchActive}
|
||||||
|
active="shared"
|
||||||
|
createdBy={project.createdBy}
|
||||||
|
setProjectDuplicateData={setProjectDuplicateData}
|
||||||
|
handleDuplicateWorkspaceProject={handleDuplicateWorkspaceProject}
|
||||||
|
setActiveFolder={setActiveFolder}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const sharedProject = async () => {
|
||||||
|
try {
|
||||||
|
const sharedWithMe = await sharedWithMeProjects();
|
||||||
|
console.log('sharedWithMe: ', sharedWithMe);
|
||||||
|
setSharedWithMeProjects(sharedWithMe)
|
||||||
|
} catch {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isSearchActive) {
|
if (!isSearchActive) {
|
||||||
fetchAllProjects();
|
fetchAllProjects();
|
||||||
}
|
}
|
||||||
}, [isSearchActive]);
|
}, [isSearchActive]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (activeFolder === "shared") {
|
||||||
|
sharedProject()
|
||||||
|
}
|
||||||
|
}, [activeFolder])
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="dashboard-home-container">
|
<div className="dashboard-home-container">
|
||||||
<DashboardNavBar
|
<DashboardNavBar
|
||||||
@@ -171,7 +200,8 @@ const DashboardProjects: React.FC = () => {
|
|||||||
Shared with me
|
Shared with me
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div className="cards-container">{renderProjects()}</div>
|
<div className="cards-container">{activeFolder == "myProjects" ? renderProjects() : renderSharedProjects()}</div>
|
||||||
|
|
||||||
{projectDuplicateData && Object.keys(projectDuplicateData).length > 0 && (
|
{projectDuplicateData && Object.keys(projectDuplicateData).length > 0 && (
|
||||||
<ProjectSocketRes
|
<ProjectSocketRes
|
||||||
setIsSearchActive={setIsSearchActive}
|
setIsSearchActive={setIsSearchActive}
|
||||||
@@ -183,4 +213,142 @@ const DashboardProjects: React.FC = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default DashboardProjects;
|
export default DashboardProjects;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// const MyProjects = () => {
|
||||||
|
// const { projectSocket } = useSocketStore();
|
||||||
|
// const { userId, organization } = getUserData();
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
// const handleDeleteProject = async (projectId: any) => {
|
||||||
|
// try {
|
||||||
|
// // const deletedProject = await deleteProject(
|
||||||
|
// // projectId,
|
||||||
|
// // userId,
|
||||||
|
// // organization
|
||||||
|
// // );
|
||||||
|
// // console.log('deletedProject: ', deletedProject);
|
||||||
|
// const deleteProjects = {
|
||||||
|
// projectId,
|
||||||
|
// 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 <div className="empty-state">No projects found</div>;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return projectList.map((project) => (
|
||||||
|
// <DashboardCard
|
||||||
|
// key={project._id}
|
||||||
|
// projectName={project.projectName}
|
||||||
|
// thumbnail={project.thumbnail}
|
||||||
|
// projectId={project._id}
|
||||||
|
// createdAt={project.createdAt}
|
||||||
|
// handleDeleteProject={handleDeleteProject}
|
||||||
|
// setIsSearchActive={setIsSearchActive}
|
||||||
|
// handleDuplicateWorkspaceProject={handleDuplicateWorkspaceProject}
|
||||||
|
// setProjectDuplicateData={setProjectDuplicateData}
|
||||||
|
// />
|
||||||
|
// ));
|
||||||
|
// };
|
||||||
|
|
||||||
|
// const renderSharedProjects = () => {
|
||||||
|
// if (activeFolder !== "shared") return null;
|
||||||
|
|
||||||
|
// const projectList = workspaceProjects[Object.keys(workspaceProjects)[0]];
|
||||||
|
|
||||||
|
// if (!projectList?.length) {
|
||||||
|
// return <div className="empty-state">No projects found</div>;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return projectList.map((project) => (
|
||||||
|
// <DashboardCard
|
||||||
|
// key={project._id}
|
||||||
|
// projectName={project.projectName}
|
||||||
|
// thumbnail={project.thumbnail}
|
||||||
|
// projectId={project._id}
|
||||||
|
// createdAt={project.createdAt}
|
||||||
|
// handleDeleteProject={handleDeleteProject}
|
||||||
|
// setIsSearchActive={setIsSearchActive}
|
||||||
|
// handleDuplicateWorkspaceProject={handleDuplicateWorkspaceProject}
|
||||||
|
// setProjectDuplicateData={setProjectDuplicateData}
|
||||||
|
// />
|
||||||
|
// ));
|
||||||
|
// };
|
||||||
|
|
||||||
|
// useEffect(() => {
|
||||||
|
// if (!isSearchActive) {
|
||||||
|
// fetchAllProjects();
|
||||||
|
// }
|
||||||
|
// }, [isSearchActive]);
|
||||||
|
|
||||||
|
// return null
|
||||||
|
// }
|
||||||
@@ -14,12 +14,24 @@ interface Project {
|
|||||||
createdAt: string;
|
createdAt: string;
|
||||||
isViewed?: string
|
isViewed?: string
|
||||||
}
|
}
|
||||||
|
interface RecentProject {
|
||||||
|
_id: string;
|
||||||
|
projectName: string;
|
||||||
|
thumbnail: string;
|
||||||
|
createdBy: { _id: string, userName: string };
|
||||||
|
projectUuid?: string;
|
||||||
|
createdAt: string;
|
||||||
|
isViewed?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RecentProjectData {
|
||||||
|
[key: string]: RecentProject[];
|
||||||
|
}
|
||||||
interface ProjectsData {
|
interface ProjectsData {
|
||||||
[key: string]: Project[];
|
[key: string]: Project[];
|
||||||
}
|
}
|
||||||
interface ProjectSocketResProps {
|
interface ProjectSocketResProps {
|
||||||
setRecentProjects?: React.Dispatch<React.SetStateAction<ProjectsData>>;
|
setRecentProjects?: React.Dispatch<React.SetStateAction<RecentProjectData>>;
|
||||||
setWorkspaceProjects?: React.Dispatch<React.SetStateAction<ProjectsData>>;
|
setWorkspaceProjects?: React.Dispatch<React.SetStateAction<ProjectsData>>;
|
||||||
setIsSearchActive?: React.Dispatch<React.SetStateAction<boolean>>;
|
setIsSearchActive?: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
}
|
}
|
||||||
@@ -52,7 +64,7 @@ const ProjectSocketRes = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleDuplicate = async (data: any) => {
|
const handleDuplicate = async (data: any) => {
|
||||||
// console.log("Project duplicate response:", data);
|
console.log("Project duplicate response:", data);
|
||||||
if (data?.message === "Project Duplicated successfully") {
|
if (data?.message === "Project Duplicated successfully") {
|
||||||
if (setWorkspaceProjects) {
|
if (setWorkspaceProjects) {
|
||||||
const allProjects = await getAllProjects(userId, organization);
|
const allProjects = await getAllProjects(userId, organization);
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import { RemoveIcon } from "../../../icons/ExportCommonIcons";
|
|||||||
import PositionInput from "../customInput/PositionInputs";
|
import PositionInput from "../customInput/PositionInputs";
|
||||||
import RotationInput from "../customInput/RotationInput";
|
import RotationInput from "../customInput/RotationInput";
|
||||||
import { useSelectedFloorItem, useObjectPosition, useObjectRotation } from "../../../../store/builder/store";
|
import { useSelectedFloorItem, useObjectPosition, useObjectRotation } from "../../../../store/builder/store";
|
||||||
|
import { useSceneContext } from "../../../../modules/scene/sceneContext";
|
||||||
|
import { center } from "@turf/turf";
|
||||||
|
|
||||||
interface UserData {
|
interface UserData {
|
||||||
id: number; // Unique identifier for the user data
|
id: number; // Unique identifier for the user data
|
||||||
@@ -18,6 +20,10 @@ const AssetProperties: React.FC = () => {
|
|||||||
const { selectedFloorItem } = useSelectedFloorItem();
|
const { selectedFloorItem } = useSelectedFloorItem();
|
||||||
const { objectPosition } = useObjectPosition();
|
const { objectPosition } = useObjectPosition();
|
||||||
const { objectRotation } = useObjectRotation();
|
const { objectRotation } = useObjectRotation();
|
||||||
|
const { assetStore } = useSceneContext();
|
||||||
|
const { assets, setCurrentAnimation } = assetStore()
|
||||||
|
const [hoveredIndex, setHoveredIndex] = useState<any>(null);
|
||||||
|
const [isPlaying, setIsplaying] = useState(false);
|
||||||
// Function to handle adding new user data
|
// Function to handle adding new user data
|
||||||
const handleAddUserData = () => {
|
const handleAddUserData = () => {
|
||||||
const newUserData: UserData = {
|
const newUserData: UserData = {
|
||||||
@@ -45,6 +51,12 @@ const AssetProperties: React.FC = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleAnimationClick = (animation: string) => {
|
||||||
|
if (selectedFloorItem) {
|
||||||
|
const isPlaying = selectedFloorItem.animationState?.playing || false;
|
||||||
|
setCurrentAnimation(selectedFloorItem.uuid, animation, !isPlaying);
|
||||||
|
}
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<div className="asset-properties-container">
|
<div className="asset-properties-container">
|
||||||
{/* Name */}
|
{/* Name */}
|
||||||
@@ -96,6 +108,40 @@ const AssetProperties: React.FC = () => {
|
|||||||
+ Add
|
+ Add
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
<div style={{ display: "flex", flexDirection: "column", outline: "1px solid var(--border-color)" }}>
|
||||||
|
{selectedFloorItem.uuid && <div style={{ display: "flex", alignItems: "center", justifyContent: "center" }}>Animations</div>}
|
||||||
|
{assets.map((asset) => (
|
||||||
|
<div key={asset.modelUuid} className="asset-item">
|
||||||
|
{asset.modelUuid === selectedFloorItem.uuid &&
|
||||||
|
asset.animations &&
|
||||||
|
asset.animations.length > 0 &&
|
||||||
|
asset.animations.map((animation, index) => (
|
||||||
|
<div
|
||||||
|
key={index}
|
||||||
|
style={{ gap: "15px", cursor: "pointer", padding: "5px" }}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
onClick={() => handleAnimationClick(animation)}
|
||||||
|
onMouseEnter={() => setHoveredIndex(index)}
|
||||||
|
onMouseLeave={() => setHoveredIndex(null)}
|
||||||
|
style={{
|
||||||
|
height: "20px",
|
||||||
|
width: "100%",
|
||||||
|
borderRadius: "5px",
|
||||||
|
background:
|
||||||
|
hoveredIndex === index
|
||||||
|
? "#7b4cd3"
|
||||||
|
: "transparent",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{animation.charAt(0).toUpperCase() +
|
||||||
|
animation.slice(1).toLowerCase()}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,15 +7,23 @@ import { access } from "fs";
|
|||||||
import MultiEmailInvite from "../ui/inputs/MultiEmailInvite";
|
import MultiEmailInvite from "../ui/inputs/MultiEmailInvite";
|
||||||
import { useActiveUsers } from "../../store/builder/store";
|
import { useActiveUsers } from "../../store/builder/store";
|
||||||
import { getUserData } from "../../functions/getUserData";
|
import { getUserData } from "../../functions/getUserData";
|
||||||
|
import { getProjectSharedList } from "../../services/factoryBuilder/collab/getProjectSharedList";
|
||||||
|
import { useParams } from "react-router-dom";
|
||||||
|
import { projection } from "@turf/turf";
|
||||||
|
import { shareAccess } from "../../services/factoryBuilder/collab/shareAccess";
|
||||||
|
import { getAvatarColor } from "../../modules/collaboration/functions/getAvatarColor";
|
||||||
|
|
||||||
interface UserListTemplateProps {
|
interface UserListTemplateProps {
|
||||||
user: User;
|
user: User;
|
||||||
}
|
}
|
||||||
|
|
||||||
const UserListTemplate: React.FC<UserListTemplateProps> = ({ user }) => {
|
const UserListTemplate: React.FC<UserListTemplateProps> = ({ user }) => {
|
||||||
const [accessSelection, setAccessSelection] = useState<string>(user.access);
|
const [accessSelection, setAccessSelection] = useState<string>(user?.Access);
|
||||||
|
const { projectId } = useParams();
|
||||||
|
|
||||||
function accessUpdate({ option }: AccessOption) {
|
const accessUpdate = async ({ option }: AccessOption) => {
|
||||||
|
if (!projectId) return
|
||||||
|
const accessSelection = await shareAccess(projectId, user.userId, option)
|
||||||
setAccessSelection(option);
|
setAccessSelection(option);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,18 +34,22 @@ const UserListTemplate: React.FC<UserListTemplateProps> = ({ user }) => {
|
|||||||
{user.profileImage ? (
|
{user.profileImage ? (
|
||||||
<img
|
<img
|
||||||
src={user.profileImage || "https://via.placeholder.com/150"}
|
src={user.profileImage || "https://via.placeholder.com/150"}
|
||||||
alt={`${user.name}'s profile`}
|
alt={`${user.
|
||||||
|
userName}'s profile`}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div
|
<div
|
||||||
className="no-profile-container"
|
className="no-profile-container"
|
||||||
style={{ background: user.color }}
|
style={{ background: getAvatarColor(1, user.userId) }}
|
||||||
>
|
>
|
||||||
{user.name[0]}
|
{user.
|
||||||
|
userName.charAt(0).toUpperCase()}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="user-name">{user.name}</div>
|
<div className="user-name"> {user.
|
||||||
|
userName.charAt(0).toUpperCase() + user.
|
||||||
|
userName.slice(1).toLowerCase()}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="user-access">
|
<div className="user-access">
|
||||||
<RegularDropDown
|
<RegularDropDown
|
||||||
@@ -61,39 +73,27 @@ const CollaborationPopup: React.FC<CollaborateProps> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const { activeUsers } = useActiveUsers();
|
const { activeUsers } = useActiveUsers();
|
||||||
const { userName } = getUserData();
|
const { userName } = getUserData();
|
||||||
|
const [users, setUsers] = useState([])
|
||||||
|
const { projectId } = useParams();
|
||||||
|
|
||||||
|
function getData() {
|
||||||
|
if (!projectId) return;
|
||||||
|
getProjectSharedList(projectId).then((allUser) => {
|
||||||
|
const accesMail = allUser?.datas || []
|
||||||
|
console.log('accesMail: ', accesMail);
|
||||||
|
setUsers(accesMail)
|
||||||
|
}).catch((err) => {
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// console.log("activeUsers: ", activeUsers);
|
getData();
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
//
|
||||||
}, [activeUsers]);
|
}, [activeUsers]);
|
||||||
const users = [
|
|
||||||
{
|
|
||||||
name: "Alice Johnson",
|
|
||||||
email: "alice.johnson@example.com",
|
|
||||||
profileImage: "",
|
|
||||||
color: "#FF6600",
|
|
||||||
access: "Admin",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Bob Smith",
|
|
||||||
email: "bob.smith@example.com",
|
|
||||||
profileImage: "",
|
|
||||||
color: "#488EF6",
|
|
||||||
access: "Viewer",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Charlie Brown",
|
|
||||||
email: "charlie.brown@example.com",
|
|
||||||
profileImage: "",
|
|
||||||
color: "#48AC2A",
|
|
||||||
access: "Viewer",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Diana Prince",
|
|
||||||
email: "diana.prince@example.com",
|
|
||||||
profileImage: "",
|
|
||||||
color: "#D44242",
|
|
||||||
access: "Viewer",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
return (
|
return (
|
||||||
<RenderOverlay>
|
<RenderOverlay>
|
||||||
<div
|
<div
|
||||||
@@ -112,7 +112,7 @@ const CollaborationPopup: React.FC<CollaborateProps> = ({
|
|||||||
<div className="header">
|
<div className="header">
|
||||||
<div className="content">Share this file</div>
|
<div className="content">Share this file</div>
|
||||||
<div className="content">
|
<div className="content">
|
||||||
<div className="copy-link-button">copy link</div>
|
{/* <div className="copy-link-button">copy link</div> */}
|
||||||
<div
|
<div
|
||||||
className="close-button"
|
className="close-button"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@@ -124,7 +124,7 @@ const CollaborationPopup: React.FC<CollaborateProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="invite-input-container">
|
<div className="invite-input-container">
|
||||||
<MultiEmailInvite />
|
<MultiEmailInvite users={users} getData={getData} />
|
||||||
</div>
|
</div>
|
||||||
<div className="split"></div>
|
<div className="split"></div>
|
||||||
<section>
|
<section>
|
||||||
@@ -142,7 +142,7 @@ const CollaborationPopup: React.FC<CollaborateProps> = ({
|
|||||||
<div className="you-container">
|
<div className="you-container">
|
||||||
<div className="your-name">
|
<div className="your-name">
|
||||||
<div className="user-profile">{userName && userName[0].toUpperCase()}</div>
|
<div className="user-profile">{userName && userName[0].toUpperCase()}</div>
|
||||||
{userName}
|
{userName && userName.charAt(0).toUpperCase() + userName.slice(1).toLowerCase()}
|
||||||
</div>
|
</div>
|
||||||
<div className="indicater">you</div>
|
<div className="indicater">you</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import { useProjectName } from "../../store/builder/store";
|
|||||||
import { getAllProjects } from "../../services/dashboard/getAllProjects";
|
import { getAllProjects } from "../../services/dashboard/getAllProjects";
|
||||||
import { useComparisonProduct } from "../../store/simulation/useSimulationStore";
|
import { useComparisonProduct } from "../../store/simulation/useSimulationStore";
|
||||||
import { getUserData } from "../../functions/getUserData";
|
import { getUserData } from "../../functions/getUserData";
|
||||||
|
import { recentlyViewed } from "../../services/dashboard/recentlyViewed";
|
||||||
|
import { sharedWithMeProjects } from "../../services/dashboard/sharedWithMeProject";
|
||||||
|
|
||||||
interface LoadingPageProps {
|
interface LoadingPageProps {
|
||||||
progress: number; // Expect progress as a percentage (0-100)
|
progress: number; // Expect progress as a percentage (0-100)
|
||||||
@@ -19,18 +21,51 @@ const LoadingPage: React.FC<LoadingPageProps> = ({ progress }) => {
|
|||||||
|
|
||||||
const validatedProgress = Math.min(100, Math.max(0, progress));
|
const validatedProgress = Math.min(100, Math.max(0, progress));
|
||||||
|
|
||||||
useEffect(() => {
|
// useEffect(() => {
|
||||||
if (!userId) return;
|
// if (!userId) return;
|
||||||
|
|
||||||
getAllProjects(userId, organization).then((projects) => {
|
|
||||||
if (!projects || !projects.Projects) return;
|
// // getAllProjects(userId, organization).then((projects) => {
|
||||||
const filterProject = projects?.Projects.find((val: any) => val.projectUuid === projectId || val._id === projectId);
|
// // sharedWithMeProjects().then((shared) => {
|
||||||
if (filterProject) {
|
// // console.log('filterProject: ', shared);
|
||||||
setProjectName(filterProject.projectName);
|
// // const filterProject = (projects?.Projects || shared)?.find((val: any) => val.projectUuid === projectId || val._id === projectId)
|
||||||
|
// // console.log('filterProject: ', filterProject);
|
||||||
|
// // // setProjectName(filterProject?.projectName)
|
||||||
|
|
||||||
|
// // })
|
||||||
|
// // }).catch(() => {
|
||||||
|
// // console.error("Error fetching projects")
|
||||||
|
// // })
|
||||||
|
// }, []);
|
||||||
|
useEffect(() => {
|
||||||
|
if (!userId) {
|
||||||
|
console.error("User data not found in localStorage");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchProjects = async () => {
|
||||||
|
try {
|
||||||
|
const projects = await getAllProjects(userId, organization);
|
||||||
|
const shared = await sharedWithMeProjects();
|
||||||
|
|
||||||
|
const allProjects = [...(projects?.Projects || []), ...(shared || [])];
|
||||||
|
|
||||||
|
const matchedProject = allProjects.find(
|
||||||
|
(val: any) => val.projectUuid === projectId || val._id === projectId
|
||||||
|
);
|
||||||
|
|
||||||
|
if (matchedProject) {
|
||||||
|
setProjectName(matchedProject.projectName);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
console.warn("Project not found with given ID:", projectId);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching projects:", error);
|
||||||
}
|
}
|
||||||
}).catch((error) => {
|
};
|
||||||
console.log(error);
|
|
||||||
})
|
fetchProjects();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,72 +1,135 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
|
import { getSearchUsers } from "../../../services/factoryBuilder/collab/getSearchUsers";
|
||||||
|
import { useParams } from "react-router-dom";
|
||||||
|
import { shareProject } from "../../../services/factoryBuilder/collab/shareProject";
|
||||||
|
import { getUserData } from "../../../functions/getUserData";
|
||||||
|
|
||||||
const MultiEmailInvite: React.FC = () => {
|
interface UserData {
|
||||||
const [emails, setEmails] = useState<string[]>([]);
|
_id: string;
|
||||||
|
Email: string;
|
||||||
|
userName: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MultiEmailProps {
|
||||||
|
users: any,
|
||||||
|
getData: any,
|
||||||
|
}
|
||||||
|
const MultiEmailInvite: React.FC<MultiEmailProps> = ({ users, getData }) => {
|
||||||
|
const [emails, setEmails] = useState<any>([]);
|
||||||
|
const [searchedEmail, setSearchedEmail] = useState<UserData[]>([]);
|
||||||
|
const [inputFocus, setInputFocus] = useState(false);
|
||||||
const [inputValue, setInputValue] = useState("");
|
const [inputValue, setInputValue] = useState("");
|
||||||
|
const { projectId } = useParams();
|
||||||
|
const { userId } = getUserData();
|
||||||
|
|
||||||
const handleAddEmail = () => {
|
const handleAddEmail = async (selectedUser: UserData) => {
|
||||||
|
if (!projectId || !selectedUser) return
|
||||||
const trimmedEmail = inputValue.trim();
|
const trimmedEmail = inputValue.trim();
|
||||||
|
setEmails((prev: any[]) => {
|
||||||
|
if (!selectedUser) return prev;
|
||||||
|
const isNotCurrentUser = selectedUser._id !== userId;
|
||||||
|
const alreadyExistsInEmails = prev.some(email => email._id === selectedUser._id);
|
||||||
|
const alreadyExistsInUsers = users.some((val: any) => val.userId === selectedUser._id);
|
||||||
|
if (isNotCurrentUser && !alreadyExistsInEmails && !alreadyExistsInUsers) {
|
||||||
|
return [...prev, selectedUser];
|
||||||
|
}
|
||||||
|
|
||||||
// Validate email
|
return prev;
|
||||||
if (!trimmedEmail || !validateEmail(trimmedEmail)) {
|
});
|
||||||
alert("Please enter a valid email address.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for duplicates
|
|
||||||
if (emails.includes(trimmedEmail)) {
|
|
||||||
alert("This email has already been added.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add email to the list
|
|
||||||
setEmails([...emails, trimmedEmail]);
|
|
||||||
setInputValue(""); // Clear the input field after adding
|
setInputValue(""); // Clear the input field after adding
|
||||||
};
|
};
|
||||||
|
const handleSearchMail = async (e: any) => {
|
||||||
|
setInputValue(e.target.value);
|
||||||
|
const trimmedEmail = e.target.value.trim();
|
||||||
|
|
||||||
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
if (trimmedEmail.length < 3) return;
|
||||||
if (e.key === "Enter" || e.key === ",") {
|
try {
|
||||||
e.preventDefault();
|
const searchedMail = await getSearchUsers(trimmedEmail);
|
||||||
handleAddEmail();
|
const filteredEmail = searchedMail.sharchMail?.filtered;
|
||||||
|
if (filteredEmail) {
|
||||||
|
setSearchedEmail(filteredEmail)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to search mail:", error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleRemoveEmail = (emailToRemove: string) => {
|
|
||||||
setEmails(emails.filter((email) => email !== emailToRemove));
|
const handleKeyDown = async (e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||||
|
if (e.key === "Enter" || e.key === "," && searchedEmail.length > 0) {
|
||||||
|
e.preventDefault();
|
||||||
|
handleAddEmail(searchedEmail[0]);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleRemoveEmail = (idToRemove: string) => {
|
||||||
|
setEmails((prev: any) => prev.filter((email: any) => email._id !== idToRemove));
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
const validateEmail = (email: string) => {
|
const validateEmail = (email: string) => {
|
||||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||||
return emailRegex.test(email);
|
return emailRegex.test(email);
|
||||||
};
|
};
|
||||||
|
|
||||||
const [inputFocus, setInputFocus] = useState(false);
|
const handleInvite = () => {
|
||||||
|
if (!projectId) return;
|
||||||
|
try {
|
||||||
|
emails.forEach((user: any) => {
|
||||||
|
shareProject(user._id, projectId)
|
||||||
|
.then((res) => {
|
||||||
|
console.log("sharedProject:", res);
|
||||||
|
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error("Error sharing project:", err);
|
||||||
|
});
|
||||||
|
setEmails([])
|
||||||
|
setInputValue("")
|
||||||
|
});
|
||||||
|
setTimeout(() => {
|
||||||
|
getData()
|
||||||
|
}, 1000);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("General error:", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="multi-email-invite-input-container">
|
<div className="multi-email-invite-input-container">
|
||||||
<div className={`multi-email-invite-input${inputFocus ? " active" : ""}`}>
|
<div className={`multi-email-invite-input${inputFocus ? " active" : ""}`}>
|
||||||
{emails.map((email, index) => (
|
{emails.map((email: any, index: number) => (
|
||||||
<div key={index} className="entered-emails">
|
<div key={index} className="entered-emails">
|
||||||
{email}
|
{email.Email}
|
||||||
<span onClick={() => handleRemoveEmail(email)}>×</span>
|
<span onClick={() => handleRemoveEmail(email._id)}>×</span>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={inputValue}
|
value={inputValue}
|
||||||
onChange={(e) => setInputValue(e.target.value)}
|
onChange={(e) => handleSearchMail(e)}
|
||||||
onFocus={() => setInputFocus(true)}
|
onFocus={() => setInputFocus(true)}
|
||||||
onBlur={() => setInputFocus(false)}
|
// onBlur={() => setInputFocus(false)}
|
||||||
onKeyDown={handleKeyDown}
|
onKeyDown={handleKeyDown}
|
||||||
placeholder="Enter email and press Enter or comma to seperate"
|
placeholder="Enter email and press Enter or comma to seperate"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div onClick={handleAddEmail} className="invite-button">
|
<div onClick={handleInvite} className="invite-button">
|
||||||
Invite
|
Add
|
||||||
</div>
|
|
||||||
<div className="users-list-container">
|
|
||||||
{/* list available users */}
|
|
||||||
</div>
|
</div>
|
||||||
|
{inputFocus && inputValue.length > 2 && searchedEmail && searchedEmail.length > 0 && (
|
||||||
|
<div className="users-list-container">
|
||||||
|
{/* list available users here */}
|
||||||
|
{searchedEmail.map((val: any, i: any) => (
|
||||||
|
<div onClick={(e) => {
|
||||||
|
handleAddEmail(val)
|
||||||
|
setInputFocus(false)
|
||||||
|
}} key={i} >
|
||||||
|
{val?.Email}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader';
|
|||||||
import { ThreeEvent, useFrame, useThree } from '@react-three/fiber';
|
import { ThreeEvent, useFrame, useThree } from '@react-three/fiber';
|
||||||
import { useActiveTool, useDeletableFloorItem, useLimitDistance, useRenderDistance, useSelectedFloorItem, useSocketStore, useToggleView, useToolMode } from '../../../../../store/builder/store';
|
import { useActiveTool, useDeletableFloorItem, useLimitDistance, useRenderDistance, useSelectedFloorItem, useSocketStore, useToggleView, useToolMode } from '../../../../../store/builder/store';
|
||||||
import { AssetBoundingBox } from '../../functions/assetBoundingBox';
|
import { AssetBoundingBox } from '../../functions/assetBoundingBox';
|
||||||
import { CameraControls } from '@react-three/drei';
|
import { CameraControls, Html } from '@react-three/drei';
|
||||||
import useModuleStore, { useSubModuleStore } from '../../../../../store/useModuleStore';
|
import useModuleStore, { useSubModuleStore } from '../../../../../store/useModuleStore';
|
||||||
import { useLeftData, useTopData } from '../../../../../store/visualization/useZone3DWidgetStore';
|
import { useLeftData, useTopData } from '../../../../../store/visualization/useZone3DWidgetStore';
|
||||||
import { useSelectedAsset } from '../../../../../store/simulation/useSimulationStore';
|
import { useSelectedAsset } from '../../../../../store/simulation/useSimulationStore';
|
||||||
@@ -23,7 +23,7 @@ function Model({ asset }: { readonly asset: Asset }) {
|
|||||||
const { subModule } = useSubModuleStore();
|
const { subModule } = useSubModuleStore();
|
||||||
const { activeModule } = useModuleStore();
|
const { activeModule } = useModuleStore();
|
||||||
const { assetStore, eventStore, productStore } = useSceneContext();
|
const { assetStore, eventStore, productStore } = useSceneContext();
|
||||||
const { removeAsset } = assetStore();
|
const { assets, removeAsset, setAnimations } = assetStore();
|
||||||
const { setTop } = useTopData();
|
const { setTop } = useTopData();
|
||||||
const { setLeft } = useLeftData();
|
const { setLeft } = useLeftData();
|
||||||
const { getIsEventInProduct } = productStore();
|
const { getIsEventInProduct } = productStore();
|
||||||
@@ -46,6 +46,9 @@ function Model({ asset }: { readonly asset: Asset }) {
|
|||||||
const { selectedVersion } = selectedVersionStore();
|
const { selectedVersion } = selectedVersionStore();
|
||||||
const { projectId } = useParams();
|
const { projectId } = useParams();
|
||||||
const { userId, organization } = getUserData();
|
const { userId, organization } = getUserData();
|
||||||
|
const [animationNames, setAnimationNames] = useState<string[]>([]);
|
||||||
|
const mixerRef = useRef<THREE.AnimationMixer>();
|
||||||
|
const actions = useRef<{ [name: string]: THREE.AnimationAction }>({});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setDeletableFloorItem(null);
|
setDeletableFloorItem(null);
|
||||||
@@ -60,11 +63,45 @@ function Model({ asset }: { readonly asset: Asset }) {
|
|||||||
const loadModel = async () => {
|
const loadModel = async () => {
|
||||||
try {
|
try {
|
||||||
// Check Cache
|
// Check Cache
|
||||||
|
// const assetId = asset.assetId;
|
||||||
|
// const cachedModel = THREE.Cache.get(assetId);
|
||||||
|
// if (cachedModel) {
|
||||||
|
// setGltfScene(cachedModel.scene.clone());
|
||||||
|
// calculateBoundingBox(cachedModel.scene);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Check Cache
|
||||||
|
// const assetId = asset.assetId;
|
||||||
|
// console.log('assetId: ', assetId);
|
||||||
|
// const cachedModel = THREE.Cache.get(assetId);
|
||||||
|
// console.log('cachedModel: ', cachedModel);
|
||||||
|
// if (cachedModel) {
|
||||||
|
// setGltfScene(cachedModel.scene.clone());
|
||||||
|
// calculateBoundingBox(cachedModel.scene);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
const assetId = asset.assetId;
|
const assetId = asset.assetId;
|
||||||
const cachedModel = THREE.Cache.get(assetId);
|
const cachedModel = THREE.Cache.get(assetId);
|
||||||
if (cachedModel) {
|
if (cachedModel) {
|
||||||
setGltfScene(cachedModel.scene.clone());
|
const clonedScene = cachedModel.scene.clone();
|
||||||
calculateBoundingBox(cachedModel.scene);
|
clonedScene.animations = cachedModel.animations || [];
|
||||||
|
setGltfScene(clonedScene);
|
||||||
|
calculateBoundingBox(clonedScene);
|
||||||
|
if (cachedModel.animations && clonedScene.animations.length > 0) {
|
||||||
|
const animationName = clonedScene.animations.map((clip: any) => clip.name);
|
||||||
|
setAnimationNames(animationName)
|
||||||
|
setAnimations(asset.modelUuid, animationName)
|
||||||
|
mixerRef.current = new THREE.AnimationMixer(clonedScene);
|
||||||
|
|
||||||
|
clonedScene.animations.forEach((animation: any) => {
|
||||||
|
const action = mixerRef.current!.clipAction(animation);
|
||||||
|
actions.current[animation.name] = action;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.log('No animations');
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -259,6 +296,32 @@ function Model({ asset }: { readonly asset: Asset }) {
|
|||||||
clearSelectedAsset()
|
clearSelectedAsset()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
useFrame((_, delta) => {
|
||||||
|
if (mixerRef.current) {
|
||||||
|
mixerRef.current.update(delta);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handlePlay = (clipName: string) => {
|
||||||
|
console.log('clipName: ', clipName, asset.animationState);
|
||||||
|
if (!mixerRef.current) return;
|
||||||
|
|
||||||
|
Object.values(actions.current).forEach((action) => action.stop());
|
||||||
|
|
||||||
|
const action = actions.current[clipName];
|
||||||
|
if (action && asset.animationState?.playing) {
|
||||||
|
action.reset().setLoop(THREE.LoopOnce, 1).play();
|
||||||
|
console.log(`Playing: ${clipName}`);
|
||||||
|
} else {
|
||||||
|
console.warn(`No action found for: ${clipName}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
handlePlay(asset.animationState?.current || '');
|
||||||
|
|
||||||
|
}, [asset])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<group
|
<group
|
||||||
@@ -306,7 +369,18 @@ function Model({ asset }: { readonly asset: Asset }) {
|
|||||||
<AssetBoundingBox boundingBox={boundingBox} />
|
<AssetBoundingBox boundingBox={boundingBox} />
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
</group>
|
{/* <group >
|
||||||
|
<Html>
|
||||||
|
<div style={{ position: 'absolute', }}>
|
||||||
|
{animationNames.map((name) => (
|
||||||
|
<button key={name} onClick={() => handlePlay(name)} style={{ margin: 4 }}>
|
||||||
|
{name}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</Html>
|
||||||
|
</group> */}
|
||||||
|
</group >
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -40,10 +40,10 @@ export default function Scene({ layout }: { readonly layout: 'Main Layout' | 'Co
|
|||||||
if (!canvas) return;
|
if (!canvas) return;
|
||||||
const screenshotDataUrl = (canvas as HTMLCanvasElement)?.toDataURL("image/png");
|
const screenshotDataUrl = (canvas as HTMLCanvasElement)?.toDataURL("image/png");
|
||||||
const updateProjects = {
|
const updateProjects = {
|
||||||
projectId: project.projectUuid,
|
projectId: project?.projectUuid,
|
||||||
organization,
|
organization,
|
||||||
userId,
|
userId,
|
||||||
projectName: project.projectName,
|
projectName: project?.projectName,
|
||||||
thumbnail: screenshotDataUrl,
|
thumbnail: screenshotDataUrl,
|
||||||
};
|
};
|
||||||
if (projectSocket) {
|
if (projectSocket) {
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ import { SceneProvider } from "../modules/scene/sceneContext";
|
|||||||
import { getVersionHistoryApi } from "../services/factoryBuilder/versionControl/getVersionHistoryApi";
|
import { getVersionHistoryApi } from "../services/factoryBuilder/versionControl/getVersionHistoryApi";
|
||||||
import { useVersionHistoryStore } from "../store/builder/useVersionHistoryStore";
|
import { useVersionHistoryStore } from "../store/builder/useVersionHistoryStore";
|
||||||
import { VersionProvider } from "../modules/builder/version/versionContext";
|
import { VersionProvider } from "../modules/builder/version/versionContext";
|
||||||
|
import { recentlyViewed } from "../services/dashboard/recentlyViewed";
|
||||||
|
import { sharedWithMeProjects } from "../services/dashboard/sharedWithMeProject";
|
||||||
|
|
||||||
const Project: React.FC = () => {
|
const Project: React.FC = () => {
|
||||||
let navigate = useNavigate();
|
let navigate = useNavigate();
|
||||||
@@ -54,18 +56,32 @@ const Project: React.FC = () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
getAllProjects(userId, organization).then((projects) => {
|
const fetchProjects = async () => {
|
||||||
if (!projects || !projects.Projects) return;
|
try {
|
||||||
const filterProject = projects?.Projects.find((val: any) => val.projectUuid === projectId || val._id === projectId)
|
const projects = await getAllProjects(userId, organization);
|
||||||
setProjectName(filterProject.projectName)
|
const shared = await sharedWithMeProjects();
|
||||||
viewProject(organization, filterProject._id, userId).then((viewedProject) => {
|
|
||||||
});
|
|
||||||
}).catch(() => {
|
|
||||||
console.error("Error fetching projects")
|
|
||||||
})
|
|
||||||
|
|
||||||
|
const allProjects = [...(projects?.Projects || []), ...(shared || [])];
|
||||||
|
|
||||||
|
const matchedProject = allProjects.find(
|
||||||
|
(val: any) => val.projectUuid === projectId || val._id === projectId
|
||||||
|
);
|
||||||
|
|
||||||
|
if (matchedProject) {
|
||||||
|
setProjectName(matchedProject.projectName);
|
||||||
|
await viewProject(organization, matchedProject._id, userId);
|
||||||
|
} else {
|
||||||
|
console.warn("Project not found with given ID:", projectId);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching projects:", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fetchProjects();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!projectId) return;
|
if (!projectId) return;
|
||||||
getVersionHistoryApi(projectId).then((data) => {
|
getVersionHistoryApi(projectId).then((data) => {
|
||||||
|
|||||||
30
app/src/services/dashboard/sharedWithMeProject.ts
Normal file
30
app/src/services/dashboard/sharedWithMeProject.ts
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
|
||||||
|
|
||||||
|
export const sharedWithMeProjects = async (
|
||||||
|
) => {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${url_Backend_dwinzo}/api/V1/sharedWithMe`, {
|
||||||
|
method: "GET",
|
||||||
|
headers: {
|
||||||
|
Authorization: "Bearer <access_token>",
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
token: localStorage.getItem("token") || "",
|
||||||
|
refresh_token: localStorage.getItem("refreshToken") || "",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const newAccessToken = response.headers.get("x-access-token");
|
||||||
|
if (newAccessToken) {
|
||||||
|
//console.log("New token received:", newAccessToken);
|
||||||
|
localStorage.setItem("token", newAccessToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error("Failed to fetch assets");
|
||||||
|
}
|
||||||
|
|
||||||
|
return await response.json();
|
||||||
|
} catch (error: any) {
|
||||||
|
echo.error("Failed to get asset image");
|
||||||
|
console.log(error.message);
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
|
||||||
|
|
||||||
|
export const getProjectSharedList = async (projectId: string) => {
|
||||||
|
try {
|
||||||
|
const response = await fetch(
|
||||||
|
`${url_Backend_dwinzo}/api/V1/projectsharedList?projectId=${projectId}`,
|
||||||
|
{
|
||||||
|
method: "GET",
|
||||||
|
headers: {
|
||||||
|
Authorization: "Bearer <access_token>",
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
token: localStorage.getItem("token") || "",
|
||||||
|
refresh_token: localStorage.getItem("refreshToken") || "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error("Failed to get users");
|
||||||
|
}
|
||||||
|
|
||||||
|
return await response.json();
|
||||||
|
} catch (error: any) {
|
||||||
|
echo.error("Failed to get users");
|
||||||
|
console.log(error.message);
|
||||||
|
}
|
||||||
|
};
|
||||||
27
app/src/services/factoryBuilder/collab/getSearchUsers.ts
Normal file
27
app/src/services/factoryBuilder/collab/getSearchUsers.ts
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
|
||||||
|
|
||||||
|
export const getSearchUsers = async (searchMail: string) => {
|
||||||
|
try {
|
||||||
|
const response = await fetch(
|
||||||
|
`${url_Backend_dwinzo}/api/V1/searchMail?searchMail=${searchMail}`,
|
||||||
|
{
|
||||||
|
method: "GET",
|
||||||
|
headers: {
|
||||||
|
Authorization: "Bearer <access_token>",
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
token: localStorage.getItem("token") || "",
|
||||||
|
refresh_token: localStorage.getItem("refreshToken") || "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error("Failed to get users");
|
||||||
|
}
|
||||||
|
|
||||||
|
return await response.json();
|
||||||
|
} catch (error: any) {
|
||||||
|
echo.error("Failed to get users");
|
||||||
|
console.log(error.message);
|
||||||
|
}
|
||||||
|
};
|
||||||
43
app/src/services/factoryBuilder/collab/shareAccess.ts
Normal file
43
app/src/services/factoryBuilder/collab/shareAccess.ts
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
|
||||||
|
// let url_Backend_dwinzo = `http://192.168.0.102:5000`;
|
||||||
|
|
||||||
|
export const shareAccess = async (
|
||||||
|
projectId: string,
|
||||||
|
targetUserId: string,
|
||||||
|
newAccessPoint: string
|
||||||
|
) => {
|
||||||
|
const body: any = {
|
||||||
|
projectId,
|
||||||
|
targetUserId,
|
||||||
|
newAccessPoint
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(
|
||||||
|
`${url_Backend_dwinzo}/api/V1/sharedAccespoint`,
|
||||||
|
{
|
||||||
|
method: "PATCH",
|
||||||
|
headers: {
|
||||||
|
Authorization: "Bearer <access_token>",
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
token: localStorage.getItem("token") || "",
|
||||||
|
refresh_token: localStorage.getItem("refreshToken") || "",
|
||||||
|
},
|
||||||
|
body: JSON.stringify(body),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
console.error("Failed to clearPanel in the zone");
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
return result;
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof Error) {
|
||||||
|
console.error(error.message);
|
||||||
|
} else {
|
||||||
|
console.error("An unknown error occurred");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
30
app/src/services/factoryBuilder/collab/shareProject.ts
Normal file
30
app/src/services/factoryBuilder/collab/shareProject.ts
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
|
||||||
|
|
||||||
|
export const shareProject = async (addUserId: string, projectId: string) => {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${url_Backend_dwinzo}/api/V1/projectshared`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
Authorization: "Bearer <access_token>",
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
token: localStorage.getItem("token") || "",
|
||||||
|
refresh_token: localStorage.getItem("refreshToken") || "",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ addUserId, projectId }),
|
||||||
|
});
|
||||||
|
if (!response.ok) {
|
||||||
|
console.error("Failed to add project");
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
console.log("result: ", result);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof Error) {
|
||||||
|
console.log(error.message);
|
||||||
|
} else {
|
||||||
|
console.log("An unknown error occurred");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -21,7 +21,7 @@ interface AssetsStore {
|
|||||||
setOpacity: (modelUuid: string, opacity: number) => void;
|
setOpacity: (modelUuid: string, opacity: number) => void;
|
||||||
|
|
||||||
// Animation controls
|
// Animation controls
|
||||||
setAnimation: (modelUuid: string, animation: string) => void;
|
setAnimations: (modelUuid: string, animations: string[]) => void;
|
||||||
setCurrentAnimation: (modelUuid: string, current: string, isPlaying: boolean) => void;
|
setCurrentAnimation: (modelUuid: string, current: string, isPlaying: boolean) => void;
|
||||||
addAnimation: (modelUuid: string, animation: string) => void;
|
addAnimation: (modelUuid: string, animation: string) => void;
|
||||||
removeAnimation: (modelUuid: string, animation: string) => void;
|
removeAnimation: (modelUuid: string, animation: string) => void;
|
||||||
@@ -143,14 +143,13 @@ export const createAssetStore = () => {
|
|||||||
},
|
},
|
||||||
|
|
||||||
// Animation controls
|
// Animation controls
|
||||||
setAnimation: (modelUuid, animation) => {
|
setAnimations: (modelUuid, animations) => {
|
||||||
set((state) => {
|
set((state) => {
|
||||||
const asset = state.assets.find(a => a.modelUuid === modelUuid);
|
const asset = state.assets.find(a => a.modelUuid === modelUuid);
|
||||||
if (asset) {
|
if (asset) {
|
||||||
|
asset.animations = animations;
|
||||||
if (!asset.animationState) {
|
if (!asset.animationState) {
|
||||||
asset.animationState = { current: animation, playing: false };
|
asset.animationState = { current: '', playing: false };
|
||||||
} else {
|
|
||||||
asset.animationState.current = animation;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -46,7 +46,6 @@ textarea {
|
|||||||
}
|
}
|
||||||
|
|
||||||
input[type="number"] {
|
input[type="number"] {
|
||||||
|
|
||||||
// Chrome, Safari, Edge, Opera
|
// Chrome, Safari, Edge, Opera
|
||||||
&::-webkit-outer-spin-button,
|
&::-webkit-outer-spin-button,
|
||||||
&::-webkit-inner-spin-button {
|
&::-webkit-inner-spin-button {
|
||||||
@@ -487,7 +486,6 @@ input[type="number"] {
|
|||||||
position: relative;
|
position: relative;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
|
|
||||||
.check-box-style {
|
.check-box-style {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
@@ -665,6 +663,21 @@ input[type="number"] {
|
|||||||
.multi-email-invite-input-container {
|
.multi-email-invite-input-container {
|
||||||
@include flex-space-between;
|
@include flex-space-between;
|
||||||
gap: 20px;
|
gap: 20px;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.users-list-container {
|
||||||
|
position: absolute;
|
||||||
|
top: calc(100% + 8px);
|
||||||
|
background: var(--background-color);
|
||||||
|
backdrop-filter: blur(18px);
|
||||||
|
padding: 12px;
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
max-height: 200px;
|
||||||
|
border-radius: 8px;
|
||||||
|
z-index: 100;
|
||||||
|
outline: 1px solid var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
.multi-email-invite-input {
|
.multi-email-invite-input {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -764,4 +777,4 @@ input[type="number"] {
|
|||||||
background: var(--background-color-gray);
|
background: var(--background-color-gray);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
29
app/src/types/users.d.ts
vendored
29
app/src/types/users.d.ts
vendored
@@ -1,13 +1,22 @@
|
|||||||
export interface User {
|
export interface User {
|
||||||
name: string;
|
userName: string;
|
||||||
email: string;
|
Email: string;
|
||||||
profileImage: string;
|
Access: string;
|
||||||
color: string;
|
userId: string;
|
||||||
access: string;
|
profileImage?: string;
|
||||||
|
color?: string;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
// export interface User {
|
||||||
|
// name: string;
|
||||||
|
// email: string;
|
||||||
|
// profileImage: string;
|
||||||
|
// color: string;
|
||||||
|
// access: string;
|
||||||
|
// }
|
||||||
|
|
||||||
type AccessOption = {
|
type AccessOption = {
|
||||||
option: string;
|
option: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ActiveUser = {
|
export type ActiveUser = {
|
||||||
@@ -15,7 +24,7 @@ export type ActiveUser = {
|
|||||||
userName: string;
|
userName: string;
|
||||||
email: string;
|
email: string;
|
||||||
activeStatus?: string; // Optional property
|
activeStatus?: string; // Optional property
|
||||||
position?: { x: number; y: number; z: number; };
|
position?: { x: number; y: number; z: number };
|
||||||
rotation?: { x: number; y: number; z: number; };
|
rotation?: { x: number; y: number; z: number };
|
||||||
target?: { x: number; y: number; z: number; };
|
target?: { x: number; y: number; z: number };
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user