-
Notifications
You must be signed in to change notification settings - Fork 3.6k
[WEB-1907] feat: Favorites Enhancements #5262
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
Changes from all commits
790e16e
9d1ce25
827e11f
9ee459f
1054263
d8f13b3
9454e4d
4077618
4e80865
db9be42
44bcec7
11dcce2
049b96e
d51953f
e1400da
eff77c3
c4f3f9a
e700071
c26c028
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,101 @@ | ||
| from rest_framework import serializers | ||
|
|
||
| from plane.db.models import ( | ||
| UserFavorite, | ||
| Cycle, | ||
| Module, | ||
| Issue, | ||
| IssueView, | ||
| Page, | ||
| Project, | ||
| ) | ||
|
|
||
|
|
||
| class ProjectFavoriteLiteSerializer(serializers.ModelSerializer): | ||
|
|
||
| class Meta: | ||
| model = Project | ||
| fields = ["id", "name", "logo_props"] | ||
|
|
||
|
|
||
| class PageFavoriteLiteSerializer(serializers.ModelSerializer): | ||
| project_id = serializers.SerializerMethodField() | ||
|
|
||
| class Meta: | ||
| model = Page | ||
| fields = ["id", "name", "logo_props", "project_id"] | ||
|
|
||
| def get_project_id(self, obj): | ||
| project = ( | ||
| obj.projects.first() | ||
| ) # This gets the first project related to the Page | ||
| return project.id if project else None | ||
|
|
||
|
|
||
| class CycleFavoriteLiteSerializer(serializers.ModelSerializer): | ||
|
|
||
| class Meta: | ||
| model = Cycle | ||
| fields = ["id", "name", "logo_props", "project_id"] | ||
|
|
||
|
|
||
| class ModuleFavoriteLiteSerializer(serializers.ModelSerializer): | ||
|
|
||
| class Meta: | ||
| model = Module | ||
| fields = ["id", "name", "logo_props", "project_id"] | ||
|
|
||
|
|
||
| class ViewFavoriteSerializer(serializers.ModelSerializer): | ||
|
|
||
| class Meta: | ||
| model = IssueView | ||
| fields = ["id", "name", "logo_props", "project_id"] | ||
|
|
||
|
|
||
| def get_entity_model_and_serializer(entity_type): | ||
| entity_map = { | ||
| "cycle": (Cycle, CycleFavoriteLiteSerializer), | ||
| "issue": (Issue, None), | ||
| "module": (Module, ModuleFavoriteLiteSerializer), | ||
| "view": (IssueView, ViewFavoriteSerializer), | ||
| "page": (Page, PageFavoriteLiteSerializer), | ||
| "project": (Project, ProjectFavoriteLiteSerializer), | ||
| "folder": (None, None), | ||
| } | ||
| return entity_map.get(entity_type, (None, None)) | ||
|
|
||
|
|
||
| class UserFavoriteSerializer(serializers.ModelSerializer): | ||
| entity_data = serializers.SerializerMethodField() | ||
|
|
||
| class Meta: | ||
| model = UserFavorite | ||
| fields = [ | ||
| "id", | ||
| "entity_type", | ||
| "entity_identifier", | ||
| "entity_data", | ||
| "name", | ||
| "is_folder", | ||
| "sequence", | ||
| "parent", | ||
| "workspace_id", | ||
| "project_id", | ||
| ] | ||
| read_only_fields = ["workspace", "created_by", "updated_by"] | ||
|
|
||
| def get_entity_data(self, obj): | ||
| entity_type = obj.entity_type | ||
| entity_identifier = obj.entity_identifier | ||
|
|
||
| entity_model, entity_serializer = get_entity_model_and_serializer( | ||
| entity_type | ||
| ) | ||
| if entity_model and entity_serializer: | ||
| try: | ||
| entity = entity_model.objects.get(pk=entity_identifier) | ||
| return entity_serializer(entity).data | ||
| except entity_model.DoesNotExist: | ||
| return None | ||
| return None |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -40,6 +40,11 @@ | |||||
| ExportWorkspaceUserActivityEndpoint, | ||||||
| ) | ||||||
|
|
||||||
| from .workspace.favorite import ( | ||||||
| WorkspaceFavoriteEndpoint, | ||||||
| WorkspaceFavoriteGroupEndpoint, | ||||||
|
Comment on lines
+44
to
+45
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove unused imports for The static analysis tool indicates that these imports are unused. Consider removing them to clean up the code. - WorkspaceFavoriteEndpoint,
- WorkspaceFavoriteGroupEndpoint,Committable suggestion
Suggested change
ToolsRuff
|
||||||
| ) | ||||||
|
|
||||||
| from .workspace.member import ( | ||||||
| WorkSpaceMemberViewSet, | ||||||
| TeamMemberViewSet, | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,88 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Third party modules | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from rest_framework import status | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from rest_framework.response import Response | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Django modules | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from django.db.models import Q | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Module imports | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from plane.app.views.base import BaseAPIView | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from plane.db.models import UserFavorite, Workspace | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from plane.app.serializers import UserFavoriteSerializer | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from plane.app.permissions import WorkspaceEntityPermission | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| class WorkspaceFavoriteEndpoint(BaseAPIView): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| permission_classes = [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| WorkspaceEntityPermission, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def get(self, request, slug): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # the second filter is to check if the user is a member of the project | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| favorites = UserFavorite.objects.filter( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| user=request.user, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| workspace__slug=slug, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| parent__isnull=True, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ).filter( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Q(project__isnull=True) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Q(project__isnull=False) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| & Q(project__project_projectmember__member=request.user) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| & Q(project__project_projectmember__is_active=True) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| serializer = UserFavoriteSerializer(favorites, many=True) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return Response(serializer.data, status=status.HTTP_200_OK) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+20
to
+35
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Simplify filter conditions in the The nested filter conditions can be simplified for better readability and performance. favorites = UserFavorite.objects.filter(
user=request.user,
workspace__slug=slug,
parent__isnull=True,
).filter(
Q(project__isnull=True) |
Q(
project__isnull=False,
project__project_projectmember__member=request.user,
project__project_projectmember__is_active=True
)
)Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def post(self, request, slug): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| workspace = Workspace.objects.get(slug=slug) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| serializer = UserFavoriteSerializer(data=request.data) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if serializer.is_valid(): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| serializer.save( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| user_id=request.user.id, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| workspace=workspace, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| project_id=request.data.get("project_id", None), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return Response(serializer.data, status=status.HTTP_200_OK) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+37
to
+47
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add error handling for The - workspace = Workspace.objects.get(slug=slug)
+ try:
+ workspace = Workspace.objects.get(slug=slug)
+ except Workspace.DoesNotExist:
+ return Response({"detail": "Workspace not found."}, status=status.HTTP_404_NOT_FOUND)Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def patch(self, request, slug, favorite_id): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| favorite = UserFavorite.objects.get( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| user=request.user, workspace__slug=slug, pk=favorite_id | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| serializer = UserFavoriteSerializer( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| favorite, data=request.data, partial=True | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if serializer.is_valid(): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| serializer.save() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return Response(serializer.data, status=status.HTTP_200_OK) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+49
to
+59
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add error handling for The - favorite = UserFavorite.objects.get(
- user=request.user, workspace__slug=slug, pk=favorite_id
- )
+ try:
+ favorite = UserFavorite.objects.get(
+ user=request.user, workspace__slug=slug, pk=favorite_id
+ )
+ except UserFavorite.DoesNotExist:
+ return Response({"detail": "Favorite not found."}, status=status.HTTP_404_NOT_FOUND)Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def delete(self, request, slug, favorite_id): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| favorite = UserFavorite.objects.get( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| user=request.user, workspace__slug=slug, pk=favorite_id | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| favorite.delete(soft=False) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return Response(status=status.HTTP_204_NO_CONTENT) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| class WorkspaceFavoriteGroupEndpoint(BaseAPIView): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| permission_classes = [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| WorkspaceEntityPermission, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def get(self, request, slug, favorite_id): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| favorites = UserFavorite.objects.filter( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| user=request.user, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| workspace__slug=slug, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| parent_id=favorite_id, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ).filter( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Q(project__isnull=True) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Q(project__isnull=False) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| & Q(project__project_projectmember__member=request.user) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| & Q(project__project_projectmember__is_active=True) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| serializer = UserFavoriteSerializer(favorites, many=True) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return Response(serializer.data, status=status.HTTP_200_OK) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+74
to
+88
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Simplify filter conditions in the The nested filter conditions can be simplified for better readability and performance. favorites = UserFavorite.objects.filter(
user=request.user,
workspace__slug=slug,
parent_id=favorite_id,
).filter(
Q(project__isnull=True) |
Q(
project__isnull=False,
project__project_projectmember__member=request.user,
project__project_projectmember__is_active=True
)
)Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| export type IFavorite = { | ||
| id: string; | ||
| name: string; | ||
| entity_type: string; | ||
| entity_data: { | ||
| name: string; | ||
| }; | ||
| is_folder: boolean; | ||
| sort_order: number; | ||
| parent: string | null; | ||
| entity_identifier?: string | null; | ||
| children: IFavorite[]; | ||
| project_id: string | null; | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| export * from "./favorite"; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| import * as React from "react"; | ||
|
|
||
| import { ISvgIcons } from "./type"; | ||
|
|
||
| export const FavoriteFolderIcon: React.FC<ISvgIcons> = ({ className = "text-current", color = "#a3a3a3", ...rest }) => ( | ||
| <svg | ||
| width="16" | ||
| height="16" | ||
| viewBox="0 0 16 16" | ||
| fill="none" | ||
| xmlns="http://www.w3.org/2000/svg" | ||
| stroke={color} | ||
| className={`${className} stroke-2`} | ||
| {...rest} | ||
| > | ||
| <path | ||
| d="M7.33325 13.3334H2.66659C2.31296 13.3334 1.97382 13.1929 1.72378 12.9429C1.47373 12.6928 1.33325 12.3537 1.33325 12.0001V3.3334C1.33325 2.97978 1.47373 2.64064 1.72378 2.39059C1.97382 2.14054 2.31296 2.00006 2.66659 2.00006H5.26659C5.48958 1.99788 5.70955 2.05166 5.90638 2.15648C6.10322 2.2613 6.27061 2.41381 6.39325 2.60006L6.93325 3.40006C7.05466 3.58442 7.21994 3.73574 7.41425 3.84047C7.60857 3.94519 7.82585 4.00003 8.04658 4.00006H13.3333C13.6869 4.00006 14.026 4.14054 14.2761 4.39059C14.5261 4.64064 14.6666 4.97978 14.6666 5.3334V6.3334" | ||
| // stroke="#60646C" | ||
| stroke-width="1.25" | ||
| stroke-linecap="round" | ||
| stroke-linejoin="round" | ||
| /> | ||
| <path | ||
| d="M12.1373 8L13.0038 9.75535L14.9414 10.0386L13.5394 11.4041L13.8702 13.3333L12.1373 12.422L10.4044 13.3333L10.7353 11.4041L9.33325 10.0386L11.2709 9.75535L12.1373 8Z" | ||
| stroke-width="1.25" | ||
| // stroke="#60646C" | ||
| stroke-linecap="round" | ||
| stroke-linejoin="round" | ||
| fill="none" | ||
| /> | ||
| </svg> | ||
| ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Address the unused import.
The
UserFavoriteSerializeris imported but not used in this file. If it is meant to be re-exported, consider adding it to__all__.Tools
Ruff
GitHub Check: Codacy Static Code Analysis