Merge remote-tracking branch 'origin/main-demo' into feature/layout-comparison-version
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import React, { useState, useRef } from "react";
|
||||
import React, { useState, useRef, useCallback, useEffect } from "react";
|
||||
import { createPortal } from "react-dom";
|
||||
import img from "../../assets/image/image.png";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import img from "../../assets/image/image.png";
|
||||
import { getUserData } from "../../functions/getUserData";
|
||||
import {
|
||||
useLoadingProgress,
|
||||
@@ -16,7 +16,7 @@ import { getAllProjects } from "../../services/dashboard/getAllProjects";
|
||||
|
||||
interface DashBoardCardProps {
|
||||
projectName: string;
|
||||
thumbnail: any;
|
||||
thumbnail: string;
|
||||
projectId: string;
|
||||
createdAt?: string;
|
||||
isViewed?: string;
|
||||
@@ -35,13 +35,29 @@ interface DashBoardCardProps {
|
||||
projectName: string,
|
||||
thumbnail: string
|
||||
) => Promise<void>;
|
||||
active?: string;
|
||||
active?: "shared" | "trash" | "recent" | string;
|
||||
setIsSearchActive?: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
setRecentDuplicateData?: React.Dispatch<React.SetStateAction<Object>>;
|
||||
setProjectDuplicateData?: React.Dispatch<React.SetStateAction<Object>>;
|
||||
setRecentDuplicateData?: React.Dispatch<React.SetStateAction<object>>;
|
||||
setProjectDuplicateData?: React.Dispatch<React.SetStateAction<object>>;
|
||||
setActiveFolder?: React.Dispatch<React.SetStateAction<string>>;
|
||||
openKebabProjectId: string | null;
|
||||
setOpenKebabProjectId: React.Dispatch<React.SetStateAction<string | null>>;
|
||||
}
|
||||
type RelativeTimeFormatUnit = any;
|
||||
|
||||
type RelativeTimeFormatUnit =
|
||||
| "year"
|
||||
| "month"
|
||||
| "week"
|
||||
| "day"
|
||||
| "hour"
|
||||
| "minute"
|
||||
| "second";
|
||||
|
||||
const kebabOptionsMap: Record<string, string[]> = {
|
||||
default: ["rename", "delete", "duplicate", "open in new tab"],
|
||||
trash: ["restore", "delete"],
|
||||
shared: ["duplicate", "open in new tab"],
|
||||
};
|
||||
|
||||
const DashboardCard: React.FC<DashBoardCardProps> = ({
|
||||
projectName,
|
||||
@@ -58,131 +74,143 @@ const DashboardCard: React.FC<DashBoardCardProps> = ({
|
||||
setRecentDuplicateData,
|
||||
setProjectDuplicateData,
|
||||
setActiveFolder,
|
||||
openKebabProjectId,
|
||||
setOpenKebabProjectId,
|
||||
}) => {
|
||||
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 isKebabOpen = openKebabProjectId === projectId;
|
||||
const [renameValue, setRenameValue] = useState(projectName);
|
||||
const [isRenaming, setIsRenaming] = useState(false);
|
||||
const kebabRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const navigateToProject = async (e: any) => {
|
||||
if (active && active === "trash") return;
|
||||
try {
|
||||
// const viewProjects = await viewProject(organization, projectId, userId);
|
||||
// Close kebab when clicking outside
|
||||
OuterClick({
|
||||
contextClassName: [`tag-${projectId}`],
|
||||
setMenuVisible: () => {
|
||||
if (isKebabOpen) setOpenKebabProjectId(null);
|
||||
},
|
||||
});
|
||||
|
||||
setLoadingProgress(1);
|
||||
setProjectName(projectName);
|
||||
navigate(`/projects/${projectId}`);
|
||||
} catch {}
|
||||
};
|
||||
const navigateToProject = useCallback(() => {
|
||||
if (active === "trash") return;
|
||||
setLoadingProgress(1);
|
||||
setProjectName(projectName);
|
||||
navigate(`/projects/${projectId}`);
|
||||
}, [
|
||||
active,
|
||||
projectId,
|
||||
projectName,
|
||||
navigate,
|
||||
setLoadingProgress,
|
||||
setProjectName,
|
||||
]);
|
||||
|
||||
const handleOptionClick = async (option: string) => {
|
||||
switch (option) {
|
||||
case "delete":
|
||||
if (handleDeleteProject) {
|
||||
handleDeleteProject(projectId);
|
||||
} else if (handleTrashDeleteProject) {
|
||||
handleTrashDeleteProject(projectId);
|
||||
const getOptions = useCallback(() => {
|
||||
if (active === "trash") return kebabOptionsMap.trash;
|
||||
if (active === "shared" || (createdBy && createdBy._id !== userId)) {
|
||||
return kebabOptionsMap.shared;
|
||||
}
|
||||
return kebabOptionsMap.default;
|
||||
}, [active, createdBy, userId]);
|
||||
|
||||
const handleProjectName = useCallback(
|
||||
async (newName: string) => {
|
||||
setRenameValue(newName);
|
||||
if (!projectId) return;
|
||||
|
||||
try {
|
||||
const projects = await getAllProjects(userId, organization);
|
||||
const projectUuid = projects?.Projects?.find(
|
||||
(val: any) => val.projectUuid === projectId || val._id === projectId
|
||||
);
|
||||
if (!projectUuid) return;
|
||||
|
||||
const updatePayload = {
|
||||
projectId: projectUuid._id,
|
||||
organization,
|
||||
userId,
|
||||
projectName: newName,
|
||||
};
|
||||
|
||||
if (projectSocket) {
|
||||
projectSocket.emit("v1:project:update", updatePayload);
|
||||
}
|
||||
break;
|
||||
case "restore":
|
||||
if (handleRestoreProject) {
|
||||
await handleRestoreProject(projectId);
|
||||
}
|
||||
break;
|
||||
case "open in new tab":
|
||||
try {
|
||||
if (active === "shared" && createdBy) {
|
||||
// const newTab = await viewProject(
|
||||
// organization,
|
||||
// projectId,
|
||||
// createdBy?._id
|
||||
// );
|
||||
} else {
|
||||
// const newTab = await viewProject(organization, projectId, userId);
|
||||
} catch {
|
||||
// silent fail
|
||||
}
|
||||
},
|
||||
[projectId, userId, organization, projectSocket]
|
||||
);
|
||||
|
||||
setProjectName(projectName);
|
||||
setIsKebabOpen(false);
|
||||
}
|
||||
} catch (error) {}
|
||||
window.open(`/projects/${projectId}`, "_blank");
|
||||
break;
|
||||
case "rename":
|
||||
setIsRenaming(true);
|
||||
break;
|
||||
case "duplicate":
|
||||
if (handleDuplicateWorkspaceProject) {
|
||||
setProjectDuplicateData &&
|
||||
setProjectDuplicateData({
|
||||
const handleOptionClick = useCallback(
|
||||
async (option: string) => {
|
||||
switch (option) {
|
||||
case "delete":
|
||||
await (handleDeleteProject?.(projectId) ??
|
||||
handleTrashDeleteProject?.(projectId));
|
||||
break;
|
||||
case "restore":
|
||||
await handleRestoreProject?.(projectId);
|
||||
break;
|
||||
case "open in new tab":
|
||||
setProjectName(projectName);
|
||||
window.open(`/projects/${projectId}`, "_blank");
|
||||
break;
|
||||
case "rename":
|
||||
setIsRenaming(true);
|
||||
break;
|
||||
case "duplicate":
|
||||
if (handleDuplicateWorkspaceProject) {
|
||||
setProjectDuplicateData?.({ projectId, projectName, thumbnail });
|
||||
await handleDuplicateWorkspaceProject(
|
||||
projectId,
|
||||
projectName,
|
||||
thumbnail,
|
||||
});
|
||||
await handleDuplicateWorkspaceProject(
|
||||
projectId,
|
||||
projectName,
|
||||
thumbnail,
|
||||
userId
|
||||
);
|
||||
if (active === "shared" && setActiveFolder) {
|
||||
setActiveFolder("myProjects");
|
||||
}
|
||||
} else if (handleDuplicateRecentProject) {
|
||||
setRecentDuplicateData &&
|
||||
setRecentDuplicateData({
|
||||
userId
|
||||
);
|
||||
if (active === "shared") {
|
||||
setActiveFolder?.("myProjects");
|
||||
}
|
||||
} else if (handleDuplicateRecentProject) {
|
||||
setRecentDuplicateData?.({
|
||||
projectId,
|
||||
projectName,
|
||||
thumbnail,
|
||||
userId,
|
||||
});
|
||||
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);
|
||||
if (!projects || !projects.Projects) return;
|
||||
let projectUuid = projects.Projects.find(
|
||||
(val: any) => val.projectUuid === projectId || val._id === projectId
|
||||
);
|
||||
const updateProjects = {
|
||||
projectId: projectUuid?._id,
|
||||
organization,
|
||||
userId,
|
||||
projectName,
|
||||
thumbnail: undefined,
|
||||
};
|
||||
// const updatedProjectName = await updateProject(
|
||||
// projectUuid._id,
|
||||
// userId,
|
||||
// organization,
|
||||
// undefined,
|
||||
// projectName
|
||||
// );
|
||||
if (projectSocket) {
|
||||
projectSocket.emit("v1:project:update", updateProjects);
|
||||
await handleDuplicateRecentProject(
|
||||
projectId,
|
||||
projectName,
|
||||
thumbnail
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
} catch (error) {}
|
||||
};
|
||||
},
|
||||
[
|
||||
projectId,
|
||||
projectName,
|
||||
thumbnail,
|
||||
userId,
|
||||
active,
|
||||
handleDeleteProject,
|
||||
handleTrashDeleteProject,
|
||||
handleRestoreProject,
|
||||
handleDuplicateWorkspaceProject,
|
||||
handleDuplicateRecentProject,
|
||||
setProjectName,
|
||||
setProjectDuplicateData,
|
||||
setRecentDuplicateData,
|
||||
setActiveFolder,
|
||||
]
|
||||
);
|
||||
|
||||
function getRelativeTime(dateString: string): string {
|
||||
const getRelativeTime = useCallback((dateString: string): string => {
|
||||
const date = new Date(dateString);
|
||||
const now = new Date();
|
||||
const diffInSeconds = Math.floor((now.getTime() - date.getTime()) / 1000);
|
||||
@@ -198,69 +226,56 @@ const DashboardCard: React.FC<DashBoardCardProps> = ({
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
for (const [unit, seconds] of Object.entries(intervals)) {
|
||||
const diff = Math.floor(diffInSeconds / seconds);
|
||||
if (diff >= 1) return rtf.format(-diff, unit as RelativeTimeFormatUnit);
|
||||
}
|
||||
return "just now";
|
||||
}
|
||||
}, []);
|
||||
|
||||
const kebabOptionsMap: Record<string, string[]> = {
|
||||
default: ["rename", "delete", "duplicate", "open in new tab"],
|
||||
trash: ["restore", "delete"],
|
||||
shared: ["duplicate", "open in new tab"],
|
||||
};
|
||||
const [kebabPosition, setKebabPosition] = useState({ top: 0, left: 0 });
|
||||
|
||||
const getOptions = () => {
|
||||
if (active === "trash") return kebabOptionsMap.trash;
|
||||
if (active === "shared") return kebabOptionsMap.shared;
|
||||
if (createdBy && createdBy?._id !== userId) return kebabOptionsMap.shared;
|
||||
return kebabOptionsMap.default;
|
||||
};
|
||||
useEffect(() => {
|
||||
if (isKebabOpen && kebabRef.current) {
|
||||
const rect = kebabRef.current.getBoundingClientRect();
|
||||
setKebabPosition({
|
||||
top: rect.bottom + window.scrollY,
|
||||
left: rect.left + window.scrollX - 80,
|
||||
});
|
||||
}
|
||||
}, [isKebabOpen]);
|
||||
|
||||
return (
|
||||
<button
|
||||
<div
|
||||
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="" />
|
||||
)}
|
||||
<img src={thumbnail || img} alt={`${projectName} thumbnail`} />
|
||||
</div>
|
||||
|
||||
<div
|
||||
className="project-details-container"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<div className="project-details">
|
||||
{isRenaming ? (
|
||||
<input
|
||||
value={renameValue}
|
||||
onChange={(e) => {
|
||||
e.stopPropagation();
|
||||
handleProjectName(e.target.value);
|
||||
}}
|
||||
onChange={(e) => handleProjectName(e.target.value)}
|
||||
onBlur={() => {
|
||||
setIsRenaming(false);
|
||||
setProjectName(renameValue);
|
||||
}}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter") {
|
||||
setProjectName(renameValue);
|
||||
setIsRenaming(false);
|
||||
setProjectName(renameValue);
|
||||
}
|
||||
}}
|
||||
aria-label="Rename project"
|
||||
autoFocus
|
||||
/>
|
||||
) : (
|
||||
@@ -268,46 +283,37 @@ const DashboardCard: React.FC<DashBoardCardProps> = ({
|
||||
)}
|
||||
{createdAt && (
|
||||
<div className="project-data">
|
||||
{active && active === "trash" ? `Trashed by you` : `Edited `}{" "}
|
||||
{active === "trash" ? "Trashed" : "Edited"}{" "}
|
||||
{getRelativeTime(createdAt)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="users-list-container" ref={kebabRef}>
|
||||
<div className="user-profile">
|
||||
{!createdBy
|
||||
? userName
|
||||
? userName?.charAt(0).toUpperCase()
|
||||
: "A"
|
||||
: createdBy?.userName?.charAt(0).toUpperCase()}
|
||||
{(createdBy?.userName || userName || "A").charAt(0).toUpperCase()}
|
||||
</div>
|
||||
<button
|
||||
className="kebab-wrapper"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setIsKebabOpen((prev) => !prev);
|
||||
setOpenKebabProjectId(isKebabOpen ? null : projectId);
|
||||
}}
|
||||
aria-haspopup="true"
|
||||
aria-expanded={isKebabOpen}
|
||||
aria-label="Project options"
|
||||
>
|
||||
<KebabIcon />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{isKebabOpen &&
|
||||
createPortal(
|
||||
<div
|
||||
className="kebab-options-wrapper"
|
||||
style={{
|
||||
position: "fixed",
|
||||
zIndex: 9999,
|
||||
top: kebabRef.current
|
||||
? kebabRef.current.getBoundingClientRect().bottom +
|
||||
window.scrollY
|
||||
: 0,
|
||||
left: kebabRef.current
|
||||
? kebabRef.current.getBoundingClientRect().left + window.scrollX
|
||||
: 0,
|
||||
}}
|
||||
className={`kebab-options-wrapper tag-${projectId}`}
|
||||
style={{ position: "fixed", zIndex: 9999, ...kebabPosition }}
|
||||
>
|
||||
{getOptions().map((option) => (
|
||||
<button
|
||||
@@ -324,7 +330,7 @@ const DashboardCard: React.FC<DashBoardCardProps> = ({
|
||||
</div>,
|
||||
document.body
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,164 +0,0 @@
|
||||
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";
|
||||
import { generateUniqueId } from "../../functions/generateUniqueId";
|
||||
|
||||
interface Project {
|
||||
_id: string;
|
||||
projectName: string;
|
||||
thumbnail: string;
|
||||
createdBy: { _id: string; userName: 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);
|
||||
|
||||
if (JSON.stringify(projects) !== JSON.stringify(recentProjects)) {
|
||||
setRecentProjects(projects);
|
||||
}
|
||||
} catch (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 (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) {}
|
||||
};
|
||||
|
||||
const handleDuplicateRecentProject = async (
|
||||
projectId: string,
|
||||
projectName: string,
|
||||
thumbnail: string
|
||||
) => {
|
||||
const duplicateRecentProjectData = {
|
||||
userId,
|
||||
thumbnail,
|
||||
organization,
|
||||
projectUuid: generateUniqueId(),
|
||||
refProjectID: projectId,
|
||||
projectName,
|
||||
};
|
||||
projectSocket.emit("v1:project:Duplicate", duplicateRecentProjectData);
|
||||
};
|
||||
|
||||
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}
|
||||
createdBy={project.createdBy}
|
||||
createdAt={project.createdAt}
|
||||
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="dashboard-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;
|
||||
272
app/src/components/Dashboard/DashboardMain.tsx
Normal file
272
app/src/components/Dashboard/DashboardMain.tsx
Normal file
@@ -0,0 +1,272 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import DashboardNavBar from "./DashboardNavBar";
|
||||
import DashboardCard from "./DashboardCard";
|
||||
import MarketPlaceBanner from "./MarketPlaceBanner";
|
||||
import { getUserData } from "../../functions/getUserData";
|
||||
import { useSocketStore } from "../../store/builder/store";
|
||||
import { getAllProjects } from "../../services/dashboard/getAllProjects";
|
||||
import { sharedWithMeProjects } from "../../services/dashboard/sharedWithMeProject";
|
||||
import { recentlyViewed } from "../../services/dashboard/recentlyViewed";
|
||||
import { getTrash } from "../../services/dashboard/getTrash";
|
||||
import { searchProject } from "../../services/dashboard/searchProjects";
|
||||
import { trashSearchProject } from "../../services/dashboard/trashSearchProject";
|
||||
import { restoreTrash } from "../../services/dashboard/restoreTrash";
|
||||
import { generateUniqueId } from "../../functions/generateUniqueId";
|
||||
import ProjectSocketRes from "./socket/projectSocketRes";
|
||||
|
||||
interface Project {
|
||||
_id: string;
|
||||
projectName: string;
|
||||
thumbnail: string;
|
||||
createdBy: { _id: string; userName: string };
|
||||
projectUuid?: string;
|
||||
createdAt?: string;
|
||||
DeletedAt?: string;
|
||||
}
|
||||
|
||||
interface ProjectCollection {
|
||||
[key: string]: Project[];
|
||||
}
|
||||
|
||||
type Folder = "home" | "projects" | "shared" | "trash";
|
||||
|
||||
interface DashboardMainProps {
|
||||
activeFolder: Folder;
|
||||
}
|
||||
|
||||
const DashboardMain: React.FC<DashboardMainProps> = ({ activeFolder }) => {
|
||||
const [activeSubFolder, setActiveSubFolder] = useState("myProjects");
|
||||
const [projectsData, setProjectsData] = useState<ProjectCollection>({});
|
||||
const [isSearchActive, setIsSearchActive] = useState<boolean>(false);
|
||||
const [duplicateData, setDuplicateData] = useState<Object>({});
|
||||
const [openKebabProjectId, setOpenKebabProjectId] = useState<string | null>(
|
||||
null
|
||||
);
|
||||
const [projectsCache, setProjectsCache] = useState<{
|
||||
[key: string]: ProjectCollection;
|
||||
}>({});
|
||||
|
||||
const { userId, organization } = getUserData();
|
||||
const { projectSocket } = useSocketStore();
|
||||
|
||||
// #region API Fetchers
|
||||
const fetchData = async () => {
|
||||
const cacheKey =
|
||||
activeFolder + (activeFolder === "projects" ? `-${activeSubFolder}` : "");
|
||||
|
||||
if (projectsCache[cacheKey] && !isSearchActive) {
|
||||
setProjectsData(projectsCache[cacheKey]);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
let projects: ProjectCollection = {}; // initialize as empty object
|
||||
|
||||
switch (activeFolder) {
|
||||
case "home":
|
||||
projects = await recentlyViewed(organization, userId);
|
||||
break;
|
||||
case "projects":
|
||||
if (activeSubFolder === "myProjects") {
|
||||
projects = await getAllProjects(userId, organization);
|
||||
} else {
|
||||
projects = await sharedWithMeProjects();
|
||||
}
|
||||
break;
|
||||
case "trash":
|
||||
projects = await getTrash(organization);
|
||||
break;
|
||||
}
|
||||
|
||||
// Only update cache if projects is not empty
|
||||
if (
|
||||
projects &&
|
||||
JSON.stringify(projects) !== JSON.stringify(projectsData)
|
||||
) {
|
||||
setProjectsCache((prev) => ({ ...prev, [cacheKey]: projects }));
|
||||
setProjectsData(projects);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
// #region Search Handlers
|
||||
const handleSearch = async (inputValue: string) => {
|
||||
if (!inputValue.trim()) {
|
||||
setIsSearchActive(false);
|
||||
return;
|
||||
}
|
||||
|
||||
let results;
|
||||
if (activeFolder === "trash") {
|
||||
results = await trashSearchProject(organization, userId, inputValue);
|
||||
} else {
|
||||
results = await searchProject(organization, userId, inputValue);
|
||||
}
|
||||
|
||||
setIsSearchActive(true);
|
||||
setProjectsData(results?.message ? {} : results);
|
||||
};
|
||||
|
||||
// #region Socket Actions
|
||||
const handleDelete = async (projectId: string): Promise<void> => {
|
||||
if (projectSocket) {
|
||||
projectSocket.emit("v1:project:delete", {
|
||||
projectId,
|
||||
organization,
|
||||
userId,
|
||||
});
|
||||
}
|
||||
updateStateAfterRemove(projectId);
|
||||
};
|
||||
|
||||
const handlePermanentDelete = async (projectId: string): Promise<void> => {
|
||||
if (projectSocket) {
|
||||
projectSocket.emit("v1:trash:delete", {
|
||||
projectId,
|
||||
organization,
|
||||
userId,
|
||||
});
|
||||
}
|
||||
updateStateAfterRemove(projectId);
|
||||
};
|
||||
|
||||
const handleRestore = async (projectId: string): Promise<void> => {
|
||||
await restoreTrash(organization, projectId);
|
||||
updateStateAfterRemove(projectId);
|
||||
};
|
||||
|
||||
const handleDuplicate = async (
|
||||
projectId: string,
|
||||
projectName: string,
|
||||
thumbnail: string
|
||||
): Promise<void> => {
|
||||
if (projectSocket) {
|
||||
projectSocket.emit("v1:project:Duplicate", {
|
||||
userId,
|
||||
thumbnail,
|
||||
organization,
|
||||
projectUuid: generateUniqueId(),
|
||||
refProjectID: projectId,
|
||||
projectName,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// #region Project Map
|
||||
const updateStateAfterRemove = (projectId: string) => {
|
||||
setProjectsData((prev: ProjectCollection) => {
|
||||
const key = Object.keys(prev)[0];
|
||||
const updatedList = prev[key]?.filter((p) => p._id !== projectId) || [];
|
||||
return { ...prev, [key]: updatedList };
|
||||
});
|
||||
setIsSearchActive(false);
|
||||
};
|
||||
|
||||
const renderProjects = () => {
|
||||
const key = Object.keys(projectsData)[0];
|
||||
const projectList = projectsData[key];
|
||||
|
||||
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}
|
||||
createdBy={project.createdBy}
|
||||
createdAt={
|
||||
activeFolder === "trash" ? project.DeletedAt : project.createdAt
|
||||
}
|
||||
{...(activeFolder === "home" && {
|
||||
handleDeleteProject: handleDelete,
|
||||
handleDuplicateRecentProject: handleDuplicate,
|
||||
setRecentDuplicateData: setDuplicateData,
|
||||
})}
|
||||
{...(activeSubFolder === "myProjects" && {
|
||||
handleDeleteProject: handleDelete,
|
||||
handleDuplicateWorkspaceProject: handleDuplicate,
|
||||
setProjectDuplicateData: setDuplicateData,
|
||||
})}
|
||||
{...(activeSubFolder === "shared" && {
|
||||
handleDuplicateWorkspaceProject: handleDuplicate,
|
||||
setProjectDuplicateData: setDuplicateData,
|
||||
active: "shared",
|
||||
})}
|
||||
{...(activeFolder === "trash" && {
|
||||
handleRestoreProject: handleRestore,
|
||||
handleTrashDeleteProject: handlePermanentDelete,
|
||||
active: "trash",
|
||||
})}
|
||||
openKebabProjectId={openKebabProjectId}
|
||||
setOpenKebabProjectId={setOpenKebabProjectId}
|
||||
/>
|
||||
));
|
||||
};
|
||||
|
||||
// #region Use Effects
|
||||
useEffect(() => {
|
||||
if (!isSearchActive) fetchData();
|
||||
// eslint-disable-next-line
|
||||
}, [activeFolder, isSearchActive, activeSubFolder]);
|
||||
|
||||
return (
|
||||
<div className="dashboard-home-container">
|
||||
<DashboardNavBar
|
||||
page={activeFolder}
|
||||
{...(activeFolder === "trash"
|
||||
? { handleTrashSearch: handleSearch }
|
||||
: {
|
||||
handleProjectsSearch: handleSearch,
|
||||
handleRecentProjectSearch: handleSearch,
|
||||
})}
|
||||
/>
|
||||
|
||||
{activeFolder === "home" && <MarketPlaceBanner />}
|
||||
|
||||
<div
|
||||
className="dashboard-container"
|
||||
style={{ height: "calc(100% - 87px)" }}
|
||||
>
|
||||
{activeFolder === "projects" && (
|
||||
<div
|
||||
className="header-wrapper"
|
||||
style={{ display: "flex", gap: "7px" }}
|
||||
>
|
||||
<button
|
||||
className={`header ${
|
||||
activeSubFolder === "myProjects" && "active"
|
||||
}`}
|
||||
onClick={() => setActiveSubFolder("myProjects")}
|
||||
>
|
||||
My Projects
|
||||
</button>
|
||||
<button
|
||||
className={`header ${activeSubFolder === "shared" && "active"}`}
|
||||
onClick={() => setActiveSubFolder("shared")}
|
||||
>
|
||||
Shared with me
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="cards-container">{renderProjects()}</div>
|
||||
|
||||
{duplicateData && Object.keys(duplicateData).length > 0 && (
|
||||
<ProjectSocketRes
|
||||
setIsSearchActive={setIsSearchActive}
|
||||
{...(activeFolder === "home"
|
||||
? { setRecentProjects: setProjectsData }
|
||||
: { setWorkspaceProjects: setProjectsData })}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardMain;
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from "react";
|
||||
import React, { useCallback, useMemo } from "react";
|
||||
import Search from "../ui/inputs/Search";
|
||||
import { CartIcon } from "../icons/ExportModuleIcons";
|
||||
|
||||
@@ -15,19 +15,23 @@ const DashboardNavBar: React.FC<DashboardNavBarProps> = ({
|
||||
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);
|
||||
// Determine active search handler
|
||||
const activeSearchHandler = useMemo(
|
||||
() => handleProjectsSearch || handleTrashSearch || handleRecentProjectSearch,
|
||||
[handleProjectsSearch, handleTrashSearch, handleRecentProjectSearch]
|
||||
);
|
||||
|
||||
const handleSearch = useCallback(
|
||||
async (inputValue: string) => {
|
||||
if (!activeSearchHandler) return;
|
||||
try {
|
||||
await activeSearchHandler(inputValue);
|
||||
} catch (error) {
|
||||
console.error("Search failed:", error);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Search failed:", error);
|
||||
}
|
||||
};
|
||||
},
|
||||
[activeSearchHandler]
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="dashboard-navbar-container">
|
||||
@@ -40,4 +44,4 @@ const DashboardNavBar: React.FC<DashboardNavBarProps> = ({
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardNavBar;
|
||||
export default DashboardNavBar;
|
||||
|
||||
@@ -1,227 +0,0 @@
|
||||
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";
|
||||
import { sharedWithMeProjects } from "../../services/dashboard/sharedWithMeProject";
|
||||
import { duplicateProject } from "../../services/dashboard/duplicateProject";
|
||||
import { generateUniqueId } from "../../functions/generateUniqueId";
|
||||
|
||||
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 [sharedwithMeProject, setSharedWithMeProjects] = useState<any>([]);
|
||||
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 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 fetchAllProjects = async () => {
|
||||
try {
|
||||
const projects = await getAllProjects(userId, organization);
|
||||
|
||||
if (!projects || !projects.Projects) return;
|
||||
|
||||
if (JSON.stringify(projects) !== JSON.stringify(workspaceProjects)) {
|
||||
setWorkspaceProjects(projects);
|
||||
}
|
||||
} catch (error) {}
|
||||
};
|
||||
|
||||
const handleDeleteProject = async (projectId: any) => {
|
||||
try {
|
||||
// const deletedProject = await deleteProject(
|
||||
// projectId,
|
||||
// userId,
|
||||
// organization
|
||||
// );
|
||||
//
|
||||
const deleteProjects = {
|
||||
projectId,
|
||||
organization,
|
||||
userId,
|
||||
};
|
||||
|
||||
//socket for deleting the project
|
||||
if (projectSocket) {
|
||||
projectSocket.emit("v1:project:delete", deleteProjects);
|
||||
} else {
|
||||
}
|
||||
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) {}
|
||||
};
|
||||
|
||||
const handleDuplicateWorkspaceProject = async (
|
||||
projectId: string,
|
||||
projectName: string,
|
||||
thumbnail: string
|
||||
) => {
|
||||
// const duplicatedProject = await duplicateProject(
|
||||
// projectId,
|
||||
// generateUniqueId(),
|
||||
// thumbnail,
|
||||
// projectName
|
||||
// );
|
||||
// console.log("duplicatedProject: ", duplicatedProject);
|
||||
|
||||
const duplicateProjectData = {
|
||||
userId,
|
||||
thumbnail,
|
||||
organization,
|
||||
projectUuid: generateUniqueId(),
|
||||
refProjectID: 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}
|
||||
/>
|
||||
));
|
||||
};
|
||||
|
||||
const renderSharedProjects = () => {
|
||||
return sharedwithMeProject?.map((project: any) => (
|
||||
<DashboardCard
|
||||
key={project._id}
|
||||
projectName={project.projectName}
|
||||
thumbnail={project.thumbnail}
|
||||
projectId={project._id}
|
||||
createdAt={project.createdAt}
|
||||
setIsSearchActive={setIsSearchActive}
|
||||
active="shared"
|
||||
createdBy={project.createdBy}
|
||||
setProjectDuplicateData={setProjectDuplicateData}
|
||||
handleDuplicateWorkspaceProject={handleDuplicateWorkspaceProject}
|
||||
setActiveFolder={setActiveFolder}
|
||||
/>
|
||||
));
|
||||
};
|
||||
|
||||
const sharedProject = async () => {
|
||||
try {
|
||||
const sharedWithMe = await sharedWithMeProjects();
|
||||
setSharedWithMeProjects(sharedWithMe);
|
||||
} catch {}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!isSearchActive) {
|
||||
fetchAllProjects();
|
||||
}
|
||||
}, [isSearchActive]);
|
||||
|
||||
useEffect(() => {
|
||||
if (activeFolder === "shared") {
|
||||
sharedProject();
|
||||
}
|
||||
}, [activeFolder]);
|
||||
|
||||
return (
|
||||
<div className="dashboard-home-container">
|
||||
<DashboardNavBar
|
||||
page="projects"
|
||||
handleProjectsSearch={handleProjectsSearch}
|
||||
/>
|
||||
|
||||
<div
|
||||
className="dashboard-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">
|
||||
{activeFolder == "myProjects"
|
||||
? renderProjects()
|
||||
: renderSharedProjects()}
|
||||
</div>
|
||||
|
||||
{projectDuplicateData &&
|
||||
Object.keys(projectDuplicateData).length > 0 && (
|
||||
<ProjectSocketRes
|
||||
setIsSearchActive={setIsSearchActive}
|
||||
setWorkspaceProjects={setWorkspaceProjects}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardProjects;
|
||||
@@ -1,157 +0,0 @@
|
||||
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) => {
|
||||
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="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 DashboardTrash;
|
||||
@@ -1,62 +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>;
|
||||
}
|
||||
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" />
|
||||
|
||||
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="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;
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import React from "react";
|
||||
import banner from "../../assets/image/banner.png";
|
||||
|
||||
const MarketPlaceBanner = () => {
|
||||
|
||||
@@ -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,180 +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");
|
||||
console.log("refreshToken: ", refreshToken);
|
||||
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 addProject = {
|
||||
userId,
|
||||
thumbnail: savedTheme === "dark" ? darkThemeImage : lightThemeImage,
|
||||
organization: organization,
|
||||
projectUuid: projectId,
|
||||
};
|
||||
|
||||
console.log("projectSocket: ", projectSocket);
|
||||
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);
|
||||
|
||||
projectSocket.emit("v1:project:add", addProject);
|
||||
} else {
|
||||
console.error("Socket is not connected.");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error creating project:", error);
|
||||
function generateProjectId() {
|
||||
const randomBytes = new Uint8Array(12);
|
||||
crypto.getRandomValues(randomBytes);
|
||||
return Array.from(randomBytes, (byte) =>
|
||||
byte.toString(16).padStart(2, "0")
|
||||
).join("");
|
||||
}
|
||||
};
|
||||
|
||||
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>
|
||||
const handleCreateNewProject = async () => {
|
||||
const token = localStorage.getItem("token");
|
||||
const refreshToken = localStorage.getItem("refreshToken");
|
||||
if (!token || !refreshToken) {
|
||||
console.error('token expired');
|
||||
return;
|
||||
}
|
||||
|
||||
const projectId = generateProjectId();
|
||||
initializeSocket(email, organization, token, refreshToken);
|
||||
|
||||
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 {
|
||||
// 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>
|
||||
</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");
|
||||
console.warn("Tutorials comming soon");
|
||||
}}
|
||||
>
|
||||
<TutorialsIcon />
|
||||
Tutorials
|
||||
</button>
|
||||
<button
|
||||
className={
|
||||
activeTab === "Documentation"
|
||||
? "option-list active"
|
||||
: "option-list"
|
||||
}
|
||||
title="coming soon"
|
||||
disabled
|
||||
onClick={() => {
|
||||
// setActiveTab("Documentation");
|
||||
console.warn("Documentation comming soon");
|
||||
}}
|
||||
>
|
||||
<DocumentationIcon />
|
||||
Documentation
|
||||
</button>
|
||||
</div>
|
||||
<div className="side-bar-options-container">
|
||||
<button className="option-list" title="coming soon" disabled>
|
||||
<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" disabled>
|
||||
<HelpIcon />
|
||||
Help & Feedback
|
||||
</button>
|
||||
</div>
|
||||
</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>
|
||||
);
|
||||
);
|
||||
};
|
||||
|
||||
export default SidePannel;
|
||||
|
||||
@@ -1,101 +0,0 @@
|
||||
|
||||
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 RecentProject {
|
||||
_id: string;
|
||||
projectName: string;
|
||||
thumbnail: string;
|
||||
createdBy: { _id: string, userName: string };
|
||||
projectUuid?: string;
|
||||
createdAt: string;
|
||||
isViewed?: string
|
||||
}
|
||||
|
||||
interface RecentProjectData {
|
||||
[key: string]: RecentProject[];
|
||||
}
|
||||
interface ProjectsData {
|
||||
[key: string]: Project[];
|
||||
}
|
||||
interface ProjectSocketResProps {
|
||||
setRecentProjects?: React.Dispatch<React.SetStateAction<RecentProjectData>>;
|
||||
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;
|
||||
90
app/src/components/Dashboard/socket/projectSocketRes.tsx
Normal file
90
app/src/components/Dashboard/socket/projectSocketRes.tsx
Normal file
@@ -0,0 +1,90 @@
|
||||
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: { _id: string; userName: string };
|
||||
projectUuid?: string;
|
||||
createdAt?: string;
|
||||
DeletedAt?: string;
|
||||
isViewed?: string;
|
||||
}
|
||||
|
||||
interface ProjectCollection {
|
||||
[key: string]: Project[];
|
||||
}
|
||||
|
||||
interface ProjectSocketResProps {
|
||||
setRecentProjects?: React.Dispatch<React.SetStateAction<ProjectCollection>>;
|
||||
setWorkspaceProjects?: React.Dispatch<React.SetStateAction<ProjectCollection>>;
|
||||
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);
|
||||
setWorkspaceProjects(allProjects);
|
||||
} else if (setRecentProjects) {
|
||||
const recentProjects = await recentlyViewed(organization, userId);
|
||||
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);
|
||||
};
|
||||
// eslint-disable-next-line
|
||||
}, [projectSocket, userId, organization]);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
export default ProjectSocketRes;
|
||||
@@ -1,16 +1,10 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import React, { useEffect, useState, useMemo, useCallback } from "react";
|
||||
import { HelpIcon, WifiIcon } from "../icons/DashboardIcon";
|
||||
import { useLogger } from "../ui/log/LoggerContext";
|
||||
import { GetLogIcon } from "./getLogIcons";
|
||||
import {
|
||||
CurserLeftIcon,
|
||||
CurserMiddleIcon,
|
||||
CurserRightIcon,
|
||||
} from "../icons/LogIcons";
|
||||
import { CurserLeftIcon, CurserMiddleIcon, CurserRightIcon } from "../icons/LogIcons";
|
||||
import ShortcutHelper from "./shortcutHelper";
|
||||
import useVersionHistoryVisibleStore, {
|
||||
useShortcutStore,
|
||||
} from "../../store/builder/store";
|
||||
import useVersionHistoryVisibleStore, { useShortcutStore } from "../../store/builder/store";
|
||||
import { usePlayButtonStore } from "../../store/usePlayButtonStore";
|
||||
import useModuleStore, { useSubModuleStore } from "../../store/useModuleStore";
|
||||
import { useVersionContext } from "../../modules/builder/version/versionContext";
|
||||
@@ -19,7 +13,7 @@ import { useMouseNoteStore } from "../../store/useUIToggleStore";
|
||||
|
||||
const Footer: React.FC = () => {
|
||||
const { logs, setIsLogListVisible } = useLogger();
|
||||
const lastLog = logs.length > 0 ? logs[logs.length - 1] : null;
|
||||
const lastLog = logs[logs.length - 1] || null;
|
||||
|
||||
const { setActiveModule } = useModuleStore();
|
||||
const { setSubModule } = useSubModuleStore();
|
||||
@@ -30,46 +24,40 @@ const Footer: React.FC = () => {
|
||||
const { selectedVersion } = selectedVersionStore();
|
||||
|
||||
const { Leftnote, Middlenote, Rightnote } = useMouseNoteStore();
|
||||
|
||||
const [isOnline, setIsOnline] = useState<boolean>(navigator.onLine);
|
||||
|
||||
useEffect(() => {
|
||||
const handleOnline = () => {
|
||||
echo.success('You are back Online');
|
||||
setIsOnline(true);
|
||||
};
|
||||
const handleOffline = () => {
|
||||
echo.warn('Changes made now might not be saved');
|
||||
echo.error('You are now Offline.');
|
||||
setIsOnline(false);
|
||||
};
|
||||
// -------------------- Online/Offline Handlers --------------------
|
||||
const handleOnline = useCallback(() => {
|
||||
echo.success("You are back Online");
|
||||
setIsOnline(true);
|
||||
}, []);
|
||||
|
||||
const handleOffline = useCallback(() => {
|
||||
echo.warn("Changes made now might not be saved");
|
||||
echo.error("You are now Offline.");
|
||||
setIsOnline(false);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener("online", handleOnline);
|
||||
window.addEventListener("offline", handleOffline);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("online", handleOnline);
|
||||
window.removeEventListener("offline", handleOffline);
|
||||
};
|
||||
}, []);
|
||||
const mouseButtons = [
|
||||
{
|
||||
icon: <CurserLeftIcon />,
|
||||
label: Leftnote !== "" ? Leftnote : "Pan",
|
||||
mouse: "left",
|
||||
},
|
||||
{
|
||||
icon: <CurserMiddleIcon />,
|
||||
label: Middlenote !== "" ? Middlenote : "Scroll Zoom",
|
||||
mouse: "middle",
|
||||
},
|
||||
{
|
||||
icon: <CurserRightIcon />,
|
||||
label: Rightnote !== "" ? Rightnote : "Orbit / Cancel action",
|
||||
mouse: "right",
|
||||
},
|
||||
];
|
||||
}, [handleOnline, handleOffline]);
|
||||
|
||||
// -------------------- Mouse Buttons --------------------
|
||||
const mouseButtons = useMemo(
|
||||
() => [
|
||||
{ icon: <CurserLeftIcon />, label: Leftnote || "Pan", mouse: "left" },
|
||||
{ icon: <CurserMiddleIcon />, label: Middlenote || "Scroll Zoom", mouse: "middle" },
|
||||
{ icon: <CurserRightIcon />, label: Rightnote || "Orbit / Cancel action", mouse: "right" },
|
||||
],
|
||||
[Leftnote, Middlenote, Rightnote]
|
||||
);
|
||||
|
||||
// -------------------- Mouse Helper --------------------
|
||||
useEffect(() => {
|
||||
const cleanup = mouseActionHelper();
|
||||
return () => cleanup();
|
||||
@@ -78,6 +66,7 @@ const Footer: React.FC = () => {
|
||||
return (
|
||||
<div className="footer-container">
|
||||
<div className="footer-wrapper">
|
||||
{/* Mouse Button Info */}
|
||||
<div className="selection-wrapper">
|
||||
{mouseButtons.map(({ icon, label, mouse }) => (
|
||||
<div className="selector-wrapper" key={mouse}>
|
||||
@@ -87,13 +76,15 @@ const Footer: React.FC = () => {
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Logs and Version */}
|
||||
<div className="logs-wrapper">
|
||||
<div className="bg-dummy left-top"></div>
|
||||
<div className="bg-dummy right-bottom"></div>
|
||||
<div className="bg-dummy left-top" />
|
||||
<div className="bg-dummy right-bottom" />
|
||||
|
||||
<div className="log-container">
|
||||
<button
|
||||
id="log-details-buttton"
|
||||
className={`logs-detail ${lastLog ? lastLog.type : ""}`}
|
||||
className={`logs-detail ${lastLog?.type ?? ""}`}
|
||||
onClick={() => setIsLogListVisible(true)}
|
||||
>
|
||||
{lastLog ? (
|
||||
@@ -106,6 +97,7 @@ const Footer: React.FC = () => {
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className="version"
|
||||
onClick={() => {
|
||||
@@ -119,11 +111,8 @@ const Footer: React.FC = () => {
|
||||
<HelpIcon />
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className={`wifi-connection ${
|
||||
isOnline ? "connected" : "disconnected"
|
||||
}`}
|
||||
>
|
||||
|
||||
<div className={`wifi-connection ${isOnline ? "connected" : "disconnected"}`}>
|
||||
<div className="icon">
|
||||
<WifiIcon />
|
||||
</div>
|
||||
@@ -132,12 +121,9 @@ const Footer: React.FC = () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{!isPlaying && (
|
||||
<div
|
||||
className={`shortcut-helper-overlay ${
|
||||
showShortcuts ? "visible" : ""
|
||||
}`}
|
||||
>
|
||||
{/* Shortcut Helper */}
|
||||
{!isPlaying && showShortcuts && (
|
||||
<div className="shortcut-helper-overlay visible">
|
||||
<ShortcutHelper setShowShortcuts={setShowShortcuts} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -34,7 +34,7 @@ export function HomeIcon() {
|
||||
>
|
||||
<path
|
||||
d="M6.91304 13.5V12.8333C6.91304 12.281 7.36076 11.8333 7.91304 11.8333H8.95652C9.50881 11.8333 9.95652 12.281 9.95652 12.8333V13.5C9.95652 14.0523 10.4042 14.5 10.9565 14.5H12C12.5523 14.5 13 14.0523 13 13.5V7.38889L8.21739 2.5L3 7.38889V13.5C3 14.0523 3.44772 14.5 4 14.5H5.91304C6.46533 14.5 6.91304 14.0523 6.91304 13.5Z"
|
||||
stroke="var(--text-button-color)"
|
||||
stroke="var(--text-color)"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
@@ -51,7 +51,7 @@ export function ProjectsIcon() {
|
||||
>
|
||||
<path
|
||||
d="M12.1538 1.5H5.69231C5.20268 1.5 4.7331 1.68437 4.38688 2.01256C4.04066 2.34075 3.84615 2.78587 3.84615 3.25C3.35652 3.25 2.88695 3.43437 2.54073 3.76256C2.1945 4.09075 2 4.53587 2 5V13.75C2 14.2141 2.1945 14.6592 2.54073 14.9874C2.88695 15.3156 3.35652 15.5 3.84615 15.5H10.3077C10.7973 15.5 11.2669 15.3156 11.6131 14.9874C11.9593 14.6592 12.1538 14.2141 12.1538 13.75C12.6435 13.75 13.1131 13.5656 13.4593 13.2374C13.8055 12.9092 14 12.4641 14 12V3.25C14 2.78587 13.8055 2.34075 13.4593 2.01256C13.1131 1.68437 12.6435 1.5 12.1538 1.5ZM12.1538 12.875V5C12.1538 4.53587 11.9593 4.09075 11.6131 3.76256C11.2669 3.43437 10.7973 3.25 10.3077 3.25H4.76923C4.76923 3.01794 4.86648 2.79538 5.03959 2.63128C5.2127 2.46719 5.44749 2.375 5.69231 2.375H12.1538C12.3987 2.375 12.6334 2.46719 12.8066 2.63128C12.9797 2.79538 13.0769 3.01794 13.0769 3.25V12C13.0769 12.2321 12.9797 12.4546 12.8066 12.6187C12.6334 12.7828 12.3987 12.875 12.1538 12.875ZM2.92308 5C2.92308 4.76794 3.02033 4.54538 3.19344 4.38128C3.36655 4.21719 3.60134 4.125 3.84615 4.125H10.3077C10.5525 4.125 10.7873 4.21719 10.9604 4.38128C11.1335 4.54538 11.2308 4.76794 11.2308 5V13.75C11.2308 13.9821 11.1335 14.2046 10.9604 14.3687C10.7873 14.5328 10.5525 14.625 10.3077 14.625H3.84615C3.60134 14.625 3.36655 14.5328 3.19344 14.3687C3.02033 14.2046 2.92308 13.9821 2.92308 13.75V5Z"
|
||||
fill="var(--text-button-color)"
|
||||
fill="var(--text-color)"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
@@ -70,103 +70,103 @@ export function TutorialsIcon() {
|
||||
cx="8.157"
|
||||
cy="8.35866"
|
||||
r="6.17928"
|
||||
stroke="var(--text-button-color)"
|
||||
stroke="var(--text-color)"
|
||||
strokeWidth="0.562865"
|
||||
/>
|
||||
<path
|
||||
d="M7.31894 7.8336L7.30273 7.72125C10.0583 7.32407 11.5796 5.74901 12.1058 5.09033L12.1945 5.1612C11.6598 5.83032 10.1146 7.43067 7.31894 7.8336Z"
|
||||
fill="var(--text-button-color)"
|
||||
stroke="var(--text-button-color)"
|
||||
fill="var(--text-color)"
|
||||
stroke="var(--text-color)"
|
||||
strokeWidth="0.562865"
|
||||
/>
|
||||
<path
|
||||
d="M7.3313 8.19434C7.56713 8.19434 7.7583 8.00316 7.7583 7.76734C7.7583 7.53151 7.56713 7.34033 7.3313 7.34033C7.09547 7.34033 6.9043 7.53151 6.9043 7.76734C6.9043 8.00316 7.09547 8.19434 7.3313 8.19434Z"
|
||||
fill="var(--text-button-color)"
|
||||
stroke="var(--text-button-color)"
|
||||
fill="var(--text-color)"
|
||||
stroke="var(--text-color)"
|
||||
strokeWidth="0.562865"
|
||||
/>
|
||||
<path
|
||||
d="M12.134 5.56787C12.3699 5.56787 12.561 5.3767 12.561 5.14087C12.561 4.90504 12.3699 4.71387 12.134 4.71387C11.8982 4.71387 11.707 4.90504 11.707 5.14087C11.707 5.3767 11.8982 5.56787 12.134 5.56787Z"
|
||||
fill="var(--text-button-color)"
|
||||
stroke="var(--text-button-color)"
|
||||
fill="var(--text-color)"
|
||||
stroke="var(--text-color)"
|
||||
strokeWidth="0.562865"
|
||||
/>
|
||||
<path
|
||||
d="M5.67763 13.0492C5.15009 12.385 4.31304 10.9992 4.63359 9.18018L4.74534 9.20001C4.43251 10.9751 5.25078 12.3292 5.76636 12.9785L5.67763 13.0492Z"
|
||||
fill="var(--text-button-color)"
|
||||
stroke="var(--text-button-color)"
|
||||
fill="var(--text-color)"
|
||||
stroke="var(--text-color)"
|
||||
strokeWidth="0.562865"
|
||||
/>
|
||||
<path
|
||||
d="M4.68921 9.63867C4.92504 9.63867 5.11621 9.4475 5.11621 9.21167C5.11621 8.97584 4.92504 8.78467 4.68921 8.78467C4.45338 8.78467 4.26221 8.97584 4.26221 9.21167C4.26221 9.4475 4.45338 9.63867 4.68921 9.63867Z"
|
||||
fill="var(--text-button-color)"
|
||||
stroke="var(--text-button-color)"
|
||||
fill="var(--text-color)"
|
||||
stroke="var(--text-color)"
|
||||
strokeWidth="0.562865"
|
||||
/>
|
||||
<path
|
||||
d="M5.70923 13.4238C5.94506 13.4238 6.13623 13.2327 6.13623 12.9968C6.13623 12.761 5.94506 12.5698 5.70923 12.5698C5.4734 12.5698 5.28223 12.761 5.28223 12.9968C5.28223 13.2327 5.4734 13.4238 5.70923 13.4238Z"
|
||||
fill="var(--text-button-color)"
|
||||
stroke="var(--text-button-color)"
|
||||
fill="var(--text-color)"
|
||||
stroke="var(--text-color)"
|
||||
strokeWidth="0.562865"
|
||||
/>
|
||||
<path
|
||||
d="M12.4429 9.6101L12.3293 9.60692C12.369 8.18736 11.8263 6.82867 10.801 5.7813C9.73352 4.69047 8.2434 4.07147 6.70876 4.0804L6.70801 3.96684C8.27081 3.96078 9.79333 4.58917 10.8822 5.70181C11.9291 6.77143 12.4833 8.1595 12.4429 9.6101Z"
|
||||
fill="var(--text-button-color)"
|
||||
stroke="var(--text-button-color)"
|
||||
fill="var(--text-color)"
|
||||
stroke="var(--text-color)"
|
||||
strokeWidth="0.562865"
|
||||
/>
|
||||
<path
|
||||
d="M12.3792 10.0142C12.615 10.0142 12.8062 9.82299 12.8062 9.58716C12.8062 9.35133 12.615 9.16016 12.3792 9.16016C12.1433 9.16016 11.9521 9.35133 11.9521 9.58716C11.9521 9.82299 12.1433 10.0142 12.3792 10.0142Z"
|
||||
fill="var(--text-button-color)"
|
||||
stroke="var(--text-button-color)"
|
||||
fill="var(--text-color)"
|
||||
stroke="var(--text-color)"
|
||||
strokeWidth="0.562865"
|
||||
/>
|
||||
<path
|
||||
d="M6.72974 4.4585C6.96556 4.4585 7.15674 4.26732 7.15674 4.0315C7.15674 3.79567 6.96556 3.60449 6.72974 3.60449C6.49391 3.60449 6.30273 3.79567 6.30273 4.0315C6.30273 4.26732 6.49391 4.4585 6.72974 4.4585Z"
|
||||
fill="var(--text-button-color)"
|
||||
stroke="var(--text-button-color)"
|
||||
fill="var(--text-color)"
|
||||
stroke="var(--text-color)"
|
||||
strokeWidth="0.562865"
|
||||
/>
|
||||
<path
|
||||
d="M8.93738 12.7561C5.51197 12.5871 3.18964 10.3443 2.15833 8.30167C1.40017 6.79989 1.28161 5.33657 1.84898 4.48256C2.21511 3.93139 2.7529 3.64179 3.57572 3.69903L3.45825 3.81649C2.67632 3.76183 2.28567 4.03042 1.94346 4.5454C1.39865 5.36549 1.51979 6.78505 2.25963 8.25049C3.27641 10.2647 5.56617 12.476 8.94298 12.6426L8.93738 12.7561Z"
|
||||
fill="var(--text-button-color)"
|
||||
stroke="var(--text-button-color)"
|
||||
fill="var(--text-color)"
|
||||
stroke="var(--text-color)"
|
||||
strokeWidth="0.562865"
|
||||
/>
|
||||
<path
|
||||
d="M4.12372 13.5272C2.54078 13.2677 1.46328 11.9915 1.38697 10.4835C1.31368 9.03292 2.2066 7.3084 4.36675 6.72559L4.39628 6.83521C2.2973 7.40152 1.42936 9.07259 1.50053 10.4778C1.54767 11.4101 2.02721 12.4642 3.18398 12.9967C3.46147 13.1244 3.45807 13.0965 3.77132 13.2139L4.12372 13.5272Z"
|
||||
fill="var(--text-button-color)"
|
||||
stroke="var(--text-button-color)"
|
||||
fill="var(--text-color)"
|
||||
stroke="var(--text-color)"
|
||||
strokeWidth="0.562865"
|
||||
/>
|
||||
<path
|
||||
d="M4.36157 7.21436C4.5974 7.21436 4.78858 7.02318 4.78858 6.78735C4.78858 6.55153 4.5974 6.36035 4.36157 6.36035C4.12575 6.36035 3.93457 6.55153 3.93457 6.78735C3.93457 7.02318 4.12575 7.21436 4.36157 7.21436Z"
|
||||
fill="var(--text-button-color)"
|
||||
stroke="var(--text-button-color)"
|
||||
fill="var(--text-color)"
|
||||
stroke="var(--text-color)"
|
||||
strokeWidth="0.562865"
|
||||
/>
|
||||
<path
|
||||
d="M11.5155 15.1026C11.4663 15.1026 11.4165 15.1003 11.3659 15.0956C10.4065 15.0064 9.53752 14.1202 9.04102 12.7247L9.14807 12.6865C9.62928 14.0393 10.4622 14.8976 11.3764 14.9825C11.8685 15.0293 12.3041 14.8309 12.5152 14.4681C12.7337 14.0928 12.7265 13.7453 12.3977 13.2744L12.5152 13.1569C12.8703 13.6657 12.8548 14.1099 12.6133 14.5252C12.4016 14.8889 11.9895 15.1026 11.5155 15.1026Z"
|
||||
fill="var(--text-button-color)"
|
||||
stroke="var(--text-button-color)"
|
||||
fill="var(--text-color)"
|
||||
stroke="var(--text-color)"
|
||||
strokeWidth="0.562865"
|
||||
/>
|
||||
<path
|
||||
d="M8.9187 13.1187C9.15453 13.1187 9.34571 12.9275 9.34571 12.6917C9.34571 12.4558 9.15453 12.2646 8.9187 12.2646C8.68287 12.2646 8.4917 12.4558 8.4917 12.6917C8.4917 12.9275 8.68287 13.1187 8.9187 13.1187Z"
|
||||
fill="var(--text-button-color)"
|
||||
stroke="var(--text-button-color)"
|
||||
fill="var(--text-color)"
|
||||
stroke="var(--text-color)"
|
||||
strokeWidth="0.562865"
|
||||
/>
|
||||
<path
|
||||
d="M10.0987 3.65056L9.99072 3.61513C10.2487 2.83274 10.7045 2.32867 11.2414 2.23252C11.572 2.17286 11.8969 2.28566 12.0886 2.52597C12.2781 2.76339 12.4042 2.98817 12.2684 3.30782L12.1509 3.19035C12.2699 2.91023 12.1625 2.80064 12.0001 2.59683C11.8344 2.38923 11.5514 2.29248 11.2616 2.34441C10.7669 2.43284 10.3432 2.90906 10.0987 3.65056Z"
|
||||
fill="var(--text-button-color)"
|
||||
stroke="var(--text-button-color)"
|
||||
fill="var(--text-color)"
|
||||
stroke="var(--text-color)"
|
||||
strokeWidth="0.562865"
|
||||
/>
|
||||
<path
|
||||
d="M10.0535 4.04004C10.2893 4.04004 10.4805 3.84887 10.4805 3.61304C10.4805 3.37721 10.2893 3.18604 10.0535 3.18604C9.81764 3.18604 9.62646 3.37721 9.62646 3.61304C9.62646 3.84887 9.81764 4.04004 10.0535 4.04004Z"
|
||||
fill="var(--text-button-color)"
|
||||
stroke="var(--text-button-color)"
|
||||
fill="var(--text-color)"
|
||||
stroke="var(--text-color)"
|
||||
strokeWidth="0.562865"
|
||||
/>
|
||||
</svg>
|
||||
@@ -184,12 +184,12 @@ export function DocumentationIcon() {
|
||||
>
|
||||
<path
|
||||
d="M8 5.10589C7.2666 4.17245 6.13604 3.23901 3.33413 3.17051C3.15009 3.16602 3 3.3155 3 3.4996C3 4.86525 3 10.0354 3 11.5645C3 11.7486 3.1501 11.8932 3.33409 11.8992C6.13603 11.9908 7.2666 13.233 8 14.1665M8 5.10589C8.7334 4.17245 9.86393 3.23901 12.6659 3.17051C12.8499 3.16602 13 3.31214 13 3.49624C13 5.02281 13 10.0374 13 11.564C13 11.7481 12.8499 11.8932 12.6659 11.8992C9.864 11.9908 8.7334 13.233 8 14.1665M8 5.10589V14.1665"
|
||||
stroke="var(--text-button-color)"
|
||||
stroke="var(--text-color)"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M12.8232 4.5H14.333C14.5171 4.5 14.6663 4.64924 14.6663 4.83333V13.526C14.6663 13.7957 14.3485 13.9749 14.102 13.8654C13.5719 13.6299 12.6873 13.3421 11.5291 13.3421C9.56827 13.3421 7.99967 14.5 7.99967 14.5C7.99967 14.5 6.43105 13.3421 4.47026 13.3421C3.31197 13.3421 2.42738 13.6299 1.89732 13.8654C1.65079 13.9749 1.33301 13.7957 1.33301 13.526V4.83333C1.33301 4.64924 1.48225 4.5 1.66634 4.5H3.17615"
|
||||
stroke="var(--text-button-color)"
|
||||
stroke="var(--text-color)"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
@@ -208,7 +208,7 @@ export function HelpIcon() {
|
||||
<g clipPath="url(#clip0_764_1941)">
|
||||
<path
|
||||
d="M6 12.5C2.6862 12.5 0 9.8138 0 6.5C0 3.1862 2.6862 0.5 6 0.5C9.3138 0.5 12 3.1862 12 6.5C12 9.8138 9.3138 12.5 6 12.5ZM3.552 4.8404V4.9016C3.552 4.98117 3.58361 5.05747 3.63987 5.11373C3.69613 5.16999 3.77244 5.2016 3.852 5.2016H4.4502C4.48952 5.2016 4.52845 5.19386 4.56478 5.17881C4.6011 5.16376 4.63411 5.14171 4.66191 5.11391C4.68971 5.08611 4.71176 5.0531 4.72681 5.01678C4.74186 4.98045 4.7496 4.94152 4.7496 4.9022C4.7496 4.1282 5.3484 3.7148 6.1536 3.7148C6.9384 3.7148 7.4544 4.1282 7.4544 4.7168C7.4544 5.2736 7.1652 5.5322 6.4428 5.8628L6.2364 5.9552C5.6274 6.224 5.4 6.626 5.4 7.3286V7.4C5.4 7.47957 5.43161 7.55587 5.48787 7.61213C5.54413 7.66839 5.62044 7.7 5.7 7.7H6.2982C6.33752 7.7 6.37645 7.69226 6.41278 7.67721C6.4491 7.66216 6.48211 7.64011 6.50991 7.61231C6.53771 7.58451 6.55976 7.5515 6.57481 7.51518C6.58986 7.47885 6.5976 7.43992 6.5976 7.4006C6.5976 7.091 6.6804 6.9668 6.9276 6.8534L7.1346 6.7604C8.0016 6.368 8.652 5.852 8.652 4.7264V4.6646C8.652 3.4778 7.62 2.6 6.1536 2.6C4.6668 2.6 3.552 3.4568 3.552 4.8404ZM5.1 9.4946C5.1 10.0148 5.4954 10.4 5.9946 10.4C6.5046 10.4 6.9 10.0148 6.9 9.4946C6.9 8.9744 6.5046 8.6 5.9946 8.6C5.4954 8.6 5.1 8.9744 5.1 9.4946Z"
|
||||
fill="var(--text-button-color)"
|
||||
fill="var(--text-color)"
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
@@ -236,17 +236,17 @@ export function LogoutIcon() {
|
||||
>
|
||||
<path
|
||||
d="M4 3.5C4.00605 2.41248 4.05428 1.82353 4.43847 1.43934C4.87781 1 5.58489 1 6.99914 1H7.49914C8.91334 1 9.62044 1 10.0598 1.43934C10.4991 1.87868 10.4991 2.58578 10.4991 4V8C10.4991 9.4142 10.4991 10.1213 10.0598 10.5606C9.62044 11 8.91334 11 7.49914 11H6.99914C5.58489 11 4.87781 11 4.43847 10.5606C4.05428 10.1764 4.00605 9.5875 4 8.5"
|
||||
stroke="var(--text-button-color)"
|
||||
stroke="var(--text-color)"
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
<path
|
||||
opacity="0.5"
|
||||
d="M4 9.75C2.82149 9.75 2.23223 9.75 1.86611 9.3839C1.5 9.01775 1.5 8.4285 1.5 7.25V4.75C1.5 3.57149 1.5 2.98224 1.86611 2.61612C2.23223 2.25 2.82149 2.25 4 2.25"
|
||||
stroke="var(--text-button-color)"
|
||||
stroke="var(--text-color)"
|
||||
/>
|
||||
<path
|
||||
d="M7.5 6H3M3 6L4 7M3 6L4 5"
|
||||
stroke="var(--text-button-color)"
|
||||
stroke="var(--text-color)"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
|
||||
@@ -1,17 +1,5 @@
|
||||
import React, { useEffect } from "react";
|
||||
import {
|
||||
useLoadingProgress,
|
||||
useRenameModeStore,
|
||||
useSaveVersion,
|
||||
useSelectedComment,
|
||||
useSocketStore,
|
||||
useWidgetSubOption,
|
||||
} from "../../../store/builder/store";
|
||||
import useModuleStore, { useThreeDStore } from "../../../store/useModuleStore";
|
||||
import { usePlayButtonStore } from "../../../store/usePlayButtonStore";
|
||||
import { useSelectedZoneStore } from "../../../store/visualization/useZoneStore";
|
||||
import { useFloatingWidget } from "../../../store/visualization/useDroppedObjectsStore";
|
||||
import { useSelectedUserStore } from "../../../store/collaboration/useCollabStore";
|
||||
import { useEffect } from "react";
|
||||
import { useLoadingProgress, useRenameModeStore, useSaveVersion, useSelectedComment, useSocketStore, useWidgetSubOption } from "../../../store/builder/store";
|
||||
import KeyPressListener from "../../../utils/shortcutkeys/handleShortcutKeys";
|
||||
import LoadingPage from "../../templates/LoadingPage";
|
||||
import ModuleToggle from "../../ui/ModuleToggle";
|
||||
@@ -23,21 +11,29 @@ import Tools from "../../ui/Tools";
|
||||
import SimulationPlayer from "../../ui/simulation/simulationPlayer";
|
||||
import ControlsPlayer from "../controls/ControlsPlayer";
|
||||
import SelectFloorPlan from "../../temporary/SelectFloorPlan";
|
||||
import { createHandleDrop } from "../../../modules/visualization/functions/handleUiDrop";
|
||||
import Scene from "../../../modules/scene/scene";
|
||||
import { useComparisonProduct, useMainProduct } from "../../../store/simulation/useSimulationStore";
|
||||
import { useProductContext } from "../../../modules/simulation/products/productContext";
|
||||
import RegularDropDown from "../../ui/inputs/RegularDropDown";
|
||||
import RenameTooltip from "../../ui/features/RenameTooltip";
|
||||
import VersionSaved from "../sidebarRight/versionHisory/VersionSaved";
|
||||
import Footer from "../../footer/Footer";
|
||||
import ThreadChat from "../../ui/collaboration/ThreadChat";
|
||||
import Scene from "../../../modules/scene/scene";
|
||||
import useModuleStore, { useThreeDStore } from "../../../store/useModuleStore";
|
||||
import { usePlayButtonStore } from "../../../store/usePlayButtonStore";
|
||||
import { useSelectedZoneStore } from "../../../store/visualization/useZoneStore";
|
||||
import { useFloatingWidget } from "../../../store/visualization/useDroppedObjectsStore";
|
||||
import { useSelectedUserStore } from "../../../store/collaboration/useCollabStore";
|
||||
import { createHandleDrop } from "../../../modules/visualization/functions/handleUiDrop";
|
||||
import { useComparisonProduct, useMainProduct } from "../../../store/simulation/useSimulationStore";
|
||||
import { useProductContext } from "../../../modules/simulation/products/productContext";
|
||||
import { setAssetsApi } from "../../../services/factoryBuilder/asset/floorAsset/setAssetsApi";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { useSceneContext } from "../../../modules/scene/sceneContext";
|
||||
import { useVersionHistoryStore } from "../../../store/builder/useVersionHistoryStore";
|
||||
import { useVersionContext } from "../../../modules/builder/version/versionContext";
|
||||
import VersionSaved from "../sidebarRight/versionHisory/VersionSaved";
|
||||
import Footer from "../../footer/Footer";
|
||||
import ThreadChat from "../../ui/collaboration/ThreadChat";
|
||||
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();
|
||||
@@ -59,11 +55,19 @@ function MainScene() {
|
||||
const { products } = productStore();
|
||||
const { setName, selectedAssets, setSelectedAssets } = assetStore();
|
||||
const { projectId } = useParams()
|
||||
const { organization, userId } = getUserData();
|
||||
const { isRenameMode, setIsRenameMode } = useRenameModeStore();
|
||||
const { versionHistory } = useVersionHistoryStore();
|
||||
const { selectedVersionStore } = useVersionContext();
|
||||
const { selectedVersion, setSelectedVersion } = selectedVersionStore();
|
||||
const { selectedComment, commentPositionState } = useSelectedComment();
|
||||
const { resetStates } = useRestStates();
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
resetStates();
|
||||
}
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (activeModule !== 'simulation') {
|
||||
@@ -73,10 +77,20 @@ function MainScene() {
|
||||
}, [activeModule, clearComparisonProduct, setIsVersionSaved])
|
||||
|
||||
useEffect(() => {
|
||||
if (versionHistory.length > 0) {
|
||||
setSelectedVersion(versionHistory[0])
|
||||
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 && 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);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useState, useEffect, useMemo, useCallback } from "react";
|
||||
import { useDecalStore } from "../../../../store/builder/store";
|
||||
import { getFilteredAssets } from "./assetsHelpers/filteredAssetsHelper";
|
||||
import { fetchCategoryDecals } from "./assetsHelpers/fetchDecalsHelper";
|
||||
import { useBuilderStore } from "../../../../store/builder/useBuilderStore";
|
||||
import {
|
||||
fetchAllAssets,
|
||||
fetchCategoryAssets,
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
import { ArrowIcon } from "../../../icons/ExportCommonIcons";
|
||||
|
||||
const Assets: React.FC = () => {
|
||||
const { selectedSubCategory, setSelectedSubCategory } = useDecalStore();
|
||||
const { selectedDecalCategory, setSelectedDecalCategory } = useBuilderStore();
|
||||
const [searchValue, setSearchValue] = useState<string | null>(null);
|
||||
const [selectedCategory, setSelectedCategory] = useState<string | null>(null);
|
||||
const [assets, setAssets] = useState<AssetProp[] | DecalProp[]>([]);
|
||||
@@ -31,9 +31,9 @@ const Assets: React.FC = () => {
|
||||
assets,
|
||||
searchValue,
|
||||
selectedCategory,
|
||||
selectedSubCategory,
|
||||
selectedDecalCategory,
|
||||
}),
|
||||
[assets, searchValue, selectedCategory, selectedSubCategory]
|
||||
[assets, searchValue, selectedCategory, selectedDecalCategory]
|
||||
);
|
||||
|
||||
const handleFetchCategory = useCallback(
|
||||
@@ -43,14 +43,14 @@ const Assets: React.FC = () => {
|
||||
if (category === "Decals") {
|
||||
const res = await fetchCategoryDecals("Safety");
|
||||
setAssets(res);
|
||||
setSelectedSubCategory("Safety");
|
||||
setSelectedDecalCategory("Safety");
|
||||
} else {
|
||||
const res = await fetchCategoryAssets(category);
|
||||
setAssets(res);
|
||||
}
|
||||
setIsLoading(false);
|
||||
},
|
||||
[setSelectedSubCategory]
|
||||
[setSelectedDecalCategory]
|
||||
);
|
||||
|
||||
const fetchGlobalSearch = useCallback(async (term: string) => {
|
||||
@@ -75,7 +75,7 @@ const Assets: React.FC = () => {
|
||||
|
||||
return (
|
||||
<div className="assets-container-main">
|
||||
<Search onChange={setSearchValue} value={searchValue} />
|
||||
<Search debounced onChange={setSearchValue} value={searchValue} />
|
||||
<div className="assets-list-section">
|
||||
<section>
|
||||
{isLoading ? (
|
||||
@@ -90,7 +90,7 @@ const Assets: React.FC = () => {
|
||||
className="back-button"
|
||||
onClick={() => {
|
||||
setSelectedCategory(null);
|
||||
setSelectedSubCategory(null);
|
||||
setSelectedDecalCategory(null);
|
||||
setAssets([]);
|
||||
setSearchValue(null);
|
||||
}}
|
||||
@@ -106,14 +106,13 @@ const Assets: React.FC = () => {
|
||||
{ACTIVE_DECAL_SUBCATEGORIES.map((cat) => (
|
||||
<div
|
||||
key={cat.name}
|
||||
className={`catogory-asset-filter-wrapper ${
|
||||
selectedSubCategory === cat.name ? "active" : ""
|
||||
}`}
|
||||
className={`catogory-asset-filter-wrapper ${selectedDecalCategory === cat.name ? "active" : ""
|
||||
}`}
|
||||
onClick={async () => {
|
||||
setIsLoading(true);
|
||||
const res = await fetchCategoryDecals(cat.name);
|
||||
setAssets(res);
|
||||
setSelectedSubCategory(cat.name);
|
||||
setSelectedDecalCategory(cat.name);
|
||||
setIsLoading(false);
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -2,19 +2,19 @@ interface FilterProps {
|
||||
assets: AssetProp[] | DecalProp[];
|
||||
searchValue: string | null;
|
||||
selectedCategory: string | null;
|
||||
selectedSubCategory: string | null;
|
||||
selectedDecalCategory: string | null;
|
||||
}
|
||||
|
||||
export const getFilteredAssets = ({
|
||||
assets,
|
||||
searchValue,
|
||||
selectedCategory,
|
||||
selectedSubCategory,
|
||||
selectedDecalCategory,
|
||||
}: FilterProps) => {
|
||||
const term = searchValue?.trim().toLowerCase();
|
||||
if (!term) return assets;
|
||||
|
||||
if (selectedCategory === "Decals" || selectedSubCategory) {
|
||||
if (selectedCategory === "Decals" || selectedDecalCategory) {
|
||||
return (assets as DecalProp[]).filter((a) =>
|
||||
a.decalName?.toLowerCase().includes(term)
|
||||
);
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import React from "react";
|
||||
import { useDroppedDecal, useSelectedItem } from "../../../../../store/builder/store";
|
||||
import { useSelectedItem } from "../../../../../store/builder/store";
|
||||
import { useBuilderStore } from "../../../../../store/builder/useBuilderStore";
|
||||
|
||||
export const RenderAsset: React.FC<{ asset: AssetProp | DecalProp; index: number }> = ({ asset, index }) => {
|
||||
const { setSelectedItem } = useSelectedItem();
|
||||
const { setDroppedDecal } = useDroppedDecal();
|
||||
const { setDroppedDecal } = useBuilderStore();
|
||||
|
||||
if ("decalName" in asset) {
|
||||
return (
|
||||
|
||||
@@ -282,7 +282,7 @@ const AisleProperties: React.FC = () => {
|
||||
<button
|
||||
key={val.id}
|
||||
title={val.brief || val.id}
|
||||
className={`aisle-list ${aisleColor === val.color ? "selected" : ""}`}
|
||||
className={`aisle-list ${aisleColor === val.id ? "selected" : ""}`}
|
||||
onClick={() => setAisleColor(val.id)}
|
||||
aria-pressed={aisleColor === val.id}
|
||||
>
|
||||
|
||||
@@ -28,7 +28,7 @@ const GlobalProperties: React.FC = () => {
|
||||
const { email, userId, organization } = getUserData();
|
||||
|
||||
const optimizeScene = async (value: any) => {
|
||||
|
||||
if (!projectId) return;
|
||||
setEnvironment(
|
||||
organization,
|
||||
userId,
|
||||
@@ -44,7 +44,7 @@ const GlobalProperties: React.FC = () => {
|
||||
};
|
||||
|
||||
const limitRenderDistance = async () => {
|
||||
|
||||
if (!projectId) return;
|
||||
if (limitDistance) {
|
||||
setEnvironment(
|
||||
organization,
|
||||
@@ -78,6 +78,7 @@ const GlobalProperties: React.FC = () => {
|
||||
}
|
||||
|
||||
const updatedDist = async (value: number) => {
|
||||
if (!projectId) return;
|
||||
setRenderDistance(value);
|
||||
// setDistance(value);
|
||||
const data = await setEnvironment(
|
||||
@@ -95,6 +96,8 @@ const GlobalProperties: React.FC = () => {
|
||||
// Function to toggle roof visibility
|
||||
const changeRoofVisibility = async () => {
|
||||
|
||||
if (!projectId) return;
|
||||
|
||||
//using REST
|
||||
const data = await setEnvironment(
|
||||
organization,
|
||||
@@ -123,6 +126,8 @@ const GlobalProperties: React.FC = () => {
|
||||
};
|
||||
|
||||
const changeWallVisibility = async () => {
|
||||
if (!projectId) return;
|
||||
|
||||
//using REST
|
||||
const data = await setEnvironment(
|
||||
organization,
|
||||
@@ -150,6 +155,8 @@ const GlobalProperties: React.FC = () => {
|
||||
};
|
||||
|
||||
const shadowVisibility = async () => {
|
||||
if (!projectId) return;
|
||||
|
||||
//using REST
|
||||
const data = await setEnvironment(
|
||||
organization,
|
||||
|
||||
@@ -20,6 +20,8 @@ import { useSocketStore } from "../../../../store/builder/store";
|
||||
import { getUserData } from "../../../../functions/getUserData";
|
||||
import { aisleTextureList } from "./AisleProperties";
|
||||
|
||||
import { upsertAisleApi } from "../../../../services/factoryBuilder/aisle/upsertAisleApi";
|
||||
|
||||
const SelectedAisleProperties: React.FC = () => {
|
||||
const [collapsePresets, setCollapsePresets] = useState(false);
|
||||
const [collapseTexture, setCollapseTexture] = useState(true);
|
||||
@@ -44,23 +46,28 @@ const SelectedAisleProperties: React.FC = () => {
|
||||
const updateBackend = (updatedAisle: Aisle) => {
|
||||
if (updatedAisle && projectId) {
|
||||
|
||||
// API
|
||||
if (!socket?.connected) {
|
||||
|
||||
// upsertAisleApi(updatedAisle.aisleUuid, updatedAisle.points, updatedAisle.type, projectId, selectedVersion?.versionId || '');
|
||||
// API
|
||||
|
||||
// SOCKET
|
||||
upsertAisleApi(updatedAisle.aisleUuid, updatedAisle.points, updatedAisle.type, projectId, selectedVersion?.versionId || '');
|
||||
|
||||
const data = {
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization,
|
||||
aisleUuid: updatedAisle.aisleUuid,
|
||||
points: updatedAisle.points,
|
||||
type: updatedAisle.type
|
||||
} else {
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization,
|
||||
aisleUuid: updatedAisle.aisleUuid,
|
||||
points: updatedAisle.points,
|
||||
type: updatedAisle.type
|
||||
}
|
||||
|
||||
socket.emit('v1:model-aisle:add', data);
|
||||
}
|
||||
|
||||
socket.emit('v1:model-aisle:add', data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,8 +7,8 @@ import { useSocketStore } from "../../../../store/builder/store";
|
||||
import InputRange from "../../../ui/inputs/InputRange";
|
||||
|
||||
import { getUserData } from "../../../../functions/getUserData";
|
||||
// import { upsertWallApi } from "../../../../services/factoryBuilder/wall/upsertWallApi";
|
||||
// import { upsertFloorApi } from "../../../../services/factoryBuilder/floor/upsertFloorApi";
|
||||
import { upsertWallApi } from "../../../../services/factoryBuilder/wall/upsertWallApi";
|
||||
import { upsertFloorApi } from "../../../../services/factoryBuilder/floor/upsertFloorApi";
|
||||
|
||||
const SelectedDecalProperties = () => {
|
||||
const { selectedDecal, setSelectedDecal } = useBuilderStore();
|
||||
@@ -24,39 +24,45 @@ const SelectedDecalProperties = () => {
|
||||
const updateBackend = (updatedData: Wall | Floor) => {
|
||||
if ('wallUuid' in updatedData) {
|
||||
if (projectId && updatedData) {
|
||||
// API
|
||||
if (!socket?.connected) {
|
||||
// API
|
||||
|
||||
// upsertWallApi(projectId, selectedVersion?.versionId || '', updatedData);
|
||||
upsertWallApi(projectId, selectedVersion?.versionId || '', updatedData);
|
||||
} else {
|
||||
|
||||
// SOCKET
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
wallData: updatedData,
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization
|
||||
const data = {
|
||||
wallData: updatedData,
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization
|
||||
}
|
||||
|
||||
socket.emit('v1:model-Wall:add', data);
|
||||
}
|
||||
|
||||
socket.emit('v1:model-Wall:add', data);
|
||||
}
|
||||
} else if ('floorUuid' in updatedData) {
|
||||
if (projectId && updatedData) {
|
||||
// API
|
||||
if (!socket?.connected) {
|
||||
// API
|
||||
|
||||
// upsertFloorApi(projectId, selectedVersion?.versionId || '', updatedData);
|
||||
upsertFloorApi(projectId, selectedVersion?.versionId || '', updatedData);
|
||||
} else {
|
||||
|
||||
// SOCKET
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
floorData: updatedData,
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization
|
||||
const data = {
|
||||
floorData: updatedData,
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization
|
||||
}
|
||||
|
||||
socket.emit('v1:model-Floor:add', data);
|
||||
}
|
||||
|
||||
socket.emit('v1:model-Floor:add', data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import { getUserData } from "../../../../functions/getUserData";
|
||||
import { useSocketStore } from "../../../../store/builder/store";
|
||||
import { materials } from "./FloorProperties";
|
||||
|
||||
// import { upsertFloorApi } from "../../../../services/factoryBuilder/floor/upsertFloorApi";
|
||||
import { upsertFloorApi } from "../../../../services/factoryBuilder/floor/upsertFloorApi";
|
||||
|
||||
const SelectedFloorProperties = () => {
|
||||
const [depth, setDepth] = useState("");
|
||||
@@ -43,22 +43,25 @@ const SelectedFloorProperties = () => {
|
||||
if (!isNaN(parsed) && floor) {
|
||||
const updatedFloor = updateFloor(floor.floorUuid, { floorDepth: parsed });
|
||||
if (projectId) {
|
||||
if (!socket?.connected) {
|
||||
|
||||
// API
|
||||
// API
|
||||
|
||||
// upsertFloorApi(projectId, selectedVersion?.versionId || '', floor);
|
||||
upsertFloorApi(projectId, selectedVersion?.versionId || '', floor);
|
||||
} else {
|
||||
|
||||
// SOCKET
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
floorData: updatedFloor,
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization
|
||||
const data = {
|
||||
floorData: updatedFloor,
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization
|
||||
}
|
||||
|
||||
socket.emit('v1:model-Floor:add', data);
|
||||
}
|
||||
|
||||
socket.emit('v1:model-Floor:add', data);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -69,10 +72,40 @@ const SelectedFloorProperties = () => {
|
||||
if (!isNaN(parsed) && floor) {
|
||||
const updatedFloor = updateFloor(floor.floorUuid, { bevelStrength: parsed });
|
||||
if (projectId) {
|
||||
if (!socket?.connected) {
|
||||
|
||||
// API
|
||||
|
||||
upsertFloorApi(projectId, selectedVersion?.versionId || '', floor);
|
||||
} else {
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
floorData: updatedFloor,
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization
|
||||
}
|
||||
|
||||
socket.emit('v1:model-Floor:add', data);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleIsBeveledToggle = () => {
|
||||
setIsBeveled(!isBeveled);
|
||||
if (!floor) return;
|
||||
const updatedFloor = updateFloor(floor.floorUuid, { isBeveled: !floor.isBeveled });
|
||||
if (projectId) {
|
||||
if (!socket?.connected) {
|
||||
|
||||
// API
|
||||
|
||||
// upsertFloorApi(projectId, selectedVersion?.versionId || '', floor);
|
||||
upsertFloorApi(projectId, selectedVersion?.versionId || '', floor);
|
||||
} else {
|
||||
|
||||
// SOCKET
|
||||
|
||||
@@ -87,30 +120,6 @@ const SelectedFloorProperties = () => {
|
||||
socket.emit('v1:model-Floor:add', data);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleIsBeveledToggle = () => {
|
||||
setIsBeveled(!isBeveled);
|
||||
if (!floor) return;
|
||||
const updatedFloor = updateFloor(floor.floorUuid, { isBeveled: !floor.isBeveled });
|
||||
if (projectId) {
|
||||
|
||||
// API
|
||||
|
||||
// upsertFloorApi(projectId, selectedVersion?.versionId || '', floor);
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
floorData: updatedFloor,
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization
|
||||
}
|
||||
|
||||
socket.emit('v1:model-Floor:add', data);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@@ -119,22 +128,25 @@ const SelectedFloorProperties = () => {
|
||||
const key = activeSurface === "top" ? "topMaterial" : "sideMaterial";
|
||||
const updatedFloor = updateFloor(floor.floorUuid, { [key]: material.textureId });
|
||||
if (projectId) {
|
||||
if (!socket?.connected) {
|
||||
|
||||
// API
|
||||
// API
|
||||
|
||||
// upsertFloorApi(projectId, selectedVersion?.versionId || '', floor);
|
||||
upsertFloorApi(projectId, selectedVersion?.versionId || '', floor);
|
||||
} else {
|
||||
|
||||
// SOCKET
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
floorData: updatedFloor,
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization
|
||||
const data = {
|
||||
floorData: updatedFloor,
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization
|
||||
}
|
||||
|
||||
socket.emit('v1:model-Floor:add', data);
|
||||
}
|
||||
|
||||
socket.emit('v1:model-Floor:add', data);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@@ -11,7 +11,7 @@ import { useParams } from "react-router-dom";
|
||||
import { getUserData } from "../../../../functions/getUserData";
|
||||
import { useSocketStore } from "../../../../store/builder/store";
|
||||
|
||||
// import { upsertWallApi } from "../../../../services/factoryBuilder/wall/upsertWallApi";
|
||||
import { upsertWallApi } from "../../../../services/factoryBuilder/wall/upsertWallApi";
|
||||
|
||||
const SelectedWallProperties = () => {
|
||||
const [height, setHeight] = useState("");
|
||||
@@ -47,22 +47,25 @@ const SelectedWallProperties = () => {
|
||||
if (!isNaN(height) && wall) {
|
||||
const updatedWall = updateWall(wall.wallUuid, { wallHeight: height });
|
||||
if (updatedWall && projectId) {
|
||||
if (!socket?.connected) {
|
||||
|
||||
// API
|
||||
// API
|
||||
|
||||
// upsertWallApi(projectId, selectedVersion?.versionId || '', updatedWall);
|
||||
upsertWallApi(projectId, selectedVersion?.versionId || '', updatedWall);
|
||||
} else {
|
||||
|
||||
// SOCKET
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
wallData: updatedWall,
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization
|
||||
const data = {
|
||||
wallData: updatedWall,
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization
|
||||
}
|
||||
|
||||
socket.emit('v1:model-Wall:add', data);
|
||||
}
|
||||
|
||||
socket.emit('v1:model-Wall:add', data);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -73,10 +76,41 @@ const SelectedWallProperties = () => {
|
||||
if (!isNaN(thickness) && wall) {
|
||||
const updatedWall = updateWall(wall.wallUuid, { wallThickness: thickness });
|
||||
if (updatedWall && projectId) {
|
||||
if (!socket?.connected) {
|
||||
|
||||
// API
|
||||
|
||||
upsertWallApi(projectId, selectedVersion?.versionId || '', updatedWall);
|
||||
} else {
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
wallData: updatedWall,
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization
|
||||
}
|
||||
|
||||
socket.emit('v1:model-Wall:add', data);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleSelectMaterial = (material: { textureId: string; textureName: string }) => {
|
||||
if (!wall) return;
|
||||
|
||||
const updated = (activeSide === "side1" ? { insideMaterial: material.textureId } : { outsideMaterial: material.textureId })
|
||||
const updatedWall = updateWall(wall.wallUuid, updated);
|
||||
if (updatedWall && projectId) {
|
||||
if (!socket?.connected) {
|
||||
|
||||
// API
|
||||
|
||||
// upsertWallApi(projectId, selectedVersion?.versionId || '', updatedWall);
|
||||
} else {
|
||||
|
||||
// SOCKET
|
||||
|
||||
@@ -93,31 +127,6 @@ const SelectedWallProperties = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleSelectMaterial = (material: { textureId: string; textureName: string }) => {
|
||||
if (!wall) return;
|
||||
|
||||
const updated = (activeSide === "side1" ? { insideMaterial: material.textureId } : { outsideMaterial: material.textureId })
|
||||
const updatedWall = updateWall(wall.wallUuid, updated);
|
||||
if (updatedWall && projectId) {
|
||||
|
||||
// API
|
||||
|
||||
// upsertWallApi(projectId, selectedVersion?.versionId || '', updatedWall);
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
wallData: updatedWall,
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization
|
||||
}
|
||||
|
||||
socket.emit('v1:model-Wall:add', data);
|
||||
}
|
||||
};
|
||||
|
||||
if (!wall) return null;
|
||||
|
||||
const selectedMaterials = {
|
||||
|
||||
@@ -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);
|
||||
})
|
||||
};
|
||||
|
||||
|
||||
@@ -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,146 +8,144 @@ import { getUserData } from "../../../../functions/getUserData";
|
||||
import { useVersionContext } from "../../../../modules/builder/version/versionContext";
|
||||
|
||||
const VersionSaved = () => {
|
||||
const { versionHistory, addVersion, createNewVersion, setCreateNewVersion } = useVersionHistoryStore();
|
||||
const { selectedVersionStore } = useVersionContext();
|
||||
const { 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();
|
||||
|
||||
const latestVersion = versionHistory?.[0];
|
||||
|
||||
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 (!latestVersion || !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() || latestVersion.versionName) ?? latestVersion.timeStamp;
|
||||
const updatedDescription = (description.trim() || latestVersion.versionName) ?? latestVersion.timeStamp;
|
||||
const handleSave = () => {
|
||||
if (!selectedVersion || !projectId) return;
|
||||
|
||||
createVersionApi(projectId, userId, latestVersion.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 (!latestVersion) 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 @{latestVersion.createdBy}{" "}{new Date(latestVersion.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;
|
||||
|
||||
@@ -37,7 +37,7 @@ export const RenderInNewWindow: React.FC<NewWindowProps> = ({
|
||||
copyStyles = true,
|
||||
noopener = true,
|
||||
className,
|
||||
theme = "light",
|
||||
theme = localStorage.getItem('theme') ?? 'light',
|
||||
}) => {
|
||||
const [mounted, setMounted] = useState(false);
|
||||
const childWindowRef = useRef<Window | null>(null);
|
||||
|
||||
@@ -65,32 +65,34 @@ const Messages: React.FC<MessageProps> = ({ val, i, setMessages, mode, setIsEdit
|
||||
|
||||
if (isEditableThread && editedThread) {
|
||||
try {
|
||||
// const editThreadTitle = await editThreadTitleApi(projectId, (val as CommentSchema).threadId, value, selectedVersion?.versionId || "")
|
||||
// if (editThreadTitle.message == "ThreadTitle updated Successfully") {
|
||||
// const editedThread: CommentSchema = {
|
||||
// state: 'active',
|
||||
// threadId: editThreadTitle.data.replyId,
|
||||
// creatorId: userId,
|
||||
// createdAt: getRelativeTime(editThreadTitle.data.createdAt),
|
||||
// threadTitle: value,
|
||||
// lastUpdatedAt: new Date().toISOString(),
|
||||
// position: editThreadTitle.data.position,
|
||||
// rotation: [0, 0, 0],
|
||||
// comments: [],
|
||||
// }
|
||||
// updateComment((val as CommentSchema).threadId, editedThread)
|
||||
// }
|
||||
// projectId, userId, threadTitle, organization, threadId
|
||||
const threadEdit = {
|
||||
projectId,
|
||||
userId,
|
||||
threadTitle: value,
|
||||
organization,
|
||||
threadId: (val as CommentSchema).threadId || selectedComment.threadId,
|
||||
versionId: selectedVersion?.versionId || ""
|
||||
}
|
||||
if (!threadSocket?.active) {
|
||||
const editThreadTitle = await editThreadTitleApi(projectId, (val as CommentSchema).threadId, value, selectedVersion?.versionId || "")
|
||||
if (editThreadTitle.message == "ThreadTitle updated Successfully") {
|
||||
const editedThread: CommentSchema = {
|
||||
state: 'active',
|
||||
threadId: editThreadTitle.data.replyId,
|
||||
creatorId: userId,
|
||||
createdAt: getRelativeTime(editThreadTitle.data.createdAt),
|
||||
threadTitle: value,
|
||||
lastUpdatedAt: new Date().toISOString(),
|
||||
position: editThreadTitle.data.position,
|
||||
rotation: [0, 0, 0],
|
||||
comments: [],
|
||||
}
|
||||
updateComment((val as CommentSchema).threadId, editedThread)
|
||||
}
|
||||
} else {
|
||||
const threadEdit = {
|
||||
projectId,
|
||||
userId,
|
||||
threadTitle: value,
|
||||
organization,
|
||||
threadId: (val as CommentSchema).threadId || selectedComment.threadId,
|
||||
versionId: selectedVersion?.versionId || ""
|
||||
}
|
||||
|
||||
threadSocket.emit('v1:thread:updateTitle', threadEdit)
|
||||
threadSocket.emit('v1:thread:updateTitle', threadEdit)
|
||||
}
|
||||
} catch {
|
||||
}
|
||||
} else {
|
||||
@@ -277,7 +279,7 @@ const Messages: React.FC<MessageProps> = ({ val, i, setMessages, mode, setIsEdit
|
||||
<div className="message">
|
||||
{"comment" in val ? val.comment : val.threadTitle}
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div >
|
||||
)}
|
||||
|
||||
@@ -5,12 +5,14 @@ interface SearchProps {
|
||||
value?: string | null; // The current value of the search input
|
||||
placeholder?: string; // Placeholder text for the input
|
||||
onChange: (value: string) => void; // Callback function to handle input changes
|
||||
debounced?: boolean; // New prop: whether to debounce onChange
|
||||
}
|
||||
|
||||
const Search: React.FC<SearchProps> = ({
|
||||
value = "",
|
||||
placeholder = "Search",
|
||||
onChange,
|
||||
debounced = false, // Default false
|
||||
}) => {
|
||||
// State to track the input value and focus status
|
||||
const [inputValue, setInputValue] = useState(value);
|
||||
@@ -19,7 +21,10 @@ const Search: React.FC<SearchProps> = ({
|
||||
const handleInputChange = (event: ChangeEvent<HTMLInputElement>) => {
|
||||
const newValue = event.target.value;
|
||||
setInputValue(newValue);
|
||||
onChange(newValue); // Call the onChange prop with the new value
|
||||
|
||||
if (!debounced) {
|
||||
onChange(newValue); // Immediate call if not debounced
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
@@ -29,26 +34,36 @@ const Search: React.FC<SearchProps> = ({
|
||||
}
|
||||
}, [value]);
|
||||
|
||||
// Handle debounced effect
|
||||
useEffect(() => {
|
||||
if (!debounced) return;
|
||||
|
||||
const timer = setTimeout(() => {
|
||||
onChange(inputValue ?? "");
|
||||
}, 500);
|
||||
|
||||
return () => clearTimeout(timer);
|
||||
}, [inputValue, debounced, onChange]);
|
||||
|
||||
const handleClear = () => {
|
||||
echo.warn("Search field cleared.");
|
||||
console.warn("Search field cleared.");
|
||||
setInputValue("");
|
||||
onChange(""); // Clear the input value
|
||||
onChange(""); // Clear immediately
|
||||
};
|
||||
|
||||
const handleFocus = () => {
|
||||
setIsFocused(true); // Set focus state to true
|
||||
setIsFocused(true);
|
||||
};
|
||||
|
||||
const handleBlur = () => {
|
||||
setIsFocused(false); // Set focus state to false
|
||||
setIsFocused(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="search-wrapper">
|
||||
<div
|
||||
className={`search-container ${
|
||||
isFocused || inputValue ? "active" : ""
|
||||
}`}
|
||||
className={`search-container ${isFocused || inputValue ? "active" : ""
|
||||
}`}
|
||||
>
|
||||
<div className="icon-container">
|
||||
<SearchIcon />
|
||||
@@ -76,4 +91,4 @@ const Search: React.FC<SearchProps> = ({
|
||||
);
|
||||
};
|
||||
|
||||
export default Search;
|
||||
export default Search;
|
||||
@@ -162,7 +162,6 @@ const LogList: React.FC = () => {
|
||||
) : (
|
||||
<RenderInNewWindow
|
||||
title="Log list"
|
||||
theme={localStorage.getItem("theme")}
|
||||
onClose={() => {
|
||||
setOpen(false);
|
||||
setIsLogListVisible(false);
|
||||
|
||||
Reference in New Issue
Block a user