confrim before delete a workspace or a project
deploy to danak / build_and_deploy (push) Has been cancelled

This commit is contained in:
hamid zarghami
2026-07-20 11:37:43 +03:30
parent d3fd21177b
commit a3ce347f02
8 changed files with 156 additions and 21 deletions
+74
View File
@@ -0,0 +1,74 @@
import { FC, useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import Button from "./Button";
import DefaulModal from "./DefaulModal";
import Input from "./Input";
type Props = {
isOpen: boolean;
close: () => void;
onConfirm: () => void;
isLoading?: boolean;
itemName: string;
message?: string;
};
const DeleteNameConfirmModal: FC<Props> = ({
isOpen,
close,
onConfirm,
isLoading = false,
itemName,
message,
}) => {
const { t } = useTranslation("global");
const [typedName, setTypedName] = useState("");
useEffect(() => {
if (!isOpen) {
setTypedName("");
}
}, [isOpen]);
const isNameMatched = typedName.trim() === itemName.trim();
return (
<DefaulModal
open={isOpen}
close={close}
title_header={t("confrim.subject")}
isHeader
>
<div className="mt-6">
<div className="text-sm text-center">
{message ?? t("taskmanager.delete_confirm_message", { name: itemName })}
</div>
<div className="mt-5">
<Input
value={typedName}
onChange={(e) => setTypedName(e.target.value)}
placeholder={t("taskmanager.delete_confirm_placeholder")}
className="text-center"
/>
</div>
<div className="flex gap-4 justify-center mt-10">
<Button
label={t("confrim.yes")}
onClick={onConfirm}
isLoading={isLoading}
disabled={!isNameMatched}
/>
<Button
label={t("confrim.cancel")}
className="bg-transparent text-black border border-primary"
onClick={close}
/>
</div>
</div>
</DefaulModal>
);
};
export default DeleteNameConfirmModal;
+3
View File
@@ -1020,6 +1020,9 @@
"select_date": "انتخاب تاریخ",
"projects": "پروژه‌ها",
"delete_project": "پاک کردن",
"project_deleted": "پروژه با موفقیت حذف شد",
"delete_confirm_message": "برای حذف «{{name}}»، نام آن را در کادر زیر وارد کنید.",
"delete_confirm_placeholder": "نام را وارد کنید",
"add_new_column": "اضافه کردن ستون جدید",
"add_column": "اضافه کردن",
"column_name": "نام ستون",
+33 -7
View File
@@ -1,30 +1,48 @@
import { FC, useState } from "react";
import { useParams } from "react-router-dom";
import { useTranslation } from "react-i18next";
import { toast } from "../../../components/Toast";
import DeleteNameConfirmModal from "../../../components/DeleteNameConfirmModal";
import PageLoading from "../../../components/PageLoading";
import { ErrorType } from "../../../helpers/types";
import { useGetWorkspaceDetail } from "../workspace/hooks/useWorkspaceData";
import { WorkspaceDetailType } from "../workspace/types/WorkspaceTypes";
import ProjectGrid from "./components/ProjectGrid";
import ProjectListHeader from "./components/ProjectListHeader";
import WorkspaceInfoBanner from "./components/WorkspaceInfoBanner";
import { useGetProjectsByWorkspace } from "./hooks/useProjectData";
import { useDeleteProject, useGetProjectsByWorkspace } from "./hooks/useProjectData";
import type { ProjectItem } from "./types/ProjectTypes";
const List: FC = () => {
const { t } = useTranslation("global");
const { workspaceId = "" } = useParams();
const getProjects = useGetProjectsByWorkspace(workspaceId);
const getWorkspaceDetail = useGetWorkspaceDetail(workspaceId);
const deleteProject = useDeleteProject();
const workspace: WorkspaceDetailType | undefined =
getWorkspaceDetail.data?.data?.workspace ?? getWorkspaceDetail.data?.data;
const apiProjects: ProjectItem[] =
const projects: ProjectItem[] =
getProjects.data?.data?.projects ?? getProjects.data?.data ?? [];
const [deletedIds, setDeletedIds] = useState<string[]>([]);
const projects = apiProjects.filter((project) => !deletedIds.includes(project.id));
const [deleteTarget, setDeleteTarget] = useState<ProjectItem | null>(null);
const handleDelete = (id: string) => {
setDeletedIds((prev) => [...prev, id]);
const handleConfirmDelete = () => {
if (!deleteTarget) return;
deleteProject.mutate(
{ id: deleteTarget.id, workspaceId },
{
onSuccess: () => {
toast(t("taskmanager.project_deleted"), "success");
setDeleteTarget(null);
},
onError: (error: ErrorType) => {
toast(error.response?.data?.error.message[0], "error");
},
},
);
};
const isLoading = getProjects.isPending || getWorkspaceDetail.isPending;
@@ -40,8 +58,16 @@ const List: FC = () => {
{isLoading ? (
<PageLoading />
) : (
<ProjectGrid projects={projects} onDelete={handleDelete} />
<ProjectGrid projects={projects} onDelete={setDeleteTarget} />
)}
<DeleteNameConfirmModal
isOpen={!!deleteTarget}
close={() => setDeleteTarget(null)}
onConfirm={handleConfirmDelete}
isLoading={deleteProject.isPending}
itemName={deleteTarget?.name ?? ""}
/>
</div>
);
};
@@ -7,7 +7,7 @@ import ProjectCardMenu from "./ProjectCardMenu";
type Props = {
project: ProjectItem;
onDelete: (id: string) => void;
onDelete: (project: ProjectItem) => void;
};
const ProjectCard: FC<Props> = ({ project, onDelete }) => {
@@ -28,7 +28,7 @@ const ProjectCard: FC<Props> = ({ project, onDelete }) => {
{project.name}
</Link>
<ProjectCardMenu onEdit={handleEdit} onDelete={() => onDelete(project.id)} />
<ProjectCardMenu onEdit={handleEdit} onDelete={() => onDelete(project)} />
</div>
</div>
);
@@ -5,7 +5,7 @@ import ProjectCard from "./ProjectCard";
type Props = {
projects: ProjectItem[];
onDelete: (id: string) => void;
onDelete: (project: ProjectItem) => void;
};
const ProjectGrid: FC<Props> = ({ projects, onDelete }) => {
@@ -52,3 +52,16 @@ export const useUpdateProject = () => {
},
});
};
export const useDeleteProject = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: ({ id }: { id: string; workspaceId: string }) => api.deleteProject(id),
onSuccess: (_data, variables) => {
queryClient.invalidateQueries({
queryKey: ["projects", variables.workspaceId],
});
},
});
};
@@ -25,3 +25,8 @@ export const updateProject = async (id: string, params: UpdateProjectType) => {
const { data } = await axios.patch(`/task-manager/projects/${id}`, params);
return data;
};
export const deleteProject = async (id: string) => {
const { data } = await axios.delete(`/task-manager/projects/${id}`);
return data;
};
+25 -11
View File
@@ -1,12 +1,12 @@
import { Add, Edit } from "iconsax-react";
import { FC } from "react";
import { Add, Edit, Trash } from "iconsax-react";
import { FC, useState } from "react";
import { useTranslation } from "react-i18next";
import { Link } from "react-router-dom";
import { toast } from '../../../components/Toast';
import { toast } from "../../../components/Toast";
import Button from "../../../components/Button";
import DeleteNameConfirmModal from "../../../components/DeleteNameConfirmModal";
import PageLoading from "../../../components/PageLoading";
import Td from "../../../components/Td";
import TrashWithConfrim from "../../../components/TrashWithConfrim";
import { Pages } from "../../../config/Pages";
import { ErrorType } from "../../../helpers/types";
import { useDeleteWorkspace, useGetWorkspaces } from "./hooks/useWorkspaceData";
@@ -16,16 +16,20 @@ const WorkspaceList: FC = () => {
const { t } = useTranslation("global");
const getWorkspaces = useGetWorkspaces();
const deleteWorkspace = useDeleteWorkspace();
const [deleteTarget, setDeleteTarget] = useState<WorkspaceItemType | null>(null);
const workspaces: WorkspaceItemType[] = getWorkspaces.data?.data?.workspaces ?? getWorkspaces.data?.data ?? [];
const handleDelete = (id: string) => {
deleteWorkspace.mutate(id, {
const handleConfirmDelete = () => {
if (!deleteTarget) return;
deleteWorkspace.mutate(deleteTarget.id, {
onSuccess: () => {
toast(t("taskmanager.workspace_deleted"), 'success');
toast(t("taskmanager.workspace_deleted"), "success");
setDeleteTarget(null);
},
onError: (error: ErrorType) => {
toast(error.response?.data?.error.message[0], 'error');
toast(error.response?.data?.error.message[0], "error");
},
});
};
@@ -94,9 +98,11 @@ const WorkspaceList: FC = () => {
color="#8C90A3"
/>
</Link>
<TrashWithConfrim
onDelete={() => handleDelete(item.id)}
isLoading={deleteWorkspace.isPending}
<Trash
onClick={() => setDeleteTarget(item)}
color="#888"
size={20}
className="cursor-pointer"
/>
</div>
</Td>
@@ -106,6 +112,14 @@ const WorkspaceList: FC = () => {
</table>
</div>
)}
<DeleteNameConfirmModal
isOpen={!!deleteTarget}
close={() => setDeleteTarget(null)}
onConfirm={handleConfirmDelete}
isLoading={deleteWorkspace.isPending}
itemName={deleteTarget?.name ?? ""}
/>
</div>
);
};