diff --git a/web/components/auth-screens/project/join-project.tsx b/web/components/auth-screens/project/join-project.tsx index 0f5ccd297..5713e2ad8 100644 --- a/web/components/auth-screens/project/join-project.tsx +++ b/web/components/auth-screens/project/join-project.tsx @@ -1,23 +1,20 @@ import { useState } from "react"; import Image from "next/image"; import { useRouter } from "next/router"; -import { mutate } from "swr"; -// services -import { ProjectService } from "services/project"; +// mobx store +import { useMobxStore } from "lib/mobx/store-provider"; // ui import { Button } from "@plane/ui"; // icons import { ClipboardList } from "lucide-react"; // images import JoinProjectImg from "public/auth/project-not-authorized.svg"; -// fetch-keys -import { USER_PROJECT_VIEW } from "constants/fetch-keys"; - -const projectService = new ProjectService(); export const JoinProject: React.FC = () => { const [isJoiningProject, setIsJoiningProject] = useState(false); + const { project: projectStore } = useMobxStore(); + const router = useRouter(); const { workspaceSlug, projectId } = router.query; @@ -25,16 +22,10 @@ export const JoinProject: React.FC = () => { if (!workspaceSlug || !projectId) return; setIsJoiningProject(true); - projectService - .joinProject(workspaceSlug as string, [projectId as string]) - .then(async () => { - await mutate(USER_PROJECT_VIEW(projectId.toString())); - setIsJoiningProject(false); - }) - .catch((err) => { - console.error(err); - setIsJoiningProject(false); - }); + + projectStore.joinProject(workspaceSlug.toString(), [projectId.toString()]).finally(() => { + setIsJoiningProject(false); + }); }; return ( diff --git a/web/components/project/card.tsx b/web/components/project/card.tsx index 550a83383..e04ad7efc 100644 --- a/web/components/project/card.tsx +++ b/web/components/project/card.tsx @@ -1,6 +1,5 @@ import React, { useState } from "react"; import { useRouter } from "next/router"; -import Link from "next/link"; import { observer } from "mobx-react-lite"; import { useMobxStore } from "lib/mobx/store-provider"; import { RootStore } from "store/root"; @@ -95,136 +94,136 @@ export const ProjectCard: React.FC = observer((props) => { )} {/* Card Information */} -
- - -
-
+
{ + if (project.is_member) router.push(`/${workspaceSlug?.toString()}/projects/${project.id}/issues`); + else setJoinProjectModal(true); + }} + className="flex flex-col rounded bg-custom-background-100 border border-custom-border-200 cursor-pointer" + > +
+
- {project.name} + {project.name} -
-
-
- - {project.emoji - ? renderEmoji(project.emoji) - : project.icon_prop - ? renderEmoji(project.icon_prop) - : null} - -
+
+
+
+ + {project.emoji + ? renderEmoji(project.emoji) + : project.icon_prop + ? renderEmoji(project.icon_prop) + : null} + +
-
-

{project.name}

- -

{project.identifier}

- {project.network === 0 && } -
-
-
- -
- - -
+
+

{project.name}

+ +

{project.identifier}

+ {project.network === 0 && } +
-
-

{project.description}

-
- 0 ? `${project.members.length} Members` : "No Member" +
+ + - )} - - {!project.is_member ? ( -
- -
- ) : null} -
+ }} + > + +
- - +
+
+ +
+

{project.description}

+
+ 0 ? `${project.members.length} Members` : "No Member" + } + position="top" + > + {projectMembersIds.length > 0 ? ( +
+ + {projectMembersIds.map((memberId) => { + const member = project.members?.find((m) => m.id === memberId); + + if (!member) return null; + + return ; + })} + +
+ ) : ( + No Member Yet + )} +
+ {(isOwner || isMember) && ( + + )} + + {!project.is_member ? ( +
+ +
+ ) : null} +
+
); diff --git a/web/layouts/auth-layout/project-wrapper.tsx b/web/layouts/auth-layout/project-wrapper.tsx index 91645222e..e3eca37ff 100644 --- a/web/layouts/auth-layout/project-wrapper.tsx +++ b/web/layouts/auth-layout/project-wrapper.tsx @@ -104,8 +104,11 @@ export const ProjectAuthWrapper: FC = observer((props) => { } ); + const projectsList = workspaceSlug ? projectStore.projects[workspaceSlug.toString()] : null; + const projectExists = projectId ? projectsList?.find((project) => project.id === projectId.toString()) : null; + // check if the project member apis is loading - if (!userStore.projectMemberInfo && userStore.hasPermissionToProject === null) { + if (!userStore.projectMemberInfo && projectId && userStore.hasPermissionToProject[projectId.toString()] === null) return (
@@ -113,32 +116,31 @@ export const ProjectAuthWrapper: FC = observer((props) => {
); - } // check if the user don't have permission to access the project - if (userStore.hasPermissionToProject === false && !userStore.projectNotFound) { - ; - } + if (projectExists && projectId && userStore.hasPermissionToProject[projectId.toString()] === false) + return ; // check if the project info is not found. - if (userStore.hasPermissionToProject === false && userStore.projectNotFound) { -
- { - const e = new KeyboardEvent("keydown", { - key: "p", - }); - document.dispatchEvent(e); - }, - }} - /> -
; - } + if (!projectExists && projectId && userStore.hasPermissionToProject[projectId.toString()] === false) + return ( +
+ { + const e = new KeyboardEvent("keydown", { + key: "p", + }); + document.dispatchEvent(e); + }, + }} + /> +
+ ); return <>{children}; }); diff --git a/web/layouts/auth-layout/workspace-wrapper.tsx b/web/layouts/auth-layout/workspace-wrapper.tsx index e1ae8e4db..c8b1666ee 100644 --- a/web/layouts/auth-layout/workspace-wrapper.tsx +++ b/web/layouts/auth-layout/workspace-wrapper.tsx @@ -43,7 +43,11 @@ export const WorkspaceAuthWrapper: FC = observer((props) ); // while data is being loaded - if (!userStore.workspaceMemberInfo && userStore.hasPermissionToWorkspace === null) { + if ( + !userStore.workspaceMemberInfo && + workspaceSlug && + userStore.hasPermissionToWorkspace[workspaceSlug.toString()] === null + ) { return (
@@ -53,7 +57,11 @@ export const WorkspaceAuthWrapper: FC = observer((props) ); } // while user does not have access to view that workspace - if (userStore.hasPermissionToWorkspace !== null && userStore.hasPermissionToWorkspace === false) { + if ( + userStore.hasPermissionToWorkspace !== null && + workspaceSlug && + userStore.hasPermissionToWorkspace[workspaceSlug.toString()] === false + ) { return (
diff --git a/web/store/project/project.store.ts b/web/store/project/project.store.ts index a1716579c..a32906fe6 100644 --- a/web/store/project/project.store.ts +++ b/web/store/project/project.store.ts @@ -522,6 +522,11 @@ export class ProjectStore implements IProjectStore { }; joinProject = async (workspaceSlug: string, projectIds: string[]) => { + const newPermissions: { [projectId: string]: boolean } = {}; + projectIds.forEach((projectId) => { + newPermissions[projectId] = true; + }); + try { this.loader = true; this.error = null; @@ -530,6 +535,10 @@ export class ProjectStore implements IProjectStore { await this.fetchProjects(workspaceSlug); runInAction(() => { + this.rootStore.user.hasPermissionToProject = { + ...this.rootStore.user.hasPermissionToProject, + ...newPermissions, + }; this.loader = false; this.error = null; }); diff --git a/web/store/user.store.ts b/web/store/user.store.ts index cbb861288..a9c5ec25d 100644 --- a/web/store/user.store.ts +++ b/web/store/user.store.ts @@ -18,11 +18,14 @@ export interface IUserStore { dashboardInfo: any; workspaceMemberInfo: IWorkspaceMemberMe | null; - hasPermissionToWorkspace: boolean | null; + hasPermissionToWorkspace: { + [workspaceSlug: string]: boolean | null; + }; projectMemberInfo: IProjectMember | null; - projectNotFound: boolean; - hasPermissionToProject: boolean | null; + hasPermissionToProject: { + [projectId: string]: boolean | null; + }; fetchCurrentUser: () => Promise; fetchCurrentUserSettings: () => Promise; @@ -46,11 +49,14 @@ class UserStore implements IUserStore { dashboardInfo: any = null; workspaceMemberInfo: IWorkspaceMemberMe | null = null; - hasPermissionToWorkspace: boolean | null = null; + hasPermissionToWorkspace: { + [workspaceSlug: string]: boolean | null; + } = {}; projectMemberInfo: IProjectMember | null = null; - projectNotFound: boolean = false; - hasPermissionToProject: boolean | null = null; + hasPermissionToProject: { + [projectId: string]: boolean | null; + } = {}; // root store rootStore; // services @@ -68,7 +74,6 @@ class UserStore implements IUserStore { workspaceMemberInfo: observable.ref, hasPermissionToWorkspace: observable.ref, projectMemberInfo: observable.ref, - projectNotFound: observable.ref, hasPermissionToProject: observable.ref, // action fetchCurrentUser: action, @@ -128,14 +133,21 @@ class UserStore implements IUserStore { fetchUserWorkspaceInfo = async (workspaceSlug: string) => { try { const response = await this.workspaceService.workspaceMemberMe(workspaceSlug.toString()); + runInAction(() => { this.workspaceMemberInfo = response; - this.hasPermissionToWorkspace = true; + this.hasPermissionToWorkspace = { + ...this.hasPermissionToWorkspace, + [workspaceSlug]: true, + }; }); return response; } catch (error) { runInAction(() => { - this.hasPermissionToWorkspace = false; + this.hasPermissionToWorkspace = { + ...this.hasPermissionToWorkspace, + [workspaceSlug]: false, + }; }); throw error; } @@ -147,18 +159,20 @@ class UserStore implements IUserStore { runInAction(() => { this.projectMemberInfo = response; - this.hasPermissionToWorkspace = true; + this.hasPermissionToProject = { + ...this.hasPermissionToProject, + [projectId]: true, + }; }); return response; } catch (error: any) { runInAction(() => { - this.hasPermissionToWorkspace = false; + this.hasPermissionToProject = { + ...this.hasPermissionToProject, + [projectId]: false, + }; }); - if (error?.status === 404) { - runInAction(() => { - this.projectNotFound = true; - }); - } + throw error; } };