From 308f8530c7240f346754858f04563f522d786832 Mon Sep 17 00:00:00 2001 From: Prateek Shourya Date: Mon, 15 Dec 2025 20:37:32 +0530 Subject: [PATCH] [WEB-5668] fix: add fetchWorkspaceLevelProjectEntities method and update project-related fetch keys - Implemented fetchWorkspaceLevelProjectEntities in UserPermissionStore to load necessary project data when joining a project. - Updated PROJECT_* constants in fetch-keys.ts to include projectRole as a parameter for better permission handling. - Refactored project data fetching in ProjectAuthWrapper to utilize the new projectRole parameter. - Removed unused fetchProjectDetails call in JoinProjectModal after project join. --- apps/web/ce/store/user/permission.store.ts | 4 ++ .../components/project/join-project-modal.tsx | 3 -- apps/web/core/constants/fetch-keys.ts | 53 ++++++++++++------- .../layouts/auth-layout/project-wrapper.tsx | 26 +++++---- .../core/store/user/base-permissions.store.ts | 11 ++++ 5 files changed, 61 insertions(+), 36 deletions(-) diff --git a/apps/web/ce/store/user/permission.store.ts b/apps/web/ce/store/user/permission.store.ts index e5a27f5347d..ea062133dea 100644 --- a/apps/web/ce/store/user/permission.store.ts +++ b/apps/web/ce/store/user/permission.store.ts @@ -21,4 +21,8 @@ export class UserPermissionStore extends BaseUserPermissionStore implements IUse (workspaceSlug: string, projectId?: string): EUserPermissions | undefined => this.getProjectRole(workspaceSlug, projectId) ); + + fetchWorkspaceLevelProjectEntities = (workspaceSlug: string, projectId: string): void => { + void this.store.projectRoot.project.fetchProjectDetails(workspaceSlug, projectId); + }; } diff --git a/apps/web/core/components/project/join-project-modal.tsx b/apps/web/core/components/project/join-project-modal.tsx index 3bb8aca5f81..dc3de75856d 100644 --- a/apps/web/core/components/project/join-project-modal.tsx +++ b/apps/web/core/components/project/join-project-modal.tsx @@ -6,7 +6,6 @@ import { Button } from "@plane/propel/button"; import type { IProject } from "@plane/types"; // ui // hooks -import { useProject } from "@/hooks/store/use-project"; import { useUserPermissions } from "@/hooks/store/user"; import { useAppRouter } from "@/hooks/use-app-router"; @@ -24,7 +23,6 @@ export function JoinProjectModal(props: TJoinProjectModalProps) { const [isJoiningLoading, setIsJoiningLoading] = useState(false); // store hooks const { joinProject } = useUserPermissions(); - const { fetchProjectDetails } = useProject(); // router const router = useAppRouter(); @@ -34,7 +32,6 @@ export function JoinProjectModal(props: TJoinProjectModalProps) { joinProject(workspaceSlug, project.id) .then(() => { router.push(`/${workspaceSlug}/projects/${project.id}/issues`); - fetchProjectDetails(workspaceSlug, project.id); handleClose(); }) .finally(() => { diff --git a/apps/web/core/constants/fetch-keys.ts b/apps/web/core/constants/fetch-keys.ts index ab7a6d69352..0a54ccc1967 100644 --- a/apps/web/core/constants/fetch-keys.ts +++ b/apps/web/core/constants/fetch-keys.ts @@ -1,4 +1,4 @@ -import type { IJiraMetadata } from "@plane/types"; +import type { EUserPermissions, IJiraMetadata } from "@plane/types"; const paramsToKey = (params: any) => { const { @@ -70,6 +70,9 @@ export const WORKSPACE_INVITATION = (invitationId: string) => `WORKSPACE_INVITAT export const WORKSPACE_MEMBER_ME_INFORMATION = (workspaceSlug: string) => `WORKSPACE_MEMBER_ME_INFORMATION_${workspaceSlug.toUpperCase()}`; +export const WORKSPACE_MEMBER_ACTIVITY = (workspaceSlug: string) => + `WORKSPACE_MEMBER_ACTIVITY_${workspaceSlug.toUpperCase()}`; + export const WORKSPACE_PROJECTS_ROLES_INFORMATION = (workspaceSlug: string) => `WORKSPACE_PROJECTS_ROLES_INFORMATION_${workspaceSlug.toUpperCase()}`; @@ -154,29 +157,41 @@ export const PROJECT_DETAILS = (workspaceSlug: string, projectId: string) => export const PROJECT_ME_INFORMATION = (workspaceSlug: string, projectId: string) => `PROJECT_ME_INFORMATION_${projectId.toString().toUpperCase()}`; -export const PROJECT_LABELS = (workspaceSlug: string, projectId: string) => - `PROJECT_LABELS_${projectId.toString().toUpperCase()}`; +export const PROJECT_LABELS = (projectId: string, projectRole: EUserPermissions | undefined) => + `PROJECT_LABELS_${projectId.toString().toUpperCase()}_${projectRole}`; + +export const PROJECT_MEMBERS = (projectId: string, projectRole: EUserPermissions | undefined) => + `PROJECT_MEMBERS_${projectId.toString().toUpperCase()}_${projectRole}`; + +export const PROJECT_STATES = (projectId: string, projectRole: EUserPermissions | undefined) => + `PROJECT_STATES_${projectId.toString().toUpperCase()}_${projectRole}`; + +export const PROJECT_INTAKE_STATE = (projectId: string, projectRole: EUserPermissions | undefined) => + `PROJECT_INTAKE_STATE_${projectId.toString().toUpperCase()}_${projectRole}`; + +export const PROJECT_ESTIMATES = (projectId: string, projectRole: EUserPermissions | undefined) => + `PROJECT_ESTIMATES_${projectId.toString().toUpperCase()}_${projectRole}`; -export const PROJECT_MEMBERS = (workspaceSlug: string, projectId: string) => - `PROJECT_MEMBERS_${projectId.toString().toUpperCase()}`; +export const PROJECT_ALL_CYCLES = (projectId: string, projectRole: EUserPermissions | undefined) => + `PROJECT_ALL_CYCLES_${projectId.toString().toUpperCase()}_${projectRole}`; -export const PROJECT_STATES = (workspaceSlug: string, projectId: string) => - `PROJECT_STATES_${projectId.toString().toUpperCase()}`; +export const PROJECT_MODULES = (projectId: string, projectRole: EUserPermissions | undefined) => + `PROJECT_MODULES_${projectId.toString().toUpperCase()}_${projectRole}`; -export const PROJECT_INTAKE_STATE = (workspaceSlug: string, projectId: string) => - `PROJECT_INTAKE_STATE_${projectId.toString().toUpperCase()}`; +export const PROJECT_VIEWS = (projectId: string, projectRole: EUserPermissions | undefined) => + `PROJECT_VIEWS_${projectId.toString().toUpperCase()}_${projectRole}`; -export const PROJECT_ESTIMATES = (workspaceSlug: string, projectId: string) => - `PROJECT_ESTIMATES_${projectId.toString().toUpperCase()}`; +export const PROJECT_MEMBER_PREFERENCES = (projectId: string, projectRole: EUserPermissions | undefined) => + `PROJECT_MEMBER_PREFERENCES_${projectId.toString().toUpperCase()}_${projectRole}`; -export const PROJECT_ALL_CYCLES = (workspaceSlug: string, projectId: string) => - `PROJECT_ALL_CYCLES_${projectId.toString().toUpperCase()}`; +export const PROJECT_WORKFLOWS = (projectId: string, projectRole: EUserPermissions | undefined) => + `PROJECT_WORKFLOWS_${projectId.toString().toUpperCase()}_${projectRole}`; -export const PROJECT_MODULES = (workspaceSlug: string, projectId: string) => - `PROJECT_MODULES_${projectId.toString().toUpperCase()}`; +export const EPICS_PROPERTIES_AND_OPTIONS = (projectId: string, projectRole: EUserPermissions | undefined) => + `EPICS_PROPERTIES_AND_OPTIONS_${projectId.toString().toUpperCase()}_${projectRole}`; -export const PROJECT_VIEWS = (workspaceSlug: string, projectId: string) => - `PROJECT_VIEWS_${projectId.toString().toUpperCase()}`; +export const WORK_ITEM_TYPES_PROPERTIES_AND_OPTIONS = (projectId: string, projectRole: EUserPermissions | undefined) => + `WORK_ITEM_TYPES_PROPERTIES_AND_OPTIONS_${projectId.toString().toUpperCase()}_${projectRole}`; -export const PROJECT_MEMBER_PREFERENCES = (workspaceSlug: string, projectId: string) => - `PROJECT_MEMBER_PREFERENCES_${projectId.toString().toUpperCase()}`; +export const PROJECT_MILESTONES = (projectId: string, projectRole: EUserPermissions | undefined) => + `PROJECT_MILESTONES_${projectId.toString().toUpperCase()}_${projectRole}`; diff --git a/apps/web/core/layouts/auth-layout/project-wrapper.tsx b/apps/web/core/layouts/auth-layout/project-wrapper.tsx index 00888ed8345..6df49cbea59 100644 --- a/apps/web/core/layouts/auth-layout/project-wrapper.tsx +++ b/apps/web/core/layouts/auth-layout/project-wrapper.tsx @@ -44,7 +44,7 @@ export const ProjectAuthWrapper = observer(function ProjectAuthWrapper(props: IP // states const [isJoiningProject, setIsJoiningProject] = useState(false); // store hooks - const { fetchUserProjectInfo, allowPermissions } = useUserPermissions(); + const { fetchUserProjectInfo, allowPermissions, getProjectRoleByWorkspaceSlugAndProjectId } = useUserPermissions(); const { fetchProjectDetails } = useProject(); const { joinProject } = useUserPermissions(); const { fetchAllCycles } = useCycle(); @@ -65,8 +65,8 @@ export const ProjectAuthWrapper = observer(function ProjectAuthWrapper(props: IP workspaceSlug, projectId ); + const currentProjectRole = getProjectRoleByWorkspaceSlugAndProjectId(workspaceSlug, projectId); const isWorkspaceAdmin = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.WORKSPACE, workspaceSlug); - // Initialize module timeline chart useEffect(() => { initGantt(); @@ -82,50 +82,50 @@ export const ProjectAuthWrapper = observer(function ProjectAuthWrapper(props: IP useSWR(PROJECT_ME_INFORMATION(workspaceSlug, projectId), () => fetchUserProjectInfo(workspaceSlug, projectId)); // fetching project member preferences useSWR( - currentUserData?.id ? PROJECT_MEMBER_PREFERENCES(workspaceSlug, projectId) : null, + currentUserData?.id ? PROJECT_MEMBER_PREFERENCES(projectId, currentProjectRole) : null, currentUserData?.id ? () => fetchProjectMemberPreferences(workspaceSlug, projectId, currentUserData.id) : null, { revalidateIfStale: false, revalidateOnFocus: false } ); // fetching project labels - useSWR(PROJECT_LABELS(workspaceSlug, projectId), () => fetchProjectLabels(workspaceSlug, projectId), { + useSWR(PROJECT_LABELS(projectId, currentProjectRole), () => fetchProjectLabels(workspaceSlug, projectId), { revalidateIfStale: false, revalidateOnFocus: false, }); // fetching project members - useSWR(PROJECT_MEMBERS(workspaceSlug, projectId), () => fetchProjectMembers(workspaceSlug, projectId), { + useSWR(PROJECT_MEMBERS(projectId, currentProjectRole), () => fetchProjectMembers(workspaceSlug, projectId), { revalidateIfStale: false, revalidateOnFocus: false, }); // fetching project states - useSWR(PROJECT_STATES(workspaceSlug, projectId), () => fetchProjectStates(workspaceSlug, projectId), { + useSWR(PROJECT_STATES(projectId, currentProjectRole), () => fetchProjectStates(workspaceSlug, projectId), { revalidateIfStale: false, revalidateOnFocus: false, }); // fetching project intake state - useSWR(PROJECT_INTAKE_STATE(workspaceSlug, projectId), () => fetchProjectIntakeState(workspaceSlug, projectId), { + useSWR(PROJECT_INTAKE_STATE(projectId, currentProjectRole), () => fetchProjectIntakeState(workspaceSlug, projectId), { revalidateIfStale: false, revalidateOnFocus: false, }); // fetching project estimates - useSWR(PROJECT_ESTIMATES(workspaceSlug, projectId), () => getProjectEstimates(workspaceSlug, projectId), { + useSWR(PROJECT_ESTIMATES(projectId, currentProjectRole), () => getProjectEstimates(workspaceSlug, projectId), { revalidateIfStale: false, revalidateOnFocus: false, }); // fetching project cycles - useSWR(PROJECT_ALL_CYCLES(workspaceSlug, projectId), () => fetchAllCycles(workspaceSlug, projectId), { + useSWR(PROJECT_ALL_CYCLES(projectId, currentProjectRole), () => fetchAllCycles(workspaceSlug, projectId), { revalidateIfStale: false, revalidateOnFocus: false, }); // fetching project modules useSWR( - PROJECT_MODULES(workspaceSlug, projectId), + PROJECT_MODULES(projectId, currentProjectRole), async () => { await Promise.all([fetchModulesSlim(workspaceSlug, projectId), fetchModules(workspaceSlug, projectId)]); }, { revalidateIfStale: false, revalidateOnFocus: false } ); // fetching project views - useSWR(PROJECT_VIEWS(workspaceSlug, projectId), () => fetchViews(workspaceSlug, projectId), { + useSWR(PROJECT_VIEWS(projectId, currentProjectRole), () => fetchViews(workspaceSlug, projectId), { revalidateIfStale: false, revalidateOnFocus: false, }); @@ -133,9 +133,7 @@ export const ProjectAuthWrapper = observer(function ProjectAuthWrapper(props: IP // handle join project const handleJoinProject = () => { setIsJoiningProject(true); - joinProject(workspaceSlug, projectId) - .then(() => fetchProjectDetails(workspaceSlug, projectId)) - .finally(() => setIsJoiningProject(false)); + joinProject(workspaceSlug, projectId).finally(() => setIsJoiningProject(false)); }; const isProjectLoading = (isParentLoading || isProjectDetailsLoading) && !projectDetailsError; diff --git a/apps/web/core/store/user/base-permissions.store.ts b/apps/web/core/store/user/base-permissions.store.ts index 0d9e28612d4..a4b34a05d11 100644 --- a/apps/web/core/store/user/base-permissions.store.ts +++ b/apps/web/core/store/user/base-permissions.store.ts @@ -36,6 +36,7 @@ export interface IBaseUserPermissionStore { workspaceSlug: string, projectId?: string ) => EUserPermissions | undefined; + fetchWorkspaceLevelProjectEntities: (workspaceSlug: string, projectId: string) => void; allowPermissions: ( allowPermissions: ETempUserRole[], level: TUserPermissionsLevel, @@ -148,6 +149,15 @@ export abstract class BaseUserPermissionStore implements IBaseUserPermissionStor projectId?: string ) => EUserPermissions | undefined; + /** + * @description Fetches project-level entities that are not automatically loaded by the project wrapper. + * This is used when joining a project to ensure all necessary workspace-level project data is available. + * @param { string } workspaceSlug + * @param { string } projectId + * @returns { Promise } + */ + abstract fetchWorkspaceLevelProjectEntities: (workspaceSlug: string, projectId: string) => void; + /** * @description Returns whether the user has the permission to access a page * @param { string } page @@ -309,6 +319,7 @@ export abstract class BaseUserPermissionStore implements IBaseUserPermissionStor runInAction(() => { set(this.workspaceProjectsPermissions, [workspaceSlug, projectId], projectMemberRole); }); + void this.fetchWorkspaceLevelProjectEntities(workspaceSlug, projectId); } } catch (error) { console.error("Error user joining the project", error);