refactor: Simplify imports and clean up DashboardCard component for improved readability and maintainability
This commit is contained in:
@@ -3,11 +3,7 @@ import { createPortal } from "react-dom";
|
|||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import img from "../../assets/image/image.png";
|
import img from "../../assets/image/image.png";
|
||||||
import { getUserData } from "../../functions/getUserData";
|
import { getUserData } from "../../functions/getUserData";
|
||||||
import {
|
import { useLoadingProgress, useProjectName, useSocketStore } from "../../store/builder/store";
|
||||||
useLoadingProgress,
|
|
||||||
useProjectName,
|
|
||||||
useSocketStore,
|
|
||||||
} from "../../store/builder/store";
|
|
||||||
import OuterClick from "../../utils/outerClick";
|
import OuterClick from "../../utils/outerClick";
|
||||||
import { KebabIcon } from "../icons/ExportCommonIcons";
|
import { KebabIcon } from "../icons/ExportCommonIcons";
|
||||||
import { getAllProjects } from "../../services/dashboard/getAllProjects";
|
import { getAllProjects } from "../../services/dashboard/getAllProjects";
|
||||||
@@ -15,323 +11,275 @@ import { getAllProjects } from "../../services/dashboard/getAllProjects";
|
|||||||
// import { updateProject } from "../../services/dashboard/updateProject";
|
// import { updateProject } from "../../services/dashboard/updateProject";
|
||||||
|
|
||||||
interface DashBoardCardProps {
|
interface DashBoardCardProps {
|
||||||
projectName: string;
|
projectName: string;
|
||||||
thumbnail: string;
|
thumbnail: string;
|
||||||
projectId: string;
|
projectId: string;
|
||||||
createdAt?: string;
|
createdAt?: string;
|
||||||
isViewed?: string;
|
isViewed?: string;
|
||||||
createdBy?: { _id: string; userName: string };
|
createdBy?: { _id: string; userName: string };
|
||||||
handleDeleteProject?: (projectId: string) => Promise<void>;
|
handleDeleteProject?: (projectId: string) => Promise<void>;
|
||||||
handleTrashDeleteProject?: (projectId: string) => Promise<void>;
|
handleTrashDeleteProject?: (projectId: string) => Promise<void>;
|
||||||
handleRestoreProject?: (projectId: string) => Promise<void>;
|
handleRestoreProject?: (projectId: string) => Promise<void>;
|
||||||
handleDuplicateWorkspaceProject?: (
|
handleDuplicateWorkspaceProject?: (projectId: string, projectName: string, thumbnail: string, userId?: string) => Promise<void>;
|
||||||
projectId: string,
|
handleDuplicateRecentProject?: (projectId: string, projectName: string, thumbnail: string) => Promise<void>;
|
||||||
projectName: string,
|
active?: "shared" | "trash" | "recent" | string;
|
||||||
thumbnail: string,
|
setIsSearchActive?: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
userId?: string
|
setRecentDuplicateData?: React.Dispatch<React.SetStateAction<object>>;
|
||||||
) => Promise<void>;
|
setProjectDuplicateData?: React.Dispatch<React.SetStateAction<object>>;
|
||||||
handleDuplicateRecentProject?: (
|
setActiveFolder?: React.Dispatch<React.SetStateAction<string>>;
|
||||||
projectId: string,
|
openKebabProjectId: string | null;
|
||||||
projectName: string,
|
setOpenKebabProjectId: React.Dispatch<React.SetStateAction<string | null>>;
|
||||||
thumbnail: string
|
|
||||||
) => Promise<void>;
|
|
||||||
active?: "shared" | "trash" | "recent" | string;
|
|
||||||
setIsSearchActive?: React.Dispatch<React.SetStateAction<boolean>>;
|
|
||||||
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 =
|
type RelativeTimeFormatUnit = "year" | "month" | "week" | "day" | "hour" | "minute" | "second";
|
||||||
| "year"
|
|
||||||
| "month"
|
|
||||||
| "week"
|
|
||||||
| "day"
|
|
||||||
| "hour"
|
|
||||||
| "minute"
|
|
||||||
| "second";
|
|
||||||
|
|
||||||
const kebabOptionsMap: Record<string, string[]> = {
|
const kebabOptionsMap: Record<string, string[]> = {
|
||||||
default: ["rename", "delete", "duplicate", "open in new tab"],
|
default: ["rename", "delete", "duplicate", "open in new tab"],
|
||||||
trash: ["restore", "delete"],
|
trash: ["restore", "delete"],
|
||||||
shared: ["duplicate", "open in new tab"],
|
shared: ["duplicate", "open in new tab"],
|
||||||
};
|
};
|
||||||
|
|
||||||
const DashboardCard: React.FC<DashBoardCardProps> = ({
|
const DashboardCard: React.FC<DashBoardCardProps> = ({
|
||||||
projectName,
|
|
||||||
thumbnail,
|
|
||||||
projectId,
|
|
||||||
active,
|
|
||||||
handleDeleteProject,
|
|
||||||
handleRestoreProject,
|
|
||||||
handleTrashDeleteProject,
|
|
||||||
handleDuplicateWorkspaceProject,
|
|
||||||
handleDuplicateRecentProject,
|
|
||||||
createdAt,
|
|
||||||
createdBy,
|
|
||||||
setRecentDuplicateData,
|
|
||||||
setProjectDuplicateData,
|
|
||||||
setActiveFolder,
|
|
||||||
openKebabProjectId,
|
|
||||||
setOpenKebabProjectId,
|
|
||||||
}) => {
|
|
||||||
const navigate = useNavigate();
|
|
||||||
const { setProjectName } = useProjectName();
|
|
||||||
const { userId, organization, userName } = getUserData();
|
|
||||||
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);
|
|
||||||
|
|
||||||
// Close kebab when clicking outside
|
|
||||||
OuterClick({
|
|
||||||
contextClassName: [`tag-${projectId}`],
|
|
||||||
setMenuVisible: () => {
|
|
||||||
if (isKebabOpen) setOpenKebabProjectId(null);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const navigateToProject = useCallback(() => {
|
|
||||||
if (active === "trash") return;
|
|
||||||
setLoadingProgress(1);
|
|
||||||
setProjectName(projectName);
|
|
||||||
navigate(`/projects/${projectId}`);
|
|
||||||
}, [
|
|
||||||
active,
|
|
||||||
projectId,
|
|
||||||
projectName,
|
projectName,
|
||||||
navigate,
|
thumbnail,
|
||||||
setLoadingProgress,
|
projectId,
|
||||||
setProjectName,
|
active,
|
||||||
]);
|
handleDeleteProject,
|
||||||
|
handleRestoreProject,
|
||||||
|
handleTrashDeleteProject,
|
||||||
|
handleDuplicateWorkspaceProject,
|
||||||
|
handleDuplicateRecentProject,
|
||||||
|
createdAt,
|
||||||
|
createdBy,
|
||||||
|
setRecentDuplicateData,
|
||||||
|
setProjectDuplicateData,
|
||||||
|
setActiveFolder,
|
||||||
|
openKebabProjectId,
|
||||||
|
setOpenKebabProjectId,
|
||||||
|
}) => {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const { setProjectName } = useProjectName();
|
||||||
|
const { userId, organization, userName } = getUserData();
|
||||||
|
const { projectSocket } = useSocketStore();
|
||||||
|
const { setLoadingProgress } = useLoadingProgress();
|
||||||
|
|
||||||
const getOptions = useCallback(() => {
|
const isKebabOpen = openKebabProjectId === projectId;
|
||||||
if (active === "trash") return kebabOptionsMap.trash;
|
const [renameValue, setRenameValue] = useState(projectName);
|
||||||
if (active === "shared" || (createdBy && createdBy._id !== userId)) {
|
const [isRenaming, setIsRenaming] = useState(false);
|
||||||
return kebabOptionsMap.shared;
|
const kebabRef = useRef<HTMLDivElement>(null);
|
||||||
}
|
|
||||||
return kebabOptionsMap.default;
|
|
||||||
}, [active, createdBy, userId]);
|
|
||||||
|
|
||||||
const handleProjectName = useCallback(
|
// Close kebab when clicking outside
|
||||||
async (newName: string) => {
|
OuterClick({
|
||||||
setRenameValue(newName);
|
contextClassName: [`tag-${projectId}`],
|
||||||
if (!projectId) return;
|
setMenuVisible: () => {
|
||||||
|
if (isKebabOpen) setOpenKebabProjectId(null);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
try {
|
const navigateToProject = useCallback(() => {
|
||||||
const projects = await getAllProjects(userId, organization);
|
if (active === "trash") return;
|
||||||
const projectUuid = projects?.Projects?.find(
|
setLoadingProgress(1);
|
||||||
(val: any) => val.projectUuid === projectId || val._id === projectId
|
setProjectName(projectName);
|
||||||
);
|
navigate(`/projects/${projectId}`);
|
||||||
if (!projectUuid) return;
|
}, [active, projectId, projectName, navigate, setLoadingProgress, setProjectName]);
|
||||||
|
|
||||||
const updatePayload = {
|
const getOptions = useCallback(() => {
|
||||||
projectId: projectUuid._id,
|
if (active === "trash") return kebabOptionsMap.trash;
|
||||||
organization,
|
if (active === "shared" || (createdBy && createdBy._id !== userId)) {
|
||||||
userId,
|
return kebabOptionsMap.shared;
|
||||||
projectName: newName,
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// silent fail
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[projectId, userId, organization, projectSocket]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleOptionClick = useCallback(
|
||||||
|
async (option: string) => {
|
||||||
|
switch (option) {
|
||||||
|
case "delete":
|
||||||
|
await (active === "trash" ? handleTrashDeleteProject?.(projectId) : handleDeleteProject?.(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, userId);
|
||||||
|
if (active === "shared") {
|
||||||
|
setActiveFolder?.("myProjects");
|
||||||
|
}
|
||||||
|
} else if (handleDuplicateRecentProject) {
|
||||||
|
setRecentDuplicateData?.({
|
||||||
|
projectId,
|
||||||
|
projectName,
|
||||||
|
thumbnail,
|
||||||
|
userId,
|
||||||
|
});
|
||||||
|
await handleDuplicateRecentProject(projectId, projectName, thumbnail);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[
|
||||||
|
projectId,
|
||||||
|
projectName,
|
||||||
|
thumbnail,
|
||||||
|
userId,
|
||||||
|
active,
|
||||||
|
handleDeleteProject,
|
||||||
|
handleTrashDeleteProject,
|
||||||
|
handleRestoreProject,
|
||||||
|
handleDuplicateWorkspaceProject,
|
||||||
|
handleDuplicateRecentProject,
|
||||||
|
setProjectName,
|
||||||
|
setProjectDuplicateData,
|
||||||
|
setRecentDuplicateData,
|
||||||
|
setActiveFolder,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
const getRelativeTime = useCallback((dateString: string): string => {
|
||||||
|
const date = new Date(dateString);
|
||||||
|
const now = new Date();
|
||||||
|
const diffInSeconds = Math.floor((now.getTime() - date.getTime()) / 1000);
|
||||||
|
|
||||||
|
const intervals: Record<RelativeTimeFormatUnit, number> = {
|
||||||
|
year: 31536000,
|
||||||
|
month: 2592000,
|
||||||
|
week: 604800,
|
||||||
|
day: 86400,
|
||||||
|
hour: 3600,
|
||||||
|
minute: 60,
|
||||||
|
second: 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (projectSocket) {
|
const rtf = new Intl.RelativeTimeFormat("en", { numeric: "auto" });
|
||||||
projectSocket.emit("v1:project:update", updatePayload);
|
for (const [unit, seconds] of Object.entries(intervals)) {
|
||||||
|
const diff = Math.floor(diffInSeconds / seconds);
|
||||||
|
if (diff >= 1) return rtf.format(-diff, unit as RelativeTimeFormatUnit);
|
||||||
}
|
}
|
||||||
} catch {
|
return "just now";
|
||||||
// silent fail
|
}, []);
|
||||||
}
|
|
||||||
},
|
|
||||||
[projectId, userId, organization, projectSocket]
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleOptionClick = useCallback(
|
const [kebabPosition, setKebabPosition] = useState({ top: 0, left: 0 });
|
||||||
async (option: string) => {
|
|
||||||
switch (option) {
|
useEffect(() => {
|
||||||
case "delete":
|
if (isKebabOpen && kebabRef.current) {
|
||||||
await (handleDeleteProject?.(projectId) ??
|
const rect = kebabRef.current.getBoundingClientRect();
|
||||||
handleTrashDeleteProject?.(projectId));
|
setKebabPosition({
|
||||||
break;
|
top: rect.bottom + window.scrollY,
|
||||||
case "restore":
|
left: rect.left + window.scrollX - 80,
|
||||||
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,
|
|
||||||
userId
|
|
||||||
);
|
|
||||||
if (active === "shared") {
|
|
||||||
setActiveFolder?.("myProjects");
|
|
||||||
}
|
|
||||||
} else if (handleDuplicateRecentProject) {
|
|
||||||
setRecentDuplicateData?.({
|
|
||||||
projectId,
|
|
||||||
projectName,
|
|
||||||
thumbnail,
|
|
||||||
userId,
|
|
||||||
});
|
});
|
||||||
await handleDuplicateRecentProject(
|
}
|
||||||
projectId,
|
}, [isKebabOpen]);
|
||||||
projectName,
|
|
||||||
thumbnail
|
|
||||||
);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[
|
|
||||||
projectId,
|
|
||||||
projectName,
|
|
||||||
thumbnail,
|
|
||||||
userId,
|
|
||||||
active,
|
|
||||||
handleDeleteProject,
|
|
||||||
handleTrashDeleteProject,
|
|
||||||
handleRestoreProject,
|
|
||||||
handleDuplicateWorkspaceProject,
|
|
||||||
handleDuplicateRecentProject,
|
|
||||||
setProjectName,
|
|
||||||
setProjectDuplicateData,
|
|
||||||
setRecentDuplicateData,
|
|
||||||
setActiveFolder,
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
const getRelativeTime = useCallback((dateString: string): string => {
|
return (
|
||||||
const date = new Date(dateString);
|
<div className="dashboard-card-container" onClick={navigateToProject} title={projectName}>
|
||||||
const now = new Date();
|
<div className="dashboard-card-wrapper">
|
||||||
const diffInSeconds = Math.floor((now.getTime() - date.getTime()) / 1000);
|
<div className="preview-container">
|
||||||
|
<img src={thumbnail || img} alt={`${projectName} thumbnail`} />
|
||||||
|
</div>
|
||||||
|
|
||||||
const intervals: Record<RelativeTimeFormatUnit, number> = {
|
<div className="project-details-container" onClick={(e) => e.stopPropagation()}>
|
||||||
year: 31536000,
|
<div className="project-details">
|
||||||
month: 2592000,
|
{isRenaming ? (
|
||||||
week: 604800,
|
<input
|
||||||
day: 86400,
|
value={renameValue}
|
||||||
hour: 3600,
|
onChange={(e) => handleProjectName(e.target.value)}
|
||||||
minute: 60,
|
onBlur={() => {
|
||||||
second: 1,
|
setIsRenaming(false);
|
||||||
};
|
setProjectName(renameValue);
|
||||||
|
}}
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
if (e.key === "Enter") {
|
||||||
|
setIsRenaming(false);
|
||||||
|
setProjectName(renameValue);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
aria-label="Rename project"
|
||||||
|
autoFocus
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<span>{renameValue}</span>
|
||||||
|
)}
|
||||||
|
{createdAt && (
|
||||||
|
<div className="project-data">
|
||||||
|
{active === "trash" ? "Trashed" : "Edited"} {getRelativeTime(createdAt)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
const rtf = new Intl.RelativeTimeFormat("en", { numeric: "auto" });
|
<div className="users-list-container" ref={kebabRef}>
|
||||||
for (const [unit, seconds] of Object.entries(intervals)) {
|
<div className="user-profile">{(createdBy?.userName || userName || "A").charAt(0).toUpperCase()}</div>
|
||||||
const diff = Math.floor(diffInSeconds / seconds);
|
<button
|
||||||
if (diff >= 1) return rtf.format(-diff, unit as RelativeTimeFormatUnit);
|
className="kebab-wrapper"
|
||||||
}
|
onClick={(e) => {
|
||||||
return "just now";
|
e.stopPropagation();
|
||||||
}, []);
|
setOpenKebabProjectId(isKebabOpen ? null : projectId);
|
||||||
|
}}
|
||||||
const [kebabPosition, setKebabPosition] = useState({ top: 0, left: 0 });
|
aria-haspopup="true"
|
||||||
|
aria-expanded={isKebabOpen}
|
||||||
useEffect(() => {
|
aria-label="Project options"
|
||||||
if (isKebabOpen && kebabRef.current) {
|
>
|
||||||
const rect = kebabRef.current.getBoundingClientRect();
|
<KebabIcon />
|
||||||
setKebabPosition({
|
</button>
|
||||||
top: rect.bottom + window.scrollY,
|
</div>
|
||||||
left: rect.left + window.scrollX - 80,
|
</div>
|
||||||
});
|
|
||||||
}
|
|
||||||
}, [isKebabOpen]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className="dashboard-card-container"
|
|
||||||
onClick={navigateToProject}
|
|
||||||
title={projectName}
|
|
||||||
>
|
|
||||||
<div className="dashboard-card-wrapper">
|
|
||||||
<div className="preview-container">
|
|
||||||
<img src={thumbnail || img} alt={`${projectName} thumbnail`} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
|
||||||
className="project-details-container"
|
|
||||||
onClick={(e) => e.stopPropagation()}
|
|
||||||
>
|
|
||||||
<div className="project-details">
|
|
||||||
{isRenaming ? (
|
|
||||||
<input
|
|
||||||
value={renameValue}
|
|
||||||
onChange={(e) => handleProjectName(e.target.value)}
|
|
||||||
onBlur={() => {
|
|
||||||
setIsRenaming(false);
|
|
||||||
setProjectName(renameValue);
|
|
||||||
}}
|
|
||||||
onKeyDown={(e) => {
|
|
||||||
if (e.key === "Enter") {
|
|
||||||
setIsRenaming(false);
|
|
||||||
setProjectName(renameValue);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
aria-label="Rename project"
|
|
||||||
autoFocus
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<span>{renameValue}</span>
|
|
||||||
)}
|
|
||||||
{createdAt && (
|
|
||||||
<div className="project-data">
|
|
||||||
{active === "trash" ? "Trashed" : "Edited"}{" "}
|
|
||||||
{getRelativeTime(createdAt)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="users-list-container" ref={kebabRef}>
|
|
||||||
<div className="user-profile">
|
|
||||||
{(createdBy?.userName || userName || "A").charAt(0).toUpperCase()}
|
|
||||||
</div>
|
</div>
|
||||||
<button
|
|
||||||
className="kebab-wrapper"
|
|
||||||
onClick={(e) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
setOpenKebabProjectId(isKebabOpen ? null : projectId);
|
|
||||||
}}
|
|
||||||
aria-haspopup="true"
|
|
||||||
aria-expanded={isKebabOpen}
|
|
||||||
aria-label="Project options"
|
|
||||||
>
|
|
||||||
<KebabIcon />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{isKebabOpen &&
|
{isKebabOpen &&
|
||||||
createPortal(
|
createPortal(
|
||||||
<div
|
<div className={`kebab-options-wrapper tag-${projectId}`} style={{ position: "fixed", zIndex: 9999, ...kebabPosition }}>
|
||||||
className={`kebab-options-wrapper tag-${projectId}`}
|
{getOptions().map((option) => (
|
||||||
style={{ position: "fixed", zIndex: 9999, ...kebabPosition }}
|
<button
|
||||||
>
|
key={option}
|
||||||
{getOptions().map((option) => (
|
className="option"
|
||||||
<button
|
onClick={(e) => {
|
||||||
key={option}
|
e.stopPropagation();
|
||||||
className="option"
|
handleOptionClick(option);
|
||||||
onClick={(e) => {
|
}}
|
||||||
e.stopPropagation();
|
>
|
||||||
handleOptionClick(option);
|
{option}
|
||||||
}}
|
</button>
|
||||||
>
|
))}
|
||||||
{option}
|
</div>,
|
||||||
</button>
|
document.body
|
||||||
))}
|
)}
|
||||||
</div>,
|
</div>
|
||||||
document.body
|
);
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default DashboardCard;
|
export default DashboardCard;
|
||||||
|
|||||||
Reference in New Issue
Block a user