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 DashboardNavBar from "./DashboardNavBar";
import { projectTutorialApi } from "../../services/dashboard/projectTutorialApi";
import { useNavigate } from "react-router-dom";
import { TutorialCard } from "./TutorialCard";
import { useSocketStore } from "../../store/socket/useSocketStore";
import { getUserData } from "../../functions/getUserData";
import { ALPHA_ORG } from "../../pages/Dashboard";
import { useLoadingProgress, useProjectName } from "../../store/builder/store";
import DashboardNavBar from "./DashboardNavBar";
const DUMMY_DATA = [
//remove later
{
_id: "1",
name: "Robotic Arm Control",
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(),
},
];
import darkThemeImage from "../../assets/image/darkThemeProject.png";
import lightThemeImage from "../../assets/image/lightThemeProject.png";
import { createProjectApi } from "../../services/dashboard/createProjectApi";
import { getUseCaseProjectsApi } from "../../services/dashboard/getuseCaseProjectsApi";
const DashboardUseCases: React.FC = () => {
const [useCases, setUseCases] = useState<Tutorial[]>(DUMMY_DATA || []); // default []
const { organization } = getUserData();
const navigate = useNavigate();
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(() => {
const fetchTutorials = async () => {
try {
const res = await projectTutorialApi();
if (res && Array.isArray(res) && res.length > 0) {
setUseCases(res);
const res = await getUseCaseProjectsApi();
if (res.Projects.length > 0) {
setUseCases(res.Projects);
}
} catch (error) {
console.error("Error fetching useCases:", error);
@@ -46,6 +38,47 @@ const DashboardUseCases: React.FC = () => {
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 (
<div className="dashboard-home-container">
<DashboardNavBar page="tutorial" />
@@ -54,18 +87,21 @@ const DashboardUseCases: React.FC = () => {
{organization === ALPHA_ORG && (
<div className="tutorials-main-header">
<div className="tutorial-buttons-container">
<button className="add-tutorials-button">
<button
onClick={() => {
handleCreateNewUseCase();
}}
className="add-tutorials-button"
>
<span>+</span>
</button>
</div>
</div>
)}
{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">
No Use Cases available click on '+' button to add
</div>
<div className="empty-state">No Use Cases available click on '+' button to add</div>
)}
</div>
</div>

View File

@@ -2,7 +2,6 @@ import React, { useEffect, useState } from "react";
import { getUserData } from "../../functions/getUserData";
import { useSocketStore } from "../../store/socket/useSocketStore";
import ProjectSocketRes from "./socket/projectSocketRes";
import DashboardNavBar from "./DashboardNavBar";
import DashboardCard from "./DashboardCard";
import MarketPlaceBanner from "./MarketPlaceBanner";
@@ -18,13 +17,16 @@ import { generateUniqueId } from "../../functions/generateUniqueId";
interface DashboardMainProps {
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 [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 { userId, organization } = getUserData();
@@ -173,7 +175,7 @@ const DashboardMain: React.FC<DashboardMainProps> = ({ activeFolder }) => {
const key = Object.keys(projectsData)[0];
const projectList = projectsData[key];
if (!projectList?.length) {
if (!projectList || projectList.length === 0) {
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>
<ProjectSocketRes setIsSearchActive={setIsSearchActive} {...(activeFolder === "home" ? { setRecentProjects: setProjectsData } : { setWorkspaceProjects: setProjectsData })} />
</div>
</div>
);

View File

@@ -22,6 +22,8 @@ const DashboardTutorial: React.FC = () => {
fetchTutorials();
}, []);
const handleNavigateToTutorial = () => {};
return (
<div className="dashboard-home-container">
<DashboardNavBar page="tutorial" />
@@ -36,7 +38,7 @@ const DashboardTutorial: React.FC = () => {
</div>
<div className="tutorials-list">
{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>
)}

View File

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

View File

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

View File

@@ -5,14 +5,23 @@ import SidePannel from "../components/Dashboard/SidePannel";
import DashboardTutorial from "../components/Dashboard/DashboardTutorial";
import DashboardMain from "../components/Dashboard/DashboardMain";
import DashboardUseCases from "../components/Dashboard/DasboardUseCases";
import ProjectSocketRes from "../components/Dashboard/socket/projectSocketRes";
import { useNavigate } from "react-router-dom";
export const ALPHA_ORG = "hexrfactory";
const Dashboard: React.FC = () => {
const navigate = useNavigate();
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();
useEffect(() => {
if (email === "" || organization === "") {
navigate("/");
}
const token = localStorage.getItem("token");
const refreshToken = localStorage.getItem("refreshToken");
if (token && refreshToken) {
@@ -20,12 +29,31 @@ const Dashboard: React.FC = () => {
}
}, [email, organization]);
if (email === "" || organization === "") {
return null;
}
return (
<div className="dashboard-main">
<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 === "Use Cases" && <DashboardUseCases />}
<ProjectSocketRes
setIsSearchActive={setIsSearchActive}
{...((activeTab.toLowerCase() as Folder) === "home" ? { setRecentProjects: setProjectsData } : { setWorkspaceProjects: setProjectsData })}
/>
</div>
);
};

View File

@@ -1,6 +1,6 @@
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 {
const response = await fetch(`${url_Backend_dwinzo}/api/V1/NewProject`, {
method: "POST",
@@ -10,7 +10,7 @@ export const createProjectApi = async (projectUuid: string, userId: string, thum
token: localStorage.getItem("token") || "",
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");
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 {
_id: string;
name: string;
projectName: string;
thumbnail?: string;
updatedAt: string;
createdAt: string;
}