280 lines
11 KiB
TypeScript
280 lines
11 KiB
TypeScript
import React, { useState, useRef, useEffect } from "react";
|
|
import img from "../../assets/image/image.png";
|
|
import { useNavigate } from "react-router-dom";
|
|
import { getUserData } from "./functions/getUserData";
|
|
import { useLoadingProgress, useProjectName, useSocketStore } from "../../store/builder/store";
|
|
import { viewProject } from "../../services/dashboard/viewProject";
|
|
import OuterClick from "../../utils/outerClick";
|
|
import { KebabIcon } from "../icons/ExportCommonIcons";
|
|
import { getAllProjects } from "../../services/dashboard/getAllProjects";
|
|
|
|
interface DashBoardCardProps {
|
|
projectName: string;
|
|
thumbnail: any;
|
|
projectId: string;
|
|
createdAt?: string;
|
|
handleDeleteProject?: (projectId: string) => Promise<void>;
|
|
handleTrashDeleteProject?: (projectId: string) => Promise<void>;
|
|
handleRestoreProject?: (projectId: string) => Promise<void>;
|
|
handleDuplicateWorkspaceProject?: (
|
|
projectId: string,
|
|
projectName: string,
|
|
thumbnail: string
|
|
) => Promise<void>;
|
|
handleDuplicateRecentProject?: (
|
|
projectId: string,
|
|
projectName: string,
|
|
thumbnail: string
|
|
) => Promise<void>;
|
|
active?: string;
|
|
setIsSearchActive?: React.Dispatch<React.SetStateAction<boolean>>;
|
|
setRecentDuplicateData?: React.Dispatch<React.SetStateAction<Object>>;
|
|
setProjectDuplicateData?: React.Dispatch<React.SetStateAction<Object>>;
|
|
}
|
|
type RelativeTimeFormatUnit = any;
|
|
|
|
const DashboardCard: React.FC<DashBoardCardProps> = ({
|
|
projectName,
|
|
thumbnail,
|
|
projectId,
|
|
active,
|
|
handleDeleteProject,
|
|
handleRestoreProject,
|
|
handleTrashDeleteProject,
|
|
handleDuplicateWorkspaceProject,
|
|
handleDuplicateRecentProject,
|
|
createdAt,
|
|
setRecentDuplicateData,
|
|
setProjectDuplicateData,
|
|
}) => {
|
|
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 kebabRef = useRef<HTMLDivElement>(null);
|
|
|
|
const navigateToProject = async (e: any) => {
|
|
if (active && active == "trash") return;
|
|
try {
|
|
const viewedProject = await viewProject(organization, projectId, userId);
|
|
console.log("Viewed project:", viewedProject);
|
|
} catch (error) {
|
|
console.error("Error opening project:", error);
|
|
}
|
|
setLoadingProgress(1)
|
|
setProjectName(projectName);
|
|
navigate(`/${projectId}`);
|
|
};
|
|
|
|
const handleOptionClick = async (option: string) => {
|
|
switch (option) {
|
|
case "delete":
|
|
if (handleDeleteProject) {
|
|
await handleDeleteProject(projectId);
|
|
} else if (handleTrashDeleteProject) {
|
|
await handleTrashDeleteProject(projectId);
|
|
}
|
|
break;
|
|
case "restore":
|
|
if (handleRestoreProject) {
|
|
await handleRestoreProject(projectId);
|
|
}
|
|
break;
|
|
case "open in new tab":
|
|
try {
|
|
await viewProject(organization, projectId, userId);
|
|
setProjectName(projectName);
|
|
setIsKebabOpen(false);
|
|
} catch (error) {
|
|
console.error("Error opening project in new tab:", error);
|
|
}
|
|
window.open(`/${projectId}`, "_blank");
|
|
break;
|
|
case "rename":
|
|
setIsRenaming(true);
|
|
break;
|
|
case "duplicate":
|
|
if (handleDuplicateWorkspaceProject) {
|
|
setProjectDuplicateData &&
|
|
setProjectDuplicateData({
|
|
projectId,
|
|
projectName,
|
|
thumbnail,
|
|
});
|
|
await handleDuplicateWorkspaceProject(projectId, projectName, thumbnail);
|
|
} else if (handleDuplicateRecentProject) {
|
|
setRecentDuplicateData &&
|
|
setRecentDuplicateData({
|
|
projectId,
|
|
projectName,
|
|
thumbnail,
|
|
});
|
|
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);
|
|
console.log("projects: ", projects);
|
|
let projectUuid = projects.Projects.find(
|
|
(val: any) => val.projectUuid === projectId || val._id === projectId
|
|
);
|
|
const updateProjects = {
|
|
projectId: projectUuid,
|
|
organization,
|
|
userId,
|
|
projectName,
|
|
thumbnail: undefined,
|
|
};
|
|
|
|
if (projectSocket) {
|
|
projectSocket.emit("v1:project:update", updateProjects);
|
|
}
|
|
} catch (error) { }
|
|
};
|
|
|
|
function getRelativeTime(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,
|
|
};
|
|
|
|
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);
|
|
}
|
|
}
|
|
return "just now";
|
|
}
|
|
|
|
return (
|
|
<button
|
|
className="dashboard-card-container"
|
|
onClick={navigateToProject}
|
|
title={projectName}
|
|
>
|
|
<div className="dashboard-card-wrapper">
|
|
<div className="preview-container">
|
|
{thumbnail ? <img src={thumbnail} alt="" /> : <img src={img} alt="" />}
|
|
</div>
|
|
<div className="project-details-container" onClick={(e) => { e.stopPropagation() }}>
|
|
<div className="project-details">
|
|
{isRenaming ? (
|
|
<input
|
|
value={renameValue}
|
|
onChange={(e) => {
|
|
e.stopPropagation();
|
|
handleProjectName(e.target.value);
|
|
}}
|
|
onBlur={() => {
|
|
setIsRenaming(false);
|
|
setProjectName(renameValue);
|
|
}}
|
|
onKeyDown={(e) => {
|
|
if (e.key === "Enter") {
|
|
setProjectName(renameValue);
|
|
setIsRenaming(false);
|
|
}
|
|
}}
|
|
autoFocus
|
|
/>
|
|
) : (
|
|
<span>{renameValue}</span>
|
|
)}
|
|
{createdAt && (
|
|
<div className="project-data">
|
|
{active && active == "trash" ? `Trashed by you` : `Edited `}{" "}
|
|
{getRelativeTime(createdAt)}
|
|
</div>
|
|
)}
|
|
</div>
|
|
<div className="users-list-container" ref={kebabRef}>
|
|
<div className="user-profile">
|
|
{userName ? userName.charAt(0).toUpperCase() : "A"}
|
|
</div>
|
|
<button
|
|
className="kebab-wrapper"
|
|
onClick={(e) => {
|
|
e.stopPropagation();
|
|
console.log("Kebab menu clicked");
|
|
setIsKebabOpen((prev) => !prev);
|
|
}}
|
|
>
|
|
<KebabIcon />
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{isKebabOpen && active !== "trash" && (
|
|
<div className="kebab-options-wrapper">
|
|
{["rename", "delete", "duplicate", "open in new tab"].map(
|
|
(option) => (
|
|
<button
|
|
key={option}
|
|
className="option"
|
|
title={""}
|
|
onClick={(e) => {
|
|
console.log(option);
|
|
e.stopPropagation();
|
|
handleOptionClick(option);
|
|
}}
|
|
>
|
|
{option}
|
|
</button>
|
|
)
|
|
)}
|
|
</div>
|
|
)}
|
|
{isKebabOpen && active && active == "trash" && (
|
|
<div className="kebab-options-wrapper">
|
|
{["restore", "delete"].map((option) => (
|
|
<button
|
|
key={option}
|
|
className="option"
|
|
onClick={(e) => {
|
|
console.log("option", option);
|
|
e.stopPropagation();
|
|
handleOptionClick(option);
|
|
}}
|
|
>
|
|
{option}
|
|
</button>
|
|
))}
|
|
</div>
|
|
)}
|
|
</button>
|
|
);
|
|
};
|
|
|
|
export default DashboardCard; |