Skip to content

Commit

Permalink
[WEB-3038]feat: home preferences (#6308)
Browse files Browse the repository at this point in the history
* WIP

* WIP

* WIP

* WIP

* Create home preference if not exist

* chore: handled the unique state name validation (#6299)

* fix: changed the response structure (#6301)

* [WEB-1964]chore: cycles actions restructuring (#6298)

* chore: cycles quick actions restructuring

* chore: added additional actions to cycle list actions

* chore: cycle quick action structure

* chore: added additional actions to cycle list actions

* chore: added end cycle hook

* fix: updated end cycle export

---------

Co-authored-by: gurusinath <gurusainath007@gmail.com>

* fix: active cycle graph tooltip and endpoint validation (#6306)

* [WEB-2870]feat: language support (#6215)

* fix: adding language support package

* fix: language support implementation using mobx

* fix: adding more languages for support

* fix: profile settings translations

* feat: added language support for sidebar and user settings

* feat: added language support for deactivation modal

* fix: added project sync after transfer issues (#6200)

* code refactor and improvement (#6203)

* chore: package code refactoring

* chore: component restructuring and refactor

* chore: comment create improvement

* refactor: enhance workspace and project wrapper modularity (#6207)

* [WEB-2678]feat: added functionality to add labels directly from dropdown (#6211)

* enhancement:added functionality to add features directly from dropdown

* fix: fixed import order

* fix: fixed lint errors

* chore: added common component for project activity (#6212)

* chore: added common component for project activity

* fix: added enum

* fix: added enum for initiatives

* - Do not clear temp files that are locked. (#6214)

- Handle edge cases in sync workspace

* fix: labels empty state for drop down (#6216)

* refactor: remove cn helper function from the editor package (#6217)

* * feat: added language support to issue create modal in sidebar
* fix: project activity type

* * fix: added missing translations
* fix: modified translation for plurals

* fix: fixed spanish translation

* dev: language type error in space user profile types

* fix: type fixes

* chore: added alpha tag

---------

Co-authored-by: sriram veeraghanta <veeraghanta.sriram@gmail.com>
Co-authored-by: Anmol Singh Bhatia <121005188+anmolsinghbhatia@users.noreply.github.com>
Co-authored-by: Prateek Shourya <prateekshourya29@gmail.com>
Co-authored-by: Akshita Goyal <36129505+gakshita@users.noreply.github.com>
Co-authored-by: Satish Gandham <satish.iitg@gmail.com>
Co-authored-by: Aaryan Khandelwal <65252264+aaryan610@users.noreply.github.com>
Co-authored-by: gurusinath <gurusainath007@gmail.com>

* feat: introduced stacked bar chart and tree map chart. (#6305)

* feat: add issue attachment external endpoint (#6307)

* [PE-97] chore: re-order pages options (#6303)

* chore: re-order pages dropdown options

* chore: re-order pages dropdown options

* fix: remove localdb tracing

* [WEB-2937] feat: home recent activies list endpoint (#6295)

* Crud for wuick links

* Validate quick link existence

* Add custom method for destroy and retrieve

* Add List method

* Remove print statements

* List all the workspace quick links

* feat: endpoint to get recently active items

* Resolve conflicts

* Resolve conflicts

* Add filter to only list required entities

* Return required fields

* Add filter

* Add filter

* fix: remove emoji edit for uneditable pages (#6304)

* Removed duplicate imports

* feat: patch api

* Enable sort order to be updatable

* Return key name
only insert missing keys
use serializer to return data

* Remove random generation of sort_order

* Remove name field
Remove random generation of sort_order

---------

Co-authored-by: Bavisetti Narayan <72156168+NarayanBavisetti@users.noreply.github.com>
Co-authored-by: Vamsi Krishna <46787868+mathalav55@users.noreply.github.com>
Co-authored-by: gurusinath <gurusainath007@gmail.com>
Co-authored-by: Anmol Singh Bhatia <121005188+anmolsinghbhatia@users.noreply.github.com>
Co-authored-by: sriram veeraghanta <veeraghanta.sriram@gmail.com>
Co-authored-by: Prateek Shourya <prateekshourya29@gmail.com>
Co-authored-by: Akshita Goyal <36129505+gakshita@users.noreply.github.com>
Co-authored-by: Satish Gandham <satish.iitg@gmail.com>
Co-authored-by: Aaryan Khandelwal <65252264+aaryan610@users.noreply.github.com>
Co-authored-by: Nikhil <118773738+pablohashescobar@users.noreply.github.com>
  • Loading branch information
11 people authored Jan 6, 2025
1 parent fbbca0c commit 1acaae9
Show file tree
Hide file tree
Showing 6 changed files with 149 additions and 33 deletions.
3 changes: 2 additions & 1 deletion apiserver/plane/app/serializers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
WorkspaceMemberMeSerializer,
WorkspaceUserPropertiesSerializer,
WorkspaceUserLinkSerializer,
WorkspaceRecentVisitSerializer
WorkspaceRecentVisitSerializer,
WorkspaceHomePreferenceSerializer,
)
from .project import (
ProjectSerializer,
Expand Down
72 changes: 52 additions & 20 deletions apiserver/plane/app/serializers/workspace.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,19 @@
WorkspaceUserProperties,
WorkspaceUserLink,
UserRecentVisit,
Issue,
Page,
Issue,
Page,
Project,
ProjectMember
ProjectMember,
WorkspaceHomePreference,
)
from plane.utils.constants import RESTRICTED_WORKSPACE_SLUGS

# Django imports
from django.core.validators import URLValidator
from django.core.exceptions import ValidationError


class WorkSpaceSerializer(DynamicBaseSerializer):
owner = UserLiteSerializer(read_only=True)
total_members = serializers.IntegerField(read_only=True)
Expand Down Expand Up @@ -119,6 +121,7 @@ class Meta:
fields = "__all__"
read_only_fields = ["workspace", "user"]


class WorkspaceUserLinkSerializer(BaseSerializer):
class Meta:
model = WorkspaceUserLink
Expand All @@ -129,7 +132,7 @@ def to_internal_value(self, data):
url = data.get("url", "")
if url and not url.startswith(("http://", "https://")):
data["url"] = "http://" + url

return super().to_internal_value(data)

def validate_url(self, value):
Expand All @@ -141,74 +144,96 @@ def validate_url(self, value):

return value


class IssueRecentVisitSerializer(serializers.ModelSerializer):
project_identifier = serializers.SerializerMethodField()

class Meta:
model = Issue
fields = ["name", "state", "priority", "assignees", "type", "sequence_id", "project_id", "project_identifier"]
fields = [
"name",
"state",
"priority",
"assignees",
"type",
"sequence_id",
"project_id",
"project_identifier",
]

def get_project_identifier(self, obj):
project = obj.project

return project.identifier if project else None


class ProjectMemberSerializer(BaseSerializer):
member = UserLiteSerializer(read_only=True)

class Meta:
model = ProjectMember
fields = ["member"]


class ProjectRecentVisitSerializer(serializers.ModelSerializer):
project_members = serializers.SerializerMethodField()
project_members = serializers.SerializerMethodField()

class Meta:
model = Project
fields = ["id", "name", "logo_props", "project_members", "identifier"]

def get_project_members(self, obj):
members = ProjectMember.objects.filter(project_id=obj.id).select_related('member')
members = ProjectMember.objects.filter(project_id=obj.id).select_related(
"member"
)

serializer = ProjectMemberSerializer(members, many=True)
return serializer.data



class PageRecentVisitSerializer(serializers.ModelSerializer):
project_id = serializers.SerializerMethodField()
project_identifier = serializers.SerializerMethodField()

class Meta:
model = Page
fields = ["id", "name", "logo_props", "project_id", "owned_by", "project_identifier"]
fields = [
"id",
"name",
"logo_props",
"project_id",
"owned_by",
"project_identifier",
]

def get_project_id(self, obj):
return obj.project_id if hasattr(obj, 'project_id') else obj.projects.values_list('id', flat=True).first()

return (
obj.project_id
if hasattr(obj, "project_id")
else obj.projects.values_list("id", flat=True).first()
)

def get_project_identifier(self, obj):
project = obj.projects.first()

return project.identifier if project else None


def get_entity_model_and_serializer(entity_type):
entity_map = {
"issue": (Issue, IssueRecentVisitSerializer),
"page": (Page, PageRecentVisitSerializer),
"project": (Project, ProjectRecentVisitSerializer)
"project": (Project, ProjectRecentVisitSerializer),
}
return entity_map.get(entity_type, (None, None))


class WorkspaceRecentVisitSerializer(BaseSerializer):
entity_data = serializers.SerializerMethodField()

class Meta:
model = UserRecentVisit
fields = [
"id",
"entity_name",
"entity_identifier",
"entity_data",
"visited_at"
]
fields = ["id", "entity_name", "entity_identifier", "entity_data", "visited_at"]
read_only_fields = ["workspace", "owner", "created_by", "updated_by"]

def get_entity_data(self, obj):
Expand All @@ -225,3 +250,10 @@ def get_entity_data(self, obj):
except entity_model.DoesNotExist:
return None
return None


class WorkspaceHomePreferenceSerializer(BaseSerializer):
class Meta:
model = WorkspaceHomePreference
fields = ["key", "is_enabled", "sort_order"]
read_only_fields = ["worspace", "created_by", "update_by"]
33 changes: 21 additions & 12 deletions apiserver/plane/app/urls/workspace.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@
WorkspaceFavoriteGroupEndpoint,
WorkspaceDraftIssueViewSet,
QuickLinkViewSet,
UserRecentVisitViewSet
UserRecentVisitViewSet,
WorkspacePreferenceViewSet,
)


Expand Down Expand Up @@ -215,25 +216,33 @@
WorkspaceDraftIssueViewSet.as_view({"post": "create_draft_to_issue"}),
name="workspace-drafts-issues",
),

# quick link
path(
"workspaces/<str:slug>/quick-links/",
QuickLinkViewSet.as_view({"get": "list", "post": "create"}),
name="workspace-quick-links"
name="workspace-quick-links",
),
path(
"workspaces/<str:slug>/quick-links/<uuid:pk>/",
QuickLinkViewSet.as_view(
{"get": "retrieve", "patch": "partial_update", "delete": "destroy"}
),
name="workspace-quick-links",
),
# Widgets
path(
"workspaces/<str:slug>/home-preferences/",
WorkspacePreferenceViewSet.as_view(),
name="workspace-home-preference",
),
path(
"workspaces/<str:slug>/quick-links/<uuid:pk>/",
QuickLinkViewSet.as_view({
"get": "retrieve",
"patch": "partial_update",
"delete": "destroy"
}),
name="workspace-quick-links"
"workspaces/<str:slug>/home-preferences/<str:key>/",
WorkspacePreferenceViewSet.as_view(),
name="workspace-home-preference",
),
path(
"workspaces/<str:slug>/recent-visits/",
UserRecentVisitViewSet.as_view({"get": "list"}),
name="workspace-recent-visits"
)
name="workspace-recent-visits",
),
]
1 change: 1 addition & 0 deletions apiserver/plane/app/views/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@

from .workspace.draft import WorkspaceDraftIssueViewSet

from .workspace.preference import WorkspacePreferenceViewSet
from .workspace.favorite import (
WorkspaceFavoriteEndpoint,
WorkspaceFavoriteGroupEndpoint,
Expand Down
72 changes: 72 additions & 0 deletions apiserver/plane/app/views/workspace/preference.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Module imports
from ..base import BaseAPIView
from plane.db.models.workspace import WorkspaceHomePreference
from plane.app.permissions import allow_permission, ROLE
from plane.db.models import Workspace
from plane.app.serializers.workspace import WorkspaceHomePreferenceSerializer

# Third party imports
from rest_framework.response import Response
from rest_framework import status


class WorkspacePreferenceViewSet(BaseAPIView):
model = WorkspaceHomePreference

def get_serializer_class(self):
return WorkspaceHomePreferenceSerializer

@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST], level="WORKSPACE")
def get(self, request, slug):
workspace = Workspace.objects.get(slug=slug)

get_preference = WorkspaceHomePreference.objects.filter(
user=request.user, workspace_id=workspace.id
)

create_preference_keys = []

keys = [key for key, _ in WorkspaceHomePreference.HomeWidgetKeys.choices]

for preference in keys:
if preference not in get_preference.values_list("key", flat=True):
create_preference_keys.append(preference)

preference = WorkspaceHomePreference.objects.bulk_create(
[
WorkspaceHomePreference(
key=key, user=request.user, workspace=workspace
)
for key in create_preference_keys
],
batch_size=10,
ignore_conflicts=True,
)
preference = WorkspaceHomePreference.objects.filter(
user=request.user, workspace_id=workspace.id
)

return Response(
preference.values("key", "is_enabled", "config", "sort_order"),
status=status.HTTP_200_OK,
)

@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST], level="WORKSPACE")
def patch(self, request, slug, key):
preference = WorkspaceHomePreference.objects.filter(
key=key, workspace__slug=slug
).first()

if preference:
serializer = WorkspaceHomePreferenceSerializer(
preference, 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)

return Response(
{"detail": "Preference not found"}, status=status.HTTP_400_BAD_REQUEST
)
1 change: 1 addition & 0 deletions apiserver/plane/db/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
WorkspaceTheme,
WorkspaceUserProperties,
WorkspaceUserLink,
WorkspaceHomePreference
)

from .favorite import UserFavorite
Expand Down

0 comments on commit 1acaae9

Please sign in to comment.