Merge remote-tracking branch 'origin/main-demo' into main-dev
This commit is contained in:
@@ -270,6 +270,7 @@ const DashboardCard: React.FC<DashBoardCardProps> = ({
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handleOptionClick(option);
|
||||
setOpenKebabProjectId(null);
|
||||
}}
|
||||
>
|
||||
{option}
|
||||
|
||||
@@ -13,22 +13,7 @@ import { trashSearchProjectApi } from "../../services/dashboard/trashSearchProje
|
||||
import { restoreTrashApi } from "../../services/dashboard/restoreTrashApi";
|
||||
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";
|
||||
import { Modal } from "../templates/PreviewModal";
|
||||
|
||||
interface DashboardMainProps {
|
||||
activeFolder: Folder;
|
||||
@@ -36,11 +21,16 @@ interface DashboardMainProps {
|
||||
|
||||
const DashboardMain: React.FC<DashboardMainProps> = ({ activeFolder }) => {
|
||||
const [activeSubFolder, setActiveSubFolder] = useState("myProjects");
|
||||
const [projectsData, setProjectsData] = useState<ProjectCollection>({});
|
||||
const [projectsData, setProjectsData] = useState<DashboardProjectCollection>({});
|
||||
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 [projectsCache, setProjectsCache] = useState<{
|
||||
[key: string]: DashboardProjectCollection;
|
||||
}>({});
|
||||
const [showDelete, setShowDelete] = useState(false);
|
||||
const [confirmText, setConfirmText] = useState(""); // 🔹 For confirmation input
|
||||
const [deleteTargetId, setDeleteTargetId] = useState<string | null>(null); // 🔹 Store project id to delete
|
||||
|
||||
const { userId, organization } = getUserData();
|
||||
const { projectSocket } = useSocketStore();
|
||||
@@ -55,7 +45,7 @@ const DashboardMain: React.FC<DashboardMainProps> = ({ activeFolder }) => {
|
||||
}
|
||||
|
||||
try {
|
||||
let projects: ProjectCollection = {}; // initialize as empty object
|
||||
let projects: DashboardProjectCollection = {};
|
||||
|
||||
switch (activeFolder) {
|
||||
case "home":
|
||||
@@ -71,9 +61,10 @@ const DashboardMain: React.FC<DashboardMainProps> = ({ activeFolder }) => {
|
||||
case "trash":
|
||||
projects = await getTrashApi();
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
// Only update cache if projects is not empty
|
||||
if (projects && JSON.stringify(projects) !== JSON.stringify(projectsData)) {
|
||||
setProjectsCache((prev) => ({ ...prev, [cacheKey]: projects }));
|
||||
setProjectsData(projects);
|
||||
@@ -113,15 +104,20 @@ const DashboardMain: React.FC<DashboardMainProps> = ({ activeFolder }) => {
|
||||
updateStateAfterRemove(projectId);
|
||||
};
|
||||
|
||||
const handlePermanentDelete = async (projectId: string): Promise<void> => {
|
||||
const handlePermanentDelete = async (): Promise<void> => {
|
||||
if (!deleteTargetId) return;
|
||||
|
||||
if (projectSocket) {
|
||||
projectSocket.emit("v1:trash:delete", {
|
||||
projectId,
|
||||
projectId: deleteTargetId,
|
||||
organization,
|
||||
userId,
|
||||
});
|
||||
}
|
||||
updateStateAfterRemove(projectId);
|
||||
updateStateAfterRemove(deleteTargetId);
|
||||
setShowDelete(false);
|
||||
setConfirmText("");
|
||||
setDeleteTargetId(null);
|
||||
};
|
||||
|
||||
const handleRestore = async (projectId: string): Promise<void> => {
|
||||
@@ -129,7 +125,11 @@ const DashboardMain: React.FC<DashboardMainProps> = ({ activeFolder }) => {
|
||||
updateStateAfterRemove(projectId);
|
||||
};
|
||||
|
||||
const handleDuplicate = async (projectId: string, projectName: string, thumbnail: string): Promise<void> => {
|
||||
const handleDuplicate = async (
|
||||
projectId: string,
|
||||
projectName: string,
|
||||
thumbnail: string
|
||||
): Promise<void> => {
|
||||
if (projectSocket) {
|
||||
projectSocket.emit("v1:project:Duplicate", {
|
||||
userId,
|
||||
@@ -144,7 +144,7 @@ const DashboardMain: React.FC<DashboardMainProps> = ({ activeFolder }) => {
|
||||
|
||||
// #region Project Map
|
||||
const updateStateAfterRemove = (projectId: string) => {
|
||||
setProjectsData((prev: ProjectCollection) => {
|
||||
setProjectsData((prev: DashboardProjectCollection) => {
|
||||
const key = Object.keys(prev)[0];
|
||||
const updatedList = prev[key]?.filter((p) => p._id !== projectId) || [];
|
||||
return { ...prev, [key]: updatedList };
|
||||
@@ -185,7 +185,10 @@ const DashboardMain: React.FC<DashboardMainProps> = ({ activeFolder }) => {
|
||||
})}
|
||||
{...(activeFolder === "trash" && {
|
||||
handleRestoreProject: handleRestore,
|
||||
handleTrashDeleteProject: handlePermanentDelete,
|
||||
handleTrashDeleteProject: async (id: string): Promise<void> => {
|
||||
setDeleteTargetId(id);
|
||||
setShowDelete(true);
|
||||
},
|
||||
active: "trash",
|
||||
})}
|
||||
openKebabProjectId={openKebabProjectId}
|
||||
@@ -204,7 +207,12 @@ const DashboardMain: React.FC<DashboardMainProps> = ({ activeFolder }) => {
|
||||
<div className="dashboard-home-container">
|
||||
<DashboardNavBar
|
||||
page={activeFolder}
|
||||
{...(activeFolder === "trash" ? { handleTrashSearch: handleSearch } : { handleProjectsSearch: handleSearch, handleRecentProjectSearch: handleSearch })}
|
||||
{...(activeFolder === "trash"
|
||||
? { handleTrashSearch: handleSearch }
|
||||
: {
|
||||
handleProjectsSearch: handleSearch,
|
||||
handleRecentProjectSearch: handleSearch,
|
||||
})}
|
||||
/>
|
||||
|
||||
{activeFolder === "home" && <MarketPlaceBanner />}
|
||||
@@ -212,10 +220,16 @@ const DashboardMain: React.FC<DashboardMainProps> = ({ activeFolder }) => {
|
||||
<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")}>
|
||||
<button
|
||||
className={`header ${activeSubFolder === "myProjects" && "active"}`}
|
||||
onClick={() => setActiveSubFolder("myProjects")}
|
||||
>
|
||||
My Projects
|
||||
</button>
|
||||
<button className={`header ${activeSubFolder === "shared" && "active"}`} onClick={() => setActiveSubFolder("shared")}>
|
||||
<button
|
||||
className={`header ${activeSubFolder === "shared" && "active"}`}
|
||||
onClick={() => setActiveSubFolder("shared")}
|
||||
>
|
||||
Shared with me
|
||||
</button>
|
||||
</div>
|
||||
@@ -223,8 +237,40 @@ const DashboardMain: React.FC<DashboardMainProps> = ({ activeFolder }) => {
|
||||
|
||||
<div className="cards-container">{renderProjects()}</div>
|
||||
|
||||
<ProjectSocketRes setIsSearchActive={setIsSearchActive} {...(activeFolder === "home" ? { setRecentProjects: setProjectsData } : { setWorkspaceProjects: setProjectsData })} />
|
||||
<ProjectSocketRes
|
||||
setIsSearchActive={setIsSearchActive}
|
||||
{...(activeFolder === "home"
|
||||
? { setRecentProjects: setProjectsData }
|
||||
: { setWorkspaceProjects: setProjectsData })}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 🔹 Delete Confirmation Modal */}
|
||||
<Modal
|
||||
isOpen={showDelete}
|
||||
onClose={() => setShowDelete(false)}
|
||||
type="confirm"
|
||||
title="Delete Project"
|
||||
description="This action is permanent. Please type DELETE to confirm."
|
||||
inputs={[
|
||||
{
|
||||
id: "confirmDelete",
|
||||
label: "Confirmation",
|
||||
placeholder: "Type DELETE",
|
||||
value: confirmText,
|
||||
onChange: setConfirmText,
|
||||
},
|
||||
]}
|
||||
buttons={[
|
||||
{ label: "Cancel", onClick: () => setShowDelete(false), variant: "secondary" },
|
||||
{
|
||||
label: "Delete",
|
||||
onClick: handlePermanentDelete,
|
||||
variant: "danger",
|
||||
disabled: confirmText !== "DELETE",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,61 +1,78 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import DashboardNavBar from "./DashboardNavBar";
|
||||
import DashboardCard from "./DashboardCard";
|
||||
import { projectTutorialApi } from "../../services/dashboard/projectTutorialApi";
|
||||
import { AIIcon } from "../icons/ExportCommonIcons";
|
||||
import { DeleteIcon } from "../icons/ContextMenuIcons";
|
||||
|
||||
interface Project {
|
||||
interface Tutorial {
|
||||
_id: string;
|
||||
projectName: string;
|
||||
thumbnail: string;
|
||||
createdBy: string;
|
||||
projectUuid?: string;
|
||||
name: string;
|
||||
thumbnail?: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
interface DiscardedProjects {
|
||||
[key: string]: Project[];
|
||||
}
|
||||
|
||||
const DashboardTutorial = () => {
|
||||
const [tutorialProject, setTutorialProject] = useState<DiscardedProjects>({});
|
||||
|
||||
const handleIcon = async () => {
|
||||
try {
|
||||
let tutorial = await projectTutorialApi();
|
||||
setTutorialProject(tutorial);
|
||||
} catch {}
|
||||
const TutorialCard: React.FC<{ tutorial: Tutorial }> = ({ tutorial }) => {
|
||||
return (
|
||||
<div className="tutorial-card-container">
|
||||
<div
|
||||
className="thumbnail"
|
||||
style={{
|
||||
backgroundImage: tutorial.thumbnail
|
||||
? `url(${tutorial.thumbnail})`
|
||||
: "linear-gradient(135deg, #ddd, #bbb)",
|
||||
}}
|
||||
></div>
|
||||
<div className="tutorial-details">
|
||||
<div className="tutorial-name">{tutorial.name}</div>
|
||||
<div className="updated-date">
|
||||
{new Date(tutorial.updatedAt).toLocaleDateString()}
|
||||
</div>
|
||||
<div className="delete-option">
|
||||
<DeleteIcon />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const [openKebabProjectId, setOpenKebabProjectId] = useState<string | null>(null);
|
||||
const DashboardTutorial: React.FC = () => {
|
||||
const [tutorials, setTutorials] = useState<Tutorial[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
handleIcon();
|
||||
const fetchTutorials = async () => {
|
||||
try {
|
||||
const res = await projectTutorialApi();
|
||||
if (res && Array.isArray(res)) {
|
||||
setTutorials(res);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching tutorials:", error);
|
||||
}
|
||||
};
|
||||
|
||||
fetchTutorials();
|
||||
}, []);
|
||||
|
||||
const renderTrashProjects = () => {
|
||||
const projectList = tutorialProject[Object.keys(tutorialProject)[0]];
|
||||
|
||||
if (!projectList?.length) {
|
||||
return <div className="empty-state">No deleted projects found</div>;
|
||||
}
|
||||
|
||||
return projectList.map((tutorials: any) => (
|
||||
<DashboardCard
|
||||
key={tutorials._id}
|
||||
projectName={tutorials.projectName}
|
||||
thumbnail={tutorials.thumbnail}
|
||||
projectId={tutorials._id}
|
||||
openKebabProjectId={openKebabProjectId}
|
||||
setOpenKebabProjectId={setOpenKebabProjectId}
|
||||
/>
|
||||
));
|
||||
};
|
||||
return (
|
||||
<div className="dashboard-home-container">
|
||||
<DashboardNavBar page="tutorial" />
|
||||
|
||||
<div className="dashboard-container" style={{ height: "calc(100% - 87px)" }}>
|
||||
<div className="header" style={{ display: "flex", gap: "7px" }}></div>
|
||||
<div className="cards-container">{renderTrashProjects()}</div>
|
||||
<div className="tutorials-main-header">
|
||||
<div className="tutorial-buttons-container">
|
||||
<button className="add-tutorials-button">New Tutorial</button>
|
||||
<button className="add-tutorials-button">
|
||||
<AIIcon /> New Tutorial (beta)
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="tutorials-list">
|
||||
{tutorials.length > 0 ? (
|
||||
tutorials.map((tut) => <TutorialCard key={tut._id} tutorial={tut} />)
|
||||
) : (
|
||||
<div className="empty-state">No tutorials available</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,9 +1,18 @@
|
||||
import React from "react";
|
||||
import { DocumentationIcon, HelpIcon, HomeIcon, LogoutIcon, NotificationIcon, ProjectsIcon, TutorialsIcon } from "../icons/DashboardIcon";
|
||||
import {
|
||||
DocumentationIcon,
|
||||
HelpIcon,
|
||||
HomeIcon,
|
||||
LogoutIcon,
|
||||
NotificationIcon,
|
||||
ProjectsIcon,
|
||||
TrashIcon,
|
||||
TutorialsIcon,
|
||||
} from "../icons/DashboardIcon";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import darkThemeImage from "../../assets/image/darkThemeProject.png";
|
||||
import lightThemeImage from "../../assets/image/lightThemeProject.png";
|
||||
import { SettingsIcon, TrashIcon } from "../icons/ExportCommonIcons";
|
||||
import { SettingsIcon } from "../icons/ExportCommonIcons";
|
||||
import { getUserData } from "../../functions/getUserData";
|
||||
import { useSocketStore } from "../../store/builder/store";
|
||||
|
||||
@@ -74,16 +83,33 @@ const SidePannel: React.FC<SidePannelProps> = ({ setActiveTab, activeTab }) => {
|
||||
</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 />
|
||||
<button
|
||||
className={
|
||||
activeTab === "Home" ? "option-list active" : "option-list"
|
||||
}
|
||||
onClick={() => setActiveTab("Home")}
|
||||
>
|
||||
<HomeIcon isActive={activeTab === 'Home'}/>
|
||||
Home
|
||||
</button>
|
||||
<button className={activeTab === "Projects" ? "option-list active" : "option-list"} title="Projects" onClick={() => setActiveTab("Projects")}>
|
||||
<ProjectsIcon />
|
||||
<button
|
||||
className={
|
||||
activeTab === "Projects" ? "option-list active" : "option-list"
|
||||
}
|
||||
title="Projects"
|
||||
onClick={() => setActiveTab("Projects")}
|
||||
>
|
||||
<ProjectsIcon isActive={activeTab === 'Projects'}/>
|
||||
Projects
|
||||
</button>
|
||||
<button className={activeTab === "Trash" ? "option-list active" : "option-list"} title="Trash" onClick={() => setActiveTab("Trash")}>
|
||||
<TrashIcon />
|
||||
<button
|
||||
className={
|
||||
activeTab === "Trash" ? "option-list active" : "option-list"
|
||||
}
|
||||
title="Trash"
|
||||
onClick={() => setActiveTab("Trash")}
|
||||
>
|
||||
<TrashIcon isActive={activeTab === 'Trash'}/>
|
||||
Trash
|
||||
</button>
|
||||
<button
|
||||
@@ -91,23 +117,23 @@ const SidePannel: React.FC<SidePannelProps> = ({ setActiveTab, activeTab }) => {
|
||||
title="coming soon"
|
||||
disabled
|
||||
onClick={() => {
|
||||
// setActiveTab("Tutorials");
|
||||
setActiveTab("Tutorials");
|
||||
console.warn("Tutorials comming soon");
|
||||
}}
|
||||
>
|
||||
<TutorialsIcon />
|
||||
<TutorialsIcon isActive={activeTab === 'Tutorials'}/>
|
||||
Tutorials
|
||||
</button>
|
||||
<button
|
||||
className={activeTab === "Documentation" ? "option-list active" : "option-list"}
|
||||
title="coming soon"
|
||||
disabled
|
||||
disabled // remove when added
|
||||
onClick={() => {
|
||||
// setActiveTab("Documentation");
|
||||
setActiveTab("Documentation");
|
||||
console.warn("Documentation comming soon");
|
||||
}}
|
||||
>
|
||||
<DocumentationIcon />
|
||||
<DocumentationIcon isActive={activeTab === 'Documentation'}/>
|
||||
Documentation
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -5,24 +5,14 @@ import { getAllProjectsApi } from "../../../services/dashboard/getAllProjectsApi
|
||||
import { recentlyViewedApi } from "../../../services/dashboard/recentlyViewedApi";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
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 DashboardProjectCollection {
|
||||
[key: string]: DashboardProject[];
|
||||
}
|
||||
|
||||
interface ProjectSocketResProps {
|
||||
setRecentProjects?: React.Dispatch<React.SetStateAction<ProjectCollection>>;
|
||||
setWorkspaceProjects?: React.Dispatch<React.SetStateAction<ProjectCollection>>;
|
||||
setRecentProjects?: React.Dispatch<React.SetStateAction<DashboardProjectCollection>>;
|
||||
setWorkspaceProjects?: React.Dispatch<React.SetStateAction<DashboardProjectCollection>>;
|
||||
setIsSearchActive?: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ export function NotificationIcon() {
|
||||
);
|
||||
}
|
||||
|
||||
export function HomeIcon() {
|
||||
export function HomeIcon({ isActive }: Readonly<{ isActive: boolean }>) {
|
||||
return (
|
||||
<svg
|
||||
width="16"
|
||||
@@ -34,13 +34,13 @@ 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-color)"
|
||||
stroke={!isActive ? "var(--text-color)" : "var(--text-button-color)"}
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function ProjectsIcon() {
|
||||
export function ProjectsIcon({ isActive }: Readonly<{ isActive: boolean }>) {
|
||||
return (
|
||||
<svg
|
||||
width="16"
|
||||
@@ -51,13 +51,43 @@ 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-color)"
|
||||
fill={!isActive ? "var(--text-color)" : "var(--text-button-color)"}
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function TutorialsIcon() {
|
||||
export function TrashIcon({ isActive }: Readonly<{ isActive: boolean }>) {
|
||||
return (
|
||||
<svg
|
||||
width="12"
|
||||
height="12"
|
||||
viewBox="0 0 12 12"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<g clipPath="url(#clip0_111_486)">
|
||||
<path
|
||||
d="M4.70587 5.32353V9.02941M7.17646 5.32353V9.02941M9.64704 2.85294V10.2647C9.64704 10.947 9.094 11.5 8.41175 11.5H3.47057C2.78834 11.5 2.23528 10.947 2.23528 10.2647V2.85294M0.999985 2.85294H10.8823M7.7941 2.85294V2.23529C7.7941 1.55306 7.24106 1 6.55881 1H5.32351C4.64128 1 4.08822 1.55306 4.08822 2.23529V2.85294"
|
||||
stroke={!isActive ? "var(--text-color)" : "var(--text-button-color)"}
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_111_486">
|
||||
<rect
|
||||
width="12"
|
||||
height="12"
|
||||
fill={!isActive ? "var(--text-color)" : "var(--text-button-color)"}
|
||||
/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function TutorialsIcon({ isActive }: Readonly<{ isActive: boolean }>) {
|
||||
return (
|
||||
<svg
|
||||
width="16"
|
||||
@@ -70,110 +100,110 @@ export function TutorialsIcon() {
|
||||
cx="8.157"
|
||||
cy="8.35866"
|
||||
r="6.17928"
|
||||
stroke="var(--text-color)"
|
||||
stroke={!isActive ? "var(--text-color)" : "var(--text-button-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-color)"
|
||||
stroke="var(--text-color)"
|
||||
fill={!isActive ? "var(--text-color)" : "var(--text-button-color)"}
|
||||
stroke={!isActive ? "var(--text-color)" : "var(--text-button-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-color)"
|
||||
stroke="var(--text-color)"
|
||||
fill={!isActive ? "var(--text-color)" : "var(--text-button-color)"}
|
||||
stroke={!isActive ? "var(--text-color)" : "var(--text-button-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-color)"
|
||||
stroke="var(--text-color)"
|
||||
fill={!isActive ? "var(--text-color)" : "var(--text-button-color)"}
|
||||
stroke={!isActive ? "var(--text-color)" : "var(--text-button-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-color)"
|
||||
stroke="var(--text-color)"
|
||||
fill={!isActive ? "var(--text-color)" : "var(--text-button-color)"}
|
||||
stroke={!isActive ? "var(--text-color)" : "var(--text-button-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-color)"
|
||||
stroke="var(--text-color)"
|
||||
fill={!isActive ? "var(--text-color)" : "var(--text-button-color)"}
|
||||
stroke={!isActive ? "var(--text-color)" : "var(--text-button-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-color)"
|
||||
stroke="var(--text-color)"
|
||||
fill={!isActive ? "var(--text-color)" : "var(--text-button-color)"}
|
||||
stroke={!isActive ? "var(--text-color)" : "var(--text-button-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-color)"
|
||||
stroke="var(--text-color)"
|
||||
fill={!isActive ? "var(--text-color)" : "var(--text-button-color)"}
|
||||
stroke={!isActive ? "var(--text-color)" : "var(--text-button-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-color)"
|
||||
stroke="var(--text-color)"
|
||||
fill={!isActive ? "var(--text-color)" : "var(--text-button-color)"}
|
||||
stroke={!isActive ? "var(--text-color)" : "var(--text-button-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-color)"
|
||||
stroke="var(--text-color)"
|
||||
fill={!isActive ? "var(--text-color)" : "var(--text-button-color)"}
|
||||
stroke={!isActive ? "var(--text-color)" : "var(--text-button-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-color)"
|
||||
stroke="var(--text-color)"
|
||||
fill={!isActive ? "var(--text-color)" : "var(--text-button-color)"}
|
||||
stroke={!isActive ? "var(--text-color)" : "var(--text-button-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-color)"
|
||||
stroke="var(--text-color)"
|
||||
fill={!isActive ? "var(--text-color)" : "var(--text-button-color)"}
|
||||
stroke={!isActive ? "var(--text-color)" : "var(--text-button-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-color)"
|
||||
stroke="var(--text-color)"
|
||||
fill={!isActive ? "var(--text-color)" : "var(--text-button-color)"}
|
||||
stroke={!isActive ? "var(--text-color)" : "var(--text-button-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-color)"
|
||||
stroke="var(--text-color)"
|
||||
fill={!isActive ? "var(--text-color)" : "var(--text-button-color)"}
|
||||
stroke={!isActive ? "var(--text-color)" : "var(--text-button-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-color)"
|
||||
stroke="var(--text-color)"
|
||||
fill={!isActive ? "var(--text-color)" : "var(--text-button-color)"}
|
||||
stroke={!isActive ? "var(--text-color)" : "var(--text-button-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-color)"
|
||||
stroke="var(--text-color)"
|
||||
fill={!isActive ? "var(--text-color)" : "var(--text-button-color)"}
|
||||
stroke={!isActive ? "var(--text-color)" : "var(--text-button-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-color)"
|
||||
stroke="var(--text-color)"
|
||||
fill={!isActive ? "var(--text-color)" : "var(--text-button-color)"}
|
||||
stroke={!isActive ? "var(--text-color)" : "var(--text-button-color)"}
|
||||
strokeWidth="0.562865"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function DocumentationIcon() {
|
||||
export function DocumentationIcon({ isActive }: Readonly<{ isActive: boolean }>) {
|
||||
return (
|
||||
<svg
|
||||
width="16"
|
||||
@@ -184,12 +214,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-color)"
|
||||
stroke={!isActive ? "var(--text-color)" : "var(--text-button-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-color)"
|
||||
stroke={!isActive ? "var(--text-color)" : "var(--text-button-color)"}
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
@@ -213,12 +243,7 @@ export function HelpIcon() {
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_764_1941">
|
||||
<rect
|
||||
width="12"
|
||||
height="12"
|
||||
fill="white"
|
||||
transform="translate(0 0.5)"
|
||||
/>
|
||||
<rect width="12" height="12" fill="white" transform="translate(0 0.5)" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
@@ -9,17 +9,13 @@ export function AnalysisIcon({ isActive }: Readonly<{ isActive: boolean }>) {
|
||||
>
|
||||
<path
|
||||
d="M17.5002 12.4987L15.1418 10.1404M10.8335 14.1654H5.8335M7.50016 10.832H5.8335M10.8335 8.33203C10.8335 8.82648 10.9801 9.30983 11.2548 9.72096C11.5295 10.1321 11.92 10.4525 12.3768 10.6417C12.8336 10.8309 13.3363 10.8805 13.8212 10.784C14.3062 10.6875 14.7516 10.4494 15.1013 10.0998C15.4509 9.75017 15.689 9.30471 15.7855 8.81976C15.8819 8.3348 15.8324 7.83214 15.6432 7.37532C15.454 6.91851 15.1335 6.52806 14.7224 6.25336C14.3113 5.97865 13.8279 5.83203 13.3335 5.83203C12.6705 5.83203 12.0346 6.09542 11.5657 6.56426C11.0969 7.03311 10.8335 7.66899 10.8335 8.33203Z"
|
||||
stroke={
|
||||
isActive ? "var(--icon-default-color-active)" : "var(--text-color)"
|
||||
}
|
||||
stroke={isActive ? "var(--icon-default-color-active)" : "var(--text-color)"}
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M14.1667 14.1667V16.6667C14.1667 16.8877 14.0789 17.0996 13.9226 17.2559C13.7663 17.4122 13.5543 17.5 13.3333 17.5H3.33333C3.11232 17.5 2.90036 17.4122 2.74408 17.2559C2.5878 17.0996 2.5 16.8877 2.5 16.6667V3.33333C2.5 3.11232 2.5878 2.90036 2.74408 2.74408C2.90036 2.5878 3.11232 2.5 3.33333 2.5H13.3333"
|
||||
stroke={
|
||||
isActive ? "var(--icon-default-color-active)" : "var(--text-color)"
|
||||
}
|
||||
stroke={isActive ? "var(--icon-default-color-active)" : "var(--text-color)"}
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
@@ -38,15 +34,11 @@ export function MechanicsIcon({ isActive }: Readonly<{ isActive: boolean }>) {
|
||||
>
|
||||
<path
|
||||
d="M17.69 11.7629L16.4912 11.5885C16.4161 11.5828 16.3514 11.5356 16.3226 11.466L15.9339 10.5281C15.9049 10.4588 15.917 10.3794 15.9662 10.3224L16.6834 9.35896C16.7973 9.22544 16.7898 9.02639 16.6655 8.90212L16.098 8.3348C15.974 8.21053 15.7746 8.20247 15.6414 8.31697L14.6776 9.03387C14.621 9.08282 14.541 9.09546 14.4719 9.0667L13.5338 8.67776C13.4644 8.64929 13.417 8.58341 13.4109 8.50918L13.2372 7.31068C13.2234 7.1352 13.0772 7 12.9018 7H12.0991C11.9234 7 11.7772 7.1352 11.7637 7.31068L11.5893 8.50918C11.5839 8.58399 11.5364 8.64929 11.4671 8.67776L10.5286 9.0667C10.4593 9.09546 10.3799 9.08282 10.3227 9.03387L9.35945 8.31697C9.22628 8.20276 9.0272 8.2105 8.90264 8.3348L8.33532 8.90212C8.21105 9.02639 8.20357 9.22547 8.31746 9.35896L9.03439 10.3227C9.08302 10.3794 9.09598 10.4591 9.06693 10.5281L8.67825 11.466C8.65007 11.5356 8.58448 11.5828 8.50996 11.5889L7.31062 11.7629C7.13569 11.7767 7.00049 11.9229 7.00049 12.0986V12.901C7.00049 13.0765 7.13572 13.2232 7.31062 13.2364L8.50996 13.4105C8.58448 13.4165 8.65007 13.464 8.67825 13.5333L9.06693 14.4712C9.09598 14.5408 9.08305 14.6205 9.03439 14.6772L8.31746 15.641C8.20354 15.7744 8.21102 15.9729 8.33532 16.0978L8.90264 16.6651C9.0272 16.7894 9.22628 16.7969 9.35945 16.6827L10.3227 15.9654C10.3799 15.9171 10.4593 15.9042 10.5286 15.9333L11.4671 16.3216C11.5364 16.3507 11.5839 16.4157 11.5897 16.4908L11.7637 17.6893C11.7775 17.8645 11.9234 18 12.0991 18H12.9018C13.0776 18 13.2234 17.8645 13.2372 17.6893L13.411 16.4908C13.417 16.4157 13.4645 16.3507 13.5338 16.3216L14.4722 15.9333C14.5413 15.9042 14.6213 15.9171 14.6779 15.9654L15.6414 16.6827C15.7746 16.7969 15.974 16.7894 16.098 16.6651L16.6656 16.0978C16.7902 15.9729 16.7974 15.7744 16.6834 15.641L15.9662 14.6772C15.9179 14.6205 15.9049 14.5408 15.934 14.4712L16.3226 13.5333C16.3514 13.464 16.4161 13.4165 16.4912 13.4105L17.69 13.2364C17.8652 13.2232 18.0004 13.0765 18.0004 12.901V12.0986C18.0004 11.9228 17.8652 11.7767 17.69 11.7629ZM13.8191 13.8187C13.4667 14.1706 12.9987 14.3645 12.5004 14.3645C12.0024 14.3645 11.5337 14.1706 11.1816 13.8187C10.8295 13.466 10.6353 12.9979 10.6353 12.4997C10.6353 12.0017 10.8295 11.5333 11.1816 11.1812C11.5337 10.8285 12.0024 10.6351 12.5004 10.6351C12.9987 10.6351 13.4667 10.8285 13.8191 11.1812C14.1712 11.5333 14.3652 12.0017 14.3652 12.4997C14.3652 12.9979 14.1713 13.466 13.8191 13.8187Z"
|
||||
stroke={
|
||||
isActive ? "var(--icon-default-color-active)" : "var(--text-color)"
|
||||
}
|
||||
stroke={isActive ? "var(--icon-default-color-active)" : "var(--text-color)"}
|
||||
/>
|
||||
<path
|
||||
d="M6.7961 8.78085C6.92052 8.73626 6.98909 8.60354 6.95416 8.47635L6.77216 7.58599C6.75759 7.53147 6.77438 7.47339 6.81595 7.43541L7.37949 6.92253C7.42078 6.88482 7.48083 6.87353 7.53285 6.89334L8.39565 7.15458C8.51874 7.20111 8.65747 7.1455 8.71367 7.02603L8.96969 6.48121C9.02637 6.36145 8.98098 6.21939 8.86644 6.1544L8.11541 5.65637C8.06665 5.62829 8.03718 5.57515 8.04024 5.51899L8.07603 4.75805C8.07906 4.70161 8.11263 4.65178 8.1636 4.62864L8.96614 4.20052C9.08642 4.14683 9.14452 4.00865 9.09994 3.88532L8.89619 3.31844C8.85132 3.19427 8.7186 3.12546 8.59144 3.16042L7.70081 3.34183C7.64628 3.35726 7.58873 3.34019 7.5505 3.29832L7.03784 2.73511C6.99958 2.69324 6.98804 2.63436 7.00837 2.58151L7.26967 1.71923C7.31617 1.59562 7.2606 1.45713 7.14084 1.40122L6.59627 1.14492C6.47679 1.08873 6.33417 1.13362 6.26894 1.24841L5.77118 1.99948C5.74335 2.04823 5.69024 2.07764 5.63405 2.07492L4.87311 2.03885C4.81695 2.03638 4.76629 2.00167 4.74345 1.951L4.31589 1.14877C4.26165 1.02849 4.12399 0.970357 4.0001 1.01498L3.43353 1.21897C3.30936 1.26359 3.24055 1.39603 3.27579 1.52319L3.45748 2.4141C3.47235 2.46804 3.45556 2.52642 3.41372 2.56413L2.85014 3.07729C2.8083 3.115 2.74912 3.12657 2.69626 3.10676L1.83398 2.84521C1.71093 2.79843 1.57244 2.85459 1.516 2.97404L1.25995 3.51864C1.20379 3.63812 1.24865 3.78074 1.3632 3.84569L2.11423 4.34373C2.16323 4.37156 2.19242 4.42442 2.1894 4.48058L2.1536 5.24152C2.15141 5.29824 2.11698 5.34779 2.06606 5.37091L1.26353 5.79899C1.14318 5.85296 1.08511 5.99089 1.12973 6.11423L1.33373 6.68107C1.37832 6.80524 1.51103 6.87405 1.63795 6.8394L2.52914 6.65716C2.58311 6.64228 2.64146 6.65935 2.67916 6.70119L3.19176 7.26449C3.23003 7.30633 3.24107 7.36579 3.2212 7.41838L2.95996 8.28065C2.91371 8.40399 2.96935 8.54247 3.08879 8.59863L3.63336 8.85466C3.75312 8.91109 3.89543 8.86567 3.9607 8.75141L4.45818 8.0001C4.48626 7.95165 4.53911 7.9219 4.59555 7.92517L5.35652 7.96097C5.41296 7.96375 5.46307 7.99732 5.48646 8.04879L5.91374 8.85074C5.96827 8.97108 6.10564 9.02971 6.2295 8.98509L6.7961 8.78085ZM5.58891 6.31659C5.23732 6.44295 4.85716 6.42533 4.51878 6.2662C4.18043 6.10707 3.92493 5.82571 3.79802 5.47385C3.67166 5.12201 3.68928 4.7421 3.84844 4.40403C4.00754 4.0654 4.28917 3.8099 4.64076 3.68329C4.99259 3.55662 5.37251 3.57424 5.71058 3.73393C6.04865 3.89251 6.3047 4.17389 6.43159 4.52573C6.55798 4.87759 6.54008 5.25723 6.38123 5.59561C6.22154 5.93365 5.94043 6.18967 5.58891 6.31659Z"
|
||||
stroke={
|
||||
isActive ? "var(--icon-default-color-active)" : "var(--text-color)"
|
||||
}
|
||||
stroke={isActive ? "var(--icon-default-color-active)" : "var(--text-color)"}
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
@@ -63,21 +55,15 @@ export function PropertiesIcon({ isActive }: Readonly<{ isActive: boolean }>) {
|
||||
>
|
||||
<path
|
||||
d="M17.4254 15.3839L8.67142 6.79353C8.79789 6.57037 8.90139 6.337 8.97464 6.09625C8.98245 6.01909 8.99808 5.93903 9.02639 5.85746C9.04151 5.81156 9.05423 5.76468 9.06692 5.71831C9.26711 4.6353 8.94923 3.4756 8.11182 2.63819C7.84716 2.37353 7.54982 2.16113 7.23341 2C7.21778 2 7.20216 2.00197 7.18703 2.00538L6.90578 2.07422C6.85303 2.08791 6.80762 2.12109 6.78028 2.16797C6.75294 2.21435 6.74562 2.2705 6.76025 2.32275L7.36475 4.49367C7.38722 4.57474 7.35694 4.66067 7.28906 4.70999L5.91552 5.70756C5.87352 5.73734 5.82177 5.75052 5.77049 5.74368L4.31101 5.54249C4.23142 5.53174 4.16648 5.47512 4.14498 5.39796L3.50685 3.10504C3.47754 3.00104 3.3711 2.93804 3.26613 2.96391L2.89697 3.05426C2.87794 3.05913 2.86085 3.06891 2.84375 3.07916C2.41994 3.67339 2.20606 4.37117 2.20312 5.0699C2.20947 5.94687 2.54541 6.82188 3.21435 7.49082C4.02148 8.29748 5.12649 8.61389 6.17581 8.46592C6.32718 8.44395 6.46537 8.42879 6.59231 8.42001C6.7134 8.38679 6.83256 8.34335 6.95072 8.29598C8.41851 9.78233 15.631 17.1784 15.631 17.1784C16.1266 17.674 16.9298 17.674 17.4254 17.1784C17.921 16.6828 17.921 15.8796 17.4254 15.3839ZM16.9767 16.7297C16.7291 16.9777 16.3277 16.9777 16.0797 16.7297C15.8316 16.4821 15.8316 16.0802 16.0797 15.8322C16.3277 15.5846 16.7291 15.5846 16.9767 15.8322C17.2247 16.0802 17.2247 16.4821 16.9767 16.7297Z"
|
||||
fill={
|
||||
isActive ? "var(--icon-default-color-active)" : "var(--text-color)"
|
||||
}
|
||||
fill={isActive ? "var(--icon-default-color-active)" : "var(--text-color)"}
|
||||
/>
|
||||
<path
|
||||
d="M14.6953 6.25784L16.1182 5.89699L17.4878 3.50488L16.8243 2.8755L16.1612 2.24609L13.8442 3.73926L13.5586 5.17824L11.0991 7.77054L12.2183 8.8682L14.6953 6.25784Z"
|
||||
fill={
|
||||
isActive ? "var(--icon-default-color-active)" : "var(--text-color)"
|
||||
}
|
||||
fill={isActive ? "var(--icon-default-color-active)" : "var(--text-color)"}
|
||||
/>
|
||||
<path
|
||||
d="M7.56537 10.7632C7.28215 11.0083 6.8808 10.7046 6.35539 11.2588L2.88271 14.918C2.18837 15.6494 2.21865 16.8057 2.95009 17.5C3.68153 18.1944 4.83732 18.1641 5.53164 17.4327L9.00482 13.7725C9.53023 13.2188 9.20647 12.8345 9.43598 12.5386C9.48285 12.4776 9.52582 12.4356 9.56879 12.4038C8.91157 11.731 8.28266 11.0874 7.73821 10.5312C7.71528 10.6084 7.66303 10.6792 7.56537 10.7632Z"
|
||||
fill={
|
||||
isActive ? "var(--icon-default-color-active)" : "var(--text-color)"
|
||||
}
|
||||
fill={isActive ? "var(--icon-default-color-active)" : "var(--text-color)"}
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
@@ -96,17 +82,13 @@ export function SimulationIcon({ isActive }: Readonly<{ isActive: boolean }>) {
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M6.44104 7.04762C6.57815 6.98413 6.73614 6.98413 6.87325 7.04762L12.0161 9.42958C12.198 9.51377 12.3143 9.69589 12.3143 9.89624V15.8512C12.3143 16.0347 12.2165 16.2043 12.0577 16.2962L6.9148 19.2736C6.75547 19.3659 6.55881 19.3659 6.39949 19.2736L1.25661 16.2962C1.09779 16.2043 1 16.0347 1 15.8512V9.89624C1 9.69589 1.11635 9.51377 1.29815 9.42958L6.44104 7.04762ZM2.02857 10.7297L6.14286 12.794V17.9366L2.02857 15.5546V10.7297ZM7.17143 17.9366L11.2857 15.5546V10.7297L7.17143 12.794V17.9366ZM6.65714 11.9013L10.6163 9.91477L6.65714 8.08106L2.69798 9.91477L6.65714 11.9013Z"
|
||||
fill={
|
||||
isActive ? "var(--icon-default-color-active)" : "var(--text-color)"
|
||||
}
|
||||
fill={isActive ? "var(--icon-default-color-active)" : "var(--text-color)"}
|
||||
/>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M12.441 1.04762C12.5781 0.984127 12.7361 0.984127 12.8732 1.04762L18.0161 3.42958C18.198 3.51377 18.3143 3.69589 18.3143 3.89624V9.85116C18.3143 10.0347 18.2165 10.2043 18.0577 10.2962L12.9148 13.2736C12.7555 13.3659 12.5588 13.3659 12.3995 13.2736L7.25661 10.2962C7.09779 10.2043 7 10.0347 7 9.85116V3.89624C7 3.69589 7.11635 3.51377 7.29815 3.42958L12.441 1.04762ZM8.02857 4.72968L12.1429 6.79403V11.9366L8.02857 9.55463V4.72968ZM13.1714 11.9366L17.2857 9.55463V4.72968L13.1714 6.79403V11.9366ZM12.6571 5.90129L16.6163 3.91477L12.6571 2.08106L8.69798 3.91477L12.6571 5.90129Z"
|
||||
fill={
|
||||
isActive ? "var(--icon-default-color-active)" : "var(--text-color)"
|
||||
}
|
||||
fill={isActive ? "var(--icon-default-color-active)" : "var(--text-color)"}
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
@@ -225,9 +207,7 @@ export function MoveArrowLeft() {
|
||||
|
||||
// simulation card icons
|
||||
|
||||
export function ExpandIcon({
|
||||
color = "#6F42C1",
|
||||
}: Readonly<{ color?: string }>) {
|
||||
export function ExpandIcon({ color = "#6F42C1" }: Readonly<{ color?: string }>) {
|
||||
return (
|
||||
<svg
|
||||
width="30"
|
||||
@@ -245,9 +225,7 @@ export function ExpandIcon({
|
||||
);
|
||||
}
|
||||
|
||||
export function SimulationStatusIcon({
|
||||
color = "#21FF59",
|
||||
}: Readonly<{ color?: string }>) {
|
||||
export function SimulationStatusIcon({ color = "#21FF59" }: Readonly<{ color?: string }>) {
|
||||
return (
|
||||
<svg
|
||||
width="14"
|
||||
@@ -369,18 +347,10 @@ export function StorageCapacityIcon() {
|
||||
in2="BackgroundImageFix"
|
||||
result="shape"
|
||||
/>
|
||||
<feGaussianBlur
|
||||
stdDeviation="2"
|
||||
result="effect1_foregroundBlur_4582_10883"
|
||||
/>
|
||||
<feGaussianBlur stdDeviation="2" result="effect1_foregroundBlur_4582_10883" />
|
||||
</filter>
|
||||
<clipPath id="clip0_4582_10883">
|
||||
<rect
|
||||
width="20"
|
||||
height="20"
|
||||
fill="white"
|
||||
transform="translate(0.5)"
|
||||
/>
|
||||
<rect width="20" height="20" fill="white" transform="translate(0.5)" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
@@ -398,14 +368,7 @@ export function CompareLayoutIcon() {
|
||||
>
|
||||
<g filter="url(#filter0_d_3106_8112)">
|
||||
<rect x="24" y="4" width="134" height="106" rx="6" fill="#FCFDFD" />
|
||||
<rect
|
||||
x="24.5"
|
||||
y="4.5"
|
||||
width="133"
|
||||
height="105"
|
||||
rx="5.5"
|
||||
stroke="#E5E5EA"
|
||||
/>
|
||||
<rect x="24.5" y="4.5" width="133" height="105" rx="5.5" stroke="#E5E5EA" />
|
||||
</g>
|
||||
<rect x="98" y="12" width="2" height="2" fill="#E5E5EA" />
|
||||
<rect x="98" y="19" width="2" height="2" fill="#E5E5EA" />
|
||||
@@ -591,10 +554,7 @@ export function CompareLayoutIcon() {
|
||||
in2="BackgroundImageFix"
|
||||
result="shape"
|
||||
/>
|
||||
<feGaussianBlur
|
||||
stdDeviation="2"
|
||||
result="effect1_foregroundBlur_3106_8112"
|
||||
/>
|
||||
<feGaussianBlur stdDeviation="2" result="effect1_foregroundBlur_3106_8112" />
|
||||
</filter>
|
||||
<filter
|
||||
id="filter2_f_3106_8112"
|
||||
@@ -612,10 +572,7 @@ export function CompareLayoutIcon() {
|
||||
in2="BackgroundImageFix"
|
||||
result="shape"
|
||||
/>
|
||||
<feGaussianBlur
|
||||
stdDeviation="2"
|
||||
result="effect1_foregroundBlur_3106_8112"
|
||||
/>
|
||||
<feGaussianBlur stdDeviation="2" result="effect1_foregroundBlur_3106_8112" />
|
||||
</filter>
|
||||
<filter
|
||||
id="filter3_f_3106_8112"
|
||||
@@ -633,10 +590,7 @@ export function CompareLayoutIcon() {
|
||||
in2="BackgroundImageFix"
|
||||
result="shape"
|
||||
/>
|
||||
<feGaussianBlur
|
||||
stdDeviation="2"
|
||||
result="effect1_foregroundBlur_3106_8112"
|
||||
/>
|
||||
<feGaussianBlur stdDeviation="2" result="effect1_foregroundBlur_3106_8112" />
|
||||
</filter>
|
||||
<filter
|
||||
id="filter4_f_3106_8112"
|
||||
@@ -654,10 +608,7 @@ export function CompareLayoutIcon() {
|
||||
in2="BackgroundImageFix"
|
||||
result="shape"
|
||||
/>
|
||||
<feGaussianBlur
|
||||
stdDeviation="2"
|
||||
result="effect1_foregroundBlur_3106_8112"
|
||||
/>
|
||||
<feGaussianBlur stdDeviation="2" result="effect1_foregroundBlur_3106_8112" />
|
||||
</filter>
|
||||
<filter
|
||||
id="filter5_f_3106_8112"
|
||||
@@ -675,10 +626,7 @@ export function CompareLayoutIcon() {
|
||||
in2="BackgroundImageFix"
|
||||
result="shape"
|
||||
/>
|
||||
<feGaussianBlur
|
||||
stdDeviation="2"
|
||||
result="effect1_foregroundBlur_3106_8112"
|
||||
/>
|
||||
<feGaussianBlur stdDeviation="2" result="effect1_foregroundBlur_3106_8112" />
|
||||
</filter>
|
||||
<filter
|
||||
id="filter6_d_3106_8112"
|
||||
@@ -793,10 +741,7 @@ export const ResizerIcon = () => {
|
||||
in2="BackgroundImageFix"
|
||||
result="shape"
|
||||
/>
|
||||
<feGaussianBlur
|
||||
stdDeviation="2"
|
||||
result="effect1_foregroundBlur_3106_8243"
|
||||
/>
|
||||
<feGaussianBlur stdDeviation="2" result="effect1_foregroundBlur_3106_8243" />
|
||||
</filter>
|
||||
<clipPath id="clip0_3106_8243">
|
||||
<rect
|
||||
@@ -836,21 +781,44 @@ export const LayoutIcon = () => {
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
export function FilePackageIcon({ isActive }: Readonly<{ isActive: boolean }>) {
|
||||
|
||||
return (
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<svg
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<g clipPath="url(#clip0_6049_2260)">
|
||||
<g filter="url(#filter0_f_6049_2260)">
|
||||
<path d="M12 3C13.1046 3 14 3.89543 14 5H15C16.6569 5 18 6.34315 18 8V14C18 15.6569 16.6569 17 15 17H5C3.34315 17 2 15.6569 2 14V8C2 6.34315 3.34315 5 5 5H6C6 3.89543 6.89543 3 8 3H12ZM17 9H3V14C3 15.0711 3.84198 15.9455 4.90018 15.9976L5 16H15C16.0711 16 16.9455 15.158 16.9976 14.0998L17 14V9ZM15 6H5C3.9289 6 3.05446 6.84198 3.00245 7.90018L3 8H17C17 6.9289 16.158 6.05446 15.0998 6.00245L15 6ZM12 4H8C7.47282 4 7.04092 4.40794 7.00274 4.92537L7 5H13C13 4.47282 12.5921 4.04092 12.0746 4.00274L12 4Z" fill="#FCFDFD" />
|
||||
<path
|
||||
d="M12 3C13.1046 3 14 3.89543 14 5H15C16.6569 5 18 6.34315 18 8V14C18 15.6569 16.6569 17 15 17H5C3.34315 17 2 15.6569 2 14V8C2 6.34315 3.34315 5 5 5H6C6 3.89543 6.89543 3 8 3H12ZM17 9H3V14C3 15.0711 3.84198 15.9455 4.90018 15.9976L5 16H15C16.0711 16 16.9455 15.158 16.9976 14.0998L17 14V9ZM15 6H5C3.9289 6 3.05446 6.84198 3.00245 7.90018L3 8H17C17 6.9289 16.158 6.05446 15.0998 6.00245L15 6ZM12 4H8C7.47282 4 7.04092 4.40794 7.00274 4.92537L7 5H13C13 4.47282 12.5921 4.04092 12.0746 4.00274L12 4Z"
|
||||
fill={isActive ? "#FCFDFD" : "var(--text-color)"}
|
||||
/>
|
||||
</g>
|
||||
<path d="M12 3C13.1046 3 14 3.89543 14 5H15C16.6569 5 18 6.34315 18 8V14C18 15.6569 16.6569 17 15 17H5C3.34315 17 2 15.6569 2 14V8C2 6.34315 3.34315 5 5 5H6C6 3.89543 6.89543 3 8 3H12ZM17 9H3V14C3 15.0711 3.84198 15.9455 4.90018 15.9976L5 16H15C16.0711 16 16.9455 15.158 16.9976 14.0998L17 14V9ZM15 6H5C3.9289 6 3.05446 6.84198 3.00245 7.90018L3 8H17C17 6.9289 16.158 6.05446 15.0998 6.00245L15 6ZM12 4H8C7.47282 4 7.04092 4.40794 7.00274 4.92537L7 5H13C13 4.47282 12.5921 4.04092 12.0746 4.00274L12 4Z" fill="#FCFDFD" />
|
||||
<path
|
||||
d="M12 3C13.1046 3 14 3.89543 14 5H15C16.6569 5 18 6.34315 18 8V14C18 15.6569 16.6569 17 15 17H5C3.34315 17 2 15.6569 2 14V8C2 6.34315 3.34315 5 5 5H6C6 3.89543 6.89543 3 8 3H12ZM17 9H3V14C3 15.0711 3.84198 15.9455 4.90018 15.9976L5 16H15C16.0711 16 16.9455 15.158 16.9976 14.0998L17 14V9ZM15 6H5C3.9289 6 3.05446 6.84198 3.00245 7.90018L3 8H17C17 6.9289 16.158 6.05446 15.0998 6.00245L15 6ZM12 4H8C7.47282 4 7.04092 4.40794 7.00274 4.92537L7 5H13C13 4.47282 12.5921 4.04092 12.0746 4.00274L12 4Z"
|
||||
fill={isActive ? "#FCFDFD" : "var(--text-color)"}
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<filter id="filter0_f_6049_2260" x="1.1" y="2.1" width="17.8" height="15.8" filterUnits="userSpaceOnUse" colorInterpolationFilters="sRGB">
|
||||
<filter
|
||||
id="filter0_f_6049_2260"
|
||||
x="1.1"
|
||||
y="2.1"
|
||||
width="17.8"
|
||||
height="15.8"
|
||||
filterUnits="userSpaceOnUse"
|
||||
colorInterpolationFilters="sRGB"
|
||||
>
|
||||
<feFlood floodOpacity="0" result="BackgroundImageFix" />
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape" />
|
||||
<feBlend
|
||||
mode="normal"
|
||||
in="SourceGraphic"
|
||||
in2="BackgroundImageFix"
|
||||
result="shape"
|
||||
/>
|
||||
<feGaussianBlur stdDeviation="0.45" result="effect1_foregroundBlur_6049_2260" />
|
||||
</filter>
|
||||
<clipPath id="clip0_6049_2260">
|
||||
@@ -858,6 +826,5 @@ export function FilePackageIcon({ isActive }: Readonly<{ isActive: boolean }>) {
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
)
|
||||
);
|
||||
}
|
||||
132
app/src/components/templates/PreviewModal.tsx
Normal file
132
app/src/components/templates/PreviewModal.tsx
Normal file
@@ -0,0 +1,132 @@
|
||||
import React, { ReactNode } from "react";
|
||||
import clsx from "clsx";
|
||||
import { CloseIcon } from "../icons/ExportCommonIcons";
|
||||
import RenderOverlay from "./Overlay";
|
||||
|
||||
export type ModalType = "info" | "warning" | "error" | "success" | "confirm" | "action";
|
||||
|
||||
export interface ModalButton {
|
||||
label: string;
|
||||
onClick: () => void;
|
||||
variant?: "primary" | "secondary" | "danger" | "ghost";
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export interface ModalInput {
|
||||
id: string;
|
||||
label: string;
|
||||
type?: "text" | "number" | "password" | "textarea";
|
||||
placeholder?: string;
|
||||
value: string;
|
||||
onChange: (value: string) => void;
|
||||
}
|
||||
|
||||
interface ModalProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
type?: ModalType;
|
||||
title?: string;
|
||||
description?: string | ReactNode;
|
||||
note?: string | ReactNode;
|
||||
inputs?: ModalInput[];
|
||||
buttons?: ModalButton[];
|
||||
children?: ReactNode; // optional custom content
|
||||
closeOnOverlayClick?: boolean;
|
||||
}
|
||||
|
||||
const typeColors: Record<ModalType, string> = {
|
||||
info: "modal-title--info",
|
||||
warning: "modal-title--warning",
|
||||
error: "modal-title--error",
|
||||
success: "modal-title--success",
|
||||
confirm: "modal-title--confirm",
|
||||
action: "modal-title--action",
|
||||
};
|
||||
|
||||
export const Modal: React.FC<ModalProps> = ({
|
||||
isOpen,
|
||||
onClose,
|
||||
type = "info",
|
||||
title,
|
||||
description,
|
||||
note,
|
||||
inputs,
|
||||
buttons,
|
||||
children,
|
||||
closeOnOverlayClick = true,
|
||||
}) => {
|
||||
if (!isOpen) return null;
|
||||
|
||||
return (
|
||||
<RenderOverlay>
|
||||
<div className="modal-overlay" onClick={closeOnOverlayClick ? onClose : undefined}>
|
||||
<div className="modal-container" onClick={(e) => e.stopPropagation()}>
|
||||
{/* Header */}
|
||||
<button onClick={onClose} className="modal-close" aria-label="Close">
|
||||
<CloseIcon />
|
||||
</button>
|
||||
|
||||
{title && <h2 className={clsx("modal-title", typeColors[type])}>{title}</h2>}
|
||||
|
||||
{description && <div className="modal-description">{description}</div>}
|
||||
|
||||
{/* Inputs */}
|
||||
{inputs && inputs.length > 0 && (
|
||||
<div className="modal-inputs">
|
||||
{inputs.map((input) => (
|
||||
<div key={input.id} className="modal-input-group">
|
||||
<label htmlFor={input.id} className="modal-input-label">
|
||||
{input.label}
|
||||
</label>
|
||||
{input.type === "textarea" ? (
|
||||
<textarea
|
||||
id={input.id}
|
||||
placeholder={input.placeholder}
|
||||
value={input.value}
|
||||
onChange={(e) => input.onChange(e.target.value)}
|
||||
className="modal-input modal-textarea"
|
||||
/>
|
||||
) : (
|
||||
<input
|
||||
id={input.id}
|
||||
type={input.type || "text"}
|
||||
placeholder={input.placeholder}
|
||||
value={input.value}
|
||||
onChange={(e) => input.onChange(e.target.value)}
|
||||
className="modal-input"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Custom Children */}
|
||||
{children && <div className="modal-children">{children}</div>}
|
||||
|
||||
{/* Note */}
|
||||
{note && <div className="modal-note">{note}</div>}
|
||||
|
||||
{/* Buttons */}
|
||||
{buttons && buttons.length > 0 && (
|
||||
<div className="modal-actions">
|
||||
{buttons.map((btn, idx) => (
|
||||
<button
|
||||
key={idx}
|
||||
onClick={btn.onClick}
|
||||
disabled={btn.disabled}
|
||||
className={clsx(
|
||||
"modal-button",
|
||||
btn.variant && `modal-button--${btn.variant}`
|
||||
)}
|
||||
>
|
||||
{btn.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</RenderOverlay>
|
||||
);
|
||||
};
|
||||
@@ -1,10 +1,13 @@
|
||||
import { Vector3 } from "three";
|
||||
import { useEffect } from "react";
|
||||
import { useThree } from "@react-three/fiber";
|
||||
import * as THREE from "three";
|
||||
import type { CameraControls } from "@react-three/drei";
|
||||
import { useThreeDStore } from "../../../../store/ui/useModuleStore";
|
||||
import * as CONSTANTS from "../../../../types/world/worldConstants";
|
||||
|
||||
const CameraShortcutsControls = () => {
|
||||
const { camera, controls } = useThree();
|
||||
const { toggleThreeD, setToggleThreeD } = useThreeDStore();
|
||||
|
||||
const isTextInput = (element: Element | null): boolean =>
|
||||
element instanceof HTMLInputElement ||
|
||||
@@ -18,54 +21,77 @@ const CameraShortcutsControls = () => {
|
||||
const cc = controls as CameraControls;
|
||||
|
||||
// get current target
|
||||
const target = new THREE.Vector3();
|
||||
const target = new Vector3();
|
||||
cc.getTarget(target);
|
||||
|
||||
const distance = camera.position.distanceTo(target);
|
||||
let pos: THREE.Vector3 | null = null;
|
||||
let pos: Vector3 | null = null;
|
||||
|
||||
const dir = new THREE.Vector3().subVectors(camera.position, target).normalize();
|
||||
const dir = new Vector3().subVectors(camera.position, target).normalize();
|
||||
|
||||
if (isTextInput(document.activeElement)) return;
|
||||
|
||||
switch (e.key) {
|
||||
case "1": // Front
|
||||
pos = new THREE.Vector3(0, 0, distance).add(target);
|
||||
switch (e.code) {
|
||||
case "Numpad1": // Front
|
||||
pos = new Vector3(0, 0, distance).add(target);
|
||||
LookAt(pos);
|
||||
break;
|
||||
case "3": // Right
|
||||
pos = new THREE.Vector3(distance, 0, 0).add(target);
|
||||
case "Numpad3": // Right
|
||||
pos = new Vector3(distance, 0, 0).add(target);
|
||||
LookAt(pos);
|
||||
break;
|
||||
case "7": // Top
|
||||
pos = new THREE.Vector3(0, distance, 0).add(target);
|
||||
case "Numpad7": // Top
|
||||
pos = new Vector3(0, distance, 0).add(target);
|
||||
LookAt(pos);
|
||||
break;
|
||||
case "9": {
|
||||
case "Numpad9": {
|
||||
// Opposite view logic
|
||||
if (Math.abs(dir.z) > Math.abs(dir.x) && Math.abs(dir.z) > Math.abs(dir.y)) {
|
||||
// Currently looking Front/Back → flip Z
|
||||
pos = new THREE.Vector3(0, 0, -Math.sign(dir.z) * distance).add(target);
|
||||
} else if (Math.abs(dir.x) > Math.abs(dir.z) && Math.abs(dir.x) > Math.abs(dir.y)) {
|
||||
// Currently looking Right/Left → flip X
|
||||
pos = new THREE.Vector3(-Math.sign(dir.x) * distance, 0, 0).add(target);
|
||||
pos = new Vector3(0, 0, -Math.sign(dir.z) * distance).add(target);
|
||||
LookAt(pos);
|
||||
} else if (
|
||||
Math.abs(dir.x) > Math.abs(dir.z) &&
|
||||
Math.abs(dir.x) > Math.abs(dir.y)
|
||||
) {
|
||||
pos = new Vector3(-Math.sign(dir.x) * distance, 0, 0).add(target);
|
||||
LookAt(pos);
|
||||
} else {
|
||||
// Currently looking Top/Bottom → stay Top
|
||||
pos = new THREE.Vector3(0, distance, 0).add(target);
|
||||
pos = new Vector3(0, distance, 0).add(target);
|
||||
LookAt(pos);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "Numpad5": {
|
||||
// Only apply when on Top view
|
||||
|
||||
if (!toggleThreeD) {
|
||||
console.log("cc: ", cc.camera);
|
||||
setToggleThreeD(true);
|
||||
(cc as any).mouseButtons.left = CONSTANTS.threeDimension.leftMouse;
|
||||
(cc as any).mouseButtons.right = CONSTANTS.threeDimension.rightMouse;
|
||||
} else {
|
||||
console.log("cc: ", cc.camera);
|
||||
cc.setLookAt(0, distance, 0, target.x, target.y, target.z, false).then(
|
||||
() => {
|
||||
setToggleThreeD(false);
|
||||
(cc as any).mouseButtons.left = CONSTANTS.twoDimension.leftMouse;
|
||||
(cc as any).mouseButtons.right = CONSTANTS.twoDimension.rightMouse;
|
||||
}
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (pos) {
|
||||
cc.setLookAt(
|
||||
pos.x, pos.y, pos.z, // camera position
|
||||
target.x, target.y, target.z, // keep same target
|
||||
true // smooth transition
|
||||
);
|
||||
function LookAt(pos: Vector3) {
|
||||
console.log('hi lookat');
|
||||
cc.setLookAt(pos.x, pos.y, pos.z, target.x, target.y, target.z, true);
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener("keydown", handleKeyDown);
|
||||
return () => window.removeEventListener("keydown", handleKeyDown);
|
||||
}, [controls, camera]);
|
||||
}, [controls, camera, toggleThreeD, setToggleThreeD]);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
@@ -6,11 +6,13 @@ import { useParams } from "react-router-dom";
|
||||
import * as CONSTANTS from "../../../types/world/worldConstants";
|
||||
import { getCameraApi } from "../../../services/factoryBuilder/camera/getCameraApi";
|
||||
import { useToggleView } from "../../../store/builder/store";
|
||||
import { useThreeDStore } from "../../../store/ui/useModuleStore";
|
||||
|
||||
export default function SwitchView() {
|
||||
const { toggleView } = useToggleView();
|
||||
const { controls } = useThree();
|
||||
const { projectId } = useParams();
|
||||
const { toggleThreeD } = useThreeDStore();
|
||||
|
||||
useEffect(() => {
|
||||
if (toggleView && controls) {
|
||||
@@ -39,11 +41,11 @@ export default function SwitchView() {
|
||||
(controls as any).mouseButtons.right = CONSTANTS.threeDimension.rightMouse;
|
||||
}
|
||||
}
|
||||
}, [toggleView, controls]);
|
||||
}, [toggleView, controls, projectId]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{toggleView ? (
|
||||
{(toggleView || !toggleThreeD) ? (
|
||||
<OrthographicCamera
|
||||
makeDefault
|
||||
position={CONSTANTS.twoDimension.defaultPosition}
|
||||
|
||||
@@ -14,20 +14,16 @@ const Dashboard: React.FC = () => {
|
||||
const token = localStorage.getItem("token");
|
||||
const refreshToken = localStorage.getItem("refreshToken");
|
||||
if (token && refreshToken) {
|
||||
useSocketStore
|
||||
.getState()
|
||||
.initializeSocket(email, organization, token, refreshToken);
|
||||
useSocketStore.getState().initializeSocket(email, organization, token, refreshToken);
|
||||
}
|
||||
}, [socket, email, organization]);
|
||||
|
||||
return (
|
||||
<div className="dashboard-main">
|
||||
<SidePannel setActiveTab={setActiveTab} activeTab={activeTab} />
|
||||
<DashboardMain
|
||||
activeFolder={
|
||||
activeTab.toLowerCase() as "home" | "projects" | "shared" | "trash"
|
||||
}
|
||||
/>
|
||||
{["Home", "Projects", "Shared", "Trash"].includes(activeTab) && (
|
||||
<DashboardMain activeFolder={activeTab.toLowerCase() as Folder} />
|
||||
)}
|
||||
{activeTab === "Tutorials" && <DashboardTutorial />}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -31,7 +31,7 @@ const ForgotPassword: React.FC = () => {
|
||||
try {
|
||||
const emailResponse = await checkEmailApi(email);
|
||||
|
||||
if (emailResponse.message == "OTP sent Successfully") {
|
||||
if (emailResponse.message === "OTP sent Successfully") {
|
||||
setCode(emailResponse.OTP);
|
||||
}
|
||||
} catch {}
|
||||
@@ -41,7 +41,7 @@ const ForgotPassword: React.FC = () => {
|
||||
try {
|
||||
const emailResponse = await checkEmailApi(email);
|
||||
|
||||
if (emailResponse.message == "OTP sent Successfully") {
|
||||
if (emailResponse.message === "OTP sent Successfully") {
|
||||
setStep(2);
|
||||
setCode(emailResponse.OTP);
|
||||
}
|
||||
@@ -52,7 +52,7 @@ const ForgotPassword: React.FC = () => {
|
||||
try {
|
||||
const otpResponse = await verifyOtpApi(email, Number(code));
|
||||
|
||||
if (otpResponse.message == "OTP verified successfully") {
|
||||
if (otpResponse.message === "OTP verified successfully") {
|
||||
setResetToken(otpResponse.resetToken);
|
||||
setStep(3);
|
||||
} else {
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
import React, { useEffect } from 'react'
|
||||
|
||||
function sessionValidity() {
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default sessionValidity
|
||||
@@ -22,11 +22,7 @@ $text-button-color-dark: #f3f3fd;
|
||||
// background colors
|
||||
// ---------- light mode ----------
|
||||
$background-color: linear-gradient(-45deg, #fcfdfd71 0%, #fcfdfd79 100%);
|
||||
$background-color-solid-gradient: linear-gradient(
|
||||
-45deg,
|
||||
#fcfdfd 0%,
|
||||
#fcfdfd 100%
|
||||
);
|
||||
$background-color-solid-gradient: linear-gradient(-45deg, #fcfdfd 0%, #fcfdfd 100%);
|
||||
$background-color-solid: #fcfdfd;
|
||||
$background-color-secondary: #fcfdfd4d;
|
||||
$background-color-accent: #6f42c1;
|
||||
@@ -34,26 +30,14 @@ $background-color-button: #6f42c1;
|
||||
$background-color-drop-down: #6f42c14d;
|
||||
$background-color-input: #ffffff4d;
|
||||
$background-color-input-focus: #f2f2f7;
|
||||
$background-color-drop-down-gradient: linear-gradient(
|
||||
-45deg,
|
||||
#75649366 0%,
|
||||
#40257266 100%
|
||||
);
|
||||
$background-color-drop-down-gradient: linear-gradient(-45deg, #75649366 0%, #40257266 100%);
|
||||
$background-color-selected: #e0dfff;
|
||||
$background-radial-gray-gradient: radial-gradient(
|
||||
circle,
|
||||
#bfe0f8 0%,
|
||||
#e9ebff 46%,
|
||||
#e2acff 100%
|
||||
);
|
||||
$background-radial-gray-gradient: radial-gradient(circle, #bfe0f8 0%, #e9ebff 46%, #e2acff 100%);
|
||||
$background-model: #ffffff80;
|
||||
|
||||
// ---------- dark mode ----------
|
||||
$background-color-dark: linear-gradient(-45deg, #333333b3 0%, #2d2437b3 100%);
|
||||
$background-color-solid-gradient-dark: linear-gradient(
|
||||
-45deg,
|
||||
#333333 0%,
|
||||
#2d2437 100%
|
||||
);
|
||||
$background-color-solid-gradient-dark: linear-gradient(-45deg, #333333 0%, #2d2437 100%);
|
||||
$background-color-solid-dark: #19191d;
|
||||
$background-color-secondary-dark: #19191d99;
|
||||
$background-color-accent-dark: #6f42c1;
|
||||
@@ -61,11 +45,7 @@ $background-color-button-dark: #6f42c1;
|
||||
$background-color-drop-down-dark: #50505080;
|
||||
$background-color-input-dark: #ffffff33;
|
||||
$background-color-input-focus-dark: #333333;
|
||||
$background-color-drop-down-gradient-dark: linear-gradient(
|
||||
-45deg,
|
||||
#8973b166 0%,
|
||||
#53427366 100%
|
||||
);
|
||||
$background-color-drop-down-gradient-dark: linear-gradient(-45deg, #8973b166 0%, #53427366 100%);
|
||||
$background-color-selected-dark: #403e66;
|
||||
$background-radial-gray-gradient-dark: radial-gradient(
|
||||
circle,
|
||||
@@ -73,6 +53,7 @@ $background-radial-gray-gradient-dark: radial-gradient(
|
||||
#48494b 46%,
|
||||
#52415c 100%
|
||||
);
|
||||
$background-model-dark: #00000080;
|
||||
|
||||
// border colors
|
||||
// ---------- light mode ----------
|
||||
@@ -147,12 +128,7 @@ $acent-gradient-dark: linear-gradient(90deg, #b392f0 0%, #a676ff 100%);
|
||||
$acent-gradient: linear-gradient(90deg, #6f42c1 0%, #925df3 100%);
|
||||
|
||||
$faint-gradient: radial-gradient(circle, #bfe0f8 0%, #e9ebff 46%, #e2acff 100%);
|
||||
$faint-gradient-dark: radial-gradient(
|
||||
circle,
|
||||
#31373b 0%,
|
||||
#48494b 46%,
|
||||
#52415c 100%
|
||||
);
|
||||
$faint-gradient-dark: radial-gradient(circle, #31373b 0%, #48494b 46%, #52415c 100%);
|
||||
|
||||
$font-inter: "Inter", sans-serif;
|
||||
$font-josefin-sans: "Josefin Sans", sans-serif;
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
--background-color-drop-down-gradient: #{$background-color-drop-down-gradient};
|
||||
--background-color-selected: #{$background-color-selected};
|
||||
--background-radial-gray-gradient: #{$background-radial-gray-gradient};
|
||||
--background-model: #{$background-model};
|
||||
|
||||
// border colors
|
||||
--border-color: #{$border-color};
|
||||
@@ -83,6 +84,7 @@
|
||||
--background-color-drop-down-gradient: #{$background-color-drop-down-gradient-dark};
|
||||
--background-color-selected: #{$background-color-selected-dark};
|
||||
--background-radial-gray-gradient: #{$background-radial-gray-gradient-dark};
|
||||
--background-model: #{$background-model-dark};
|
||||
|
||||
// border colors
|
||||
--border-color: #{$border-color-dark};
|
||||
|
||||
156
app/src/styles/layout/_previewModel.scss
Normal file
156
app/src/styles/layout/_previewModel.scss
Normal file
@@ -0,0 +1,156 @@
|
||||
.modal-overlay {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: var(--background-model);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 50;
|
||||
|
||||
.modal-container {
|
||||
background: var(--background-color);
|
||||
border-radius: 34px;
|
||||
padding: 24px 28px;
|
||||
max-width: 500px;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
|
||||
.modal-close {
|
||||
position: absolute;
|
||||
top: 24px;
|
||||
right: 24px;
|
||||
background: transparent;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
scale: 1.6;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
padding: 2px;
|
||||
border-radius: 6px;
|
||||
transition: background 0.2s;
|
||||
&:hover {
|
||||
background: var(--background-color-solid);
|
||||
}
|
||||
}
|
||||
|
||||
.modal-title {
|
||||
font-size: 1.12rem;
|
||||
font-weight: 600;
|
||||
margin-bottom: 8px;
|
||||
|
||||
&--info {
|
||||
color: var(--log-info-text-color);
|
||||
}
|
||||
&--warning {
|
||||
color: var(--log-warn-text-color);
|
||||
}
|
||||
&--error {
|
||||
color: var(--log-error-text-color);
|
||||
}
|
||||
&--success {
|
||||
color: var(--log-success-text-color);
|
||||
}
|
||||
&--confirm {
|
||||
color: var(--highlight-text-color);
|
||||
}
|
||||
&--action {
|
||||
color: var(--highlight-text-color);
|
||||
}
|
||||
}
|
||||
|
||||
.modal-description {
|
||||
font-size: 0.9rem;
|
||||
color: var(--input-text-color);
|
||||
margin-bottom: 24px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.modal-inputs {
|
||||
margin-bottom: 16px;
|
||||
|
||||
.modal-input-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-bottom: 12px;
|
||||
|
||||
.modal-input-label {
|
||||
font-size: 0.85rem;
|
||||
font-weight: 500;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.modal-input {
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 32px;
|
||||
padding: 8px 14px;
|
||||
font-size: 0.9rem;
|
||||
|
||||
&.modal-textarea {
|
||||
min-height: 80px;
|
||||
resize: vertical;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.modal-children {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.modal-note {
|
||||
font-size: 0.75rem;
|
||||
color: var(--text-disabled);
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.modal-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 8px;
|
||||
|
||||
.modal-button {
|
||||
padding: 6px 18px;
|
||||
border-radius: 6px;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 400;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
border-radius: 46px;
|
||||
transition: all 0.2s;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
|
||||
&:disabled {
|
||||
background: var(--background-color-secondary);
|
||||
color: var(--text-disabled);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
filter: saturate(2);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 4px 4px 8px var(--background-color-solid);
|
||||
}
|
||||
|
||||
&--primary {
|
||||
background: var(--background-color-accent);
|
||||
color: var(--text-button-color);
|
||||
}
|
||||
|
||||
&--secondary {
|
||||
background: var(--background-color-solid);
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
&--danger {
|
||||
background: var(--log-error-background-color);
|
||||
color: var(--log-error-text-color);
|
||||
}
|
||||
|
||||
&--ghost {
|
||||
background: transparent;
|
||||
color: var(--background-color-secondary);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@
|
||||
cursor: pointer;
|
||||
|
||||
&.active {
|
||||
color: var(--text-button-color);
|
||||
background: var(--background-color-button);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
@use "layout/compareLayoutPopUp";
|
||||
@use "layout/compareLayout";
|
||||
@use "layout/resourceManagement.scss";
|
||||
@use "layout/previewModel";
|
||||
|
||||
// pages
|
||||
@use "pages/dashboard";
|
||||
|
||||
16
app/src/types/dashboard.d.ts
vendored
Normal file
16
app/src/types/dashboard.d.ts
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
|
||||
interface DashboardProject {
|
||||
_id: string;
|
||||
projectName: string;
|
||||
thumbnail: string;
|
||||
createdBy: { _id: string; userName: string };
|
||||
projectUuid?: string;
|
||||
createdAt?: string;
|
||||
DeletedAt?: string;
|
||||
}
|
||||
|
||||
interface DashboardProjectCollection {
|
||||
[key: string]: DashboardProject[];
|
||||
}
|
||||
|
||||
type Folder = "home" | "projects" | "shared" | "trash";
|
||||
Reference in New Issue
Block a user