diff --git a/app/src/components/templates/CollaborationPopup.tsx b/app/src/components/templates/CollaborationPopup.tsx index c3c81ae..4b196b7 100644 --- a/app/src/components/templates/CollaborationPopup.tsx +++ b/app/src/components/templates/CollaborationPopup.tsx @@ -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 = ({ user }) => { - const [accessSelection, setAccessSelection] = useState(user.access); + const [accessSelection, setAccessSelection] = useState(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 = ({ user }) => { {user.profileImage ? ( {`${user.name}'s ) : (
- {user.name[0]} + {user. + userName.charAt(0).toUpperCase()}
)} -
{user.name}
+
{user. + userName.charAt(0).toUpperCase() + user. + userName.slice(1).toLowerCase()}
= ({ }) => { 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 (
= ({
Share this file
-
copy link
+ {/*
copy link
*/}
{ @@ -124,7 +123,7 @@ const CollaborationPopup: React.FC = ({
- +
@@ -142,7 +141,7 @@ const CollaborationPopup: React.FC = ({
{userName && userName[0].toUpperCase()}
- {userName} + {userName && userName.charAt(0).toUpperCase() + userName.slice(1).toLowerCase()}
you
diff --git a/app/src/components/ui/inputs/MultiEmailInvite.tsx b/app/src/components/ui/inputs/MultiEmailInvite.tsx index a16de66..4a3bbf3 100644 --- a/app/src/components/ui/inputs/MultiEmailInvite.tsx +++ b/app/src/components/ui/inputs/MultiEmailInvite.tsx @@ -1,72 +1,135 @@ import React, { useState } from "react"; +import { getSearchUsers } from "../../../services/factoryBuilder/collab/getSearchUsers"; +import { useParams } from "react-router-dom"; +import { shareProject } from "../../../services/factoryBuilder/collab/shareProject"; +import { getUserData } from "../../../functions/getUserData"; -const MultiEmailInvite: React.FC = () => { - const [emails, setEmails] = useState([]); +interface UserData { + _id: string; + Email: string; + userName: string; +} + +interface MultiEmailProps { + users: any, + getData: any, +} +const MultiEmailInvite: React.FC = ({ users, getData }) => { + const [emails, setEmails] = useState([]); + const [searchedEmail, setSearchedEmail] = useState([]); + const [inputFocus, setInputFocus] = useState(false); const [inputValue, setInputValue] = useState(""); + const { projectId } = useParams(); + const { userId } = getUserData(); - const handleAddEmail = () => { + const handleAddEmail = async (selectedUser: UserData) => { + if (!projectId || !selectedUser) return const trimmedEmail = inputValue.trim(); + setEmails((prev: any[]) => { + if (!selectedUser) return prev; + const isNotCurrentUser = selectedUser._id !== userId; + const alreadyExistsInEmails = prev.some(email => email._id === selectedUser._id); + const alreadyExistsInUsers = users.some((val: any) => val.userId === selectedUser._id); + if (isNotCurrentUser && !alreadyExistsInEmails && !alreadyExistsInUsers) { + return [...prev, selectedUser]; + } - // Validate email - if (!trimmedEmail || !validateEmail(trimmedEmail)) { - alert("Please enter a valid email address."); - return; - } - - // Check for duplicates - if (emails.includes(trimmedEmail)) { - alert("This email has already been added."); - return; - } - - // Add email to the list - setEmails([...emails, trimmedEmail]); + return prev; + }); setInputValue(""); // Clear the input field after adding }; + const handleSearchMail = async (e: any) => { + setInputValue(e.target.value); + const trimmedEmail = e.target.value.trim(); - const handleKeyDown = (e: React.KeyboardEvent) => { - if (e.key === "Enter" || e.key === ",") { - e.preventDefault(); - handleAddEmail(); + if (trimmedEmail.length < 3) return; + try { + const searchedMail = await getSearchUsers(trimmedEmail); + const filteredEmail = searchedMail.sharchMail?.filtered; + if (filteredEmail) { + setSearchedEmail(filteredEmail) + } + } catch (error) { + console.error("Failed to search mail:", error); } }; - const handleRemoveEmail = (emailToRemove: string) => { - setEmails(emails.filter((email) => email !== emailToRemove)); + + const handleKeyDown = async (e: React.KeyboardEvent) => { + if (e.key === "Enter" || e.key === "," && searchedEmail.length > 0) { + e.preventDefault(); + handleAddEmail(searchedEmail[0]); + } }; + const handleRemoveEmail = (idToRemove: string) => { + setEmails((prev: any) => prev.filter((email: any) => email._id !== idToRemove)); + }; + + const validateEmail = (email: string) => { const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; return emailRegex.test(email); }; - const [inputFocus, setInputFocus] = useState(false); + const handleInvite = () => { + if (!projectId) return; + try { + emails.forEach((user: any) => { + shareProject(user._id, projectId) + .then((res) => { + console.log("sharedProject:", res); + + }) + .catch((err) => { + console.error("Error sharing project:", err); + }); + setEmails([]) + setInputValue("") + }); + setTimeout(() => { + getData() + }, 1000); + } catch (error) { + console.error("General error:", error); + } + }; return (
- {emails.map((email, index) => ( + {emails.map((email: any, index: number) => (
- {email} - handleRemoveEmail(email)}>× + {email.Email} + handleRemoveEmail(email._id)}>×
))} setInputValue(e.target.value)} + onChange={(e) => handleSearchMail(e)} onFocus={() => setInputFocus(true)} - onBlur={() => setInputFocus(false)} + // onBlur={() => setInputFocus(false)} onKeyDown={handleKeyDown} placeholder="Enter email and press Enter or comma to seperate" />
-
- Invite -
-
- {/* list available users */} +
+ Add
+ {inputFocus && inputValue.length > 2 && searchedEmail && searchedEmail.length > 0 && ( +
+ {/* list available users here */} + {searchedEmail.map((val: any, i: any) => ( +
{ + handleAddEmail(val) + setInputFocus(false) + }} key={i} > + {val?.Email} +
+ ))} +
+ )}
); }; diff --git a/app/src/services/factoryBuilder/collab/getProjectSharedList.ts b/app/src/services/factoryBuilder/collab/getProjectSharedList.ts new file mode 100644 index 0000000..0c39b99 --- /dev/null +++ b/app/src/services/factoryBuilder/collab/getProjectSharedList.ts @@ -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 ", + "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); + } +}; diff --git a/app/src/services/factoryBuilder/collab/getSearchUsers.ts b/app/src/services/factoryBuilder/collab/getSearchUsers.ts new file mode 100644 index 0000000..01ab7a9 --- /dev/null +++ b/app/src/services/factoryBuilder/collab/getSearchUsers.ts @@ -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 ", + "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); + } +}; diff --git a/app/src/services/factoryBuilder/collab/shareAccess.ts b/app/src/services/factoryBuilder/collab/shareAccess.ts new file mode 100644 index 0000000..de7a824 --- /dev/null +++ b/app/src/services/factoryBuilder/collab/shareAccess.ts @@ -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 ", + "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"); + } + } +}; diff --git a/app/src/services/factoryBuilder/collab/shareProject.ts b/app/src/services/factoryBuilder/collab/shareProject.ts new file mode 100644 index 0000000..eb14ffe --- /dev/null +++ b/app/src/services/factoryBuilder/collab/shareProject.ts @@ -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 ", + "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"); + } + } +}; diff --git a/app/src/types/users.d.ts b/app/src/types/users.d.ts index 02bc3c5..2267570 100644 --- a/app/src/types/users.d.ts +++ b/app/src/types/users.d.ts @@ -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; }; -}; \ No newline at end of file + position?: { x: number; y: number; z: number }; + rotation?: { x: number; y: number; z: number }; + target?: { x: number; y: number; z: number }; +};