Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 4 commits into from
Oct 17, 2024

Conversation

anmolsinghbhatia
Copy link
Collaborator

@anmolsinghbhatia anmolsinghbhatia commented Oct 17, 2024

Changes:

This PR includes following changes:

  • The app sidebar will no longer display the "Draft" or "Favorite" options if there are no items available in those categories.

Reference:

[WEB-2625]

Summary by CodeRabbit

  • New Features

    • Added a draft_issue_count field to provide users with the count of draft issues associated with workspace members.
    • Enhanced the AppSidebar and SidebarUserMenu components to conditionally display draft-related information based on the draft issue count.
  • Bug Fixes

    • Improved logic to ensure accurate draft issue counts are reflected in the user interface.
  • Documentation

    • Updated interface definitions to include the new draft_issue_count property for better clarity on workspace member data.

@anmolsinghbhatia anmolsinghbhatia marked this pull request as ready for review October 17, 2024 10:09
Copy link
Contributor

coderabbitai bot commented Oct 17, 2024

Walkthrough

The changes in this pull request introduce a new read-only field, draft_issue_count, to the WorkspaceMemberMeSerializer class, enhancing the serialization of workspace member data. Additionally, modifications in the WorkspaceMemberUserEndpoint class allow the retrieval of this draft issue count through a subquery. Corresponding updates are made to the TypeScript interface, UI components, and state management to ensure consistency and accurate display of draft issue counts across the application.

Changes

File Path Change Summary
apiserver/plane/app/serializers/workspace.py Added draft_issue_count as a read-only integer field in WorkspaceMemberMeSerializer.
apiserver/plane/app/views/workspace/member.py Updated WorkspaceMemberUserEndpoint to include draft issue count via subquery; added new imports.
packages/types/src/workspace.d.ts Added draft_issue_count: number; to IWorkspaceMemberMe interface.
web/app/[workspaceSlug]/(projects)/sidebar.tsx Added isFavoriteEmpty constant to check if groupedFavorites is empty; modified rendering logic.
web/core/components/workspace/sidebar/user-menu.tsx Updated destructured return from useUserPermissions to include workspaceUserInfo; modified rendering logic for drafts menu item.
web/core/store/issue/workspace-draft/issue.store.ts Introduced updateWorkspaceUserDraftIssueCount method; updated related methods to maintain draft issue count.

Possibly related PRs

Suggested labels

🎨UI / UX, 🌟improvement

Suggested reviewers

  • sriramveeraghanta
  • SatishGandham

🐇 In the workspace where we play,
Draft issues count in a new way.
With serializers and queries so bright,
Our sidebar shines with data in sight!
Let's hop along, with joy we cheer,
For every draft, we hold so dear! 🐰✨


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Outside diff range and nitpick comments (6)
web/core/components/workspace/sidebar/user-menu.tsx (2)

53-54: LGTM: Added draftIssueCount extraction.

This change correctly extracts the draft issue count for the current workspace, which is essential for the new functionality to hide empty categories in the sidebar.

Consider adding a default value to handle cases where workspaceUserInfo[workspaceSlug.toString()] is undefined:

const draftIssueCount = workspaceUserInfo[workspaceSlug.toString()]?.draft_issue_count ?? 0;

This ensures draftIssueCount is always a number, simplifying the condition check later.


Line range hint 61-88: LGTM: Implemented conditional rendering for "drafts" menu item.

This change successfully implements the logic to hide the "Draft" option when there are no draft issues, which aligns with the PR objectives.

To improve code readability, consider extracting the condition into a separate variable:

{SIDEBAR_USER_MENU_ITEMS.map((link) => {
  const shouldRenderDrafts = link.key !== "drafts" || draftIssueCount > 0;
  
  if (!shouldRenderDrafts) return null;
  
  return (
    allowPermissions(link.access, EUserPermissionsLevel.WORKSPACE, workspaceSlug.toString()) && (
      // ... rest of the code
    )
  );
})}

This separation of concerns makes the condition more explicit and easier to understand at a glance.

web/app/[workspaceSlug]/(projects)/sidebar.tsx (2)

30-30: LGTM: Handling favorites state

The addition of groupedFavorites and isFavoriteEmpty is well-implemented. The use of the useFavorite hook and isEmpty function provides a robust way to manage and check the state of favorites.

Consider memoizing isFavoriteEmpty if groupedFavorites doesn't change frequently:

const isFavoriteEmpty = useMemo(() => isEmpty(groupedFavorites), [groupedFavorites]);

This optimization could prevent unnecessary re-renders if the component re-renders for reasons unrelated to favorites.

Also applies to: 54-55


99-99: LGTM: Improved rendering condition for SidebarFavoritesMenu

The modified rendering condition for SidebarFavoritesMenu correctly implements the PR objective. It ensures that the favorites menu is only displayed when there are favorites and the user has the necessary permissions.

For improved readability, consider extracting the condition into a descriptive variable:

const shouldRenderFavoritesMenu = canPerformWorkspaceMemberActions && !isFavoriteEmpty;

{shouldRenderFavoritesMenu && <SidebarFavoritesMenu />}

This change would make the code more self-documenting and easier to understand at a glance.

packages/types/src/workspace.d.ts (1)

94-94: LGTM! Consider adding JSDoc comment for clarity.

The addition of draft_issue_count to the IWorkspaceMemberMe interface is appropriate and aligns with the PR objectives. The property type (number) is suitable for representing a count.

For improved clarity, consider adding a JSDoc comment to describe the purpose of this property. For example:

/** The number of draft issues for the workspace member */
draft_issue_count: number;
apiserver/plane/app/serializers/workspace.py (1)

68-68: LGTM! Consider adding a docstring for clarity.

The addition of the draft_issue_count field as a read-only integer is a good approach to expose this information through the API. This aligns well with the PR objective of improving the display of draft information.

To improve code documentation, consider adding a brief docstring to explain the purpose of this field. For example:

class WorkspaceMemberMeSerializer(BaseSerializer):
    draft_issue_count = serializers.IntegerField(read_only=True)
    """
    The number of draft issues associated with the workspace member.
    This field is read-only and calculated on the server.
    """

This addition would help other developers understand the purpose and behavior of the field without needing to look at other parts of the codebase.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between b6a7e45 and 227e5aa.

📒 Files selected for processing (6)
  • apiserver/plane/app/serializers/workspace.py (1 hunks)
  • apiserver/plane/app/views/workspace/member.py (3 hunks)
  • packages/types/src/workspace.d.ts (1 hunks)
  • web/app/[workspaceSlug]/(projects)/sidebar.tsx (4 hunks)
  • web/core/components/workspace/sidebar/user-menu.tsx (3 hunks)
  • web/core/store/issue/workspace-draft/issue.store.ts (4 hunks)
🧰 Additional context used
🔇 Additional comments (9)
web/core/components/workspace/sidebar/user-menu.tsx (2)

27-27: LGTM: Added workspaceUserInfo to useUserPermissions hook.

This change allows access to workspace-specific user information, which is necessary for the new functionality to hide empty categories in the sidebar.


Line range hint 1-92: Overall assessment: Changes successfully implement the PR objectives.

The modifications to the SidebarUserMenu component effectively implement the requirement to hide the "Draft" option when there are no draft issues. The code is well-structured and consistent with React and MobX best practices.

A few minor suggestions have been provided to further improve code readability and robustness. These changes enhance the user experience by removing empty categories from the sidebar, as intended.

web/app/[workspaceSlug]/(projects)/sidebar.tsx (3)

2-2: LGTM: Importing isEmpty from lodash

The addition of isEmpty from lodash is a good choice for checking if an object or array is empty. It's more robust than simple length checks and handles various data types consistently.


20-20: LGTM: Importing useFavorite hook

The addition of the useFavorite hook import is appropriate and consistent with the existing import structure. This custom hook will likely provide the necessary state and actions for managing favorites in the sidebar.


Line range hint 1-107: Overall assessment: Well-implemented changes

The modifications to the AppSidebar component successfully implement the PR objective of not showing the "Favorite" option when there are no items in that category. The code is well-structured, and the changes are implemented correctly.

Key points:

  1. Proper use of isEmpty from lodash for robust empty checks.
  2. Correct implementation of the useFavorite hook.
  3. Improved rendering condition for the SidebarFavoritesMenu.

The suggestions provided for memoization and readability are minor optimizations that could further enhance the code quality.

packages/types/src/workspace.d.ts (1)

Line range hint 1-94: Summary: Appropriate type definition added for draft issue count

The addition of the draft_issue_count property to the IWorkspaceMemberMe interface is a focused and appropriate change. It aligns well with the PR objectives of improving workspace favorite and draft functionality. This change enhances the type definitions to support the new feature for tracking draft issue counts.

The modification is minimal and doesn't affect other parts of the file, maintaining the existing structure and consistency of the type definitions.

apiserver/plane/app/serializers/workspace.py (1)

68-68: Verify draft issue count calculation and consider performance implications.

While the addition of the draft_issue_count field is straightforward, it's important to ensure that the underlying implementation is correct and efficient.

Please run the following script to check for the implementation of the draft issue count calculation:

Consider the following points:

  1. Ensure that the calculation of draft_issue_count is efficient, especially if it's being computed for multiple workspace members simultaneously.
  2. If the calculation is expensive, consider implementing caching mechanisms or denormalization strategies to improve performance.
  3. Update any relevant API documentation to reflect this new field in the serializer output.
✅ Verification successful

Re-run verification scripts using file extensions instead of file types.

The previous scripts failed to recognize the python file type. Please use file extensions to search for the draft issue count implementation.


Re-run verification scripts with broader search patterns to locate draft_issue_count implementation.

The previous search did not yield results. Please use the following scripts to search for any occurrences of draft_issue_count and related terms to identify its implementation.


draft_issue_count is correctly implemented and integrated.

The draft_issue_count field is calculated using ORM annotations in apiserver/plane/app/views/workspace/member.py, ensuring it is accurately reflected in the serializer.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Look for the implementation of draft issue count calculation

# Search for functions or methods that might be calculating the draft issue count
rg -t python -g '!*/migrations/*' 'def .*draft.*issue.*count' ./apiserver

# Search for queries that might be related to counting draft issues
rg -t python -g '!*/migrations/*' 'filter.*draft.*count\(' ./apiserver

Length of output: 210


Script:

#!/bin/bash
# Description: Look for the implementation of draft issue count calculation using file extensions

# Search for functions or methods that might be calculating the draft issue count
rg -g '*.py' 'def .*draft.*issue.*count' ./apiserver

# Search for queries that might be related to counting draft issues
rg -g '*.py' 'filter.*draft.*count\(' ./apiserver

Length of output: 106


Script:

#!/bin/bash
# Description: Search for any occurrences of 'draft_issue_count' and related terms

# Search for the definition or usage of 'draft_issue_count'
rg -g '*.py' 'draft_issue_count' ./apiserver

# Search for related terms that might be involved in calculating draft issue counts
rg -g '*.py' 'draft_issues' ./apiserver

# Search for ORM annotations or aggregations related to draft issues
rg -g '*.py' 'annotate.*draft' ./apiserver

Length of output: 1789

web/core/store/issue/workspace-draft/issue.store.ts (2)

269-270: Correctly incrementing draft_issue_count in createIssue

The updateWorkspaceUserDraftIssueCount method is appropriately called to increment the draft_issue_count when a new draft issue is created.


322-323: Verify decrementing draft_issue_count in deleteIssue and moveIssue

In both deleteIssue and moveIssue, the draft_issue_count is decremented by 1. Ensure that this decrement is appropriate in all cases, and that the issue being deleted or moved was indeed counted towards the draft issues for the workspace. This helps maintain accurate counts.

Also applies to: 351-352

Comment on lines +291 to +310
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()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Simplify the query by removing unnecessary Subquery and OuterRef

Since you are fetching a single WorkspaceMember object for the current user and workspace, using a Subquery and OuterRef adds unnecessary complexity. You can simplify the code by directly querying the count of DraftIssue objects and assigning it to the WorkspaceMember instance.

Apply this diff to simplify the code:

 def get(self, request, slug):
-    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()
-    )
+    # Retrieve the workspace member
+    workspace_member = WorkspaceMember.objects.get(
+        member=request.user, workspace__slug=slug, is_active=True
+    )
+
+    # Count the draft issues
+    draft_issue_count = DraftIssue.objects.filter(
+        created_by=request.user,
+        workspace__slug=slug,
+    ).count()
+
+    # Assign the count to the instance
+    workspace_member.draft_issue_count = draft_issue_count
+
     serializer = WorkspaceMemberMeSerializer(workspace_member)
     return Response(serializer.data, status=status.HTTP_200_OK)

Note: Ensure that the WorkspaceMemberMeSerializer is updated to handle the draft_issue_count attribute appropriately. You might need to modify the serializer to include this field if it's not already defined.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
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()
# Retrieve the workspace member
workspace_member = WorkspaceMember.objects.get(
member=request.user, workspace__slug=slug, is_active=True
)
# Count the draft issues
draft_issue_count = DraftIssue.objects.filter(
created_by=request.user,
workspace__slug=slug,
).count()
# Assign the count to the instance
workspace_member.draft_issue_count = draft_issue_count
serializer = WorkspaceMemberMeSerializer(workspace_member)
return Response(serializer.data, status=status.HTTP_200_OK)

Comment on lines +142 to +148
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);
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Ensure draft_issue_count does not become negative

The updateWorkspaceUserDraftIssueCount method updates draft_issue_count by adding an increment, which could be negative. To prevent potential issues with negative counts, consider ensuring that draft_issue_count does not fall below zero.

Apply this diff to enforce a non-negative draft_issue_count:

private updateWorkspaceUserDraftIssueCount(workspaceSlug: string, increment: number) {
  const workspaceUserInfo = this.issueStore.rootStore.user.permission.workspaceUserInfo;
  const currentCount = workspaceUserInfo[workspaceSlug]?.draft_issue_count ?? 0;

+ const updatedCount = Math.max(0, currentCount + increment);
+ set(workspaceUserInfo, [workspaceSlug, "draft_issue_count"], updatedCount);
- set(workspaceUserInfo, [workspaceSlug, "draft_issue_count"], currentCount + increment);
}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
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);
}
private updateWorkspaceUserDraftIssueCount(workspaceSlug: string, increment: number) {
const workspaceUserInfo = this.issueStore.rootStore.user.permission.workspaceUserInfo;
const currentCount = workspaceUserInfo[workspaceSlug]?.draft_issue_count ?? 0;
const updatedCount = Math.max(0, currentCount + increment);
set(workspaceUserInfo, [workspaceSlug, "draft_issue_count"], updatedCount);
}

@pushya22 pushya22 merged commit a7b58e4 into preview Oct 17, 2024
22 of 23 checks passed
@pushya22 pushya22 deleted the chore-app-sidebar-item-improvements branch October 17, 2024 11:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants