From 2b419c02a5feae79b746760f25a51f5ca583193d Mon Sep 17 00:00:00 2001 From: sriramveeraghanta Date: Mon, 25 Sep 2023 20:09:39 +0530 Subject: [PATCH] fix: leave project fixes --- .../project/delete-project-modal.tsx | 77 ++++++------------- .../project/leave-project-modal.tsx | 38 ++++----- web/components/project/sidebar-list-item.tsx | 46 +++++------ web/components/project/sidebar-list.tsx | 12 +-- .../project/single-project-card.tsx | 36 ++------- web/services/project.service.ts | 4 +- web/store/project.ts | 17 +++- 7 files changed, 93 insertions(+), 137 deletions(-) diff --git a/web/components/project/delete-project-modal.tsx b/web/components/project/delete-project-modal.tsx index d6f7ba0f0..8d0833001 100644 --- a/web/components/project/delete-project-modal.tsx +++ b/web/components/project/delete-project-modal.tsx @@ -1,15 +1,7 @@ import React from "react"; - import { useRouter } from "next/router"; - -import { mutate } from "swr"; - -// react-hook-form import { Controller, useForm } from "react-hook-form"; -// headless ui import { Dialog, Transition } from "@headlessui/react"; -// services -import projectService from "services/project.service"; // hooks import useToast from "hooks/use-toast"; // icons @@ -17,16 +9,14 @@ import { ExclamationTriangleIcon } from "@heroicons/react/24/outline"; // ui import { DangerButton, Input, SecondaryButton } from "components/ui"; // types -import type { ICurrentUserResponse, IProject } from "types"; +import type { IProject } from "types"; // fetch-keys -import { PROJECTS_LIST } from "constants/fetch-keys"; +import { useMobxStore } from "lib/mobx/store-provider"; -type TConfirmProjectDeletionProps = { +type DeleteProjectModal = { isOpen: boolean; + project: IProject; onClose: () => void; - onSuccess?: () => void; - data: IProject | null; - user: ICurrentUserResponse | undefined; }; const defaultValues = { @@ -34,18 +24,16 @@ const defaultValues = { confirmDelete: "", }; -export const DeleteProjectModal: React.FC = ({ - isOpen, - data, - onClose, - onSuccess, - user, -}) => { +export const DeleteProjectModal: React.FC = (props) => { + const { isOpen, project, onClose } = props; + // store + const { project: projectStore } = useMobxStore(); + // router const router = useRouter(); const { workspaceSlug, projectId } = router.query; - + // toast const { setToastAlert } = useToast(); - + // form info const { control, formState: { isSubmitting }, @@ -54,8 +42,7 @@ export const DeleteProjectModal: React.FC = ({ watch, } = useForm({ defaultValues }); - const canDelete = - watch("projectName") === data?.name && watch("confirmDelete") === "delete my project"; + const canDelete = watch("projectName") === project?.name && watch("confirmDelete") === "delete my project"; const handleClose = () => { const timer = setTimeout(() => { @@ -67,30 +54,20 @@ export const DeleteProjectModal: React.FC = ({ }; const onSubmit = async () => { - if (!data || !workspaceSlug || !canDelete) return; + if (!workspaceSlug || !canDelete) return; - await projectService - .deleteProject(workspaceSlug.toString(), data.id, user) + await projectStore + .deleteProject(workspaceSlug.toString(), project.id) .then(() => { handleClose(); - - mutate( - PROJECTS_LIST(workspaceSlug.toString(), { is_favorite: "all" }), - (prevData) => prevData?.filter((project: IProject) => project.id !== data.id), - false - ); - - if (onSuccess) onSuccess(); - - if (projectId && projectId === data.id) router.push(`/${workspaceSlug}/projects`); }) - .catch(() => + .catch(() => { setToastAlert({ type: "error", title: "Error!", message: "Something went wrong. Please try again later.", - }) - ); + }); + }); }; return ( @@ -123,10 +100,7 @@ export const DeleteProjectModal: React.FC = ({
-

Delete Project

@@ -135,16 +109,14 @@ export const DeleteProjectModal: React.FC = ({

Are you sure you want to delete project{" "} - {data?.name}? All of the - data related to the project will be permanently removed. This action cannot be - undone + {project?.name}? All of the data related to the + project will be permanently removed. This action cannot be undone

- Enter the project name{" "} - {data?.name} to - continue: + Enter the project name {project?.name}{" "} + to continue:

= ({

- To confirm, type{" "} - delete my project{" "} + To confirm, type delete my project{" "} below:

= observer(() => { +export interface ILeaveProjectModal { + project: IProject; + isOpen: boolean; + onClose: () => void; +} + +export const LeaveProjectModal: FC = observer((props) => { + const { project, isOpen, onClose } = props; // router const router = useRouter(); const { workspaceSlug } = router.query; @@ -50,22 +57,18 @@ export const LeaveProjectModal: FC<> = observer(() => { } = useForm({ defaultValues }); const handleClose = () => { - projectStore.handleProjectLeaveModal(null); reset({ ...defaultValues }); }; const onSubmit = async (data: any) => { + if (!workspaceSlug) return; + if (data) { - if (data.projectName === projectStore?.projectLeaveDetails?.name) { + if (data.projectName === project?.name) { if (data.confirmLeave === "Leave Project") { return projectStore - .leaveProject( - projectStore.projectLeaveDetails.workspaceSlug.toString(), - projectStore.projectLeaveDetails.id.toString(), - user - ) + .leaveProject(workspaceSlug.toString(), project.id) .then((res) => { - mutateProjects(); handleClose(); router.push(`/${workspaceSlug}/projects`); }) @@ -100,10 +103,10 @@ export const LeaveProjectModal: FC<> = observer(() => { }; return ( - + = observer(() => {
= observer(() => {

Are you sure you want to leave the project - - {` "${project?.projectLeaveDetails?.name}" `} - ? All of the issues associated with you will become inaccessible. + {` "${project?.name}" `}? All of the + issues associated with you will become inaccessible.

- Enter the project name{" "} - {project?.projectLeaveDetails?.name} to - continue: + Enter the project name {project?.name}{" "} + to continue:

void; handleCopyText: () => void; shortContextMenu?: boolean; }; @@ -79,15 +74,7 @@ const navigation = (workspaceSlug: string, projectId: string) => [ ]; export const ProjectSidebarListItem: React.FC = observer((props) => { - const { - project, - sidebarCollapse, - provided, - snapshot, - handleDeleteProject, - handleCopyText, - shortContextMenu = false, - } = props; + const { project, sidebarCollapse, provided, snapshot, handleCopyText, shortContextMenu = false } = props; // store const { projectPublish, project: projectStore }: RootStore = useMobxStore(); // router @@ -97,6 +84,7 @@ export const ProjectSidebarListItem: React.FC = observer((props) => { const { setToastAlert } = useToast(); // states const [leaveProjectModalOpen, setLeaveProjectModal] = useState(false); + const [deleteProjectModalOpen, setDeleteProjectModal] = useState(false); const isAdmin = project.member_role === 20; const isViewerOrGuest = project.member_role === 10 || project.member_role === 5; @@ -129,9 +117,23 @@ export const ProjectSidebarListItem: React.FC = observer((props) => { setLeaveProjectModal(true); }; + const handleLeaveProjectModalClose = () => { + setLeaveProjectModal(false); + }; + + const handleDeleteProjectClick = () => { + setDeleteProjectModal(true); + }; + + const handleDeleteProjectModalClose = () => { + setDeleteProjectModal(false); + router.push(`/${workspaceSlug}/projects`); + }; + return ( <> - + + {({ open }) => ( <> @@ -205,7 +207,7 @@ export const ProjectSidebarListItem: React.FC = observer((props) => { ellipsis > {!shortContextMenu && isAdmin && ( - + Delete project diff --git a/web/components/project/sidebar-list.tsx b/web/components/project/sidebar-list.tsx index 5bd0b38b5..77e9f6e2d 100644 --- a/web/components/project/sidebar-list.tsx +++ b/web/components/project/sidebar-list.tsx @@ -9,7 +9,7 @@ import useToast from "hooks/use-toast"; import useUserAuth from "hooks/use-user-auth"; import useProjects from "hooks/use-projects"; // components -import { CreateProjectModal, DeleteProjectModal, ProjectSidebarListItem, LeaveProjectModal } from "components/project"; +import { CreateProjectModal, ProjectSidebarListItem, LeaveProjectModal } from "components/project"; import { PublishProjectModal } from "components/project/publish-project/modal"; // services import projectService from "services/project.service"; @@ -119,14 +119,6 @@ export const ProjectSidebarList: FC = observer(() => { setToFavorite={isFavoriteProjectCreate} user={user} /> - setDeleteProjectModal(false)} - data={projectToDelete} - user={user} - /> - {/* project leave modal */} - {/* publish project modal */}
{ sidebarCollapse={themeStore?.sidebarCollapsed || false} provided={provided} snapshot={snapshot} - handleDeleteProject={() => handleDeleteProject(project)} handleCopyText={() => handleCopyText(project.id)} shortContextMenu /> @@ -252,7 +243,6 @@ export const ProjectSidebarList: FC = observer(() => { sidebarCollapse={themeStore?.sidebarCollapsed || false} provided={provided} snapshot={snapshot} - handleDeleteProject={() => handleDeleteProject(project)} handleCopyText={() => handleCopyText(project.id)} />
diff --git a/web/components/project/single-project-card.tsx b/web/components/project/single-project-card.tsx index 547211c53..cf771d532 100644 --- a/web/components/project/single-project-card.tsx +++ b/web/components/project/single-project-card.tsx @@ -12,14 +12,7 @@ import useToast from "hooks/use-toast"; // ui import { CustomMenu, Tooltip } from "components/ui"; // icons -import { - CalendarDaysIcon, - LinkIcon, - PencilIcon, - PlusIcon, - StarIcon, - TrashIcon, -} from "@heroicons/react/24/outline"; +import { CalendarDaysIcon, LinkIcon, PencilIcon, PlusIcon, StarIcon, TrashIcon } from "@heroicons/react/24/outline"; // helpers import { renderShortDateWithYearFormat } from "helpers/date-time.helper"; import { copyTextToClipboard, truncateText } from "helpers/string.helper"; @@ -35,11 +28,7 @@ export type ProjectCardProps = { setDeleteProject: (id: string | null) => void; }; -export const SingleProjectCard: React.FC = ({ - project, - setToJoinProject, - setDeleteProject, -}) => { +export const SingleProjectCard: React.FC = ({ project, setToJoinProject, setDeleteProject }) => { const router = useRouter(); const { workspaceSlug } = router.query; @@ -53,15 +42,12 @@ export const SingleProjectCard: React.FC = ({ mutate( PROJECTS_LIST(workspaceSlug as string, { is_favorite: "all" }), - (prevData) => - (prevData ?? []).map((p) => (p.id === project.id ? { ...p, is_favorite: true } : p)), + (prevData) => (prevData ?? []).map((p) => (p.id === project.id ? { ...p, is_favorite: true } : p)), false ); projectService - .addProjectToFavorites(workspaceSlug as string, { - project: project.id, - }) + .addProjectToFavorites(workspaceSlug as string, project.id) .then(() => { setToastAlert({ type: "success", @@ -83,8 +69,7 @@ export const SingleProjectCard: React.FC = ({ mutate( PROJECTS_LIST(workspaceSlug as string, { is_favorite: "all" }), - (prevData) => - (prevData ?? []).map((p) => (p.id === project.id ? { ...p, is_favorite: false } : p)), + (prevData) => (prevData ?? []).map((p) => (p.id === project.id ? { ...p, is_favorite: false } : p)), false ); @@ -107,8 +92,7 @@ export const SingleProjectCard: React.FC = ({ }; const handleCopyText = () => { - const originURL = - typeof window !== "undefined" && window.location.origin ? window.location.origin : ""; + const originURL = typeof window !== "undefined" && window.location.origin ? window.location.origin : ""; copyTextToClipboard(`${originURL}/${workspaceSlug}/projects/${project.id}/issues`).then(() => { setToastAlert({ @@ -148,9 +132,7 @@ export const SingleProjectCard: React.FC = ({ Select to Join ) : ( - - Member - + Member )} {project.is_favorite && ( @@ -174,9 +156,7 @@ export const SingleProjectCard: React.FC = ({ renderEmoji(project.icon_prop) ) : null}
-

- {truncateText(project.description ?? "", 100)} -

+

{truncateText(project.description ?? "", 100)}

diff --git a/web/services/project.service.ts b/web/services/project.service.ts index 1e0aaa7a3..841043f6e 100644 --- a/web/services/project.service.ts +++ b/web/services/project.service.ts @@ -87,7 +87,7 @@ export class ProjectService extends APIService { }); } - async deleteProject(workspaceSlug: string, projectId: string, user: ICurrentUserResponse | undefined): Promise { + async deleteProject(workspaceSlug: string, projectId: string, user: any | undefined): Promise { return this.delete(`/api/workspaces/${workspaceSlug}/projects/${projectId}/`) .then((response) => { trackEventServices.trackProjectEvent({ projectId }, "DELETE_PROJECT", user); @@ -132,7 +132,7 @@ export class ProjectService extends APIService { }); } - async leaveProject(workspaceSlug: string, projectId: string, user: ICurrentUserResponse): Promise { + async leaveProject(workspaceSlug: string, projectId: string, user: any): Promise { return this.delete(`/api/workspaces/${workspaceSlug}/projects/${projectId}/members/leave/`) .then((response) => { trackEventServices.trackProjectEvent( diff --git a/web/store/project.ts b/web/store/project.ts index 0c92970b7..52bfbca75 100644 --- a/web/store/project.ts +++ b/web/store/project.ts @@ -67,7 +67,8 @@ export interface IProjectStore { handleProjectLeaveModal: (project: any | null) => void; - leaveProject: (workspaceSlug: string, projectSlug: string, user: any) => Promise; + leaveProject: (workspaceSlug: string, projectSlug: string) => Promise; + deleteProject: (workspaceSlug: string, projectSlug: string) => Promise; } class ProjectStore implements IProjectStore { @@ -484,12 +485,13 @@ class ProjectStore implements IProjectStore { } }; - leaveProject = async (workspaceSlug: string, projectSlug: string, user: any) => { + leaveProject = async (workspaceSlug: string, projectSlug: string) => { try { this.loader = true; this.error = null; - const response = await this.projectService.leaveProject(workspaceSlug, projectSlug, user); + const response = await this.projectService.leaveProject(workspaceSlug, projectSlug, this.rootStore.user); + await this.rootStore.workspace.getWorkspaceProjects(workspaceSlug); runInAction(() => { this.loader = false; @@ -503,6 +505,15 @@ class ProjectStore implements IProjectStore { return error; } }; + + deleteProject = async (workspaceSlug: string, projectId: string) => { + try { + await this.projectService.deleteProject(workspaceSlug, projectId, this.rootStore.user.currentUser); + await this.rootStore.workspace.getWorkspaceProjects(workspaceSlug); + } catch (error) { + console.log("Failed to delete project from project store"); + } + }; } export default ProjectStore;