Enhance dashboard components with user collaboration features and project management improvements

This commit is contained in:
2025-06-26 09:25:02 +05:30
parent 62e315e3d9
commit b3b0831a7f
10 changed files with 380 additions and 114 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 { 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"

View File

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

View File

@@ -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,142 @@ const DashboardProjects: React.FC = () => {
);
};
export default DashboardProjects;
export default DashboardProjects;
// const MyProjects = () => {
// const { projectSocket } = useSocketStore();
// const { userId, organization } = getUserData();
// const fetchAllProjects = async () => {
// try {
// const projects = await getAllProjects(userId, organization);
// if (!projects || !projects.Projects) return;
// if (JSON.stringify(projects) !== JSON.stringify(workspaceProjects)) {
// setWorkspaceProjects(projects);
// }
// } catch (error) {
// console.error("Error fetching projects:", error);
// }
// };
// const handleDeleteProject = async (projectId: any) => {
// try {
// // const deletedProject = await deleteProject(
// // projectId,
// // userId,
// // organization
// // );
// // console.log('deletedProject: ', deletedProject);
// const deleteProjects = {
// projectId,
// organization,
// userId,
// };
// //socket for deleting the project
// if (projectSocket) {
// projectSocket.emit("v1:project:delete", deleteProjects);
// } else {
// console.error("Socket is not connected.");
// }
// setWorkspaceProjects((prevDiscardedProjects: WorkspaceProjects) => {
// if (!Array.isArray(prevDiscardedProjects?.Projects)) {
// return prevDiscardedProjects;
// }
// const updatedProjectDatas = prevDiscardedProjects.Projects.filter(
// (project) => project._id !== projectId
// );
// return {
// ...prevDiscardedProjects,
// Projects: updatedProjectDatas,
// };
// });
// setIsSearchActive(false);
// } catch (error) {
// console.error("Error deleting project:", error);
// }
// };
// const handleDuplicateWorkspaceProject = async (
// projectId: string,
// projectName: string,
// thumbnail: string
// ) => {
// // await handleDuplicateProjects({
// // userId,
// // organization,
// // projectId,
// // projectName,
// // projectSocket,
// // thumbnail,
// // setWorkspaceProjects,
// // setIsSearchActive
// // });
// const duplicateProjectData = {
// userId,
// thumbnail,
// organization,
// projectUuid: projectId,
// projectName,
// };
// projectSocket.emit("v1:project:Duplicate", duplicateProjectData);
// };
// const renderProjects = () => {
// if (activeFolder !== "myProjects") return null;
// const projectList = workspaceProjects[Object.keys(workspaceProjects)[0]];
// if (!projectList?.length) {
// return <div className="empty-state">No projects found</div>;
// }
// return projectList.map((project) => (
// <DashboardCard
// key={project._id}
// projectName={project.projectName}
// thumbnail={project.thumbnail}
// projectId={project._id}
// createdAt={project.createdAt}
// handleDeleteProject={handleDeleteProject}
// setIsSearchActive={setIsSearchActive}
// handleDuplicateWorkspaceProject={handleDuplicateWorkspaceProject}
// setProjectDuplicateData={setProjectDuplicateData}
// />
// ));
// };
// const renderSharedProjects = () => {
// if (activeFolder !== "shared") return null;
// const projectList = workspaceProjects[Object.keys(workspaceProjects)[0]];
// if (!projectList?.length) {
// return <div className="empty-state">No projects found</div>;
// }
// return projectList.map((project) => (
// <DashboardCard
// key={project._id}
// projectName={project.projectName}
// thumbnail={project.thumbnail}
// projectId={project._id}
// createdAt={project.createdAt}
// handleDeleteProject={handleDeleteProject}
// setIsSearchActive={setIsSearchActive}
// handleDuplicateWorkspaceProject={handleDuplicateWorkspaceProject}
// setProjectDuplicateData={setProjectDuplicateData}
// />
// ));
// };
// useEffect(() => {
// if (!isSearchActive) {
// fetchAllProjects();
// }
// }, [isSearchActive]);
// return null
// }

View File

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