[WEB-2625] chore: workspace favorite and draft improvement (#5855)

* chore: favorite empty state updated

* chore: added draft issue count in workspace members

* chore: workspace draft count improvement

---------

Co-authored-by: NarayanBavisetti <narayan3119@gmail.com>
This commit is contained in:
Anmol Singh Bhatia 2024-10-17 17:02:25 +05:30 committed by GitHub
parent d552913171
commit a7b58e4a93
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 55 additions and 10 deletions

View file

@ -65,6 +65,7 @@ class WorkSpaceMemberSerializer(DynamicBaseSerializer):
class WorkspaceMemberMeSerializer(BaseSerializer):
draft_issue_count = serializers.IntegerField(read_only=True)
class Meta:
model = WorkspaceMember
fields = "__all__"

View file

@ -3,7 +3,11 @@ from django.db.models import (
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 @@ from plane.db.models import (
User,
Workspace,
WorkspaceMember,
DraftIssue,
)
from plane.utils.cache import cache_response, invalidate_cache
@ -283,10 +288,26 @@ class WorkspaceMemberUserViewsEndpoint(BaseAPIView):
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)

View file

@ -91,6 +91,7 @@ export interface IWorkspaceMemberMe {
updated_by: string;
view_props: IWorkspaceViewProps;
workspace: string;
draft_issue_count: number;
}
export interface ILastActiveWorkspaceDetails {

View file

@ -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<HTMLDivElement>(null);
@ -48,6 +51,8 @@ export const AppSidebar: FC = observer(() => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [windowSize]);
const isFavoriteEmpty = isEmpty(groupedFavorites);
return (
<div
className={cn(
@ -91,7 +96,7 @@ export const AppSidebar: FC = observer(() => {
"opacity-0": !sidebarCollapsed,
})}
/>
{canPerformWorkspaceMemberActions && <SidebarFavoritesMenu />}
{canPerformWorkspaceMemberActions && !isFavoriteEmpty && <SidebarFavoritesMenu />}
<SidebarProjectsList />
</div>

View file

@ -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 (
<div
className={cn("flex flex-col gap-0.5", {
"space-y-0": sidebarCollapsed,
})}
>
{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()) && (
<Tooltip
key={link.key}
@ -81,7 +84,8 @@ export const SidebarUserMenu = observer(() => {
</Link>
</Tooltip>
)
)}
);
})}
</div>
);
});

View file

@ -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;