first commit

This commit is contained in:
2025-06-10 15:28:23 +05:30
commit e22a2dc275
699 changed files with 100382 additions and 0 deletions

View File

@@ -0,0 +1,281 @@
import React, { useState, useRef, useEffect } from "react";
import img from "../../assets/image/image.png";
import { useNavigate } from "react-router-dom";
import { getUserData } from "./functions/getUserData";
import { useLoadingProgress, useProjectName, useSocketStore } from "../../store/builder/store";
import { viewProject } from "../../services/dashboard/viewProject";
import OuterClick from "../../utils/outerClick";
import { KebabIcon } from "../icons/ExportCommonIcons";
import { getAllProjects } from "../../services/dashboard/getAllProjects";
interface DashBoardCardProps {
projectName: string;
thumbnail: any;
projectId: string;
createdAt?: string;
isViewed?: string;
handleDeleteProject?: (projectId: string) => Promise<void>;
handleTrashDeleteProject?: (projectId: string) => Promise<void>;
handleRestoreProject?: (projectId: string) => Promise<void>;
handleDuplicateWorkspaceProject?: (
projectId: string,
projectName: string,
thumbnail: string
) => Promise<void>;
handleDuplicateRecentProject?: (
projectId: string,
projectName: string,
thumbnail: string
) => Promise<void>;
active?: string;
setIsSearchActive?: React.Dispatch<React.SetStateAction<boolean>>;
setRecentDuplicateData?: React.Dispatch<React.SetStateAction<Object>>;
setProjectDuplicateData?: React.Dispatch<React.SetStateAction<Object>>;
}
type RelativeTimeFormatUnit = any;
const DashboardCard: React.FC<DashBoardCardProps> = ({
projectName,
thumbnail,
projectId,
active,
handleDeleteProject,
handleRestoreProject,
handleTrashDeleteProject,
handleDuplicateWorkspaceProject,
handleDuplicateRecentProject,
createdAt,
setRecentDuplicateData,
setProjectDuplicateData,
}) => {
const navigate = useNavigate();
const { setProjectName } = useProjectName();
const { userId, organization, userName } = getUserData();
const [isKebabOpen, setIsKebabOpen] = useState(false);
const [renameValue, setRenameValue] = useState(projectName);
const [isRenaming, setIsRenaming] = useState(false);
const { projectSocket } = useSocketStore();
const { setLoadingProgress } = useLoadingProgress();
const kebabRef = useRef<HTMLDivElement>(null);
const navigateToProject = async (e: any) => {
if (active && active == "trash") return;
try {
const viewedProject = await viewProject(organization, projectId, userId);
console.log("Viewed project:", viewedProject);
} catch (error) {
console.error("Error opening project:", error);
}
setLoadingProgress(1)
setProjectName(projectName);
navigate(`/${projectId}`);
};
const handleOptionClick = async (option: string) => {
switch (option) {
case "delete":
if (handleDeleteProject) {
await handleDeleteProject(projectId);
} else if (handleTrashDeleteProject) {
await handleTrashDeleteProject(projectId);
}
break;
case "restore":
if (handleRestoreProject) {
await handleRestoreProject(projectId);
}
break;
case "open in new tab":
try {
await viewProject(organization, projectId, userId);
setProjectName(projectName);
setIsKebabOpen(false);
} catch (error) {
console.error("Error opening project in new tab:", error);
}
window.open(`/${projectId}`, "_blank");
break;
case "rename":
setIsRenaming(true);
break;
case "duplicate":
if (handleDuplicateWorkspaceProject) {
setProjectDuplicateData &&
setProjectDuplicateData({
projectId,
projectName,
thumbnail,
});
await handleDuplicateWorkspaceProject(projectId, projectName, thumbnail);
} else if (handleDuplicateRecentProject) {
setRecentDuplicateData &&
setRecentDuplicateData({
projectId,
projectName,
thumbnail,
});
await handleDuplicateRecentProject(projectId, projectName, thumbnail);
}
break;
default:
break;
}
setIsKebabOpen(false);
};
OuterClick({
contextClassName: ["kebab-wrapper", "kebab-options-wrapper"],
setMenuVisible: () => setIsKebabOpen(false),
});
const handleProjectName = async (projectName: string) => {
setRenameValue(projectName);
if (!projectId) return;
try {
const projects = await getAllProjects(userId, organization);
console.log("projects: ", projects);
let projectUuid = projects.Projects.find(
(val: any) => val.projectUuid === projectId || val._id === projectId
);
const updateProjects = {
projectId: projectUuid,
organization,
userId,
projectName,
thumbnail: undefined,
};
if (projectSocket) {
projectSocket.emit("v1:project:update", updateProjects);
}
} catch (error) { }
};
function getRelativeTime(dateString: string): string {
const date = new Date(dateString);
const now = new Date();
const diffInSeconds = Math.floor((now.getTime() - date.getTime()) / 1000);
const intervals: Record<RelativeTimeFormatUnit, number> = {
year: 31536000,
month: 2592000,
week: 604800,
day: 86400,
hour: 3600,
minute: 60,
second: 1,
};
const rtf = new Intl.RelativeTimeFormat("en", { numeric: "auto" });
for (const key in intervals) {
const unit = key as RelativeTimeFormatUnit;
const diff = Math.floor(diffInSeconds / intervals[unit]);
if (diff >= 1) {
return rtf.format(-diff, unit);
}
}
return "just now";
}
return (
<button
className="dashboard-card-container"
onClick={navigateToProject}
title={projectName}
onMouseLeave={() => setIsKebabOpen(false)}
>
<div className="dashboard-card-wrapper">
<div className="preview-container">
{thumbnail ? <img src={thumbnail} alt="" /> : <img src={img} alt="" />}
</div>
<div className="project-details-container" onClick={(e) => { e.stopPropagation() }}>
<div className="project-details">
{isRenaming ? (
<input
value={renameValue}
onChange={(e) => {
e.stopPropagation();
handleProjectName(e.target.value);
}}
onBlur={() => {
setIsRenaming(false);
setProjectName(renameValue);
}}
onKeyDown={(e) => {
if (e.key === "Enter") {
setProjectName(renameValue);
setIsRenaming(false);
}
}}
autoFocus
/>
) : (
<span>{renameValue}</span>
)}
{createdAt && (
<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"}
</div>
<button
className="kebab-wrapper"
onClick={(e) => {
e.stopPropagation();
setIsKebabOpen((prev) => !prev);
}}
>
<KebabIcon />
</button>
</div>
</div>
</div>
{isKebabOpen && active !== "trash" && (
<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) => (
<button
key={option}
className="option"
onClick={(e) => {
console.log("option", option);
e.stopPropagation();
handleOptionClick(option);
}}
>
{option}
</button>
))}
</div>
)}
</button>
);
};
export default DashboardCard;

View File

@@ -0,0 +1,168 @@
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";
import ProjectSocketRes from "./socket/projectSocketRes.dev";
interface Project {
_id: string;
projectName: string;
thumbnail: string;
createdBy: string;
projectUuid?: string;
createdAt: string;
isViewed?: 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 { projectSocket } = useSocketStore();
const [recentDuplicateData, setRecentDuplicateData] = useState<Object>({});
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) => {
console.log("projectId:delete ", projectId);
try {
//API for delete project
// const deletedProject = await deleteProject(
// projectId,
// userId,
// organization
// );
// console.log('deletedProject: ', deletedProject);
//socket for delete Project
const deleteProject = {
projectId,
organization,
userId,
};
if (projectSocket) {
projectSocket.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 handleDuplicateRecentProject = async (
projectId: string,
projectName: string,
thumbnail: string
) => {
const duplicateRecentProjectData = {
userId,
thumbnail,
organization,
projectUuid: projectId,
projectName,
};
projectSocket.emit("v1:project:Duplicate", duplicateRecentProjectData);
};
const renderProjects = () => {
const projectList = recentProjects[Object.keys(recentProjects)[0]];
console.log('projectList: ', projectList);
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}
createdAt={project.isViewed}
handleDeleteProject={handleDeleteProject}
handleDuplicateRecentProject={handleDuplicateRecentProject}
setRecentDuplicateData={setRecentDuplicateData}
/>
))
);
};
useEffect(() => {
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>
{recentDuplicateData && Object.keys(recentDuplicateData).length > 0 && (
<ProjectSocketRes
setIsSearchActive={setIsSearchActive}
setRecentProjects={setRecentProjects}
/>
)}
</div>
);
};
export default DashboardHome;

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

View File

@@ -0,0 +1,185 @@
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";
import ProjectSocketRes from "./socket/projectSocketRes.dev";
interface Project {
_id: string;
projectName: string;
thumbnail: string;
createdBy: string;
projectUuid?: string;
createdAt: string;
}
interface WorkspaceProjects {
[key: string]: Project[];
}
const DashboardProjects: React.FC = () => {
const [workspaceProjects, setWorkspaceProjects] = useState<WorkspaceProjects>(
{}
);
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 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 deletedProject = await deleteProject(
// projectId,
// userId,
// organization
// );
// console.log('deletedProject: ', deletedProject);
const deleteProjects = {
projectId,
organization: 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}
/>
));
};
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>
{projectDuplicateData && Object.keys(projectDuplicateData).length > 0 && (
<ProjectSocketRes
setIsSearchActive={setIsSearchActive}
setWorkspaceProjects={setWorkspaceProjects}
/>
)}
</div>
</div>
);
};
export default DashboardProjects;

View File

@@ -0,0 +1,160 @@
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";
import { deleteTrash } from "../../services/dashboard/deleteTrash";
import { useSocketStore } from "../../store/builder/store";
interface Project {
_id: string;
projectName: string;
thumbnail: string;
createdBy: string;
projectUuid?: string;
DeletedAt: string;
}
interface DiscardedProjects {
[key: string]: Project[];
}
const DashboardTrash: React.FC = () => {
const [discardedProjects, setDiscardedProjects] = useState<DiscardedProjects>(
{}
);
const [isSearchActive, setIsSearchActive] = useState(false);
const { userId, organization } = getUserData();
const { projectSocket } = useSocketStore();
const fetchTrashProjects = async () => {
try {
const projects = await getTrash(organization);
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 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 handleTrashDeleteProject = async (projectId: any) => {
try {
// const deletedProject = await deleteTrash(
// organization, projectId
// );
const deleteProjectTrash = {
projectId,
organization,
userId,
};
if (projectSocket) {
projectSocket.emit("v1:trash:delete", deleteProjectTrash);
}
setDiscardedProjects((prevDiscardedProjects: DiscardedProjects) => {
if (!Array.isArray(prevDiscardedProjects?.TrashDatas)) {
return prevDiscardedProjects;
}
const updatedProjectDatas = prevDiscardedProjects.TrashDatas.filter(
(project) => project._id !== projectId
);
// console.log('updatedProjectDatas: ', updatedProjectDatas);
return {
...prevDiscardedProjects,
TrashDatas: updatedProjectDatas,
};
});
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}
handleTrashDeleteProject={handleTrashDeleteProject}
active={"trash"}
createdAt={project.DeletedAt}
/>
));
};
useEffect(() => {
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;

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

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

View File

@@ -0,0 +1,180 @@
import React 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 { useLoadingProgress, 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 { email, userName, userId, organization } = getUserData();
const navigate = useNavigate();
const { setLoadingProgress } = useLoadingProgress();
const { projectSocket } = 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 () => {
const token = localStorage.getItem("token");
try {
const projectId = generateProjectId();
useSocketStore.getState().initializeSocket(email, organization, token);
//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,
};
console.log("projectSocket: ", projectSocket);
if (projectSocket) {
// console.log('addProject: ', addProject);
const handleResponse = (data: any) => {
console.log('Project add response:', data);
if (data.message === "Project created successfully") {
setLoadingProgress(1)
navigate(`/${data.data.projectId}`);
}
projectSocket.off("v1-project:response:add", handleResponse); // Clean up
};
projectSocket.on("v1-project:response:add", handleResponse);
console.log('addProject: ', addProject);
projectSocket.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;

View File

@@ -0,0 +1,36 @@
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) {
return {
email: '',
userId: '',
userName: '',
organization: '',
};
}
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,
};
};

View File

@@ -0,0 +1,89 @@
import React, { useEffect } from 'react';
import { useSocketStore } from '../../../store/builder/store';
import { getUserData } from '../functions/getUserData';
import { getAllProjects } from '../../../services/dashboard/getAllProjects';
import { recentlyViewed } from '../../../services/dashboard/recentlyViewed';
interface Project {
_id: string;
projectName: string;
thumbnail: string;
createdBy: string;
projectUuid?: string;
createdAt: string;
isViewed?: string
}
interface ProjectsData {
[key: string]: Project[];
}
interface ProjectSocketResProps {
setRecentProjects?: React.Dispatch<React.SetStateAction<ProjectsData>>;
setWorkspaceProjects?: React.Dispatch<React.SetStateAction<ProjectsData>>;
setIsSearchActive?: React.Dispatch<React.SetStateAction<boolean>>;
}
const ProjectSocketRes = ({
setRecentProjects,
setWorkspaceProjects,
setIsSearchActive,
}: ProjectSocketResProps) => {
const { projectSocket } = useSocketStore();
const { userId, organization } = getUserData();
useEffect(() => {
if (!projectSocket) return;
const handleAdd = (data: any) => {
// console.log('Add:', data);
};
const handleDelete = (data: any) => {
// console.log('Delete:', data);
};
const handleUpdate = (data: any) => {
// console.log('Update:', data);
};
const handleTrashDelete = (data: any) => {
// console.log('Trash Delete:', data);
};
const handleDuplicate = async (data: any) => {
console.log("Project duplicate response:", data);
if (data?.message === "Project Duplicated successfully") {
if (setWorkspaceProjects) {
const allProjects = await getAllProjects(userId, organization);
console.log('allProjects: ', allProjects);
setWorkspaceProjects(allProjects);
} else if (setRecentProjects) {
const recentProjects = await recentlyViewed(organization, userId);
setRecentProjects && setRecentProjects(recentProjects);
}
setIsSearchActive && setIsSearchActive(false);
} else {
console.warn("Duplication failed or unexpected response.");
}
};
projectSocket.on("v1-project:response:add", handleAdd);
projectSocket.on("v1-project:response:delete", handleDelete);
projectSocket.on("v1-project:response:update", handleUpdate);
projectSocket.on("v1-project:response:Duplicate", handleDuplicate);
projectSocket.on("v1:trash:response:delete", handleTrashDelete);
return () => {
projectSocket.off("v1-project:response:add", handleAdd);
projectSocket.off("v1-project:response:delete", handleDelete);
projectSocket.off("v1-project:response:update", handleUpdate);
projectSocket.off("v1-project:response:Duplicate", handleDuplicate);
projectSocket.off("v1:trash:response:delete", handleTrashDelete);
};
}, [projectSocket, userId, organization]);
return null;
};
export default ProjectSocketRes;