[WEB-5170] feat: navigation revamp (#8162)

This commit is contained in:
Anmol Singh Bhatia 2025-11-26 12:56:11 +05:30 committed by GitHub
parent 37c59ef0d1
commit 4806bdf99c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
110 changed files with 3789 additions and 766 deletions

View file

@ -14,6 +14,7 @@ from plane.app.views import (
ProjectPublicCoverImagesEndpoint,
UserProjectRolesEndpoint,
ProjectArchiveUnarchiveEndpoint,
ProjectMemberPreferenceEndpoint,
)
@ -125,4 +126,9 @@ urlpatterns = [
ProjectArchiveUnarchiveEndpoint.as_view(),
name="project-archive-unarchive",
),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/preferences/member/<uuid:member_id>/",
ProjectMemberPreferenceEndpoint.as_view(),
name="project-member-preference",
),
]

View file

@ -253,9 +253,4 @@ urlpatterns = [
WorkspaceUserPreferenceViewSet.as_view(),
name="workspace-user-preference",
),
path(
"workspaces/<str:slug>/sidebar-preferences/<str:key>/",
WorkspaceUserPreferenceViewSet.as_view(),
name="workspace-user-preference",
),
]

View file

@ -18,6 +18,7 @@ from .project.member import (
ProjectMemberViewSet,
ProjectMemberUserEndpoint,
UserProjectRolesEndpoint,
ProjectMemberPreferenceEndpoint,
)
from .user.base import (

View file

@ -300,3 +300,37 @@ class UserProjectRolesEndpoint(BaseAPIView):
project_members = {str(member["project_id"]): member["role"] for member in project_members}
return Response(project_members, status=status.HTTP_200_OK)
class ProjectMemberPreferenceEndpoint(BaseAPIView):
def get_project_member(self, slug, project_id, member_id):
return ProjectMember.objects.get(
project_id=project_id,
member_id=member_id,
workspace__slug=slug,
)
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST])
def patch(self, request, slug, project_id, member_id):
project_member = self.get_project_member(slug, project_id, member_id)
current_preferences = project_member.preferences or {}
current_preferences["navigation"] = request.data["navigation"]
project_member.preferences = current_preferences
project_member.save(update_fields=["preferences"])
return Response({"preferences": project_member.preferences}, status=status.HTTP_200_OK)
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST])
def get(self, request, slug, project_id, member_id):
project_member = self.get_project_member(slug, project_id, member_id)
response = {
"preferences": project_member.preferences,
"project_id": project_member.project_id,
"member_id": project_member.member_id,
"workspace_id": project_member.workspace_id,
}
return Response(response, status=status.HTTP_200_OK)

View file

@ -65,15 +65,23 @@ class WorkspaceUserPreferenceViewSet(BaseAPIView):
)
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST], level="WORKSPACE")
def patch(self, request, slug, key):
preference = WorkspaceUserPreference.objects.filter(key=key, workspace__slug=slug, user=request.user).first()
def patch(self, request, slug):
for data in request.data:
key = data.pop("key", None)
if not key:
continue
if preference:
serializer = WorkspaceUserPreferenceSerializer(preference, data=request.data, partial=True)
preference = WorkspaceUserPreference.objects.filter(key=key, workspace__slug=slug).first()
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)
if not preference:
continue
return Response({"detail": "Preference not found"}, status=status.HTTP_404_NOT_FOUND)
if "is_pinned" in data:
preference.is_pinned = data["is_pinned"]
if "sort_order" in data:
preference.sort_order = data["sort_order"]
preference.save(update_fields=["is_pinned", "sort_order"])
return Response({"message": "Successfully updated"}, status=status.HTTP_200_OK)

View file

@ -59,7 +59,7 @@ def get_default_props():
def get_default_preferences():
return {"pages": {"block_display": True}}
return {"pages": {"block_display": True}, "navigation": {"default_tab": "work_items", "hide_in_more_menu": []}}
class Project(BaseModel):

View file

@ -417,6 +417,7 @@ class WorkspaceUserPreference(BaseModel):
DRAFTS = "drafts", "Drafts"
YOUR_WORK = "your_work", "Your Work"
ARCHIVES = "archives", "Archives"
STICKIES = "stickies", "Stickies"
workspace = models.ForeignKey(
"db.Workspace",