Merge remote-tracking branch 'origin/main-dev' into main-demo

This commit is contained in:
2025-09-05 11:00:34 +05:30
16 changed files with 402 additions and 435 deletions

View File

@@ -1,70 +1,67 @@
import React, { useEffect, useState } from 'react';
import DashboardNavBar from './DashboardNavBar';
import DashboardCard from './DashboardCard';
import { projectTutorial } from '../../services/dashboard/projectTutorial';
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;
_id: string;
projectName: string;
thumbnail: string;
createdBy: string;
projectUuid?: string;
}
interface DiscardedProjects {
[key: string]: Project[];
[key: string]: Project[];
}
const DashboardTutorial = () => {
const [tutorialProject, setTutorialProject] = useState<DiscardedProjects>({})
const handleIcon = async () => {
try {
let tutorial = await projectTutorial()
setTutorialProject(tutorial)
} catch {
const [tutorialProject, setTutorialProject] = useState<DiscardedProjects>({});
const handleIcon = async () => {
try {
let tutorial = await projectTutorial();
setTutorialProject(tutorial);
} catch {}
};
}
const [openKebabProjectId, setOpenKebabProjectId] = useState<string | null>(
null
);
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}
openKebabProjectId={openKebabProjectId}
setOpenKebabProjectId={setOpenKebabProjectId}
/>
));
};
return (
<div className="dashboard-home-container">
<DashboardNavBar page="tutorial" />
const [openKebabProjectId, setOpenKebabProjectId] = useState<string | null>(
null
);
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}
openKebabProjectId={openKebabProjectId}
setOpenKebabProjectId={setOpenKebabProjectId}
/>
));
};
return (
<div className="dashboard-home-container">
<DashboardNavBar
page="tutorial"
/>
<div className="dashboard-container" style={{ height: "calc(100% - 87px)" }}>
<div className="header" style={{ display: 'flex', gap: '7px' }}></div>
<div className="cards-container">
{renderTrashProjects()}
</div>
</div>
</div>
);
}
<div
className="dashboard-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

@@ -1,12 +1,12 @@
import React from "react";
import {
DocumentationIcon,
HelpIcon,
HomeIcon,
LogoutIcon,
NotificationIcon,
ProjectsIcon,
TutorialsIcon,
DocumentationIcon,
HelpIcon,
HomeIcon,
LogoutIcon,
NotificationIcon,
ProjectsIcon,
TutorialsIcon,
} from "../icons/DashboardIcon";
import { useNavigate } from "react-router-dom";
import darkThemeImage from "../../assets/image/darkThemeProject.png";
@@ -15,176 +15,174 @@ 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;
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";
const { email, userName, userId, organization } = getUserData();
const navigate = useNavigate();
const { setLoadingProgress } = useLoadingProgress();
const { projectSocket, initializeSocket } = 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");
const refreshToken = localStorage.getItem("refreshToken");
if (!token || !refreshToken) {
return;
function generateProjectId() {
const randomBytes = new Uint8Array(12);
crypto.getRandomValues(randomBytes);
return Array.from(randomBytes, (byte) =>
byte.toString(16).padStart(2, "0")
).join("");
}
try {
const projectId = generateProjectId();
useSocketStore
.getState()
.initializeSocket(email, organization, token, refreshToken);
//API for creating new Project
// const project = await createProject(
// projectId,
// userId,
// savedTheme === "dark" ? darkThemeImage : lightThemeImage,
// organization
// );
const handleCreateNewProject = async () => {
const token = localStorage.getItem("token");
const refreshToken = localStorage.getItem("refreshToken");
if (!token || !refreshToken) {
console.error('token expired');
return;
}
const addProject = {
userId,
thumbnail: savedTheme === "dark" ? darkThemeImage : lightThemeImage,
organization: organization,
projectUuid: projectId,
};
const projectId = generateProjectId();
initializeSocket(email, organization, token, refreshToken);
if (projectSocket) {
const handleResponse = (data: any) => {
if (data.message === "Project created successfully") {
setLoadingProgress(1);
navigate(`/projects/${data.data.projectId}`);
}
projectSocket.off("v1-project:response:add", handleResponse); // Clean up
};
projectSocket.on("v1-project:response:add", handleResponse);
if (projectSocket?.connected) {
// SOCKET
const addProject = {
userId,
thumbnail: savedTheme === "dark" ? darkThemeImage : lightThemeImage,
organization: organization,
projectUuid: projectId,
};
const handleResponse = (data: any) => {
if (data.message === "Project created successfully") {
setLoadingProgress(1);
navigate(`/projects/${data.data.projectId}`);
}
projectSocket.off("v1-project:response:add", handleResponse);
};
projectSocket.on("v1-project:response:add", handleResponse);
projectSocket.emit("v1:project:add", addProject);
} else {
}
} catch (error) {}
};
projectSocket.emit("v1:project:add", addProject);
} else {
// API
// const project = await createProject(
// projectId,
// userId,
// savedTheme === "dark" ? darkThemeImage : lightThemeImage,
// organization
// );
}
};
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>
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");
console.warn("Tutorials comming soon");
}}
>
<TutorialsIcon />
Tutorials
</div>
<div
className={
activeTab === "Documentation"
? "option-list active"
: "option-list"
}
title="coming soon"
onClick={() => {
// setActiveTab("Documentation");
console.warn("Documentation comming soon");
}}
>
<DocumentationIcon />
Documentation
</div>
</div>
<div className="side-bar-options-container">
<div className="option-list" title="coming soon">
<SettingsIcon />
Settings
</div>
<div
className="option-list"
style={{ cursor: "pointer" }}
onClick={() => {
localStorage.clear();
navigate("/");
}}
>
<LogoutIcon />
Log out
</div>
<div className="option-list" title="coming soon">
<HelpIcon />
Help & Feedback
</div>
</div>
</div>
</div>
<div className="notifications-container">
<NotificationIcon />
</div>
</div>
<div className="new-project-button" onClick={handleCreateNewProject}>
+ New project
</div>
<div className="side-bar-content-container">
<div className="side-bar-options-container">
<button
className={
activeTab === "Home" ? "option-list active" : "option-list"
}
onClick={() => setActiveTab("Home")}
>
<HomeIcon />
Home
</button>
<button
className={
activeTab === "Projects" ? "option-list active" : "option-list"
}
title="Projects"
onClick={() => setActiveTab("Projects")}
>
<ProjectsIcon />
Projects
</button>
<button
className={
activeTab === "Trash" ? "option-list active" : "option-list"
}
title="Trash"
onClick={() => setActiveTab("Trash")}
>
<TrashIcon />
Trash
</button>
<button
className={
activeTab === "Tutorials" ? "option-list active" : "option-list"
}
title="coming soon"
disabled
onClick={() => {
// setActiveTab("Tutorials");
}}
>
<TutorialsIcon />
Tutorials
</button>
<button
className={
activeTab === "Documentation"
? "option-list active"
: "option-list"
}
title="coming soon"
disabled
onClick={() => {
// setActiveTab("Documentation");
}}
>
<DocumentationIcon />
Documentation
</button>
</div>
<div className="side-bar-options-container">
<button className="option-list" title="coming soon">
<SettingsIcon />
Settings
</button>
<button
className="option-list"
style={{ cursor: "pointer" }}
onClick={() => {
const theme = localStorage.getItem("theme") ?? "light";
localStorage.clear();
localStorage.setItem("theme", theme);
navigate("/");
}}
>
<LogoutIcon />
Log out
</button>
<button className="option-list" title="coming soon">
<HelpIcon />
Help & Feedback
</button>
</div>
</div>
</div>
);
);
};
export default SidePannel;

View File

@@ -33,6 +33,7 @@ import { useVersionContext } from "../../../modules/builder/version/versionConte
import { useBuilderStore } from "../../../store/builder/useBuilderStore";
import { recentlyViewed } from "../../../services/dashboard/recentlyViewed";
import { getUserData } from "../../../functions/getUserData";
import useRestStates from "../../../hooks/useResetStates";
function MainScene() {
const { setMainProduct } = useMainProduct();
@@ -60,6 +61,13 @@ function MainScene() {
const { selectedVersionStore } = useVersionContext();
const { selectedVersion, setSelectedVersion } = selectedVersionStore();
const { selectedComment, commentPositionState } = useSelectedComment();
const { resetStates } = useRestStates();
useEffect(() => {
return () => {
resetStates();
}
}, [])
useEffect(() => {
if (activeModule !== 'simulation') {
@@ -72,15 +80,17 @@ function MainScene() {
if (versionHistory.length > 0 && organization && userId) {
recentlyViewed(organization, userId).then((projects) => {
const recent_opened_verisionID = (Object.values(projects?.RecentlyViewed || {})[0] as any)?.Present_version._id;
if (recent_opened_verisionID) {
if (recent_opened_verisionID && projects.RecentlyViewed[0]._id === projectId) {
const version = versionHistory.find((ver) => ver.versionId === recent_opened_verisionID);
if (version) {
setSelectedVersion(version);
}
} else {
setSelectedVersion(versionHistory[0]);
}
})
}
}, [setSelectedVersion, versionHistory])
}, [setSelectedVersion, versionHistory, projectId])
const handleSelectVersion = (option: string) => {
const version = versionHistory.find((version) => version.versionName === option);

View File

@@ -1,10 +1,4 @@
import {
AddIcon,
ArrowIcon,
CloseIcon,
KebabIcon,
LocationIcon,
} from "../../../icons/ExportCommonIcons";
import { AddIcon, ArrowIcon, CloseIcon, KebabIcon, LocationIcon } from "../../../icons/ExportCommonIcons";
import RenameInput from "../../../ui/inputs/RenameInput";
import { useVersionHistoryStore } from "../../../../store/builder/useVersionHistoryStore";
import { useSubModuleStore } from "../../../../store/useModuleStore";
@@ -30,9 +24,8 @@ const VersionHistory = () => {
getVersionDataApi(projectId, version.versionId).then((versionData) => {
setSelectedVersion(version);
// console.log(versionData);
}).catch((err) => {
// console.log(err);
echo.error(err);
})
};

View File

@@ -1,8 +1,5 @@
import { useEffect, useState } from "react";
import {
FinishEditIcon,
RenameVersionIcon,
} from "../../../icons/ExportCommonIcons";
import { FinishEditIcon, RenameVersionIcon } from "../../../icons/ExportCommonIcons";
import RenderOverlay from "../../../templates/Overlay";
import { useVersionHistoryStore } from "../../../../store/builder/useVersionHistoryStore";
import { createVersionApi } from "../../../../services/factoryBuilder/versionControl/addVersionApi";
@@ -11,144 +8,144 @@ import { getUserData } from "../../../../functions/getUserData";
import { useVersionContext } from "../../../../modules/builder/version/versionContext";
const VersionSaved = () => {
const { addVersion, createNewVersion, setCreateNewVersion } = useVersionHistoryStore();
const { selectedVersionStore } = useVersionContext();
const { selectedVersion, setSelectedVersion } = selectedVersionStore();
const [newName, setNewName] = useState(new Date().toLocaleString("en-US", {
month: "short",
day: "numeric",
year: "numeric",
hour: "numeric",
minute: "2-digit",
}));
const [description, setDescription] = useState("");
const [showSaveFinish, setSaveFinish] = useState(false);
const { projectId } = useParams();
const { userId } = getUserData();
useEffect(() => {
if (createNewVersion) {
const defaultName = new Date().toLocaleString("en-US", {
const { addVersion, createNewVersion, setCreateNewVersion } = useVersionHistoryStore();
const { selectedVersionStore } = useVersionContext();
const { selectedVersion, setSelectedVersion } = selectedVersionStore();
const [newName, setNewName] = useState(new Date().toLocaleString("en-US", {
month: "short",
day: "numeric",
year: "numeric",
hour: "numeric",
minute: "2-digit",
});
setNewName(defaultName);
setDescription("");
}
}, [createNewVersion]);
}));
const [description, setDescription] = useState("");
const [saveFinish, setSaveFinish] = useState(false);
const { projectId } = useParams();
const { userId } = getUserData();
const handleSave = () => {
if (!selectedVersion || !projectId) return;
useEffect(() => {
if (createNewVersion) {
const defaultName = new Date().toLocaleString("en-US", {
month: "short",
day: "numeric",
year: "numeric",
hour: "numeric",
minute: "2-digit",
});
setNewName(defaultName);
setDescription("");
}
}, [createNewVersion]);
const updatedName = (newName.trim() || selectedVersion.versionName) ?? selectedVersion.timeStamp;
const updatedDescription = (description.trim() || selectedVersion.versionName) ?? selectedVersion.timeStamp;
const handleSave = () => {
if (!selectedVersion || !projectId) return;
createVersionApi(projectId, userId, selectedVersion.versionId, updatedName, updatedDescription).then((data) => {
setSaveFinish(true);
setCreateNewVersion(false);
const updatedName = (newName.trim() || selectedVersion.versionName) ?? selectedVersion.timeStamp;
const updatedDescription = (description.trim() || selectedVersion.versionName) ?? selectedVersion.timeStamp;
addVersion({
version: data.version,
versionId: data.versionId,
versionName: data.versionName,
versionDescription: data.description,
timeStamp: data.createdAt,
createdBy: data.createdBy.userName
})
createVersionApi(projectId, userId, selectedVersion.versionId, updatedName, updatedDescription).then((data) => {
setSaveFinish(true);
setCreateNewVersion(false);
setSelectedVersion({
version: data.version,
versionId: data.versionId,
versionName: data.versionName,
versionDescription: data.description,
timeStamp: data.createdAt,
createdBy: data.createdBy.userName
})
addVersion({
version: data.version,
versionId: data.versionId,
versionName: data.versionName,
versionDescription: data.description,
timeStamp: data.createdAt,
createdBy: data.createdBy.userName
})
setTimeout(() => {
setSelectedVersion({
version: data.version,
versionId: data.versionId,
versionName: data.versionName,
versionDescription: data.description,
timeStamp: data.createdAt,
createdBy: data.createdBy.userName
})
setTimeout(() => {
setSaveFinish(false);
}, 3000);
}).catch((err) => {
setSaveFinish(false);
setCreateNewVersion(false);
})
};
const handleCancel = () => {
setSaveFinish(false);
}, 3000);
}).catch((err) => {
setSaveFinish(false);
setCreateNewVersion(false);
})
};
setCreateNewVersion(false);
};
const handleCancel = () => {
setSaveFinish(false);
setCreateNewVersion(false);
};
if (!selectedVersion) return null;
if (!selectedVersion) return null;
return (
<div className={`versionSaved`}>
{createNewVersion &&
<RenderOverlay>
<div className="edit-version-popup-wrapper">
<div className="details-wrapper-popup-container">
<div className="header-wrapper">
<RenameVersionIcon />
<div className="label">Create Version</div>
</div>
<div className="details-wrapper">
<div className="version-name">
<input
type="text"
value={newName}
onChange={(e) => setNewName(e.target.value)}
placeholder="Enter new version name"
/>
<div className="label">
by @{selectedVersion.createdBy}{" "}{new Date(selectedVersion.timeStamp).toLocaleDateString("en-US", {
year: "numeric",
month: "long",
day: "2-digit",
})}
</div>
</div>
<div className="version-description">
<textarea
value={description}
onChange={(e) => setDescription(e.target.value)}
placeholder="Add description"
style={{ resize: "none" }}
/>
</div>
</div>
<div className="btn-wrapper">
<button className="cancel" onClick={handleCancel}>
Cancel
</button>
<button className="save" onClick={handleSave}>
Save
</button>
</div>
</div>
</div>
</RenderOverlay>
}
return (
<div className={`versionSaved`}>
{createNewVersion &&
<RenderOverlay>
<div className="edit-version-popup-wrapper">
<div className="details-wrapper-popup-container">
<div className="header-wrapper">
<RenameVersionIcon />
<div className="label">Create Version</div>
</div>
<div className="details-wrapper">
<div className="version-name">
<input
type="text"
value={newName}
onChange={(e) => setNewName(e.target.value)}
placeholder="Enter new version name"
/>
<div className="label">
by @{selectedVersion.createdBy}{" "}{new Date(selectedVersion.timeStamp).toLocaleDateString("en-US", {
year: "numeric",
month: "long",
day: "2-digit",
})}
</div>
</div>
<div className="version-description">
<textarea
value={description}
onChange={(e) => setDescription(e.target.value)}
placeholder="Add description"
style={{ resize: "none" }}
/>
</div>
</div>
<div className="btn-wrapper">
<button className="cancel" onClick={handleCancel}>
Cancel
</button>
<button className="save" onClick={handleSave}>
Save
</button>
</div>
</div>
</div>
</RenderOverlay>
}
{showSaveFinish && (
<RenderOverlay>
<div className="finishEdit-version-popup-wrapper">
<div className="finishEdit-wrapper-popup-container">
<div className="icon">
<FinishEditIcon />
</div>
<div className="versionname">
{newName.trim()}
</div>
<div className="success-message">Saved Successfully!</div>
</div>
</div>
</RenderOverlay>
)}
</div>
);
{saveFinish && (
<RenderOverlay>
<div className="finishEdit-version-popup-wrapper">
<div className="finishEdit-wrapper-popup-container">
<div className="icon">
<FinishEditIcon />
</div>
<div className="versionname">
{newName.trim()}
</div>
<div className="success-message">Saved Successfully!</div>
</div>
</div>
</RenderOverlay>
)}
</div>
);
};
export default VersionSaved;