Compare commits

...

5 Commits

22 changed files with 731 additions and 285 deletions

View File

@@ -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"

View File

@@ -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}

View File

@@ -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,6 @@ const DashboardProjects: React.FC = () => {
); );
}; };
export default DashboardProjects; export default DashboardProjects;

View File

@@ -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);

View File

@@ -7,15 +7,22 @@ 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 { 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 +33,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 +72,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 +111,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 +123,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 +141,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>

View File

@@ -6,6 +6,7 @@ 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 { 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)
@@ -18,19 +19,35 @@ const LoadingPage: React.FC<LoadingPageProps> = ({ progress }) => {
const { userId, organization } = getUserData(); const { userId, organization } = getUserData();
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) {
console.error("User data not found in localStorage");
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);
if (filterProject) { const shared = await sharedWithMeProjects();
setProjectName(filterProject.projectName);
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 (

View File

@@ -1,21 +1,26 @@
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import useLayoutStore from "../../store/builder/uselayoutStore"; 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 DxfParser from "dxf-parser";
import { getWallPointsFromBlueprint } from "../../modules/builder/dfx/functions/getWallPointsFromBlueprint"; import { getWallPointsFromBlueprint } from "../../modules/builder/dfx/functions/getWallPointsFromBlueprint";
import { convertDXFToThree } from "../../modules/builder/dfx/functions/convertDxfToThree"; import { convertDXFToThree } from "../../modules/builder/dfx/functions/convertDxfToThree";
import { AIIcon } from "../icons/ExportCommonIcons"; import { AIIcon } from "../icons/ExportCommonIcons";
import { useBuilderStore } from "../../store/builder/useBuilderStore";
import { useSceneContext } from "../../modules/scene/sceneContext";
type DXFData = any; type DXFData = any;
const SelectFloorPlan: React.FC = () => { const SelectFloorPlan: React.FC = () => {
// Access layout state and state setters // Access layout state and state setters
const { currentLayout, setLayout } = useLayoutStore(); const { currentLayout, setLayout } = useLayoutStore();
// Access DXF-related states and setters // Access DXF-related states and setters
const { setDfxUploaded, setDfxGenerate, setObjValue, objValue } = const { setDfxUploaded, setDxfWallGenerate, setObjValue, objValue } =
useDfxUpload(); useDfxUpload();
const { activeLayer } = useActiveLayer();
const { wallThickness, wallHeight, insideMaterial, outsideMaterial } = useBuilderStore();
const { wallStore } = useSceneContext();
const { addWall } = wallStore();
// Local state to store the parsed DXF file // Local state to store the parsed DXF file
const [parsedFile, setParsedFile] = useState<DXFData | undefined>(undefined); const [parsedFile, setParsedFile] = useState<DXFData | undefined>(undefined);
const { walls } = wallStore();
// Flag to trigger generation after file upload // Flag to trigger generation after file upload
const [generate, setGenerate] = useState<boolean>(false); const [generate, setGenerate] = useState<boolean>(false);
@@ -58,8 +63,9 @@ const SelectFloorPlan: React.FC = () => {
if (parsedFile !== undefined) { if (parsedFile !== undefined) {
getWallPointsFromBlueprint({ getWallPointsFromBlueprint({
parsedData: parsedFile, parsedData: parsedFile,
setDfxGenerate, setDxfWallGenerate,
objValue, objValue,
wallThickness, wallHeight, outsideMaterial, insideMaterial, activeLayer, addWall, walls
}); });
} }
}, [generate]); }, [generate]);

View File

@@ -1,38 +1,68 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { useParams } from "react-router-dom";
const MultiEmailInvite: React.FC = () => { import { getSearchUsers } from "../../../services/factoryBuilder/collab/getSearchUsers";
const [emails, setEmails] = useState<string[]>([]); 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 [inputValue, setInputValue] = useState("");
const [isFocused, setIsFocused] = useState(false);
const handleAddEmail = () => { const { projectId } = useParams();
const trimmedEmail = inputValue.trim(); const { userId } = getUserData();
// Validate email
if (!trimmedEmail || !validateEmail(trimmedEmail)) { const handleSearchInput = async (e: React.ChangeEvent<HTMLInputElement>) => {
alert("Please enter a valid email address."); const value = e.target.value.trim();
return; 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 setInputValue("");
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
}; };
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => { const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === "Enter" || e.key === ",") { if ((e.key === "Enter" || e.key === ",") && searchResults.length > 0) {
e.preventDefault(); e.preventDefault();
handleAddEmail(); handleAddUser(searchResults[0]);
} }
}; };
const handleRemoveEmail = (emailToRemove: string) => { const handleRemoveUser = (userIdToRemove: string) => {
setEmails(emails.filter((email) => email !== emailToRemove)); setSelectedUsers(prev => prev.filter(user => user._id !== userIdToRemove));
}; };
const validateEmail = (email: string) => { const validateEmail = (email: string) => {
@@ -40,33 +70,66 @@ const MultiEmailInvite: React.FC = () => {
return emailRegex.test(email); 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 ( 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${isFocused ? " active" : ""}`}>
{emails.map((email, index) => ( {selectedUsers.map(user => (
<div key={index} className="entered-emails"> <div key={user._id} className="entered-emails">
{email} {user.Email}
<span onClick={() => handleRemoveEmail(email)}>&times;</span> <span onClick={() => handleRemoveUser(user._id)}>&times;</span>
</div> </div>
))} ))}
<input <input
type="text" type="text"
value={inputValue} value={inputValue}
onChange={(e) => setInputValue(e.target.value)} onChange={handleSearchInput}
onFocus={() => setInputFocus(true)} onFocus={() => setIsFocused(true)}
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"
/> />
</div> </div>
<div onClick={handleAddEmail} className="invite-button"> <div onClick={handleAddEmail} className="invite-button">
Invite Add
</div>
<div className="users-list-container">
{/* list available users */}
</div> </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> </div>
); );
}; };

View File

@@ -1,5 +1,5 @@
import { useEffect, useRef } from 'react'; 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 { LineBasicMaterial, Line } from 'three';
import { TransformControls } from '@react-three/drei'; import { TransformControls } from '@react-three/drei';
import { getWallPointsFromBlueprint } from './functions/getWallPointsFromBlueprint'; import { getWallPointsFromBlueprint } from './functions/getWallPointsFromBlueprint';
@@ -7,6 +7,8 @@ import * as Types from '../../../types/world/worldTypes';
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import { getUserData } from '../../../functions/getUserData'; import { getUserData } from '../../../functions/getUserData';
import { useVersionContext } from '../version/versionContext'; 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. * 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 = () => { const DxfFile = () => {
// State management hooks // State management hooks
const { dfxuploaded, dfxWallGenerate, setObjValue, objValue } = useDfxUpload(); const { dfxuploaded, dfxWallGenerate, setObjValue, objValue, setDfxUploaded } = useDfxUpload();
const { toggleView } = useToggleView(); const { toggleView } = useToggleView();
const { socket } = useSocketStore(); const { socket } = useSocketStore();
const { selectedVersionStore } = useVersionContext(); const { selectedVersionStore } = useVersionContext();
const { selectedVersion } = selectedVersionStore(); const { selectedVersion } = selectedVersionStore();
const { projectId } = useParams(); const { projectId } = useParams();
const { userId, organization } = getUserData(); const { userId, organization } = getUserData();
const { activeLayer } = useActiveLayer();
const { wallThickness, wallHeight, insideMaterial, outsideMaterial, } = useBuilderStore();
// Refs for storing line objects // Refs for storing line objects
const lineRefs = useRef<Line[]>([]); const lineRefs = useRef<Line[]>([]);
const { wallStore } = useSceneContext();
const { addWall, } = wallStore();
const { walls } = wallStore();
/** /**
* Effect hook that runs when DXF wall generation is triggered. * Effect hook that runs when DXF wall generation is triggered.
* Loads initial points and lines from the DXF data and updates the scene. * Loads initial points and lines from the DXF data and updates the scene.
*/ */
useEffect(() => { useEffect(() => {
if (dfxWallGenerate) { if (dfxWallGenerate) {
// Store generated lines in ref dfxWallGenerate.map((wall: Wall) => {
// lines.current.push(...dfxWallGenerate); const data = {
// dfxWallGenerate.map((line: any) => { wallData: wall,
// const lineData = arrayLineToObject(line as Types.Line); projectId: projectId,
versionId: selectedVersion?.versionId || '',
userId: userId,
organization: organization
}
addWall(wall);
socket.emit('v1:model-Wall:add', data);
// API
// //REST // if (projectId) {
// upsertWallApi(projectId, selectedVersion?.versionId || '', wall);
// // 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);
// })
} }
}, [dfxWallGenerate]); }, [dfxWallGenerate]);
@@ -78,10 +76,15 @@ const DxfFile = () => {
// Recalculate wall points based on new position // Recalculate wall points based on new position
getWallPointsFromBlueprint({ getWallPointsFromBlueprint({
objValue: { x: position.x, y: position.y, z: position.z }, 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 ( return (
<> <>
{/* Render DXF lines with transform controls when DXF data is available and view is toggled */} {/* Render DXF lines with transform controls when DXF data is available and view is toggled */}

View File

@@ -1,11 +1,21 @@
import { MathUtils, Vector3, BufferGeometry } from "three"; 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 DXFData = any; // Replace with actual DXF data type
type DXFEntity = any; // Replace with actual DXF entity 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 type WallLineVertex = [Vector3, string, number, string]; // Represents a wall segment with start point, ID, weight, and type
interface Props { interface Props {
parsedData?: DXFData; // Parsed DXF file data 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 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 {Props} params - Configuration parameters
* @param {DXFData} params.parsedData - Parsed DXF file data * @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 * @param {Object} params.objValue - Contains x,y,z offsets for position adjustment
*/ */
export function getWallPointsFromBlueprint({ export function getWallPointsFromBlueprint({
parsedData, parsedData,
setDfxGenerate, setDxfWallGenerate,
objValue, objValue,
wallThickness,
wallHeight,
outsideMaterial,
insideMaterial,
activeLayer,
addWall,
walls,
}: Props) { }: Props) {
// Early return if no data is provided // Early return if no data is provided
if (!parsedData) return; if (!parsedData) return;
if (!parsedData.entities) return; if (!parsedData.entities) return;
const unit = 1000; // Conversion factor from millimeters to meters 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 // Process each entity in the DXF file
parsedData.entities.forEach((entity: DXFEntity) => { parsedData.entities.forEach((entity: DXFEntity) => {
@@ -47,31 +73,41 @@ export function getWallPointsFromBlueprint({
-entity.vertices[1].y / unit -entity.vertices[1].y / unit
).add(new Vector3(objValue.x, 0, objValue.z)); ).add(new Vector3(objValue.x, 0, objValue.z));
// Check if points already exist to avoid duplicates // Create start and end points
const existingStart = wallVertex const existingStart = findExistingPoint(startVec);
.flat() const startPoint: Point = existingStart || {
.find((v) => v[0].equals(startVec)); pointUuid: MathUtils.generateUUID(),
const startPoint: WallLineVertex = existingStart || [ pointType: "Wall",
startVec, position: [startVec.x, startVec.y, startVec.z],
MathUtils.generateUUID(), // Generate unique ID for new points layer: activeLayer,
1, // Default weight };
"WallLine", // Type identifier
];
const existingEnd = wallVertex.flat().find((v) => v[0].equals(endVec)); const existingEnd = findExistingPoint(endVec);
const endPoint: WallLineVertex = existingEnd || [ const endPoint: Point = existingEnd || {
endVec, pointUuid: MathUtils.generateUUID(),
MathUtils.generateUUID(), pointType: "Wall",
1, position: [endVec.x, endVec.y, endVec.z],
"WallLine", layer: activeLayer,
]; };
// Add the line segment to our collection // Create the wall
wallVertex.push([startPoint, endPoint]); 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) // Handle LWPOLYLINE entities (connected line segments)
else if (entity.type === "LWPOLYLINE" && entity.vertices) { 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 // Process each vertex pair in the polyline
for (let i = 0; i < entity.vertices.length - 1; i++) { for (let i = 0; i < entity.vertices.length - 1; i++) {
@@ -88,37 +124,60 @@ export function getWallPointsFromBlueprint({
-entity.vertices[i + 1].y / unit -entity.vertices[i + 1].y / unit
).add(new Vector3(objValue.x, 0, objValue.z)); ).add(new Vector3(objValue.x, 0, objValue.z));
// Check for existing points // Create start and end points
const existingStart = wallVertex const existingStart = findExistingPoint(startVec);
.flat() const startPoint: Point = existingStart || {
.find((v) => v[0].equals(startVec)); pointUuid: MathUtils.generateUUID(), // Generate unique ID for new points
const startPoint: WallLineVertex = existingStart || [ pointType: "Wall", // Type identifier
startVec, position: [startVec.x, startVec.y, startVec.z], // Position in 3D space
MathUtils.generateUUID(), layer: activeLayer,
1, };
"WallLine",
];
const existingEnd = wallVertex.flat().find((v) => v[0].equals(endVec)); const existingEnd = findExistingPoint(endVec);
const endPoint: WallLineVertex = existingEnd || [ const endPoint: Point = existingEnd || {
endVec, pointUuid: MathUtils.generateUUID(), // Generate unique ID for new points
MathUtils.generateUUID(), pointType: "Wall", // Type identifier
1, position: [endVec.x, endVec.y, endVec.z], // Position in 3D space
"WallLine", 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 // Store first point and create closing segment if this is the last vertex
if (i === 0) firstPoint = startPoint; if (i === 0) firstPoint = startPoint;
if (i === entity.vertices.length - 2 && firstPoint) { 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 // Handle ARC entities
else if (entity.type === "ARC") { else if (entity.type === "ARC") {
const { center, radius, startAngle, endAngle } = entity; const { center, radius, startAngle, endAngle } = entity;
// Validate required ARC properties // Validate required ARC properties
if ( if (
!center || !center ||
@@ -154,34 +213,46 @@ export function getWallPointsFromBlueprint({
const startVec = arcPoints[i]; const startVec = arcPoints[i];
const endVec = arcPoints[i + 1]; const endVec = arcPoints[i + 1];
// Check for existing points // Create start and end points
const existingStart = wallVertex const existingStart = findExistingPoint(startVec);
.flat() const startPoint: Point = existingStart || {
.find((v) => v[0].equals(startVec)); pointUuid: MathUtils.generateUUID(),
const startPoint: WallLineVertex = existingStart || [ pointType: "Wall",
startVec, position: [startVec.x, startVec.y, startVec.z],
MathUtils.generateUUID(), layer: activeLayer,
1, };
"WallLine",
];
const existingEnd = wallVertex.flat().find((v) => v[0].equals(endVec)); const existingEnd = findExistingPoint(endVec);
const endPoint: WallLineVertex = existingEnd || [ const endPoint: Point = existingEnd || {
endVec, pointUuid: MathUtils.generateUUID(),
MathUtils.generateUUID(), pointType: "Wall",
1, position: [endVec.x, endVec.y, endVec.z],
"WallLine", 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 // Log unsupported entity types
else { else {
console.error("Unsupported entity type:", entity.type); console.error("Unsupported entity type:", entity.type);
} }
}); });
console.log("wallVertex: ", wallVertex);
// Return the generated walls through callback if provided // Return the generated walls through callback if provided
setDfxGenerate && setDfxGenerate(wallVertex); setDxfWallGenerate && setDxfWallGenerate(wallVertex);
} }

View File

@@ -71,6 +71,7 @@ function WallCreator() {
if (wallIntersect && !pointIntersects) { if (wallIntersect && !pointIntersects) {
const wall = getWallByPoints(wallIntersect.object.userData.points); const wall = getWallByPoints(wallIntersect.object.userData.points);
console.log('wall: ', wall);
if (wall) { if (wall) {
const ThroughPoint = wallIntersect.object.userData.path.getPoints(Constants.lineConfig.lineIntersectionPoints); const ThroughPoint = wallIntersect.object.userData.path.getPoints(Constants.lineConfig.lineIntersectionPoints);
let intersectionPoint = getClosestIntersection(ThroughPoint, wallIntersect.point); let intersectionPoint = getClosestIntersection(ThroughPoint, wallIntersect.point);
@@ -294,7 +295,9 @@ function WallCreator() {
position: [position.x, position.y, position.z], position: [position.x, position.y, position.z],
layer: activeLayer layer: activeLayer
}; };
console.log('newPoint: ', newPoint);
console.log('snappedPoint: ', snappedPoint);
if (snappedPosition && snappedPoint) { if (snappedPosition && snappedPoint) {
newPoint.pointUuid = snappedPoint.pointUuid; newPoint.pointUuid = snappedPoint.pointUuid;
newPoint.position = snappedPosition; newPoint.position = snappedPosition;
@@ -316,6 +319,7 @@ function WallCreator() {
} }
} }
console.log('tempPoints: ', tempPoints);
if (tempPoints.length === 0) { if (tempPoints.length === 0) {
setTempPoints([newPoint]); setTempPoints([newPoint]);
setIsCreating(true); setIsCreating(true);
@@ -330,6 +334,7 @@ function WallCreator() {
decals: [] decals: []
}; };
addWall(wall); addWall(wall);
console.log('wall: ', wall);
// API // API

View File

@@ -12,13 +12,12 @@ interface PolygonGeneratorProps {
export default function PolygonGenerator({ export default function PolygonGenerator({
groupRef, groupRef,
}: PolygonGeneratorProps) { }: PolygonGeneratorProps) {
const { aisleStore } = useSceneContext(); const { aisleStore, wallStore } = useSceneContext();
const { aisles } = aisleStore(); const { aisles } = aisleStore();
const { scene } = useThree(); const { scene } = useThree();
const { walls } = wallStore();
useEffect(() => { useEffect(() => {
// let allLines = arrayLinesToObject(lines.current);
// const wallLines = allLines?.filter((line) => line?.type === "WallLine");
const result = aisles const result = aisles
.filter( .filter(
(aisle) => (aisle) =>
@@ -61,9 +60,12 @@ export default function PolygonGenerator({
}); });
}); });
// const wallPoints = wallLines
// .map((pair) => pair?.line.map((vals) => vals.position)) const wallPoints: THREE.Vector3[][] = walls
// .filter((wall): wall is THREE.Vector3[] => !!wall); .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)) { if (!result || result.some((line) => !line)) {
@@ -81,7 +83,7 @@ export default function PolygonGenerator({
const polygons = turf.polygonize(turf.featureCollection(validLineFeatures)); const polygons = turf.polygonize(turf.featureCollection(validLineFeatures));
// renderWallGeometry(wallPoints); renderWallGeometry(wallPoints);
if (polygons.features.length > 0) { if (polygons.features.length > 0) {
polygons.features.forEach((feature) => { polygons.features.forEach((feature) => {
@@ -116,7 +118,7 @@ export default function PolygonGenerator({
}); });
} }
}, [ aisles, scene]); }, [aisles, scene, walls]);
const renderWallGeometry = (walls: THREE.Vector3[][]) => { const renderWallGeometry = (walls: THREE.Vector3[][]) => {
walls.forEach((wall) => { walls.forEach((wall) => {

View File

@@ -29,6 +29,7 @@ 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 { sharedWithMeProjects } from "../services/dashboard/sharedWithMeProject";
const Project: React.FC = () => { const Project: React.FC = () => {
let navigate = useNavigate(); let navigate = useNavigate();
@@ -54,16 +55,29 @@ 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(() => {

View 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);
}
};

View File

@@ -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);
}
};

View 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);
}
};

View 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");
}
}
};

View 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");
}
}
};

View File

@@ -587,7 +587,7 @@ export const useDfxUpload = create<any>((set: any) => ({
dfxWallGenerate: [], dfxWallGenerate: [],
objValue: { x: 0, y: 0, z: 0 }, objValue: { x: 0, y: 0, z: 0 },
setDfxUploaded: (x: any) => set({ dfxuploaded: x }), setDfxUploaded: (x: any) => set({ dfxuploaded: x }),
setDfxGenerate: (x: any) => set({ dfxWallGenerate: x }), setDxfWallGenerate: (x: any) => set({ dfxWallGenerate: x }),
setObjValue: (x: any) => set({ objValue: x }), setObjValue: (x: any) => set({ objValue: x }),
})); }));

View File

@@ -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);
} }
} }
} }

View File

@@ -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 };
}; };

View File

@@ -1,10 +1,14 @@
import React, { useEffect } from "react"; 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 { usePlayerStore, useToggleStore } from "../../store/useUIToggleStore";
import useVersionHistoryVisibleStore, { import useVersionHistoryVisibleStore, {
useActiveSubTool, useActiveSubTool,
useActiveTool, useActiveTool,
useAddAction, useAddAction,
useDfxUpload,
useRenameModeStore, useRenameModeStore,
useSaveVersion, useSaveVersion,
useSelectedComment, useSelectedComment,
@@ -49,7 +53,7 @@ const KeyPressListener: React.FC = () => {
const { setCreateNewVersion } = useVersionHistoryStore(); const { setCreateNewVersion } = useVersionHistoryStore();
const { setVersionHistoryVisible } = useVersionHistoryVisibleStore(); const { setVersionHistoryVisible } = useVersionHistoryVisibleStore();
const { setSelectedComment } = useSelectedComment(); const { setSelectedComment } = useSelectedComment();
const { setDfxUploaded, setDxfWallGenerate } = useDfxUpload();
const isTextInput = (element: Element | null): boolean => const isTextInput = (element: Element | null): boolean =>
element instanceof HTMLInputElement || element instanceof HTMLInputElement ||
element instanceof HTMLTextAreaElement || element instanceof HTMLTextAreaElement ||
@@ -160,14 +164,14 @@ const KeyPressListener: React.FC = () => {
setCreateNewVersion(true); setCreateNewVersion(true);
setVersionHistoryVisible(true); setVersionHistoryVisible(true);
setSubModule("properties"); setSubModule("properties");
setActiveModule('builder'); setActiveModule("builder");
break; break;
case "Ctrl+H": case "Ctrl+H":
if (!isPlaying) { if (!isPlaying) {
setVersionHistoryVisible(true); setVersionHistoryVisible(true);
setSubModule("properties"); setSubModule("properties");
setActiveModule('builder'); setActiveModule("builder");
} }
break; break;
@@ -195,6 +199,7 @@ const KeyPressListener: React.FC = () => {
clearComparisonProduct(); clearComparisonProduct();
setIsLogListVisible(false); setIsLogListVisible(false);
setIsRenameMode(false); setIsRenameMode(false);
setDfxUploaded([]);
setSelectedComment(null); setSelectedComment(null);
} }