Compare commits
5 Commits
dev-r3f-wa
...
merge/wall
| Author | SHA1 | Date | |
|---|---|---|---|
| 8ebd16db83 | |||
| 898179c2c1 | |||
| 84101905ff | |||
| 3a70a13c94 | |||
| e589cb22b5 |
@@ -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 { useNavigate } from "react-router-dom";
|
||||
import { getUserData } from "../../functions/getUserData";
|
||||
@@ -14,13 +14,15 @@ interface DashBoardCardProps {
|
||||
projectId: string;
|
||||
createdAt?: string;
|
||||
isViewed?: string;
|
||||
createdBy?: { _id: string, userName: string };
|
||||
handleDeleteProject?: (projectId: string) => Promise<void>;
|
||||
handleTrashDeleteProject?: (projectId: string) => Promise<void>;
|
||||
handleRestoreProject?: (projectId: string) => Promise<void>;
|
||||
handleDuplicateWorkspaceProject?: (
|
||||
projectId: string,
|
||||
projectName: string,
|
||||
thumbnail: string
|
||||
thumbnail: string,
|
||||
userId?: string
|
||||
) => Promise<void>;
|
||||
handleDuplicateRecentProject?: (
|
||||
projectId: string,
|
||||
@@ -31,6 +33,7 @@ interface DashBoardCardProps {
|
||||
setIsSearchActive?: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
setRecentDuplicateData?: React.Dispatch<React.SetStateAction<Object>>;
|
||||
setProjectDuplicateData?: React.Dispatch<React.SetStateAction<Object>>;
|
||||
setActiveFolder?: React.Dispatch<React.SetStateAction<string>>;
|
||||
}
|
||||
type RelativeTimeFormatUnit = any;
|
||||
|
||||
@@ -45,8 +48,10 @@ const DashboardCard: React.FC<DashBoardCardProps> = ({
|
||||
handleDuplicateWorkspaceProject,
|
||||
handleDuplicateRecentProject,
|
||||
createdAt,
|
||||
createdBy,
|
||||
setRecentDuplicateData,
|
||||
setProjectDuplicateData,
|
||||
setActiveFolder
|
||||
}) => {
|
||||
const navigate = useNavigate();
|
||||
const { setProjectName } = useProjectName();
|
||||
@@ -59,10 +64,18 @@ const DashboardCard: React.FC<DashBoardCardProps> = ({
|
||||
const kebabRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const navigateToProject = async (e: any) => {
|
||||
console.log('active: ', active);
|
||||
if (active && active == "trash") return;
|
||||
setLoadingProgress(1)
|
||||
setProjectName(projectName);
|
||||
navigate(`/${projectId}`);
|
||||
try {
|
||||
const viewProjects = await viewProject(organization, projectId, userId)
|
||||
console.log('viewProjects: ', viewProjects);
|
||||
console.log('projectName: ', projectName);
|
||||
setLoadingProgress(1)
|
||||
setProjectName(projectName);
|
||||
navigate(`/${projectId}`);
|
||||
} catch {
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
const handleOptionClick = async (option: string) => {
|
||||
@@ -81,11 +94,18 @@ const DashboardCard: React.FC<DashBoardCardProps> = ({
|
||||
break;
|
||||
case "open in new tab":
|
||||
try {
|
||||
await viewProject(organization, projectId, userId);
|
||||
setProjectName(projectName);
|
||||
setIsKebabOpen(false);
|
||||
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) {
|
||||
console.error("Error opening project in new tab:", error);
|
||||
|
||||
}
|
||||
window.open(`/${projectId}`, "_blank");
|
||||
break;
|
||||
@@ -100,13 +120,17 @@ const DashboardCard: React.FC<DashBoardCardProps> = ({
|
||||
projectName,
|
||||
thumbnail,
|
||||
});
|
||||
await handleDuplicateWorkspaceProject(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);
|
||||
}
|
||||
@@ -128,7 +152,6 @@ const DashboardCard: React.FC<DashBoardCardProps> = ({
|
||||
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
|
||||
);
|
||||
@@ -173,6 +196,19 @@ const DashboardCard: React.FC<DashBoardCardProps> = ({
|
||||
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"
|
||||
@@ -212,13 +248,12 @@ const DashboardCard: React.FC<DashBoardCardProps> = ({
|
||||
<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">
|
||||
{userName ? userName.charAt(0).toUpperCase() : "A"}
|
||||
{(!createdBy) ? userName ? userName?.charAt(0).toUpperCase() : "A" : createdBy?.userName?.charAt(0).toUpperCase()}
|
||||
</div>
|
||||
<button
|
||||
className="kebab-wrapper"
|
||||
@@ -232,29 +267,9 @@ const DashboardCard: React.FC<DashBoardCardProps> = ({
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{isKebabOpen && active !== "trash" && (
|
||||
{isKebabOpen && (
|
||||
<div className="kebab-options-wrapper">
|
||||
{["rename", "delete", "duplicate", "open in new tab"].map(
|
||||
(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) => (
|
||||
{getOptions().map((option) => (
|
||||
<button
|
||||
key={option}
|
||||
className="option"
|
||||
|
||||
@@ -13,7 +13,7 @@ interface Project {
|
||||
_id: string;
|
||||
projectName: string;
|
||||
thumbnail: string;
|
||||
createdBy: string;
|
||||
createdBy: { _id: string, userName: string };
|
||||
projectUuid?: string;
|
||||
createdAt: string;
|
||||
isViewed?: string
|
||||
@@ -34,7 +34,8 @@ const DashboardHome: React.FC = () => {
|
||||
const fetchRecentProjects = async () => {
|
||||
try {
|
||||
const projects = await recentlyViewed(organization, userId);
|
||||
|
||||
console.log('projects: ', projects);
|
||||
|
||||
if (JSON.stringify(projects) !== JSON.stringify(recentProjects)) {
|
||||
setRecentProjects(projects);
|
||||
}
|
||||
@@ -126,7 +127,8 @@ const DashboardHome: React.FC = () => {
|
||||
projectName={project.projectName}
|
||||
thumbnail={project.thumbnail}
|
||||
projectId={project._id}
|
||||
createdAt={project.isViewed}
|
||||
createdBy={project.createdBy}
|
||||
createdAt={project.createdAt}
|
||||
handleDeleteProject={handleDeleteProject}
|
||||
handleDuplicateRecentProject={handleDuplicateRecentProject}
|
||||
setRecentDuplicateData={setRecentDuplicateData}
|
||||
|
||||
@@ -7,6 +7,8 @@ import { getAllProjects } from "../../services/dashboard/getAllProjects";
|
||||
import { searchProject } from "../../services/dashboard/searchProjects";
|
||||
import { deleteProject } from "../../services/dashboard/deleteProject";
|
||||
import ProjectSocketRes from "./socket/projectSocketRes.dev";
|
||||
import { sharedWithMeProjects } from "../../services/dashboard/sharedWithMeProject";
|
||||
import { duplicateProject } from "../../services/dashboard/duplicateProject";
|
||||
|
||||
interface Project {
|
||||
_id: string;
|
||||
@@ -25,12 +27,25 @@ const DashboardProjects: React.FC = () => {
|
||||
const [workspaceProjects, setWorkspaceProjects] = useState<WorkspaceProjects>(
|
||||
{}
|
||||
);
|
||||
const [sharedwithMeProject, setSharedWithMeProjects] = useState<any>([])
|
||||
const [projectDuplicateData, setProjectDuplicateData] = useState<Object>({});
|
||||
const [isSearchActive, setIsSearchActive] = useState<boolean>(false);
|
||||
const [activeFolder, setActiveFolder] = useState<string>("myProjects");
|
||||
const { projectSocket } = useSocketStore();
|
||||
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 () => {
|
||||
try {
|
||||
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) => {
|
||||
try {
|
||||
// const deletedProject = await deleteProject(
|
||||
@@ -97,18 +100,9 @@ const DashboardProjects: React.FC = () => {
|
||||
const handleDuplicateWorkspaceProject = async (
|
||||
projectId: string,
|
||||
projectName: string,
|
||||
thumbnail: string
|
||||
thumbnail: string,
|
||||
) => {
|
||||
// await handleDuplicateProjects({
|
||||
// userId,
|
||||
// organization,
|
||||
// projectId,
|
||||
// projectName,
|
||||
// projectSocket,
|
||||
// thumbnail,
|
||||
// setWorkspaceProjects,
|
||||
// setIsSearchActive
|
||||
// });
|
||||
|
||||
const duplicateProjectData = {
|
||||
userId,
|
||||
thumbnail,
|
||||
@@ -121,9 +115,7 @@ const DashboardProjects: React.FC = () => {
|
||||
|
||||
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>;
|
||||
}
|
||||
@@ -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(() => {
|
||||
if (!isSearchActive) {
|
||||
fetchAllProjects();
|
||||
}
|
||||
}, [isSearchActive]);
|
||||
|
||||
useEffect(() => {
|
||||
if (activeFolder === "shared") {
|
||||
sharedProject()
|
||||
}
|
||||
}, [activeFolder])
|
||||
|
||||
|
||||
return (
|
||||
<div className="dashboard-home-container">
|
||||
<DashboardNavBar
|
||||
@@ -171,7 +200,8 @@ const DashboardProjects: React.FC = () => {
|
||||
Shared with me
|
||||
</button>
|
||||
</div>
|
||||
<div className="cards-container">{renderProjects()}</div>
|
||||
<div className="cards-container">{activeFolder == "myProjects" ? renderProjects() : renderSharedProjects()}</div>
|
||||
|
||||
{projectDuplicateData && Object.keys(projectDuplicateData).length > 0 && (
|
||||
<ProjectSocketRes
|
||||
setIsSearchActive={setIsSearchActive}
|
||||
@@ -183,4 +213,6 @@ const DashboardProjects: React.FC = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardProjects;
|
||||
export default DashboardProjects;
|
||||
|
||||
|
||||
|
||||
@@ -14,12 +14,24 @@ interface Project {
|
||||
createdAt: 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 {
|
||||
[key: string]: Project[];
|
||||
}
|
||||
interface ProjectSocketResProps {
|
||||
setRecentProjects?: React.Dispatch<React.SetStateAction<ProjectsData>>;
|
||||
setRecentProjects?: React.Dispatch<React.SetStateAction<RecentProjectData>>;
|
||||
setWorkspaceProjects?: React.Dispatch<React.SetStateAction<ProjectsData>>;
|
||||
setIsSearchActive?: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
}
|
||||
@@ -52,7 +64,7 @@ const ProjectSocketRes = ({
|
||||
};
|
||||
|
||||
const handleDuplicate = async (data: any) => {
|
||||
// console.log("Project duplicate response:", data);
|
||||
console.log("Project duplicate response:", data);
|
||||
if (data?.message === "Project Duplicated successfully") {
|
||||
if (setWorkspaceProjects) {
|
||||
const allProjects = await getAllProjects(userId, organization);
|
||||
|
||||
@@ -7,15 +7,22 @@ import { access } from "fs";
|
||||
import MultiEmailInvite from "../ui/inputs/MultiEmailInvite";
|
||||
import { useActiveUsers } from "../../store/builder/store";
|
||||
import { getUserData } from "../../functions/getUserData";
|
||||
import { getProjectSharedList } from "../../services/factoryBuilder/collab/getProjectSharedList";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { shareAccess } from "../../services/factoryBuilder/collab/shareAccess";
|
||||
import { getAvatarColor } from "../../modules/collaboration/functions/getAvatarColor";
|
||||
|
||||
interface UserListTemplateProps {
|
||||
user: 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);
|
||||
}
|
||||
|
||||
@@ -26,18 +33,22 @@ const UserListTemplate: React.FC<UserListTemplateProps> = ({ user }) => {
|
||||
{user.profileImage ? (
|
||||
<img
|
||||
src={user.profileImage || "https://via.placeholder.com/150"}
|
||||
alt={`${user.name}'s profile`}
|
||||
alt={`${user.
|
||||
userName}'s profile`}
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
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 className="user-name">{user.name}</div>
|
||||
<div className="user-name"> {user.
|
||||
userName.charAt(0).toUpperCase() + user.
|
||||
userName.slice(1).toLowerCase()}</div>
|
||||
</div>
|
||||
<div className="user-access">
|
||||
<RegularDropDown
|
||||
@@ -61,39 +72,27 @@ const CollaborationPopup: React.FC<CollaborateProps> = ({
|
||||
}) => {
|
||||
const { activeUsers } = useActiveUsers();
|
||||
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(() => {
|
||||
// console.log("activeUsers: ", activeUsers);
|
||||
getData();
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
//
|
||||
}, [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 (
|
||||
<RenderOverlay>
|
||||
<div
|
||||
@@ -112,7 +111,7 @@ const CollaborationPopup: React.FC<CollaborateProps> = ({
|
||||
<div className="header">
|
||||
<div className="content">Share this file</div>
|
||||
<div className="content">
|
||||
<div className="copy-link-button">copy link</div>
|
||||
{/* <div className="copy-link-button">copy link</div> */}
|
||||
<div
|
||||
className="close-button"
|
||||
onClick={() => {
|
||||
@@ -124,7 +123,7 @@ const CollaborationPopup: React.FC<CollaborateProps> = ({
|
||||
</div>
|
||||
</div>
|
||||
<div className="invite-input-container">
|
||||
<MultiEmailInvite />
|
||||
<MultiEmailInvite users={users} getData={getData} />
|
||||
</div>
|
||||
<div className="split"></div>
|
||||
<section>
|
||||
@@ -142,7 +141,7 @@ const CollaborationPopup: React.FC<CollaborateProps> = ({
|
||||
<div className="you-container">
|
||||
<div className="your-name">
|
||||
<div className="user-profile">{userName && userName[0].toUpperCase()}</div>
|
||||
{userName}
|
||||
{userName && userName.charAt(0).toUpperCase() + userName.slice(1).toLowerCase()}
|
||||
</div>
|
||||
<div className="indicater">you</div>
|
||||
</div>
|
||||
|
||||
@@ -6,6 +6,7 @@ import { useProjectName } from "../../store/builder/store";
|
||||
import { getAllProjects } from "../../services/dashboard/getAllProjects";
|
||||
import { useComparisonProduct } from "../../store/simulation/useSimulationStore";
|
||||
import { getUserData } from "../../functions/getUserData";
|
||||
import { sharedWithMeProjects } from "../../services/dashboard/sharedWithMeProject";
|
||||
|
||||
interface LoadingPageProps {
|
||||
progress: number; // Expect progress as a percentage (0-100)
|
||||
@@ -18,19 +19,35 @@ const LoadingPage: React.FC<LoadingPageProps> = ({ progress }) => {
|
||||
const { userId, organization } = getUserData();
|
||||
|
||||
const validatedProgress = Math.min(100, Math.max(0, progress));
|
||||
|
||||
useEffect(() => {
|
||||
if (!userId) return;
|
||||
if (!userId) {
|
||||
console.error("User data not found in localStorage");
|
||||
return;
|
||||
}
|
||||
|
||||
getAllProjects(userId, organization).then((projects) => {
|
||||
if (!projects || !projects.Projects) return;
|
||||
const filterProject = projects?.Projects.find((val: any) => val.projectUuid === projectId || val._id === projectId);
|
||||
if (filterProject) {
|
||||
setProjectName(filterProject.projectName);
|
||||
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 (
|
||||
|
||||
@@ -1,21 +1,26 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import useLayoutStore from "../../store/builder/uselayoutStore";
|
||||
import { useDfxUpload } from "../../store/builder/store";
|
||||
import { useActiveLayer, useDfxUpload } from "../../store/builder/store";
|
||||
import DxfParser from "dxf-parser";
|
||||
import { getWallPointsFromBlueprint } from "../../modules/builder/dfx/functions/getWallPointsFromBlueprint";
|
||||
import { convertDXFToThree } from "../../modules/builder/dfx/functions/convertDxfToThree";
|
||||
import { AIIcon } from "../icons/ExportCommonIcons";
|
||||
import { useBuilderStore } from "../../store/builder/useBuilderStore";
|
||||
import { useSceneContext } from "../../modules/scene/sceneContext";
|
||||
type DXFData = any;
|
||||
const SelectFloorPlan: React.FC = () => {
|
||||
// Access layout state and state setters
|
||||
const { currentLayout, setLayout } = useLayoutStore();
|
||||
// Access DXF-related states and setters
|
||||
const { setDfxUploaded, setDfxGenerate, setObjValue, objValue } =
|
||||
const { setDfxUploaded, setDxfWallGenerate, setObjValue, objValue } =
|
||||
useDfxUpload();
|
||||
|
||||
const { activeLayer } = useActiveLayer();
|
||||
const { wallThickness, wallHeight, insideMaterial, outsideMaterial } = useBuilderStore();
|
||||
const { wallStore } = useSceneContext();
|
||||
const { addWall } = wallStore();
|
||||
// Local state to store the parsed DXF file
|
||||
const [parsedFile, setParsedFile] = useState<DXFData | undefined>(undefined);
|
||||
|
||||
const { walls } = wallStore();
|
||||
// Flag to trigger generation after file upload
|
||||
const [generate, setGenerate] = useState<boolean>(false);
|
||||
|
||||
@@ -58,8 +63,9 @@ const SelectFloorPlan: React.FC = () => {
|
||||
if (parsedFile !== undefined) {
|
||||
getWallPointsFromBlueprint({
|
||||
parsedData: parsedFile,
|
||||
setDfxGenerate,
|
||||
setDxfWallGenerate,
|
||||
objValue,
|
||||
wallThickness, wallHeight, outsideMaterial, insideMaterial, activeLayer, addWall, walls
|
||||
});
|
||||
}
|
||||
}, [generate]);
|
||||
|
||||
@@ -1,38 +1,68 @@
|
||||
import React, { useState } from "react";
|
||||
import { useParams } from "react-router-dom";
|
||||
|
||||
const MultiEmailInvite: React.FC = () => {
|
||||
const [emails, setEmails] = useState<string[]>([]);
|
||||
import { getSearchUsers } from "../../../services/factoryBuilder/collab/getSearchUsers";
|
||||
import { shareProject } from "../../../services/factoryBuilder/collab/shareProject";
|
||||
import { getUserData } from "../../../functions/getUserData";
|
||||
|
||||
interface UserData {
|
||||
_id: string;
|
||||
Email: string;
|
||||
userName: string;
|
||||
}
|
||||
|
||||
interface MultiEmailProps {
|
||||
users: Array<any>;
|
||||
getData: () => void;
|
||||
}
|
||||
|
||||
const MultiEmailInvite: React.FC<MultiEmailProps> = ({ users, getData }) => {
|
||||
const [selectedUsers, setSelectedUsers] = useState<UserData[]>([]);
|
||||
const [searchResults, setSearchResults] = useState<UserData[]>([]);
|
||||
const [inputValue, setInputValue] = useState("");
|
||||
const [isFocused, setIsFocused] = useState(false);
|
||||
|
||||
const handleAddEmail = () => {
|
||||
const trimmedEmail = inputValue.trim();
|
||||
const { projectId } = useParams();
|
||||
const { userId } = getUserData();
|
||||
|
||||
// Validate email
|
||||
if (!trimmedEmail || !validateEmail(trimmedEmail)) {
|
||||
alert("Please enter a valid email address.");
|
||||
return;
|
||||
|
||||
const handleSearchInput = async (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const value = e.target.value.trim();
|
||||
setInputValue(value);
|
||||
|
||||
if (value.length < 3) return;
|
||||
|
||||
try {
|
||||
const result = await getSearchUsers(value);
|
||||
const filtered = result?.sharchMail?.filtered || [];
|
||||
setSearchResults(filtered);
|
||||
} catch (err) {
|
||||
console.error("Search failed:", err);
|
||||
}
|
||||
};
|
||||
|
||||
const handleAddUser = (user: UserData) => {
|
||||
if (!user || user._id === userId) return;
|
||||
|
||||
const isAlreadySelected = selectedUsers.some(u => u._id === user._id);
|
||||
const isAlreadyShared = users.some(u => u.userId === user._id);
|
||||
|
||||
if (!isAlreadySelected && !isAlreadyShared) {
|
||||
setSelectedUsers(prev => [...prev, user]);
|
||||
}
|
||||
|
||||
// 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("");
|
||||
};
|
||||
|
||||
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||
if (e.key === "Enter" || e.key === ",") {
|
||||
if ((e.key === "Enter" || e.key === ",") && searchResults.length > 0) {
|
||||
e.preventDefault();
|
||||
handleAddEmail();
|
||||
handleAddUser(searchResults[0]);
|
||||
}
|
||||
};
|
||||
|
||||
const handleRemoveEmail = (emailToRemove: string) => {
|
||||
setEmails(emails.filter((email) => email !== emailToRemove));
|
||||
const handleRemoveUser = (userIdToRemove: string) => {
|
||||
setSelectedUsers(prev => prev.filter(user => user._id !== userIdToRemove));
|
||||
};
|
||||
|
||||
const validateEmail = (email: string) => {
|
||||
@@ -40,33 +70,66 @@ const MultiEmailInvite: React.FC = () => {
|
||||
return emailRegex.test(email);
|
||||
};
|
||||
|
||||
const [inputFocus, setInputFocus] = useState(false);
|
||||
const handleAddEmail = async () => {
|
||||
if (!projectId) return;
|
||||
|
||||
try {
|
||||
await Promise.all(
|
||||
selectedUsers.map(user =>
|
||||
shareProject(user._id, projectId)
|
||||
.then(res => console.log("Shared:", res))
|
||||
.catch(err => console.error("Share error:", err))
|
||||
)
|
||||
);
|
||||
|
||||
setSelectedUsers([]);
|
||||
setInputValue("");
|
||||
setTimeout(getData, 1000);
|
||||
} catch (error) {
|
||||
console.error("Invite error:", error);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<div className="multi-email-invite-input-container">
|
||||
<div className={`multi-email-invite-input${inputFocus ? " active" : ""}`}>
|
||||
{emails.map((email, index) => (
|
||||
<div key={index} className="entered-emails">
|
||||
{email}
|
||||
<span onClick={() => handleRemoveEmail(email)}>×</span>
|
||||
<div className={`multi-email-invite-input${isFocused ? " active" : ""}`}>
|
||||
{selectedUsers.map(user => (
|
||||
<div key={user._id} className="entered-emails">
|
||||
{user.Email}
|
||||
<span onClick={() => handleRemoveUser(user._id)}>×</span>
|
||||
</div>
|
||||
))}
|
||||
|
||||
<input
|
||||
type="text"
|
||||
value={inputValue}
|
||||
onChange={(e) => setInputValue(e.target.value)}
|
||||
onFocus={() => setInputFocus(true)}
|
||||
onBlur={() => setInputFocus(false)}
|
||||
onChange={handleSearchInput}
|
||||
onFocus={() => setIsFocused(true)}
|
||||
onKeyDown={handleKeyDown}
|
||||
placeholder="Enter email and press Enter or comma to seperate"
|
||||
placeholder="Enter email and press Enter or comma"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div onClick={handleAddEmail} className="invite-button">
|
||||
Invite
|
||||
</div>
|
||||
<div className="users-list-container">
|
||||
{/* list available users */}
|
||||
Add
|
||||
</div>
|
||||
|
||||
{isFocused && inputValue.length > 2 && searchResults.length > 0 && (
|
||||
<div className="users-list-container">
|
||||
{searchResults.map(user => (
|
||||
<div
|
||||
key={user._id}
|
||||
onClick={() => {
|
||||
handleAddUser(user);
|
||||
setIsFocused(false);
|
||||
}}
|
||||
>
|
||||
{user.Email}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { useDfxUpload, useSocketStore, useToggleView, useUpdateScene } from '../../../store/builder/store';
|
||||
import { useActiveLayer, useDfxUpload, useSocketStore, useToggleView, useUpdateScene } from '../../../store/builder/store';
|
||||
import { LineBasicMaterial, Line } from 'three';
|
||||
import { TransformControls } from '@react-three/drei';
|
||||
import { getWallPointsFromBlueprint } from './functions/getWallPointsFromBlueprint';
|
||||
@@ -7,6 +7,8 @@ import * as Types from '../../../types/world/worldTypes';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { getUserData } from '../../../functions/getUserData';
|
||||
import { useVersionContext } from '../version/versionContext';
|
||||
import { useBuilderStore } from '../../../store/builder/useBuilderStore';
|
||||
import { useSceneContext } from '../../scene/sceneContext';
|
||||
|
||||
/**
|
||||
* DxfFile component handles the rendering and manipulation of DXf file data in a 3D scene.
|
||||
@@ -15,48 +17,44 @@ import { useVersionContext } from '../version/versionContext';
|
||||
*/
|
||||
const DxfFile = () => {
|
||||
// State management hooks
|
||||
const { dfxuploaded, dfxWallGenerate, setObjValue, objValue } = useDfxUpload();
|
||||
const { dfxuploaded, dfxWallGenerate, setObjValue, objValue, setDfxUploaded } = useDfxUpload();
|
||||
const { toggleView } = useToggleView();
|
||||
const { socket } = useSocketStore();
|
||||
const { selectedVersionStore } = useVersionContext();
|
||||
const { selectedVersion } = selectedVersionStore();
|
||||
const { projectId } = useParams();
|
||||
const { userId, organization } = getUserData();
|
||||
|
||||
const { activeLayer } = useActiveLayer();
|
||||
const { wallThickness, wallHeight, insideMaterial, outsideMaterial, } = useBuilderStore();
|
||||
// Refs for storing line objects
|
||||
const lineRefs = useRef<Line[]>([]);
|
||||
const { wallStore } = useSceneContext();
|
||||
const { addWall, } = wallStore();
|
||||
const { walls } = wallStore();
|
||||
|
||||
/**
|
||||
* Effect hook that runs when DXF wall generation is triggered.
|
||||
* Loads initial points and lines from the DXF data and updates the scene.
|
||||
*/
|
||||
useEffect(() => {
|
||||
|
||||
if (dfxWallGenerate) {
|
||||
// Store generated lines in ref
|
||||
// lines.current.push(...dfxWallGenerate);
|
||||
// dfxWallGenerate.map((line: any) => {
|
||||
// const lineData = arrayLineToObject(line as Types.Line);
|
||||
dfxWallGenerate.map((wall: Wall) => {
|
||||
const data = {
|
||||
wallData: wall,
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization
|
||||
}
|
||||
addWall(wall);
|
||||
socket.emit('v1:model-Wall:add', data);
|
||||
// API
|
||||
|
||||
// //REST
|
||||
|
||||
// // setLine(organization, lineData.layer!, lineData.line!, lineData.type!);
|
||||
|
||||
// //SOCKET
|
||||
|
||||
// const input = {
|
||||
// organization,
|
||||
// layer: lineData.layer,
|
||||
// line: lineData.line,
|
||||
// type: lineData.type,
|
||||
// socketId: socket.id,
|
||||
// versionId: selectedVersion?.versionId || '',
|
||||
// projectId,
|
||||
// userId
|
||||
// }
|
||||
|
||||
// socket.emit('v1:Line:create', input);
|
||||
|
||||
// })
|
||||
// if (projectId) {
|
||||
// upsertWallApi(projectId, selectedVersion?.versionId || '', wall);
|
||||
// }
|
||||
})
|
||||
}
|
||||
}, [dfxWallGenerate]);
|
||||
|
||||
@@ -78,10 +76,15 @@ const DxfFile = () => {
|
||||
// Recalculate wall points based on new position
|
||||
getWallPointsFromBlueprint({
|
||||
objValue: { x: position.x, y: position.y, z: position.z },
|
||||
setDfxGenerate: () => { },
|
||||
setDxfWallGenerate: () => { },
|
||||
wallThickness, wallHeight, outsideMaterial, insideMaterial, activeLayer, addWall, walls
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!toggleView) {
|
||||
setDfxUploaded([])
|
||||
}
|
||||
}, [toggleView])
|
||||
return (
|
||||
<>
|
||||
{/* Render DXF lines with transform controls when DXF data is available and view is toggled */}
|
||||
|
||||
@@ -1,11 +1,21 @@
|
||||
import { MathUtils, Vector3, BufferGeometry } from "three";
|
||||
import { useActiveLayer } from "../../../../store/builder/store";
|
||||
import { useBuilderStore } from "../../../../store/builder/useBuilderStore";
|
||||
import { wait } from "@testing-library/user-event/dist/utils";
|
||||
type DXFData = any; // Replace with actual DXF data type
|
||||
type DXFEntity = any; // Replace with actual DXF entity type
|
||||
type WallLineVertex = [Vector3, string, number, string]; // Represents a wall segment with start point, ID, weight, and type
|
||||
interface Props {
|
||||
parsedData?: DXFData; // Parsed DXF file data
|
||||
setDfxGenerate?: (walls: WallLineVertex[][]) => void; // Callback to set generated walls
|
||||
setDxfWallGenerate?: any; // Callback to set generated walls
|
||||
objValue: any; // Object position values for offset calculation
|
||||
wallThickness: number;
|
||||
wallHeight: number;
|
||||
outsideMaterial: string;
|
||||
insideMaterial: string;
|
||||
activeLayer: number; // Active layer for wall points
|
||||
addWall: (wall: Wall) => void; // Function to add a wall to the scene
|
||||
walls: Wall[]; // Array of walls to be processed
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -15,20 +25,36 @@ interface Props {
|
||||
*
|
||||
* @param {Props} params - Configuration parameters
|
||||
* @param {DXFData} params.parsedData - Parsed DXF file data
|
||||
* @param {Function} params.setDfxGenerate - Callback to store generated walls
|
||||
* @param {Function} params.setDxfWallGenerate - Callback to store generated walls
|
||||
* @param {Object} params.objValue - Contains x,y,z offsets for position adjustment
|
||||
*/
|
||||
export function getWallPointsFromBlueprint({
|
||||
parsedData,
|
||||
setDfxGenerate,
|
||||
setDxfWallGenerate,
|
||||
objValue,
|
||||
wallThickness,
|
||||
wallHeight,
|
||||
outsideMaterial,
|
||||
insideMaterial,
|
||||
activeLayer,
|
||||
addWall,
|
||||
walls,
|
||||
}: Props) {
|
||||
// Early return if no data is provided
|
||||
if (!parsedData) return;
|
||||
if (!parsedData.entities) return;
|
||||
|
||||
const unit = 1000; // Conversion factor from millimeters to meters
|
||||
const wallVertex: WallLineVertex[][] = []; // Stores all generated wall segments
|
||||
const wallVertex: any[] = []; // Array to store wall vertices
|
||||
const findExistingPoint = (vec: Vector3): Point | undefined => {
|
||||
for (const wall of wallVertex) {
|
||||
for (const pt of wall.points) {
|
||||
const pos = new Vector3(...pt.position);
|
||||
if (pos.equals(vec)) return pt;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
// Process each entity in the DXF file
|
||||
parsedData.entities.forEach((entity: DXFEntity) => {
|
||||
@@ -47,31 +73,41 @@ export function getWallPointsFromBlueprint({
|
||||
-entity.vertices[1].y / unit
|
||||
).add(new Vector3(objValue.x, 0, objValue.z));
|
||||
|
||||
// Check if points already exist to avoid duplicates
|
||||
const existingStart = wallVertex
|
||||
.flat()
|
||||
.find((v) => v[0].equals(startVec));
|
||||
const startPoint: WallLineVertex = existingStart || [
|
||||
startVec,
|
||||
MathUtils.generateUUID(), // Generate unique ID for new points
|
||||
1, // Default weight
|
||||
"WallLine", // Type identifier
|
||||
];
|
||||
// Create start and end points
|
||||
const existingStart = findExistingPoint(startVec);
|
||||
const startPoint: Point = existingStart || {
|
||||
pointUuid: MathUtils.generateUUID(),
|
||||
pointType: "Wall",
|
||||
position: [startVec.x, startVec.y, startVec.z],
|
||||
layer: activeLayer,
|
||||
};
|
||||
|
||||
const existingEnd = wallVertex.flat().find((v) => v[0].equals(endVec));
|
||||
const endPoint: WallLineVertex = existingEnd || [
|
||||
endVec,
|
||||
MathUtils.generateUUID(),
|
||||
1,
|
||||
"WallLine",
|
||||
];
|
||||
const existingEnd = findExistingPoint(endVec);
|
||||
const endPoint: Point = existingEnd || {
|
||||
pointUuid: MathUtils.generateUUID(),
|
||||
pointType: "Wall",
|
||||
position: [endVec.x, endVec.y, endVec.z],
|
||||
layer: activeLayer,
|
||||
};
|
||||
|
||||
// Add the line segment to our collection
|
||||
wallVertex.push([startPoint, endPoint]);
|
||||
// Create the wall
|
||||
const wallSet: Wall = {
|
||||
wallUuid: MathUtils.generateUUID(),
|
||||
points: [startPoint, endPoint], // Store start and end points
|
||||
outsideMaterial: insideMaterial,
|
||||
insideMaterial: outsideMaterial,
|
||||
wallThickness: wallThickness,
|
||||
wallHeight: wallHeight,
|
||||
decals: [],
|
||||
};
|
||||
wallVertex.push(wallSet); // Store wall segment in array
|
||||
// Add the wall to the scene
|
||||
// addWall(wallSet);
|
||||
}
|
||||
|
||||
// Handle LWPOLYLINE entities (connected line segments)
|
||||
else if (entity.type === "LWPOLYLINE" && entity.vertices) {
|
||||
let firstPoint: WallLineVertex | undefined; // Store first point for closing the polyline
|
||||
let firstPoint: Point | undefined; // Store first point for closing the polyline
|
||||
|
||||
// Process each vertex pair in the polyline
|
||||
for (let i = 0; i < entity.vertices.length - 1; i++) {
|
||||
@@ -88,37 +124,60 @@ export function getWallPointsFromBlueprint({
|
||||
-entity.vertices[i + 1].y / unit
|
||||
).add(new Vector3(objValue.x, 0, objValue.z));
|
||||
|
||||
// Check for existing points
|
||||
const existingStart = wallVertex
|
||||
.flat()
|
||||
.find((v) => v[0].equals(startVec));
|
||||
const startPoint: WallLineVertex = existingStart || [
|
||||
startVec,
|
||||
MathUtils.generateUUID(),
|
||||
1,
|
||||
"WallLine",
|
||||
];
|
||||
// Create start and end points
|
||||
const existingStart = findExistingPoint(startVec);
|
||||
const startPoint: Point = existingStart || {
|
||||
pointUuid: MathUtils.generateUUID(), // Generate unique ID for new points
|
||||
pointType: "Wall", // Type identifier
|
||||
position: [startVec.x, startVec.y, startVec.z], // Position in 3D space
|
||||
layer: activeLayer,
|
||||
};
|
||||
|
||||
const existingEnd = wallVertex.flat().find((v) => v[0].equals(endVec));
|
||||
const endPoint: WallLineVertex = existingEnd || [
|
||||
endVec,
|
||||
MathUtils.generateUUID(),
|
||||
1,
|
||||
"WallLine",
|
||||
];
|
||||
const existingEnd = findExistingPoint(endVec);
|
||||
const endPoint: Point = existingEnd || {
|
||||
pointUuid: MathUtils.generateUUID(), // Generate unique ID for new points
|
||||
pointType: "Wall", // Type identifier
|
||||
position: [endVec.x, endVec.y, endVec.z], // Position in 3D space
|
||||
layer: activeLayer,
|
||||
};
|
||||
|
||||
wallVertex.push([startPoint, endPoint]);
|
||||
// Create the wall segment
|
||||
const wallSet: Wall = {
|
||||
wallUuid: MathUtils.generateUUID(),
|
||||
points: [startPoint, endPoint], // Store start and end points
|
||||
outsideMaterial: insideMaterial,
|
||||
insideMaterial: outsideMaterial,
|
||||
wallThickness: wallThickness,
|
||||
wallHeight: wallHeight,
|
||||
decals: [],
|
||||
};
|
||||
|
||||
// Add the wall segment
|
||||
// addWall(wallSet);
|
||||
wallVertex.push(wallSet);
|
||||
// Store first point and create closing segment if this is the last vertex
|
||||
if (i === 0) firstPoint = startPoint;
|
||||
if (i === entity.vertices.length - 2 && firstPoint) {
|
||||
wallVertex.push([endPoint, firstPoint]);
|
||||
const closingWallSet: Wall = {
|
||||
wallUuid: MathUtils.generateUUID(),
|
||||
points: [endPoint, firstPoint], // Create closing segment
|
||||
outsideMaterial: insideMaterial,
|
||||
insideMaterial: outsideMaterial,
|
||||
wallThickness: wallThickness,
|
||||
wallHeight: wallHeight,
|
||||
decals: [],
|
||||
};
|
||||
// Add the closing wall
|
||||
wallVertex.push(closingWallSet);
|
||||
// addWall(closingWallSet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle ARC entities
|
||||
else if (entity.type === "ARC") {
|
||||
const { center, radius, startAngle, endAngle } = entity;
|
||||
|
||||
// Validate required ARC properties
|
||||
if (
|
||||
!center ||
|
||||
@@ -154,34 +213,46 @@ export function getWallPointsFromBlueprint({
|
||||
const startVec = arcPoints[i];
|
||||
const endVec = arcPoints[i + 1];
|
||||
|
||||
// Check for existing points
|
||||
const existingStart = wallVertex
|
||||
.flat()
|
||||
.find((v) => v[0].equals(startVec));
|
||||
const startPoint: WallLineVertex = existingStart || [
|
||||
startVec,
|
||||
MathUtils.generateUUID(),
|
||||
1,
|
||||
"WallLine",
|
||||
];
|
||||
// Create start and end points
|
||||
const existingStart = findExistingPoint(startVec);
|
||||
const startPoint: Point = existingStart || {
|
||||
pointUuid: MathUtils.generateUUID(),
|
||||
pointType: "Wall",
|
||||
position: [startVec.x, startVec.y, startVec.z],
|
||||
layer: activeLayer,
|
||||
};
|
||||
|
||||
const existingEnd = wallVertex.flat().find((v) => v[0].equals(endVec));
|
||||
const endPoint: WallLineVertex = existingEnd || [
|
||||
endVec,
|
||||
MathUtils.generateUUID(),
|
||||
1,
|
||||
"WallLine",
|
||||
];
|
||||
const existingEnd = findExistingPoint(endVec);
|
||||
const endPoint: Point = existingEnd || {
|
||||
pointUuid: MathUtils.generateUUID(),
|
||||
pointType: "Wall",
|
||||
position: [endVec.x, endVec.y, endVec.z],
|
||||
layer: activeLayer,
|
||||
};
|
||||
|
||||
wallVertex.push([startPoint, endPoint]);
|
||||
// Create the wall segment
|
||||
const wallSet: Wall = {
|
||||
wallUuid: MathUtils.generateUUID(),
|
||||
points: [startPoint, endPoint],
|
||||
outsideMaterial: insideMaterial,
|
||||
insideMaterial: outsideMaterial,
|
||||
wallThickness: wallThickness,
|
||||
wallHeight: wallHeight,
|
||||
decals: [],
|
||||
};
|
||||
|
||||
// Add the wall segment
|
||||
// addWall(wallSet);
|
||||
wallVertex.push(wallSet);
|
||||
}
|
||||
}
|
||||
|
||||
// Log unsupported entity types
|
||||
else {
|
||||
console.error("Unsupported entity type:", entity.type);
|
||||
}
|
||||
});
|
||||
|
||||
console.log("wallVertex: ", wallVertex);
|
||||
// Return the generated walls through callback if provided
|
||||
setDfxGenerate && setDfxGenerate(wallVertex);
|
||||
}
|
||||
setDxfWallGenerate && setDxfWallGenerate(wallVertex);
|
||||
}
|
||||
|
||||
@@ -71,6 +71,7 @@ function WallCreator() {
|
||||
|
||||
if (wallIntersect && !pointIntersects) {
|
||||
const wall = getWallByPoints(wallIntersect.object.userData.points);
|
||||
console.log('wall: ', wall);
|
||||
if (wall) {
|
||||
const ThroughPoint = wallIntersect.object.userData.path.getPoints(Constants.lineConfig.lineIntersectionPoints);
|
||||
let intersectionPoint = getClosestIntersection(ThroughPoint, wallIntersect.point);
|
||||
@@ -294,7 +295,9 @@ function WallCreator() {
|
||||
position: [position.x, position.y, position.z],
|
||||
layer: activeLayer
|
||||
};
|
||||
console.log('newPoint: ', newPoint);
|
||||
|
||||
console.log('snappedPoint: ', snappedPoint);
|
||||
if (snappedPosition && snappedPoint) {
|
||||
newPoint.pointUuid = snappedPoint.pointUuid;
|
||||
newPoint.position = snappedPosition;
|
||||
@@ -316,6 +319,7 @@ function WallCreator() {
|
||||
}
|
||||
}
|
||||
|
||||
console.log('tempPoints: ', tempPoints);
|
||||
if (tempPoints.length === 0) {
|
||||
setTempPoints([newPoint]);
|
||||
setIsCreating(true);
|
||||
@@ -330,6 +334,7 @@ function WallCreator() {
|
||||
decals: []
|
||||
};
|
||||
addWall(wall);
|
||||
console.log('wall: ', wall);
|
||||
|
||||
// API
|
||||
|
||||
|
||||
@@ -12,13 +12,12 @@ interface PolygonGeneratorProps {
|
||||
export default function PolygonGenerator({
|
||||
groupRef,
|
||||
}: PolygonGeneratorProps) {
|
||||
const { aisleStore } = useSceneContext();
|
||||
const { aisleStore, wallStore } = useSceneContext();
|
||||
const { aisles } = aisleStore();
|
||||
const { scene } = useThree();
|
||||
const { walls } = wallStore();
|
||||
|
||||
useEffect(() => {
|
||||
// let allLines = arrayLinesToObject(lines.current);
|
||||
// const wallLines = allLines?.filter((line) => line?.type === "WallLine");
|
||||
const result = aisles
|
||||
.filter(
|
||||
(aisle) =>
|
||||
@@ -61,9 +60,12 @@ export default function PolygonGenerator({
|
||||
});
|
||||
});
|
||||
|
||||
// const wallPoints = wallLines
|
||||
// .map((pair) => pair?.line.map((vals) => vals.position))
|
||||
// .filter((wall): wall is THREE.Vector3[] => !!wall);
|
||||
|
||||
const wallPoints: THREE.Vector3[][] = walls
|
||||
.map((wall) =>
|
||||
wall.points.map((pt) => new THREE.Vector3(pt.position[0], pt.position[1], pt.position[2]))
|
||||
)
|
||||
.filter(points => points.length === 2);
|
||||
|
||||
|
||||
if (!result || result.some((line) => !line)) {
|
||||
@@ -81,7 +83,7 @@ export default function PolygonGenerator({
|
||||
|
||||
const polygons = turf.polygonize(turf.featureCollection(validLineFeatures));
|
||||
|
||||
// renderWallGeometry(wallPoints);
|
||||
renderWallGeometry(wallPoints);
|
||||
|
||||
if (polygons.features.length > 0) {
|
||||
polygons.features.forEach((feature) => {
|
||||
@@ -116,7 +118,7 @@ export default function PolygonGenerator({
|
||||
});
|
||||
}
|
||||
|
||||
}, [ aisles, scene]);
|
||||
}, [aisles, scene, walls]);
|
||||
|
||||
const renderWallGeometry = (walls: THREE.Vector3[][]) => {
|
||||
walls.forEach((wall) => {
|
||||
|
||||
@@ -29,6 +29,7 @@ import { SceneProvider } from "../modules/scene/sceneContext";
|
||||
import { getVersionHistoryApi } from "../services/factoryBuilder/versionControl/getVersionHistoryApi";
|
||||
import { useVersionHistoryStore } from "../store/builder/useVersionHistoryStore";
|
||||
import { VersionProvider } from "../modules/builder/version/versionContext";
|
||||
import { sharedWithMeProjects } from "../services/dashboard/sharedWithMeProject";
|
||||
|
||||
const Project: React.FC = () => {
|
||||
let navigate = useNavigate();
|
||||
@@ -54,16 +55,29 @@ const Project: React.FC = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
getAllProjects(userId, organization).then((projects) => {
|
||||
if (!projects || !projects.Projects) return;
|
||||
const filterProject = projects?.Projects.find((val: any) => val.projectUuid === projectId || val._id === projectId)
|
||||
setProjectName(filterProject.projectName)
|
||||
viewProject(organization, filterProject._id, userId).then((viewedProject) => {
|
||||
});
|
||||
}).catch(() => {
|
||||
console.error("Error fetching projects")
|
||||
})
|
||||
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);
|
||||
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(() => {
|
||||
|
||||
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,32 @@
|
||||
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") || "",
|
||||
},
|
||||
}
|
||||
);
|
||||
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 get users");
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
} catch (error: any) {
|
||||
echo.error("Failed to get users");
|
||||
console.log(error.message);
|
||||
}
|
||||
};
|
||||
32
app/src/services/factoryBuilder/collab/getSearchUsers.ts
Normal file
32
app/src/services/factoryBuilder/collab/getSearchUsers.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
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") || "",
|
||||
},
|
||||
}
|
||||
);
|
||||
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 get users");
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
} catch (error: any) {
|
||||
echo.error("Failed to get users");
|
||||
console.log(error.message);
|
||||
}
|
||||
};
|
||||
48
app/src/services/factoryBuilder/collab/shareAccess.ts
Normal file
48
app/src/services/factoryBuilder/collab/shareAccess.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
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),
|
||||
}
|
||||
);
|
||||
const newAccessToken = response.headers.get("x-access-token");
|
||||
if (newAccessToken) {
|
||||
//console.log("New token received:", newAccessToken);
|
||||
localStorage.setItem("token", newAccessToken);
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
};
|
||||
36
app/src/services/factoryBuilder/collab/shareProject.ts
Normal file
36
app/src/services/factoryBuilder/collab/shareProject.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
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 }),
|
||||
});
|
||||
const newAccessToken = response.headers.get("x-access-token");
|
||||
if (newAccessToken) {
|
||||
//console.log("New token received:", newAccessToken);
|
||||
localStorage.setItem("token", newAccessToken);
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -587,7 +587,7 @@ export const useDfxUpload = create<any>((set: any) => ({
|
||||
dfxWallGenerate: [],
|
||||
objValue: { x: 0, y: 0, z: 0 },
|
||||
setDfxUploaded: (x: any) => set({ dfxuploaded: x }),
|
||||
setDfxGenerate: (x: any) => set({ dfxWallGenerate: x }),
|
||||
setDxfWallGenerate: (x: any) => set({ dfxWallGenerate: x }),
|
||||
setObjValue: (x: any) => set({ objValue: x }),
|
||||
}));
|
||||
|
||||
|
||||
@@ -46,7 +46,6 @@ textarea {
|
||||
}
|
||||
|
||||
input[type="number"] {
|
||||
|
||||
// Chrome, Safari, Edge, Opera
|
||||
&::-webkit-outer-spin-button,
|
||||
&::-webkit-inner-spin-button {
|
||||
@@ -487,7 +486,6 @@ input[type="number"] {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
|
||||
|
||||
.check-box-style {
|
||||
position: absolute;
|
||||
height: 20px;
|
||||
@@ -665,6 +663,21 @@ input[type="number"] {
|
||||
.multi-email-invite-input-container {
|
||||
@include flex-space-between;
|
||||
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 {
|
||||
width: 100%;
|
||||
@@ -764,4 +777,4 @@ input[type="number"] {
|
||||
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 {
|
||||
name: string;
|
||||
email: string;
|
||||
profileImage: string;
|
||||
color: string;
|
||||
access: string;
|
||||
userName: string;
|
||||
Email: string;
|
||||
Access: string;
|
||||
userId: string;
|
||||
profileImage?: string;
|
||||
color?: string;
|
||||
|
||||
}
|
||||
// export interface User {
|
||||
// name: string;
|
||||
// email: string;
|
||||
// profileImage: string;
|
||||
// color: string;
|
||||
// access: string;
|
||||
// }
|
||||
|
||||
type AccessOption = {
|
||||
option: string;
|
||||
option: string;
|
||||
};
|
||||
|
||||
export type ActiveUser = {
|
||||
@@ -15,7 +24,7 @@ export type ActiveUser = {
|
||||
userName: string;
|
||||
email: string;
|
||||
activeStatus?: string; // Optional property
|
||||
position?: { x: number; y: number; z: number; };
|
||||
rotation?: { x: number; y: number; z: number; };
|
||||
target?: { x: number; y: number; z: number; };
|
||||
};
|
||||
position?: { x: number; y: number; z: number };
|
||||
rotation?: { x: number; y: number; z: number };
|
||||
target?: { x: number; y: number; z: number };
|
||||
};
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
import React, { useEffect } from "react";
|
||||
import useModuleStore, { useSubModuleStore, useThreeDStore } from "../../store/useModuleStore";
|
||||
import useModuleStore, {
|
||||
useSubModuleStore,
|
||||
useThreeDStore,
|
||||
} from "../../store/useModuleStore";
|
||||
import { usePlayerStore, useToggleStore } from "../../store/useUIToggleStore";
|
||||
import useVersionHistoryVisibleStore, {
|
||||
useActiveSubTool,
|
||||
useActiveTool,
|
||||
useAddAction,
|
||||
useDfxUpload,
|
||||
useRenameModeStore,
|
||||
useSaveVersion,
|
||||
useSelectedComment,
|
||||
@@ -49,7 +53,7 @@ const KeyPressListener: React.FC = () => {
|
||||
const { setCreateNewVersion } = useVersionHistoryStore();
|
||||
const { setVersionHistoryVisible } = useVersionHistoryVisibleStore();
|
||||
const { setSelectedComment } = useSelectedComment();
|
||||
|
||||
const { setDfxUploaded, setDxfWallGenerate } = useDfxUpload();
|
||||
const isTextInput = (element: Element | null): boolean =>
|
||||
element instanceof HTMLInputElement ||
|
||||
element instanceof HTMLTextAreaElement ||
|
||||
@@ -160,14 +164,14 @@ const KeyPressListener: React.FC = () => {
|
||||
setCreateNewVersion(true);
|
||||
setVersionHistoryVisible(true);
|
||||
setSubModule("properties");
|
||||
setActiveModule('builder');
|
||||
setActiveModule("builder");
|
||||
break;
|
||||
|
||||
case "Ctrl+H":
|
||||
if (!isPlaying) {
|
||||
setVersionHistoryVisible(true);
|
||||
setSubModule("properties");
|
||||
setActiveModule('builder');
|
||||
setActiveModule("builder");
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -195,6 +199,7 @@ const KeyPressListener: React.FC = () => {
|
||||
clearComparisonProduct();
|
||||
setIsLogListVisible(false);
|
||||
setIsRenameMode(false);
|
||||
setDfxUploaded([]);
|
||||
setSelectedComment(null);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user