diff --git a/apiserver/plane/app/serializers/workspace.py b/apiserver/plane/app/serializers/workspace.py index abfbd001e5e..1a2b89bba61 100644 --- a/apiserver/plane/app/serializers/workspace.py +++ b/apiserver/plane/app/serializers/workspace.py @@ -65,6 +65,7 @@ class Meta: class WorkspaceMemberMeSerializer(BaseSerializer): + draft_issue_count = serializers.IntegerField(read_only=True) class Meta: model = WorkspaceMember fields = "__all__" diff --git a/apiserver/plane/app/views/workspace/member.py b/apiserver/plane/app/views/workspace/member.py index 0a2f1539f44..c71df21ac4d 100644 --- a/apiserver/plane/app/views/workspace/member.py +++ b/apiserver/plane/app/views/workspace/member.py @@ -3,7 +3,11 @@ CharField, Count, Q, + OuterRef, + Subquery, + IntegerField, ) +from django.db.models.functions import Coalesce from django.db.models.functions import Cast # Third party modules @@ -34,6 +38,7 @@ User, Workspace, WorkspaceMember, + DraftIssue, ) from plane.utils.cache import cache_response, invalidate_cache @@ -283,10 +288,26 @@ def post(self, request, slug): class WorkspaceMemberUserEndpoint(BaseAPIView): def get(self, request, slug): - workspace_member = WorkspaceMember.objects.get( - member=request.user, - workspace__slug=slug, - is_active=True, + draft_issue_count = ( + DraftIssue.objects.filter( + created_by=request.user, + workspace_id=OuterRef("workspace_id"), + ) + .values("workspace_id") + .annotate(count=Count("id")) + .values("count") + ) + + workspace_member = ( + WorkspaceMember.objects.filter( + member=request.user, workspace__slug=slug, is_active=True + ) + .annotate( + draft_issue_count=Coalesce( + Subquery(draft_issue_count, output_field=IntegerField()), 0 + ) + ) + .first() ) serializer = WorkspaceMemberMeSerializer(workspace_member) return Response(serializer.data, status=status.HTTP_200_OK) diff --git a/packages/types/src/workspace.d.ts b/packages/types/src/workspace.d.ts index fb466e3c62a..412083c4288 100644 --- a/packages/types/src/workspace.d.ts +++ b/packages/types/src/workspace.d.ts @@ -91,6 +91,7 @@ export interface IWorkspaceMemberMe { updated_by: string; view_props: IWorkspaceViewProps; workspace: string; + draft_issue_count: number; } export interface ILastActiveWorkspaceDetails { diff --git a/web/app/[workspaceSlug]/(projects)/sidebar.tsx b/web/app/[workspaceSlug]/(projects)/sidebar.tsx index f4ee6e91f74..18451d4bbd7 100644 --- a/web/app/[workspaceSlug]/(projects)/sidebar.tsx +++ b/web/app/[workspaceSlug]/(projects)/sidebar.tsx @@ -1,4 +1,5 @@ import { FC, useEffect, useRef } from "react"; +import isEmpty from "lodash/isEmpty"; import { observer } from "mobx-react"; // plane helpers import { useOutsideClickDetector } from "@plane/helpers"; @@ -16,8 +17,9 @@ import { SidebarFavoritesMenu } from "@/components/workspace/sidebar/favorites/f import { cn } from "@/helpers/common.helper"; // hooks import { useAppTheme, useUserPermissions } from "@/hooks/store"; -// plane web components +import { useFavorite } from "@/hooks/store/use-favorite"; import useSize from "@/hooks/use-window-size"; +// plane web components import { SidebarAppSwitcher } from "@/plane-web/components/sidebar"; import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions"; @@ -25,6 +27,7 @@ export const AppSidebar: FC = observer(() => { // store hooks const { allowPermissions } = useUserPermissions(); const { toggleSidebar, sidebarCollapsed } = useAppTheme(); + const { groupedFavorites } = useFavorite(); const windowSize = useSize(); // refs const ref = useRef(null); @@ -48,6 +51,8 @@ export const AppSidebar: FC = observer(() => { // eslint-disable-next-line react-hooks/exhaustive-deps }, [windowSize]); + const isFavoriteEmpty = isEmpty(groupedFavorites); + return (
{ "opacity-0": !sidebarCollapsed, })} /> - {canPerformWorkspaceMemberActions && } + {canPerformWorkspaceMemberActions && !isFavoriteEmpty && }
diff --git a/web/core/components/workspace/sidebar/user-menu.tsx b/web/core/components/workspace/sidebar/user-menu.tsx index d9716a805b3..4ca6a7e860b 100644 --- a/web/core/components/workspace/sidebar/user-menu.tsx +++ b/web/core/components/workspace/sidebar/user-menu.tsx @@ -24,7 +24,7 @@ export const SidebarUserMenu = observer(() => { const { captureEvent } = useEventTracker(); const { isMobile } = usePlatformOS(); const { data: currentUser } = useUser(); - const { allowPermissions } = useUserPermissions(); + const { allowPermissions, workspaceUserInfo } = useUserPermissions(); // router params const { workspaceSlug } = useParams(); // pathname @@ -50,14 +50,17 @@ export const SidebarUserMenu = observer(() => { /> ); + const draftIssueCount = workspaceUserInfo[workspaceSlug.toString()]?.draft_issue_count; + return (
- {SIDEBAR_USER_MENU_ITEMS.map( - (link) => + {SIDEBAR_USER_MENU_ITEMS.map((link) => { + if (link.key === "drafts" && draftIssueCount === 0) return null; + return ( allowPermissions(link.access, EUserPermissionsLevel.WORKSPACE, workspaceSlug.toString()) && ( { ) - )} + ); + })}
); }); diff --git a/web/core/store/issue/workspace-draft/issue.store.ts b/web/core/store/issue/workspace-draft/issue.store.ts index 188ff70f94c..aef03d3411c 100644 --- a/web/core/store/issue/workspace-draft/issue.store.ts +++ b/web/core/store/issue/workspace-draft/issue.store.ts @@ -139,6 +139,13 @@ export class WorkspaceDraftIssues implements IWorkspaceDraftIssues { }); } + private updateWorkspaceUserDraftIssueCount(workspaceSlug: string, increment: number) { + const workspaceUserInfo = this.issueStore.rootStore.user.permission.workspaceUserInfo; + const currentCount = workspaceUserInfo[workspaceSlug]?.draft_issue_count ?? 0; + + set(workspaceUserInfo, [workspaceSlug, "draft_issue_count"], currentCount + increment); + } + // computed get issueIds() { const workspaceSlug = this.issueStore.workspaceSlug; @@ -259,6 +266,8 @@ export class WorkspaceDraftIssues implements IWorkspaceDraftIssues { total_count: this.paginationInfo.total_count + 1, }); } + // Update draft issue count in workspaceUserInfo + this.updateWorkspaceUserDraftIssueCount(workspaceSlug, 1); }); } @@ -310,6 +319,8 @@ export class WorkspaceDraftIssues implements IWorkspaceDraftIssues { total_count: this.paginationInfo.total_count - 1, }); } + // Update draft issue count in workspaceUserInfo + this.updateWorkspaceUserDraftIssueCount(workspaceSlug, -1); }); this.loader = undefined; @@ -337,6 +348,8 @@ export class WorkspaceDraftIssues implements IWorkspaceDraftIssues { total_count: this.paginationInfo.total_count - 1, }); } + // Update draft issue count in workspaceUserInfo + this.updateWorkspaceUserDraftIssueCount(workspaceSlug, -1); }); this.loader = undefined;