merged with main
This commit is contained in:
135
app/src/components/Dashboard/DashboardCard.tsx
Normal file
135
app/src/components/Dashboard/DashboardCard.tsx
Normal file
@@ -0,0 +1,135 @@
|
||||
import React, { useState, useRef } from "react";
|
||||
import img from "../../assets/image/image.png";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { getUserData } from "./functions/getUserData";
|
||||
import { useProjectName } from "../../store/builder/store";
|
||||
import { viewProject } from "../../services/dashboard/viewProject";
|
||||
import OuterClick from "../../utils/outerClick";
|
||||
import { KebabIcon } from "../icons/ExportCommonIcons";
|
||||
|
||||
interface DashBoardCardProps {
|
||||
projectName: string;
|
||||
thumbnail: any;
|
||||
projectId: string;
|
||||
handleDeleteProject?: (projectId: string) => Promise<void>;
|
||||
handleRestoreProject?: (projectId: string) => Promise<void>;
|
||||
}
|
||||
|
||||
const DashboardCard: React.FC<DashBoardCardProps> = ({
|
||||
projectName,
|
||||
handleDeleteProject,
|
||||
thumbnail,
|
||||
projectId,
|
||||
handleRestoreProject,
|
||||
}) => {
|
||||
const navigate = useNavigate();
|
||||
const { setProjectName } = useProjectName();
|
||||
const { userId, organization, userName } = getUserData();
|
||||
const [isKebabOpen, setIsKebabOpen] = useState(false);
|
||||
|
||||
const kebabRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const navigateToProject = async () => {
|
||||
try {
|
||||
const viewedProject = await viewProject(organization, projectId, userId);
|
||||
console.log("Viewed project:", viewedProject);
|
||||
} catch (error) {
|
||||
console.error("Error opening project:", error);
|
||||
}
|
||||
|
||||
setProjectName(projectName);
|
||||
navigate(`/${projectId}`);
|
||||
};
|
||||
|
||||
const handleOptionClick = async (option: string) => {
|
||||
console.log('option: ', option);
|
||||
switch (option) {
|
||||
case "delete":
|
||||
if (handleDeleteProject) {
|
||||
await handleDeleteProject(projectId);
|
||||
}
|
||||
break;
|
||||
case "restore":
|
||||
if (handleRestoreProject) {
|
||||
await handleRestoreProject(projectId);
|
||||
}
|
||||
break;
|
||||
case "openInNewTab":
|
||||
window.open(`/${projectId}`, "_blank");
|
||||
break;
|
||||
case "rename":
|
||||
// Add rename logic here
|
||||
break;
|
||||
case "duplicate":
|
||||
// Add duplication logic here
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
setIsKebabOpen(false);
|
||||
};
|
||||
|
||||
OuterClick({
|
||||
contextClassName: ["kebab-wrapper", "kebab-options-wrapper"],
|
||||
setMenuVisible: () => setIsKebabOpen(false),
|
||||
});
|
||||
return (
|
||||
<button
|
||||
className="dashboard-card-container"
|
||||
onClick={navigateToProject}
|
||||
title={projectName}
|
||||
>
|
||||
<div className="dashboard-card-wrapper">
|
||||
<div className="preview-container">
|
||||
{thumbnail ? (
|
||||
<img src={thumbnail} alt="" />
|
||||
) : (
|
||||
<img src={img} alt="" />
|
||||
)}
|
||||
</div>
|
||||
<div className="project-details-container">
|
||||
<div className="project-details">
|
||||
<div className="project-name">{projectName}</div>
|
||||
<div className="project-data">24-12-2025</div>
|
||||
</div>
|
||||
<div className="users-list-container" ref={kebabRef}>
|
||||
<div className="user-profile">
|
||||
{userName ? userName.charAt(0).toUpperCase() : "A"}
|
||||
</div>
|
||||
|
||||
<button
|
||||
className="kebab-wrapper"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation(); // Prevents click from bubbling up
|
||||
console.log("Kebab menu clicked");
|
||||
setIsKebabOpen((prev) => !prev);
|
||||
}}
|
||||
>
|
||||
<KebabIcon />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{isKebabOpen && (
|
||||
<div className="kebab-options-wrapper">
|
||||
{["rename", "restore", "delete", "duplicate", "open in new tab"].map((option) => (
|
||||
<button
|
||||
key={option}
|
||||
className="option"
|
||||
onClick={(e) => {
|
||||
console.log(option);
|
||||
e.stopPropagation();
|
||||
handleOptionClick(option);
|
||||
}}
|
||||
>
|
||||
{option}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardCard;
|
||||
144
app/src/components/Dashboard/DashboardHome.tsx
Normal file
144
app/src/components/Dashboard/DashboardHome.tsx
Normal file
@@ -0,0 +1,144 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import DashboardCard from "./DashboardCard";
|
||||
import DashboardNavBar from "./DashboardNavBar";
|
||||
import MarketPlaceBanner from "./MarketPlaceBanner";
|
||||
import { getUserData } from "./functions/getUserData";
|
||||
import { useSocketStore } from "../../store/builder/store";
|
||||
import { recentlyViewed } from "../../services/dashboard/recentlyViewed";
|
||||
import { searchProject } from "../../services/dashboard/searchProjects";
|
||||
import { deleteProject } from "../../services/dashboard/deleteProject";
|
||||
|
||||
interface Project {
|
||||
_id: string;
|
||||
projectName: string;
|
||||
thumbnail: string;
|
||||
createdBy: string;
|
||||
projectUuid?: string;
|
||||
}
|
||||
|
||||
interface RecentProjectsData {
|
||||
[key: string]: Project[];
|
||||
}
|
||||
|
||||
const DashboardHome: React.FC = () => {
|
||||
const [recentProjects, setRecentProjects] = useState<RecentProjectsData>({});
|
||||
const [isSearchActive, setIsSearchActive] = useState<boolean>(false);
|
||||
const { userId, organization } = getUserData();
|
||||
const { dashBoardSocket } = useSocketStore();
|
||||
|
||||
const fetchRecentProjects = async () => {
|
||||
try {
|
||||
const projects = await recentlyViewed(organization, userId);
|
||||
console.log('RecentlyViewed: ', projects);
|
||||
|
||||
if (JSON.stringify(projects) !== JSON.stringify(recentProjects)) {
|
||||
setRecentProjects(projects);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching recent projects:", error);
|
||||
}
|
||||
};
|
||||
|
||||
const handleRecentProjectSearch = async (inputValue: string) => {
|
||||
if (!inputValue.trim()) {
|
||||
setIsSearchActive(false);
|
||||
return;
|
||||
}
|
||||
const filterRecentProcess = await searchProject(
|
||||
organization,
|
||||
userId,
|
||||
inputValue
|
||||
);
|
||||
setIsSearchActive(true);
|
||||
setRecentProjects(filterRecentProcess.message ? {} : filterRecentProcess);
|
||||
};
|
||||
|
||||
const handleDeleteProject = async (projectId: any) => {
|
||||
try {
|
||||
//API for delete project
|
||||
const deletedProject = await deleteProject(
|
||||
projectId,
|
||||
userId,
|
||||
organization
|
||||
);
|
||||
|
||||
//socket for delete Project
|
||||
// const deleteProject = {
|
||||
// projectId,
|
||||
// organization,
|
||||
// userId,
|
||||
// };
|
||||
|
||||
// if (dashBoardSocket) {
|
||||
// const handleResponse = (data: any) => {
|
||||
// console.log("Project add response:", data);
|
||||
// dashBoardSocket.off("v1-project:response:delete", handleResponse);
|
||||
// };
|
||||
|
||||
// dashBoardSocket.on("v1-project:response:delete", handleResponse);
|
||||
// dashBoardSocket.emit("v1:project:delete", deleteProject);
|
||||
// }
|
||||
|
||||
setRecentProjects((prevDiscardedProjects: RecentProjectsData) => {
|
||||
if (!Array.isArray(prevDiscardedProjects?.RecentlyViewed)) {
|
||||
return prevDiscardedProjects;
|
||||
}
|
||||
const updatedProjectDatas = prevDiscardedProjects.RecentlyViewed.filter(
|
||||
(project) => project._id !== projectId
|
||||
);
|
||||
return {
|
||||
...prevDiscardedProjects,
|
||||
RecentlyViewed: updatedProjectDatas,
|
||||
};
|
||||
});
|
||||
setIsSearchActive(false);
|
||||
} catch (error) {
|
||||
console.error("Error deleting project:", error);
|
||||
}
|
||||
};
|
||||
|
||||
const renderProjects = () => {
|
||||
const projectList = recentProjects[Object.keys(recentProjects)[0]];
|
||||
|
||||
if (!projectList?.length) {
|
||||
return <div className="empty-state">No recent projects found</div>;
|
||||
}
|
||||
|
||||
return projectList && projectList.map((project) => (
|
||||
<DashboardCard
|
||||
key={project._id}
|
||||
projectName={project.projectName}
|
||||
thumbnail={project.thumbnail}
|
||||
projectId={project._id}
|
||||
handleDeleteProject={handleDeleteProject}
|
||||
/>
|
||||
));
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
console.log('isSearchActive: ', isSearchActive);
|
||||
if (!isSearchActive) {
|
||||
fetchRecentProjects();
|
||||
}
|
||||
}, [isSearchActive]);
|
||||
|
||||
return (
|
||||
<div className="dashboard-home-container">
|
||||
<DashboardNavBar
|
||||
page="home"
|
||||
handleRecentProjectSearch={handleRecentProjectSearch}
|
||||
/>
|
||||
|
||||
<MarketPlaceBanner />
|
||||
|
||||
<div className="container">
|
||||
<h2 className="section-header">Recents</h2>
|
||||
<div className="cards-container">{renderProjects()}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardHome;
|
||||
|
||||
|
||||
43
app/src/components/Dashboard/DashboardNavBar.tsx
Normal file
43
app/src/components/Dashboard/DashboardNavBar.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
import React from "react";
|
||||
import Search from "../ui/inputs/Search";
|
||||
import { CartIcon } from "../icons/ExportModuleIcons";
|
||||
|
||||
interface DashboardNavBarProps {
|
||||
page: React.ReactNode;
|
||||
handleProjectsSearch?: (inputValue: string) => Promise<void>;
|
||||
handleTrashSearch?: (inputValue: string) => Promise<void>;
|
||||
handleRecentProjectSearch?: (inputValue: string) => Promise<void>;
|
||||
}
|
||||
|
||||
const DashboardNavBar: React.FC<DashboardNavBarProps> = ({
|
||||
page,
|
||||
handleProjectsSearch,
|
||||
handleTrashSearch,
|
||||
handleRecentProjectSearch,
|
||||
}) => {
|
||||
const handleSearch = async (inputValue: string) => {
|
||||
try {
|
||||
if (handleProjectsSearch) {
|
||||
await handleProjectsSearch(inputValue);
|
||||
} else if (handleTrashSearch) {
|
||||
await handleTrashSearch(inputValue);
|
||||
} else if (handleRecentProjectSearch) {
|
||||
await handleRecentProjectSearch(inputValue);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Search failed:", error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="dashboard-navbar-container">
|
||||
<div className="title">{page}</div>
|
||||
<div className="market-place-button">
|
||||
<CartIcon isActive /> Market Place
|
||||
</div>
|
||||
<Search onChange={handleSearch} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardNavBar;
|
||||
156
app/src/components/Dashboard/DashboardProjects.tsx
Normal file
156
app/src/components/Dashboard/DashboardProjects.tsx
Normal file
@@ -0,0 +1,156 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import DashboardNavBar from "./DashboardNavBar";
|
||||
import DashboardCard from "./DashboardCard";
|
||||
import { getUserData } from "./functions/getUserData";
|
||||
import { useSocketStore } from "../../store/builder/store";
|
||||
import { getAllProjects } from "../../services/dashboard/getAllProjects";
|
||||
import { searchProject } from "../../services/dashboard/searchProjects";
|
||||
import { deleteProject } from "../../services/dashboard/deleteProject";
|
||||
|
||||
interface Project {
|
||||
_id: string;
|
||||
projectName: string;
|
||||
thumbnail: string;
|
||||
createdBy: string;
|
||||
projectUuid?: string;
|
||||
}
|
||||
|
||||
interface WorkspaceProjects {
|
||||
[key: string]: Project[];
|
||||
}
|
||||
|
||||
const DashboardProjects: React.FC = () => {
|
||||
const [workspaceProjects, setWorkspaceProjects] = useState<WorkspaceProjects>(
|
||||
{}
|
||||
);
|
||||
const [isSearchActive, setIsSearchActive] = useState<boolean>(false);
|
||||
const [activeFolder, setActiveFolder] = useState<string>("myProjects");
|
||||
const { dashBoardSocket } = useSocketStore();
|
||||
const { userId, organization } = getUserData();
|
||||
|
||||
const fetchAllProjects = async () => {
|
||||
try {
|
||||
const projects = await getAllProjects(userId, organization);
|
||||
|
||||
if (JSON.stringify(projects) !== JSON.stringify(workspaceProjects)) {
|
||||
setWorkspaceProjects(projects);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching projects:", error);
|
||||
}
|
||||
};
|
||||
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 Organization = organization;
|
||||
const deletedProject = await deleteProject(
|
||||
projectId,
|
||||
userId,
|
||||
Organization
|
||||
);
|
||||
const deleteProjects = {
|
||||
projectId,
|
||||
organization: organization,
|
||||
userId,
|
||||
};
|
||||
|
||||
//socket for deleting the project
|
||||
// if (dashBoardSocket) {
|
||||
// const handleResponse = (data: any) => {
|
||||
// console.log("Project add response:", data);
|
||||
// dashBoardSocket.off("v1-project:response:delete", handleResponse); // Clean up
|
||||
// };
|
||||
|
||||
// dashBoardSocket.on("v1-project:response:delete", handleResponse);
|
||||
|
||||
// dashBoardSocket.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 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}
|
||||
handleDeleteProject={handleDeleteProject}
|
||||
/>
|
||||
));
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!isSearchActive) {
|
||||
fetchAllProjects();
|
||||
}
|
||||
}, [isSearchActive]);
|
||||
|
||||
return (
|
||||
<div className="dashboard-home-container">
|
||||
<DashboardNavBar
|
||||
page="projects"
|
||||
handleProjectsSearch={handleProjectsSearch}
|
||||
/>
|
||||
|
||||
<div className="container" style={{ height: "calc(100% - 87px)" }}>
|
||||
<div className="header-wrapper" style={{ display: "flex", gap: "7px" }}>
|
||||
<button
|
||||
className={`header ${activeFolder === "myProjects" && "active"}`}
|
||||
onClick={() => setActiveFolder("myProjects")}
|
||||
>
|
||||
My Projects
|
||||
</button>
|
||||
<button
|
||||
className={`header ${activeFolder === "shared" && "active"}`}
|
||||
onClick={() => setActiveFolder("shared")}
|
||||
>
|
||||
Shared with me
|
||||
</button>
|
||||
</div>
|
||||
<div className="cards-container">{renderProjects()}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardProjects;
|
||||
122
app/src/components/Dashboard/DashboardTrash.tsx
Normal file
122
app/src/components/Dashboard/DashboardTrash.tsx
Normal file
@@ -0,0 +1,122 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import DashboardCard from "./DashboardCard";
|
||||
import DashboardNavBar from "./DashboardNavBar";
|
||||
import { getUserData } from "./functions/getUserData";
|
||||
import { trashSearchProject } from "../../services/dashboard/trashSearchProject";
|
||||
import { restoreTrash } from "../../services/dashboard/restoreTrash";
|
||||
import { getTrash } from "../../services/dashboard/getTrash";
|
||||
|
||||
interface Project {
|
||||
_id: string;
|
||||
projectName: string;
|
||||
thumbnail: string;
|
||||
createdBy: string;
|
||||
projectUuid?: string;
|
||||
}
|
||||
|
||||
interface DiscardedProjects {
|
||||
[key: string]: Project[];
|
||||
}
|
||||
|
||||
const DashboardTrash: React.FC = () => {
|
||||
const [discardedProjects, setDiscardedProjects] = useState<DiscardedProjects>(
|
||||
{}
|
||||
);
|
||||
console.log("discardedProjects: ", discardedProjects);
|
||||
const [isSearchActive, setIsSearchActive] = useState(false);
|
||||
const { userId, organization } = getUserData();
|
||||
|
||||
const fetchTrashProjects = async () => {
|
||||
try {
|
||||
const projects = await getTrash(organization);
|
||||
console.log("organization: ", organization);
|
||||
console.log("trashedprojects: ", projects);
|
||||
|
||||
if (JSON.stringify(projects) !== JSON.stringify(discardedProjects)) {
|
||||
setDiscardedProjects(projects);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching trash projects:", error);
|
||||
}
|
||||
};
|
||||
const handleTrashSearch = async (inputValue: string) => {
|
||||
if (!inputValue.trim()) {
|
||||
setIsSearchActive(false);
|
||||
return;
|
||||
}
|
||||
if (!setDiscardedProjects || !setIsSearchActive) return;
|
||||
|
||||
const filterTrashedProcess = await trashSearchProject(
|
||||
organization,
|
||||
userId,
|
||||
inputValue
|
||||
);
|
||||
setIsSearchActive(true);
|
||||
setDiscardedProjects(
|
||||
filterTrashedProcess.message ? {} : filterTrashedProcess
|
||||
);
|
||||
};
|
||||
const handleRestoreProject = async (projectId: any) => {
|
||||
console.log('projectId: ', projectId);
|
||||
try {
|
||||
const Organization = organization;
|
||||
const restoreProject = await restoreTrash(Organization, projectId);
|
||||
console.log('restoreProject: ', restoreProject);
|
||||
|
||||
setDiscardedProjects((prevDiscardedProjects: DiscardedProjects) => {
|
||||
// Check if TrashDatas exists and is an array
|
||||
if (!Array.isArray(prevDiscardedProjects?.TrashDatas)) {
|
||||
console.error("TrashDatas is not an array", prevDiscardedProjects);
|
||||
return prevDiscardedProjects;
|
||||
}
|
||||
const updatedTrashDatas = prevDiscardedProjects.TrashDatas.filter(
|
||||
(project) => project._id !== projectId
|
||||
);
|
||||
return {
|
||||
...prevDiscardedProjects,
|
||||
TrashDatas: updatedTrashDatas,
|
||||
};
|
||||
});
|
||||
setIsSearchActive(false);
|
||||
} catch (error) {
|
||||
console.error("Error deleting project:", error);
|
||||
}
|
||||
};
|
||||
const renderTrashProjects = () => {
|
||||
const projectList = discardedProjects[Object.keys(discardedProjects)[0]];
|
||||
|
||||
if (!projectList?.length) {
|
||||
return <div className="empty-state">No deleted projects found</div>;
|
||||
}
|
||||
|
||||
return projectList.map((project) => (
|
||||
<DashboardCard
|
||||
key={project._id}
|
||||
projectName={project.projectName}
|
||||
thumbnail={project.thumbnail}
|
||||
projectId={project._id}
|
||||
handleRestoreProject={handleRestoreProject}
|
||||
/>
|
||||
));
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
console.log("isSearchActive:trash ", isSearchActive);
|
||||
if (!isSearchActive) {
|
||||
fetchTrashProjects();
|
||||
}
|
||||
}, [isSearchActive]);
|
||||
|
||||
return (
|
||||
<div className="dashboard-home-container">
|
||||
<DashboardNavBar page="trash" handleTrashSearch={handleTrashSearch} />
|
||||
|
||||
<div className="container" style={{ height: "calc(100% - 87px)" }}>
|
||||
<div className="header" style={{ display: "flex", gap: "7px" }}></div>
|
||||
<div className="cards-container">{renderTrashProjects()}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardTrash;
|
||||
62
app/src/components/Dashboard/DashboardTutorial.tsx
Normal file
62
app/src/components/Dashboard/DashboardTutorial.tsx
Normal file
@@ -0,0 +1,62 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import DashboardNavBar from './DashboardNavBar';
|
||||
import DashboardCard from './DashboardCard';
|
||||
import { projectTutorial } from '../../services/dashboard/projectTutorial';
|
||||
interface Project {
|
||||
_id: string;
|
||||
projectName: string;
|
||||
thumbnail: string;
|
||||
createdBy: string;
|
||||
projectUuid?: string;
|
||||
}
|
||||
|
||||
interface DiscardedProjects {
|
||||
[key: string]: Project[];
|
||||
}
|
||||
|
||||
const DashboardTutorial = () => {
|
||||
const [tutorialProject, setTutorialProject] = useState<DiscardedProjects>({})
|
||||
const handleIcon = async () => {
|
||||
try {
|
||||
let tutorial = await projectTutorial()
|
||||
setTutorialProject(tutorial)
|
||||
} catch {
|
||||
|
||||
}
|
||||
}
|
||||
useEffect(() => {
|
||||
handleIcon()
|
||||
}, [])
|
||||
const renderTrashProjects = () => {
|
||||
const projectList = tutorialProject[Object.keys(tutorialProject)[0]];
|
||||
|
||||
if (!projectList?.length) {
|
||||
return <div className="empty-state">No deleted projects found</div>;
|
||||
}
|
||||
|
||||
return projectList.map((tutorials: any) => (
|
||||
<DashboardCard
|
||||
key={tutorials._id}
|
||||
projectName={tutorials.projectName}
|
||||
thumbnail={tutorials.thumbnail}
|
||||
projectId={tutorials._id}
|
||||
/>
|
||||
));
|
||||
};
|
||||
return (
|
||||
<div className="dashboard-home-container">
|
||||
<DashboardNavBar
|
||||
page="tutorial"
|
||||
/>
|
||||
|
||||
<div className="container" style={{ height: "calc(100% - 87px)" }}>
|
||||
<div className="header" style={{ display: 'flex', gap: '7px' }}></div>
|
||||
<div className="cards-container">
|
||||
{renderTrashProjects()}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default DashboardTutorial;
|
||||
44
app/src/components/Dashboard/MarketPlaceBanner.tsx
Normal file
44
app/src/components/Dashboard/MarketPlaceBanner.tsx
Normal file
@@ -0,0 +1,44 @@
|
||||
import React from "react";
|
||||
import banner from "../../assets/image/banner.png";
|
||||
|
||||
const MarketPlaceBanner = () => {
|
||||
return (
|
||||
<div className="market-place-banner-container">
|
||||
{/* market place banner */}
|
||||
<img src={banner} alt="" />
|
||||
<div className="hero-text">
|
||||
NEW
|
||||
<br /> FALL
|
||||
<br /> COLLECTION
|
||||
</div>
|
||||
<div className="context">Unlock Creativity with Premium 3D Assets!</div>
|
||||
<div className="arrow-context">
|
||||
<svg
|
||||
width="169"
|
||||
height="120"
|
||||
viewBox="0 0 169 120"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M167.189 2C154.638 36.335 104.466 106.204 4.18872 111"
|
||||
stroke="white"
|
||||
strokeWidth="3"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M10.662 118.326L1.59439 111.524L9.47334 103.374"
|
||||
stroke="white"
|
||||
strokeWidth="3"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div className="explore-button">Explore Now</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default MarketPlaceBanner;
|
||||
129
app/src/components/Dashboard/SidePannel.tsx
Normal file
129
app/src/components/Dashboard/SidePannel.tsx
Normal file
@@ -0,0 +1,129 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import {
|
||||
DocumentationIcon,
|
||||
HelpIcon,
|
||||
HomeIcon,
|
||||
LogoutIcon,
|
||||
NotificationIcon,
|
||||
ProjectsIcon,
|
||||
TutorialsIcon,
|
||||
} from "../icons/DashboardIcon";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import darkThemeImage from "../../assets/image/darkThemeProject.png";
|
||||
import lightThemeImage from "../../assets/image/lightThemeProject.png";
|
||||
import { SettingsIcon, TrashIcon } from "../icons/ExportCommonIcons";
|
||||
import { getUserData } from "./functions/getUserData";
|
||||
import { useSocketStore } from "../../store/builder/store";
|
||||
import { createProject } from "../../services/dashboard/createProject";
|
||||
interface SidePannelProps {
|
||||
setActiveTab: React.Dispatch<React.SetStateAction<string>>;
|
||||
activeTab: string
|
||||
}
|
||||
|
||||
const SidePannel: React.FC<SidePannelProps> = ({ setActiveTab, activeTab }) => {
|
||||
const { userName, userId, organization } = getUserData();
|
||||
const navigate = useNavigate();
|
||||
const { dashBoardSocket } = useSocketStore();
|
||||
const savedTheme = localStorage.getItem("theme") ?? "light";
|
||||
function generateProjectId() {
|
||||
const randomBytes = new Uint8Array(12);
|
||||
crypto.getRandomValues(randomBytes);
|
||||
return Array.from(randomBytes, (byte) =>
|
||||
byte.toString(16).padStart(2, "0")).join("");
|
||||
}
|
||||
|
||||
|
||||
const handleCreateNewProject = async () => {
|
||||
try {
|
||||
const projectId = generateProjectId();
|
||||
console.log('projectId: ', projectId);
|
||||
navigate(`/${projectId}`);
|
||||
//API for creating new Project
|
||||
const project = await createProject(
|
||||
projectId,
|
||||
userId,
|
||||
savedTheme === "dark" ? darkThemeImage : lightThemeImage,
|
||||
organization
|
||||
);
|
||||
console.log('Created project: ', project);
|
||||
const addProject = {
|
||||
userId,
|
||||
thumbnail: savedTheme === "dark" ? darkThemeImage : lightThemeImage,
|
||||
organization: organization,
|
||||
projectUuid: projectId,
|
||||
};
|
||||
// socket for creating new project
|
||||
// console.log('dashBoardSocket: ', dashBoardSocket);
|
||||
// if (dashBoardSocket) {
|
||||
// // console.log('addProject: ', addProject);
|
||||
// const handleResponse = (data: any) => {
|
||||
// console.log('Project add response:', data);
|
||||
// dashBoardSocket.off("v1-project:response:add", handleResponse); // Clean up
|
||||
// };
|
||||
// dashBoardSocket.on("v1-project:response:add", handleResponse);
|
||||
|
||||
// console.log('addProject: ', addProject);
|
||||
// dashBoardSocket.emit("v1:project:add", addProject);
|
||||
// } else {
|
||||
// console.error("Socket is not connected.");
|
||||
// }
|
||||
} catch (error) {
|
||||
console.error("Error creating project:", error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="side-pannel-container">
|
||||
<div className="side-pannel-header">
|
||||
<div className="user-container">
|
||||
<div className="user-profile">{userName?.charAt(0).toUpperCase()}</div>
|
||||
<div className="user-name">{userName ? userName.charAt(0).toUpperCase() + userName.slice(1).toLowerCase() : "Anonymous"}</div>
|
||||
</div>
|
||||
<div className="notifications-container">
|
||||
<NotificationIcon />
|
||||
</div>
|
||||
</div>
|
||||
<div className="new-project-button" style={{ cursor: "pointer" }} onClick={handleCreateNewProject}>+ New project</div>
|
||||
<div className="side-bar-content-container">
|
||||
<div className="side-bar-options-container">
|
||||
<div className={activeTab === "Home" ? "option-list active" : "option-list"} onClick={() => setActiveTab("Home")}>
|
||||
<HomeIcon />
|
||||
Home
|
||||
</div>
|
||||
<div className={activeTab === "Projects" ? "option-list active" : "option-list"} title="Projects" onClick={() => setActiveTab("Projects")}>
|
||||
<ProjectsIcon />
|
||||
Projects
|
||||
</div>
|
||||
<div className={activeTab === "Trash" ? "option-list active" : "option-list"} title="Trash" onClick={() => setActiveTab("Trash")}>
|
||||
<TrashIcon />
|
||||
Trash
|
||||
</div>
|
||||
<div className={activeTab === "Tutorials" ? "option-list active" : "option-list"} title="coming soon" onClick={() => setActiveTab("Tutorials")}>
|
||||
<TutorialsIcon />
|
||||
Tutorials
|
||||
</div>
|
||||
<div className={activeTab === "Documentation" ? "option-list active" : "option-list"} title="coming soon" onClick={() => setActiveTab("Documentation")}>
|
||||
<DocumentationIcon />
|
||||
Documentation
|
||||
</div>
|
||||
</div>
|
||||
<div className="side-bar-options-container" title="coming soon">
|
||||
<div className="option-list">
|
||||
<SettingsIcon />
|
||||
Settings
|
||||
</div>
|
||||
<div className="option-list" style={{ cursor: "pointer" }}>
|
||||
<LogoutIcon />
|
||||
Log out
|
||||
</div>
|
||||
<div className="option-list">
|
||||
<HelpIcon />
|
||||
Help & Feedback
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SidePannel;
|
||||
31
app/src/components/Dashboard/functions/getUserData.ts
Normal file
31
app/src/components/Dashboard/functions/getUserData.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
interface UserData {
|
||||
email: string;
|
||||
userId: string;
|
||||
userName?: string; // Optional
|
||||
organization: string;
|
||||
}
|
||||
|
||||
export const getUserData = (): UserData => {
|
||||
const email = localStorage.getItem("email");
|
||||
const userId = localStorage.getItem("userId");
|
||||
const userName = localStorage.getItem("userName");
|
||||
|
||||
if (!email || !userId) {
|
||||
throw new Error("User data not found in localStorage");
|
||||
}
|
||||
|
||||
const [_, emailDomain] = email.split("@");
|
||||
|
||||
if (!emailDomain) {
|
||||
throw new Error("Invalid email format");
|
||||
}
|
||||
|
||||
const [organization] = emailDomain.split(".");
|
||||
|
||||
return {
|
||||
email: email,
|
||||
userId: userId,
|
||||
userName: userName ?? undefined,
|
||||
organization,
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user