added usecase creation and dashboard changes

This commit is contained in:
2025-09-19 17:19:15 +05:30
parent 2f17781b47
commit 643a7acec5
9 changed files with 160 additions and 68 deletions

View File

@@ -1,42 +1,34 @@
import { MathUtils } from "three";
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import DashboardNavBar from "./DashboardNavBar"; import { useNavigate } from "react-router-dom";
import { projectTutorialApi } from "../../services/dashboard/projectTutorialApi";
import { TutorialCard } from "./TutorialCard"; import { TutorialCard } from "./TutorialCard";
import { useSocketStore } from "../../store/socket/useSocketStore";
import { getUserData } from "../../functions/getUserData"; import { getUserData } from "../../functions/getUserData";
import { ALPHA_ORG } from "../../pages/Dashboard"; import { ALPHA_ORG } from "../../pages/Dashboard";
import { useLoadingProgress, useProjectName } from "../../store/builder/store";
import DashboardNavBar from "./DashboardNavBar";
const DUMMY_DATA = [ import darkThemeImage from "../../assets/image/darkThemeProject.png";
//remove later import lightThemeImage from "../../assets/image/lightThemeProject.png";
{
_id: "1", import { createProjectApi } from "../../services/dashboard/createProjectApi";
name: "Robotic Arm Control", import { getUseCaseProjectsApi } from "../../services/dashboard/getuseCaseProjectsApi";
thumbnail: "https://signfix.com.au/wp-content/uploads/2017/09/placeholder-600x400.png",
updatedAt: new Date().toISOString(),
},
{
_id: "2",
name: "Simulation Basics",
thumbnail: "https://signfix.com.au/wp-content/uploads/2017/09/placeholder-600x400.png",
updatedAt: new Date().toISOString(),
},
{
_id: "3",
name: "3D Visualization",
thumbnail: "https://signfix.com.au/wp-content/uploads/2017/09/placeholder-600x400.png",
updatedAt: new Date().toISOString(),
},
];
const DashboardUseCases: React.FC = () => { const DashboardUseCases: React.FC = () => {
const [useCases, setUseCases] = useState<Tutorial[]>(DUMMY_DATA || []); // default [] const navigate = useNavigate();
const { organization } = getUserData(); const { setLoadingProgress } = useLoadingProgress();
const { setProjectName } = useProjectName();
const [useCases, setUseCases] = useState<Tutorial[]>([]);
const { projectSocket, initializeProjectSocket } = useSocketStore();
const { organization, userId } = getUserData();
const savedTheme = localStorage.getItem("theme") ?? "light";
useEffect(() => { useEffect(() => {
const fetchTutorials = async () => { const fetchTutorials = async () => {
try { try {
const res = await projectTutorialApi(); const res = await getUseCaseProjectsApi();
if (res && Array.isArray(res) && res.length > 0) { if (res.Projects.length > 0) {
setUseCases(res); setUseCases(res.Projects);
} }
} catch (error) { } catch (error) {
console.error("Error fetching useCases:", error); console.error("Error fetching useCases:", error);
@@ -46,6 +38,47 @@ const DashboardUseCases: React.FC = () => {
fetchTutorials(); fetchTutorials();
}, []); }, []);
const handleCreateNewUseCase = async () => {
const token = localStorage.getItem("token");
const refreshToken = localStorage.getItem("refreshToken");
if (!token || !refreshToken) {
console.error("token expired");
return;
}
const projectId = MathUtils.generateUUID();
initializeProjectSocket(token, refreshToken);
if (projectSocket?.connected) {
// SOCKET
const addProject = {
userId,
thumbnail: savedTheme === "dark" ? darkThemeImage : lightThemeImage,
organization: organization,
projectUuid: projectId,
projectType: "useCase",
};
projectSocket.emit("v1:project:add", addProject);
} else {
// API
createProjectApi(projectId, userId, savedTheme === "dark" ? darkThemeImage : lightThemeImage, organization, "useCase").then((data) => {
if (data.message === "Project created Successfully") {
setLoadingProgress(1);
navigate(`/projects/${data.data.projectId}`);
}
});
}
};
const handleNavigateToUseCase = (useCase: Tutorial) => {
setLoadingProgress(1);
setProjectName(useCase.projectName);
navigate(`/projects/${useCase._id}`);
};
return ( return (
<div className="dashboard-home-container"> <div className="dashboard-home-container">
<DashboardNavBar page="tutorial" /> <DashboardNavBar page="tutorial" />
@@ -54,18 +87,21 @@ const DashboardUseCases: React.FC = () => {
{organization === ALPHA_ORG && ( {organization === ALPHA_ORG && (
<div className="tutorials-main-header"> <div className="tutorials-main-header">
<div className="tutorial-buttons-container"> <div className="tutorial-buttons-container">
<button className="add-tutorials-button"> <button
onClick={() => {
handleCreateNewUseCase();
}}
className="add-tutorials-button"
>
<span>+</span> <span>+</span>
</button> </button>
</div> </div>
</div> </div>
)} )}
{useCases.length > 0 ? ( {useCases.length > 0 ? (
useCases.map((tut) => <TutorialCard key={tut._id} tutorial={tut} />) useCases.map((tut) => <TutorialCard onClick={handleNavigateToUseCase} key={tut._id} tutorial={tut} />)
) : ( ) : (
<div className="empty-state"> <div className="empty-state">No Use Cases available click on '+' button to add</div>
No Use Cases available click on '+' button to add
</div>
)} )}
</div> </div>
</div> </div>

View File

@@ -2,7 +2,6 @@ import React, { useEffect, useState } from "react";
import { getUserData } from "../../functions/getUserData"; import { getUserData } from "../../functions/getUserData";
import { useSocketStore } from "../../store/socket/useSocketStore"; import { useSocketStore } from "../../store/socket/useSocketStore";
import ProjectSocketRes from "./socket/projectSocketRes";
import DashboardNavBar from "./DashboardNavBar"; import DashboardNavBar from "./DashboardNavBar";
import DashboardCard from "./DashboardCard"; import DashboardCard from "./DashboardCard";
import MarketPlaceBanner from "./MarketPlaceBanner"; import MarketPlaceBanner from "./MarketPlaceBanner";
@@ -18,13 +17,16 @@ import { generateUniqueId } from "../../functions/generateUniqueId";
interface DashboardMainProps { interface DashboardMainProps {
activeFolder: Folder; activeFolder: Folder;
projectsData: DashboardProjectCollection;
projectsCache: Record<string, DashboardProjectCollection>;
isSearchActive: boolean;
setProjectsData: React.Dispatch<React.SetStateAction<DashboardProjectCollection>>;
setProjectsCache: React.Dispatch<React.SetStateAction<Record<string, DashboardProjectCollection>>>;
setIsSearchActive: React.Dispatch<React.SetStateAction<boolean>>;
} }
const DashboardMain: React.FC<DashboardMainProps> = ({ activeFolder }) => { const DashboardMain: React.FC<DashboardMainProps> = ({ activeFolder, projectsData, projectsCache, isSearchActive, setProjectsData, setProjectsCache, setIsSearchActive }) => {
const [activeSubFolder, setActiveSubFolder] = useState("myProjects"); const [activeSubFolder, setActiveSubFolder] = useState("myProjects");
const [projectsData, setProjectsData] = useState<DashboardProjectCollection>({});
const [projectsCache, setProjectsCache] = useState<Record<string, DashboardProjectCollection>>({});
const [isSearchActive, setIsSearchActive] = useState<boolean>(false);
const [openKebabProjectId, setOpenKebabProjectId] = useState<string | null>(null); const [openKebabProjectId, setOpenKebabProjectId] = useState<string | null>(null);
const { userId, organization } = getUserData(); const { userId, organization } = getUserData();
@@ -173,7 +175,7 @@ const DashboardMain: React.FC<DashboardMainProps> = ({ activeFolder }) => {
const key = Object.keys(projectsData)[0]; const key = Object.keys(projectsData)[0];
const projectList = projectsData[key]; const projectList = projectsData[key];
if (!projectList?.length) { if (!projectList || projectList.length === 0) {
return <div className="empty-state">No projects found</div>; return <div className="empty-state">No projects found</div>;
} }
@@ -237,8 +239,6 @@ const DashboardMain: React.FC<DashboardMainProps> = ({ activeFolder }) => {
)} )}
<div className="cards-container">{renderProjects()}</div> <div className="cards-container">{renderProjects()}</div>
<ProjectSocketRes setIsSearchActive={setIsSearchActive} {...(activeFolder === "home" ? { setRecentProjects: setProjectsData } : { setWorkspaceProjects: setProjectsData })} />
</div> </div>
</div> </div>
); );

View File

@@ -22,6 +22,8 @@ const DashboardTutorial: React.FC = () => {
fetchTutorials(); fetchTutorials();
}, []); }, []);
const handleNavigateToTutorial = () => {};
return ( return (
<div className="dashboard-home-container"> <div className="dashboard-home-container">
<DashboardNavBar page="tutorial" /> <DashboardNavBar page="tutorial" />
@@ -36,7 +38,7 @@ const DashboardTutorial: React.FC = () => {
</div> </div>
<div className="tutorials-list"> <div className="tutorials-list">
{tutorials.length > 0 ? ( {tutorials.length > 0 ? (
tutorials.map((tut) => <TutorialCard key={tut._id} tutorial={tut} />) tutorials.map((tut) => <TutorialCard onClick={handleNavigateToTutorial} key={tut._id} tutorial={tut} />)
) : ( ) : (
<div className="empty-state">No tutorials available</div> <div className="empty-state">No tutorials available</div>
)} )}

View File

@@ -1,13 +1,15 @@
import React from "react"; import React from "react";
import { DocumentationIcon, HelpIcon, HomeIcon, LogoutIcon, NotificationIcon, ProjectsIcon, TrashIcon, TutorialsIcon } from "../icons/DashboardIcon";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import darkThemeImage from "../../assets/image/darkThemeProject.png"; import { DocumentationIcon, HelpIcon, HomeIcon, LogoutIcon, NotificationIcon, ProjectsIcon, TrashIcon, TutorialsIcon } from "../icons/DashboardIcon";
import lightThemeImage from "../../assets/image/lightThemeProject.png";
import { SettingsIcon } from "../icons/ExportCommonIcons"; import { SettingsIcon } from "../icons/ExportCommonIcons";
import { getUserData } from "../../functions/getUserData"; import { getUserData } from "../../functions/getUserData";
import { useSocketStore } from "../../store/socket/useSocketStore"; import { useSocketStore } from "../../store/socket/useSocketStore";
import { useLoadingProgress } from "../../store/builder/store";
// import { createProject } from "../../services/dashboard/createProject"; import { createProjectApi } from "../../services/dashboard/createProjectApi";
import darkThemeImage from "../../assets/image/darkThemeProject.png";
import lightThemeImage from "../../assets/image/lightThemeProject.png";
interface SidePannelProps { interface SidePannelProps {
setActiveTab: React.Dispatch<React.SetStateAction<string>>; setActiveTab: React.Dispatch<React.SetStateAction<string>>;
@@ -15,8 +17,9 @@ interface SidePannelProps {
} }
const SidePannel: React.FC<SidePannelProps> = ({ setActiveTab, activeTab }) => { const SidePannel: React.FC<SidePannelProps> = ({ setActiveTab, activeTab }) => {
const { userName, userId, organization } = getUserData();
const navigate = useNavigate(); const navigate = useNavigate();
const { setLoadingProgress } = useLoadingProgress();
const { userName, userId, organization } = getUserData();
const { projectSocket, initializeProjectSocket } = useSocketStore(); const { projectSocket, initializeProjectSocket } = useSocketStore();
const savedTheme = localStorage.getItem("theme") ?? "light"; const savedTheme = localStorage.getItem("theme") ?? "light";
@@ -49,12 +52,12 @@ const SidePannel: React.FC<SidePannelProps> = ({ setActiveTab, activeTab }) => {
projectSocket.emit("v1:project:add", addProject); projectSocket.emit("v1:project:add", addProject);
} else { } else {
// API // API
// const project = await createProject( createProjectApi(projectId, userId, savedTheme === "dark" ? darkThemeImage : lightThemeImage, organization).then((data) => {
// projectId, if (data.message === "Project created Successfully") {
// userId, setLoadingProgress(1);
// savedTheme === "dark" ? darkThemeImage : lightThemeImage, navigate(`/projects/${data.data.projectId}`);
// organization }
// ); });
} }
}; };
@@ -92,7 +95,6 @@ const SidePannel: React.FC<SidePannelProps> = ({ setActiveTab, activeTab }) => {
// disabled // disabled
onClick={() => { onClick={() => {
setActiveTab("Use Cases"); setActiveTab("Use Cases");
console.warn("Use Cases comming soon");
}} }}
> >
<TutorialsIcon isActive={activeTab === "Use Cases"} /> <TutorialsIcon isActive={activeTab === "Use Cases"} />

View File

@@ -2,24 +2,20 @@ import { getUserData } from "../../functions/getUserData";
import { ALPHA_ORG } from "../../pages/Dashboard"; import { ALPHA_ORG } from "../../pages/Dashboard";
import { DeleteIcon } from "../icons/ContextMenuIcons"; import { DeleteIcon } from "../icons/ContextMenuIcons";
export const TutorialCard: React.FC<{ tutorial: Tutorial }> = ({ tutorial }) => { export const TutorialCard: React.FC<{ tutorial: Tutorial; onClick: (tutorial: Tutorial) => void }> = ({ tutorial, onClick }) => {
const { organization } = getUserData(); const { organization } = getUserData();
return ( return (
<div className="tutorial-card-container"> <div className="tutorial-card-container" onClick={() => onClick(tutorial)}>
<div <div
className="preview-container" className="preview-container"
style={{ style={{
backgroundImage: tutorial.thumbnail backgroundImage: tutorial.thumbnail ? `url(${tutorial.thumbnail})` : "linear-gradient(135deg, #ddd, #bbb)",
? `url(${tutorial.thumbnail})`
: "linear-gradient(135deg, #ddd, #bbb)",
}} }}
></div> ></div>
<div className="tutorial-details"> <div className="tutorial-details">
<div className="context"> <div className="context">
<div className="tutorial-name">{tutorial.name}</div> <div className="tutorial-name">{tutorial.projectName}</div>
<div className="updated-date"> <div className="updated-date">{new Date(tutorial.createdAt).toLocaleDateString()}</div>
{new Date(tutorial.updatedAt).toLocaleDateString()}
</div>
</div> </div>
{organization === ALPHA_ORG && ( {organization === ALPHA_ORG && (
<div className="delete-option"> <div className="delete-option">

View File

@@ -5,14 +5,23 @@ import SidePannel from "../components/Dashboard/SidePannel";
import DashboardTutorial from "../components/Dashboard/DashboardTutorial"; import DashboardTutorial from "../components/Dashboard/DashboardTutorial";
import DashboardMain from "../components/Dashboard/DashboardMain"; import DashboardMain from "../components/Dashboard/DashboardMain";
import DashboardUseCases from "../components/Dashboard/DasboardUseCases"; import DashboardUseCases from "../components/Dashboard/DasboardUseCases";
import ProjectSocketRes from "../components/Dashboard/socket/projectSocketRes";
import { useNavigate } from "react-router-dom";
export const ALPHA_ORG = "hexrfactory"; export const ALPHA_ORG = "hexrfactory";
const Dashboard: React.FC = () => { const Dashboard: React.FC = () => {
const navigate = useNavigate();
const [activeTab, setActiveTab] = useState<string>("Home"); const [activeTab, setActiveTab] = useState<string>("Home");
const [projectsData, setProjectsData] = useState<DashboardProjectCollection>({});
const [projectsCache, setProjectsCache] = useState<Record<string, DashboardProjectCollection>>({});
const [isSearchActive, setIsSearchActive] = useState<boolean>(false);
const { organization, email } = getUserData(); const { organization, email } = getUserData();
useEffect(() => { useEffect(() => {
if (email === "" || organization === "") {
navigate("/");
}
const token = localStorage.getItem("token"); const token = localStorage.getItem("token");
const refreshToken = localStorage.getItem("refreshToken"); const refreshToken = localStorage.getItem("refreshToken");
if (token && refreshToken) { if (token && refreshToken) {
@@ -20,12 +29,31 @@ const Dashboard: React.FC = () => {
} }
}, [email, organization]); }, [email, organization]);
if (email === "" || organization === "") {
return null;
}
return ( return (
<div className="dashboard-main"> <div className="dashboard-main">
<SidePannel setActiveTab={setActiveTab} activeTab={activeTab} /> <SidePannel setActiveTab={setActiveTab} activeTab={activeTab} />
{["Home", "Projects", "Shared", "Trash"].includes(activeTab) && <DashboardMain activeFolder={activeTab.toLowerCase() as Folder} />} {["Home", "Projects", "Shared", "Trash"].includes(activeTab) && (
<DashboardMain
activeFolder={activeTab.toLowerCase() as Folder}
projectsData={projectsData}
projectsCache={projectsCache}
isSearchActive={isSearchActive}
setProjectsData={setProjectsData}
setProjectsCache={setProjectsCache}
setIsSearchActive={setIsSearchActive}
/>
)}
{activeTab === "Tutorials" && <DashboardTutorial />} {activeTab === "Tutorials" && <DashboardTutorial />}
{activeTab === "Use Cases" && <DashboardUseCases />} {activeTab === "Use Cases" && <DashboardUseCases />}
<ProjectSocketRes
setIsSearchActive={setIsSearchActive}
{...((activeTab.toLowerCase() as Folder) === "home" ? { setRecentProjects: setProjectsData } : { setWorkspaceProjects: setProjectsData })}
/>
</div> </div>
); );
}; };

View File

@@ -1,6 +1,6 @@
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
export const createProjectApi = async (projectUuid: string, userId: string, thumbnail: string, organization: string) => { export const createProjectApi = async (projectUuid: string, userId: string, thumbnail: string, organization: string, projectType?: string) => {
try { try {
const response = await fetch(`${url_Backend_dwinzo}/api/V1/NewProject`, { const response = await fetch(`${url_Backend_dwinzo}/api/V1/NewProject`, {
method: "POST", method: "POST",
@@ -10,7 +10,7 @@ export const createProjectApi = async (projectUuid: string, userId: string, thum
token: localStorage.getItem("token") || "", token: localStorage.getItem("token") || "",
refresh_token: localStorage.getItem("refreshToken") || "", refresh_token: localStorage.getItem("refreshToken") || "",
}, },
body: JSON.stringify({ projectUuid, userId, thumbnail, organization }), body: JSON.stringify({ projectUuid, userId, thumbnail, organization, projectType }),
}); });
const newAccessToken = response.headers.get("x-access-token"); const newAccessToken = response.headers.get("x-access-token");
if (newAccessToken) { if (newAccessToken) {

View File

@@ -0,0 +1,28 @@
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
export const getUseCaseProjectsApi = async () => {
try {
const response = await fetch(`${url_Backend_dwinzo}/api/V1/useCases/projects`, {
method: "GET",
headers: {
Authorization: "Bearer <access_token>",
"Content-Type": "application/json",
token: localStorage.getItem("token") || "",
refresh_token: localStorage.getItem("refreshToken") || "",
},
});
const newAccessToken = response.headers.get("x-access-token");
if (newAccessToken) {
localStorage.setItem("token", newAccessToken);
}
if (!response.ok) {
console.error("Failed to get usecase Projects");
}
const result = await response.json();
return result;
} catch {
console.log("Failed to get usecase Projects");
}
};

View File

@@ -73,7 +73,7 @@ interface ListProps {
interface Tutorial { interface Tutorial {
_id: string; _id: string;
name: string; projectName: string;
thumbnail?: string; thumbnail?: string;
updatedAt: string; createdAt: string;
} }